interp

interp , a built-in Tcl command, creates and manipulates Tcl interpreters.

See Also

island and firewall, by EF
A way to bring back some file access to safe interpreters, but only for some islands of the filesystem, or to let them access network resources, but only for some restricted hosts.
corp
Can be used to "ship" a procedure to another interpreter.
Dumping interpreter state
An attempt by RS to record the state of an interpreter.
fmail, by AK
Nicely employs slave interpreters.
exit in slave interpreter
Covers a specific trickiness in interp management.
Managing Complexity in TeamRooms, a Tcl-Based Internet Groupware Application , Mark Roseman, 1996
An article about TeamWave that explains specific interp benefits of encapsulation and security. In a conversation in mid-2001, Mark summarized, "But if you have what might be considered very coarse-grained objects that really could be conceptualized as self-contained applications, multiple Tcl interpreters are definitely an option to consider."
remote interpreter
Safe Interps
dgp: Nicely encapsulated the role of interp as to address "isolation and partition".
tkcon
An enhanced Tk console built in pure Tcl/Tk. It makes use of slave interpreters to isolate the user environment from the tkcon environment, except through certain special commands. It makes use of many tricks with interpreter slaves.
Windows focus and multiple proc , comp.lang.tcl, 2002-05-02
DKF illustrates that interps can serve as an elegant "light-weight" way to implement related proc-s in a unified fashion.

Syntax

interp option ?arg arg ...?
interp alias srcPath srcToken
interp alias srcPath srcToken {}
interp alias srcPath srcToken targetPath targetCmd ?arg arg ...?
interp aliases ?path?
interp bgerror path ?cmdPrefix?
interp cancel ?-unwind? ?- -? ?path? ?result?
interp create ?-safe? ?- -? ?path?
interp debug path ?-frame ?boolean??
interp delete ?path ...?
interp eval path arg ?arg ...?
interp exists path
interp expose path hiddenName ?exposedCmdName?
interp hidden path
interp hide path exposedCmdName ?hiddenCmdName?
interp invokehidden path ?-option ...? hiddenCmdName ?arg ...?
interp issafe ?path?
interp limit path limittype ?-option? ?value ...?
interp marktrusted path
interp recursionlimit path ?newlimit?
interp share srcPath channelId destPath
interp slaves ?path?
interp target path alias
interp transfer srcPath channelId destPath

Documentation

official reference

Description

Many newcomers to interps seem to (mis?)understand them as a variant on object orientation, in that they provide a kind of encapsulation, inheritance, and ...

The historical origin of interp was in "safe interpretation", that is, a mechanism for secure code evaluation. At a high level, this is what Java provides with its "sandbox", although far less flexibly and perhaps less securely.

Another occasionally hazardous way to think about interps is as a programming model for handling a very weak form of concurrency. Interps originated as "safe interpreters" in the agent work of such people as Marshall T. Rose, ...

[Explain insights from http://groups.google.com/groups?hl=en&frame=right&th=d0e2fc9300bdbad ]


Jeffrey Hobbs observes that, though several books mention interps, none really explains them adequately; they don't "go into enough detail for people to really understand what magic can be done. The power and flexibility of slave interpreters in Tcl is YA large advantage over other languages." (for more on Tcl's advantages, see "How Tcl is special")

Shared Resources

MGS 2006-09-11: I always thought that slave interps had their own working directory, but now I see that is not the case. Is there any way to prevent working directories in parent/child interps from being linked? Thanks.

DGP: No. The Tcl filesystem is process-wide. All interps in all threads see the same filesystem, including the same value of the current working directory.

Yes, this means that another thread can change the value of the current working directory right out from under you.

Yes, this means any use of relative pathnames is inherently thread unsafe. Write your programs accordingly.


MHo: The env array (and other system information) seems to be global and shared between all interpreters, too. Also I noticed, that there is only one global event loop. If you do, e.g., a tk_messageBox from within a slave interpreter, after events originating from the master interpreter are processed. This is because tk_messageBox is waiting for user input via the global event loop. The conclusion is, not only to avoid the usage of vwait, tkwait and update to not produce unwanted side effects by accidentely re-entering the eventloop, but not to use any tk dialog from within slaves. Or am I wrong?

Examples

Gary Petersen provides this on comp.lang.tcl as an example of using interp to create child interpreters and then using them.

#### interp create <path>
####
#### The path is not a path to a file--just a
####   string that indicates the nesting-level
####   of the interpreter. 

# Create a new interpreter named "a":
interp create a

