Requiring packages - tcl

using init.tcl we have refined the clock command for international use. This works fine until I attempt to initialize a variable within a package with the clock command. A simple package like:
package provide mylib [lindex {Revision: 1.0 } 1]
namespace eval mylib {
set X [clock format [clock seconds] -format %y%m%d]
proc naught {} {
variable X
puts $X
return
}
}
When I run pkg_mkIndex I get:
warning: error while sourcing mylib.pkg: unknown namespace in import pattern "::msgcat::mcload"
The revised clock command - clock.tcl - uses the msgcat package like:
uplevel #0 {
package require msgcat 1.6.1
If I add a puts like puts "PKG: [package require msgcat 1.6.1]" it return empty string not version
Of course clock.tcl is called from init.tcl
I note in the pkg_mkIndex code the following:
$c eval {
# Stub out the package command so packages can require other
# packages.
rename package __package_orig
proc package {what args} {
switch -- $what {
require {
return; # Ignore transitive requires
}
default {
__package_orig $what {*}$args
}
}
}
Which would explain why the msgcat package is not loaded.
How would I load the msgcat package from the clock.tcl procedure?

All your pkgIndex.tcl really needs is a line like this, which you can write by hand (or derive from a template trivially):
package ifneeded mylib 1.0 [list source [file join $dir mylib.tcl]]
That's assuming that you want your package to use immediate loading. (I always want that for my packages.) You only need to use the tooling to generate an index file if you are using lazy loading, and I don't really recommend that on a package or application of any complexity, as it can occasionally trigger weird bugs. Modern computers are fast enough to not need lazy loading (and have been so in my experience for 20 years or more).

Related

TCL how protect from override?

I have 2 scripts
listing file 1.mac:
proc setByUpvar { value } {
return [expr {$value *2}]
}
set ex_result [catch {
set c [setByUpvar 12 ]
echo "$c [argument -this]"
sym run macro -file C:/Custom/2.mac
echo "$c [argument -this]"
} errors]
listing file 2.mac:
proc setByUpvar { value } {
return [expr {$value *2}]
}
set ex_result [catch {
set c [setByUpvar 4]
echo "$c [argument -this]"
} errors]
first macro calls second macro
after run file 2.mac, in 1.mac variable $c has value from 2.mac
How protect value $c in 1.mac from overriding in 2.mac?
changing variable name in 2.mac not good for me
Tcl normally doesn't protect you against that. Script files are not normally isolated from each other. By policy/design.
For light isolation, you can put the code in the script files inside a namespace eval:
namespace eval ::mac1 {
proc setByUpvar { value } {
return [expr {$value *2}]
}
# Setting variables directly at the namespace-eval level is a bit tricky so avoid that
proc task {} {
variable ex_result
variable errors
variable c
set ex_result [catch {
set c [setByUpvar 12 ]
echo "$c [argument -this]"
sym run macro -file C:/Custom/2.mac
echo "$c [argument -this]"
} errors]
}
task
}
In this case, the code can see the other variables, but not by default. It has to go looking elsewhere.
For strong isolation, create an interpreter (with interp create) and source the script within it. Interpreters can't reach other interpreters (except under very defined circumstances that don't matter here) so the code really can't have any impact on the other script. However, you might lose access to special commands and so on; isolation really is isolation. And this is a comparatively expensive option. (Not as expensive as starting another process.)
set isolated [interp create]
interp eval $isolated [list source 1.mac]
interp delete $isolated
You can strengthen the isolation by making the interpreter be safe (disables OS access) and you can weaken it by poking callback holes with interp alias to provide the special commands you actually want.
proc myFoo args {
puts "this is a callback in the master interpreter: arguments=$args"
}
interp alias $isolated foo {} myFoo
How any of this will interact with the non-standard Tcl commands you're running (sym, argument) is a complete unknown to me. Standard commands are known, as are procedures using them.

After importing procs from a namespace with force option, the procs in the global namespace are used by the auto_load in certain conditions

I've been working on a project where scripts are written for a single hardware (say Type A) and have lot of procs very specific for that particular hardware. And now when I'm trying use the same scripts for a new hardware (say Type B) I thought of the following approach,
Re-define all the procs in a new library file within a new namespace.
When the main script starts check the hardware type.
a. If type A do nothing. The procs from the global namespace (legacy) gets used
b. If type B do a "namespace import -force" from the library to the global namespace.
The problem here is, the project uses auto_load so when I'm importing from the new library the legacy procs are not in the context yet. When a proc that is common to both the hardwares is called and if the file containing this proc also has another proc that was redefined for Type B, then that proc gets overwritten by the global namespace proc (legacy Type A) even though I have done a force import earlier.
If the wordings are difficult to follow, refer the below sample code and it's output. If the dummy proc is moved from TypeB.tcl to a new file, then there is no issue.
[wizard # work]$ cat main.tcl
#!/usr/bin/tclsh
auto_mkindex ./
set auto_path "$auto_path ./"
namespace import -force TypeB::*
print_name
dummy
print_name
[wizard # work]$ cat TypeA.tcl
proc print_name { } {
puts "From the TypeA"
}
proc dummy {} {
puts "Do nothing"
}
[wizard # work]$ cat TypeB.tcl
namespace eval TypeB {
namespace export *
proc print_name {} {
puts "From the TypeB"
}
}
[wizard # work]$ ./main.tcl
From the TypeB
Do nothing
From the TypeA
[wizard # work]$
Consider what auto_mkindex does: it creates a tclIndex file that contains
set auto_index(print_name) [list source [file join $dir TypeA.tcl]]
set auto_index(dummy) [list source [file join $dir TypeA.tcl]]
set auto_index(::TypeB::print_name) [list source [file join $dir TypeB.tcl]]
When you namespace import TypeB::*, then tcl executes source [file join $dir TypeB.tcl] and the print_name proc is pulled into the global namespace.
What happens when you call dummy? this: source [file join $dir TypeA.tcl] -- that pulls the dummy proc into the global namespace. But at the same time, the print_name proc is redefined.
At this point, you can still call TypeB::print_name
You'll have to do one of:
restructure your code,
re-execute the namespace import TypeB::*, or
be very careful about the order in which you call things, for instance by calling dummy first.

How to get name of TCL test from the test itself

I was wondering how you would find the name of the test you're running in tcl from the test itself? I couldn't find this on google.
I'm calling another proc and passing the name of the test that is calling it, as an argument. So I would like to know which tcl command can do that for me.
This isn't an encouraged use case… but you can use info frame 1 to get the information if you use it directly inside the test.
proc example {contextTest} {
puts "Called from $contextTest"
return ok
}
tcltest::test foo-1.1 {testing the foo} {
example [lindex [dict get [info frame 1] cmd] 1]
} ok
This assumes that you're using Tcl 8.5 or later, but Tcl 8.5 is the oldest currently-supported Tcl version so that's a reasonable restriction.
I read your comments ("source ... instade of my test name") as follows: You seem to source the Tcl script file containing the tests (and Donal's instrumented tcltest), rather than batch-running the script from the command line: tclsh /path/to/your/file.tcl In this setting, there will be an extra ("eval") stack frame which distorts introspection.
To turn Donal's instrumentation more robust, I suggest actually walking the Tcl stack and watching out for a valid tcltest frame. This could look as follows:
package req tcltest
proc example {} {
for {set i 1} {$i<=[info frame]} {incr i} {
set frameInfo [info frame $i]
set frameType [dict get $frameInfo type]
set cmd [dict get $frameInfo cmd]
if {$frameType eq "source" && [lindex $cmd 0] eq "tcltest::test"} {
puts "Called from [lindex $cmd 1]"
return ok
}
}
return notok
}
tcltest::test foo-1.1 {testing the foo} {
example
} ok
This will return "Called from foo-1.1" both, when called as:
$ tclsh test.tcl
Called from foo-1.1
and
$ tclsh
% source test.tcl
Called from foo-1.1
% exit
The Tcl version used (8.5, 8.6) is not relevant. However, your are advised to upgrade to 8.6, 8.5 has reached its end of life.

Is there any Tcl package/add-on that handles named arguments?

In Python, Ruby 2.0, Perl 6, and some hardware description languages, one can use named arguments. See this example. This makes the code more readable, easy to maintain, etc. Is there a way of getting it done/extension, in TCL 8.6, other than using a dictionary as a workaround?
In 8.6, use a dictionary parsed from args. The dict merge command can help:
proc example args {
if {[llength $args] % 2} {
return -code error "wrong # args: should be \"example ?-abc abc? ?-def def?\""
}
set defaults {
-abc 123
-def 456
}
set args [dict merge $defaults $args]
set abc [dict get $args -abc]
set def [dict get $args -def]
puts "abc=$abc, def=$def"
}
example; # abc=123, def=456
example -abc 789; # abc=789, def=456
example -def 789; # abc=123, def=789
example -def 246 -abc 135; # abc=135, def=246
You can go further than that with verifying (the tcl::prefix command can help) but it's a lot more work and doesn't buy you a lot more in production code. Not that that has stopped people from trying.
There are two proposals to add full named argument handling
(TIP #457, TIP #479) to 8.7 at the moment, but I'm not sure that either have really gained traction. (The problem from my perspective is the extra runtime cost that has to be borne by code that doesn't volunteer to support named arguments. There might be other issues too, such as disagreement over preferred syntax; I've not paid so much attention to that as I'm still fretting over the performance implications in a pretty hot piece of code.)
There is an entire page on the tcler's wiki that discusses named arguments: http://wiki.tcl.tk/10702
You can do it yourself with a little creativity. There are several mechanisms that allow you to do this:
procs can define other procs
proc behave just like a proc (the function definition system is not a syntax, it is a function call)
procs can use the args argument instead of positional parameter and manually process the list of arguments
you can execute code in any parent stack frame using uplevel
you can pull variables from any parent stack frame using upvar
everything is a string!
etc.
I'm not sure I've listed all the possible mechanisms.
My personal implementation of this idea is optproc: http://wiki.tcl.tk/20066
The implementation itself is quite simple:
proc optproc {name args script} {
proc $name args [
string map [list ARGS $args SCRIPT $script] {
foreach var {ARGS} {
set [lindex $var 0] [lindex $var 1]
}
foreach {var val} $args {
set [string trim $var -] $val
}
SCRIPT
}
]
}
I basically used string manipulation (string map) to directly insert the function body ($script) into the defined function without any substitutions etc. This is to avoid any $ or [] from being processed. There are many ways to do this but my go-to tool is string map.
This is similar to Donald's answer except for two things:
I don't transform args into a dict instead I manually process args and declare each local variable in a loop.
This is a meta solution. Instead of processing args I created another function (syntax) to create a function that processes args.
Usage (stolen from Donald's answer) would be:
optproc example {abc def} {
puts "abc=$abc, def=$def"
}
But note that my solution is not the only solution. There are many ways to do this limited only by creativity.

How can I callback Tcl commands from different namespaces without explicitly qualifying?

Basically the problem is to execute Tcl code defined in one namespace, using calls to functions in that namespace, in an eval inside another namespace.
The following code works fine:
namespace eval ::eggs {
namespace export e1 eeval
proc e1 {} { puts pe1 }
proc eeval body { puts "in cb enter"; eval $body; puts "in cb exit" }
}
namespace import ::eggs::*
namespace eval ::spam {
namespace export s1 scall
proc scb {} { puts pscb }
proc scall {} { puts "in call enter"; eeval {::spam::scb}; puts "in call exit" }
e1
scall
}
namespace import ::spam::*
and prints:
% % % % % % pe1
in call enter
in cb enter
pscb
in cb exit
in call exit
Now I want to replace the ::spam::scb with a plain scb. I would trade in a wrapper for eeval.
My use case:
The namespace eggs is a very basic library for regression testing. The namespace spam is a small library implementing nice to have functions. They shall be tested upon reload. For this scall is called and uses a special test function in eggs called eeval.
The eeval is the unit test. For convenience and "do-not-repeat-yourself" reasons I'd like to not have to use the fully qualified namespace name of any function defined in spam.
Ideally scall would look like this:
proc scall {} { puts "in call enter"; eeval {scb}; puts "in call exit" }
However, during execution the eval in ::eggs::eeval does not know where to find scb. Since eggs is just a test library I cannot import the spam namespace.
Is there any way one could e.g. devise a wrapper for e.g. eeval to make it run in the other namespace?
If you're going to pass the code to eval (possibly appending arguments) then the simplest method to generate the callback script is with namespace code:
eeval [namespace code {scb}]
That generates all the wrapping code to ensure that things work correctly, including handling all sorts of cases you've not dealt with:
% namespace eval boo {
puts [namespace code {foo bar}]
}
::namespace inscope ::boo {foo bar}
However, if you're doing the callback immediately then the most common approach is not the above, but rather to use uplevel 1 to do the callback:
proc eeval body {
puts "in cb enter"
uplevel 1 $body
puts "in cb exit"
}
This has the advantage that the called-back code really is in the stack context of the caller, allowing it to do useful things like access local variables. Since the code is running in the same context that you define it[*], it's fine using that for resolving command names.
[*] That's a big fat lie — Tcl's semantics are rather more complex than that — but everything works like what I've said is true.
namespace current is the DRYer you're probably looking for.
namespace eval ::spam {
proc scall {} {
puts {in call 2 enter}
eeval [namespace current]::scb
puts {in call 2 exit}
}
}
Or pass the current namespace to the eeval proc
proc ::eggs::eeval {body {namespace ""}} {
puts "in cb enter"
if {$namespace == ""} {
eval $body
} else {
namespace eval $namespace $body
}
puts "in cb exit"
}
proc ::spam::scall {} {
puts "in call 3 enter"
eeval scb [namespace current]
puts "in call 3 exit"
}