# Create a new interpreter named "b" within interpreter "a":
interp create {a b}

# Create a new interpreter named "c" within interpreter "b":
interp create {a b c}

# Make the "c" interpreter do something:
interp eval {a b c} [list puts "Hello there."]

# This is another way to create a new interpreter
#   within an interpreter (named dionysus):
interp eval {a b c} [list interp create dionysus]

# Let's get "dionysus" to do something by invoking it by name. 
interp eval {a b c} \
 [list dionysus eval [list puts "Interpreter nesting works."]]

set mycommand {puts "Interpreter paths have *nothing* to do\
with the filesystem."}

interp eval {a b c dionysus} $mycommand

Note that the creation of the deeply nested set of interps was not required by the above example - the example is for illustration purposes rather than for functionality.


DKF: You can do some cool things with interp (and other Tcl commands) to do things like dynamic downloading of code over the 'net...

package require Tk
package require http
pack [frame .f -container true]
set i [interp create -safe]
::safe::loadTk $i -use [winfo id .f]
proc http_source {interp base filename} {
    regsub {.*:/*} $filename {} trimmed
    set token [http::geturl $base$trimmed]
    set script [http::data $token]
    http::cleanup $token
    $interp eval $script
}
$i alias source http_source $i $YourBaseUrl
$i expose exit;  # WE DON'T MIND PEOPLE QUITTING THE APP!
http_source $i $YourBaseUrl InitApp.tcl
vwait forever

RS has been playing with interps too:

% interp create foo
foo

By nature interps offer best encapsulation - their master can introspect them of course. On the other hand, they're probably more heavyweight than namespaces, and would have to repeat all package loading where required.

The above code probably needs polishing up... :^)

Coupling variables between interpreters

Maybe this belongs on it's own page but...

I've been playing around with sharing or linking variables between interpreters. I've got something that works in my very limited testing. I'll post it here because it may be useful to someone else and so I might get some feedback. -- CLN

# shareVar.tcl --
#
#     Allow sharing (or linking) of variables between interpreters.
#
#
# Copyright 2002  Pinebush Technologies, Inc.
#
# Exported procs:
#     sharevar::sharevar - Sets up link between interpreters
#
# Private procs:
#     sharevar::TraceVariable - Updates other interpreter when
#                               variable is written.
#
# Example:
#     # Create a slave interpreter
#     interp create a
#
#     # Share a variable (common name)
#     sharevar::sharevar {} foo a foo
#
#     # Create a namespace to play with.
#     namespace eval X {
#         variable X
#     }
#
#     # Share a variable (different name and namespace).
#     sharevar::sharevar {} X::X a x
#
#     # Create another slave
#     interp create b
#
#     # Share a variable between them
#     sharevar::sharevar a a b b
#
#
# Global data:
#     None.
#
#-----------------------------------------------------------------------

package provide sharevar 0.1

namespace eval ::sharevar {
    namespace export \
            sharevar
}

#=======================================================================
# Public procs
#=======================================================================
# sharevar::sharevar --
#
#     Make varName a shared variable in sourcePath and targetPath.
# 
# Arguments:
#     sourcePath - First interpreter to link
#     sourceVar  - Variable in sourcePath to link to
#     targetPath - Second interpreter to link
#     targetVar  - Variable in targetPath to link to
#
# Results:
#     Updates to sourceVar in sourcePath will change targetVar in targetPath
#     and vice versa.
#
#     If sourceVar exists, targetVar is set to its value.  If sourceVar
#     does not exist, and targetVar does, sourceVar is set to
#     targetVar's value.
#
proc sharevar::sharevar { sourcePath sourceVar targetPath targetVar} {
    foreach fromInterp [list $sourcePath $targetPath] \
            fromVar    [list $sourceVar $targetVar] \
            toInterp   [list $targetPath $sourcePath] \
            toVar      [list $targetVar $sourceVar] \
            {

        # Give the interpreter the sharing proc
        interp alias $fromInterp shareTraceVariable \
                {} [namespace code TraceVariable]

        # Use that command when the variable is written.
        set traceCmd [list shareTraceVariable \
                $fromInterp $fromVar $toInterp $toVar]
        interp eval $fromInterp \
                [list trace variable ::$fromVar w $traceCmd]
        # WUZ - need to clean up later, remove trace

    }

    # Initial synchronization
    if { [interp eval $sourcePath info exists $sourceVar]} {
        TraceVariable $sourcePath $sourceVar $targetPath $targetVar
    } elseif { [interp eval $targetPath info exists $targetVar]
    && ! [interp eval $sourcePath info exists $sourceVar]} {
        TraceVariable  $targetPath $targetVar $sourcePath $sourceVar
    } else {
        # Neither exists, first write will sync.
    }

}
# sharevar::sharevar
#
#=======================================================================
# Private procs only below this line
#=======================================================================
# sharevar::TraceVariable --
#
#     React to change in one interpreter by updating the other.
# 
# Arguments:
#     fromInterp - Interpreter where value changed
#     fromVar    - Variable in fromInterp which changed
#     toInterp   - Interpreter to update
#     toVar      - Variable in toInterp to update
#     args       - (IGNORED) Added by the variable trace
#
# Results:
#     toVar is updated
#
proc sharevar::TraceVariable { fromInterp fromVar toInterp toVar args } {
    puts stderr [info level 0]
    set value [interp eval $fromInterp set ::$fromVar]
    interp eval $toInterp [list set ::$toVar $value]
}
# sharevar::TraceVariable

Discussion

 suchenwi: Another OT: Why do windows have children, but interps have slaves?
 stever:   because slaves do work? children just play?

ken: Just want to enquire whether is it possible to use interp to create a slave interp which can execute on its own but suspend and awaiting for command for the main interp to input a variable to it before it continues. I trying to emulate a discrete event simulator model of sensor nodes which my event queue is running continuously to activate the node that should be awake at a single time then update a variable so thtat the slave interp emulating that node can run and then suspend and pass control back to the main interp to continue? Is it possible? Thanks in advance

Lars H: Not with interp-created interpreters, for the same C call stack reasons as prevented your tkwait attempts at this earlier. It might be possible using threads though.

PYK 2015-04-15: And in modern versions of Tcl, there's coroutine.


Perhaps someone here can shed light on an issue. When I enter the code as listed above

package require Tk
package require http
pack [frame .f -container true]
set i [interp create -safe]
::safe::loadTk $i -use [winfo id .f]
...and the remainder of the script...

and execute it I get the following error condition:

can't read "Sinterp0(access_path,n)": no such element in array
   while executing

with the remainder of the stack trace down to the call to safe::loadTk. I was able to load Tk into the slave using the mechanism elsewhere where you need to provide the argv list to make it work, but the safe:: package always errors. This is consistent on the Cygwin WISH and tclkit interpreters.

Any ideas?


TR It seems the correct way to get Tk in an interpreter is to do (taken from the Using Tk as a loadable package page, down at the bottom there):

interp create slave
load {} Tk slave

At least, using package require inside the slave interpreter, was not really working for me.

RFox 2012-10-01 12:18:22:

On Tcl 8.5.8/Tk 8.5.8

interp eval slave package require Tk

works just fine for me.

TR: Maybe this has to do with using an embedded build of Tcl (Tcl8.6b3 here from fossil trunk) on Mac. I call the embedded Wish from the Finder and just type the two lines of code and Wish will then crash with something like

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib                0x93f9b9c6 __pthread_kill + 10
1   libsystem_c.dylib                     0x93781f78 pthread_kill + 106
2   libsystem_c.dylib                     0x93772bdd abort + 167
3   Tcl                                   0x001d8e77 Tcl_PanicVA + 227
4   Tcl                                   0x001d8e92 Tcl_Panic + 27
5   Tcl                                   0x001bf518 Tcl_RegisterChannel + 108
6   Tk                                    0x0e66fed8 TkpInit + 1207
...

Actually, I can also do what you describe with Tk 8.5.12 (also embedded build) just fine. So, it could be an issue with Tk-Cocoa on Tk 8.6, but this is not for sure. So how is [interp eval slave package require Tk] different from load {} Tk slave? The first is executed inside the slave, the second is executed in the master interp?

See also: Tcl Commands, Arts and Crafts of Tcl-Tk Programming


mocallins - 2023-06-20 22:17:03

Having some trouble with the sharevar.tcl code.

bgerror failed to handle background error.invalid command name "::namespace inscope ::sharevar TraceVariable"
    invoked from within
"shareTraceVariable h3li H3LI::Reg(acclX) {} curr_x Reg acclX w"
    (write trace on "Reg(acclX)")
    invoked from within

I found a reference to this error stating that tcl 8.4 and 8.5 sort of covered it correctly. Now with 8.6 its failing. something to do with using namespace code * correctly. But for the life of me, i can't seem to find where to implement the fix. The fix seems to be using {*}, which delists the command, but i don't see how that applies in the context of "interp" and "interp alias"

I'd appreciate a look at it.