Using CriTcl to build a Python extension ======================================== Rev 0.12: Moved Tcl lib path to Python, no longer hard-wired Rev 0.11: Added logic to transfer data and do calls both ways Rev 0.10: Initial release Typcl is a bit weird... It's a an extension to use Tcl *from* Python. It doesn't really require CriTcl and could have been done in standard C. Nevertheless, CriTcl makes it relatively easy to build the Typcl module, because there isn't any need to have Tcl headers or libraries around to build it. Then again, to run this you will need libtcl8.*.so - and also normally all runtime files plus Tk, etc. This code demonstrates using Tcl as shared library, and hooking into it at run time (Tcl's stubs architecture makes this delightfully simple). Furthermore, Typcl avoids string conversions where possible (both ways). Building Typcl requires a few things: - tclsh (probably needs 8.3 or later), to run the typcl.tcl script - the CriTcl package, in a place where tclsh will find it - a working setup of gcc (which CriTcl will launch) - Python headers (hardwired as /home/builds/Python-2.1.1 for now) Running Typcl requires some more things: - a Python interpreter (doh!) - Tcl as shared lib (e.g. /usr/lib/libtcl8.3.so) - if you want full Tcl: the Tcl runtime scripts, notably "init.tcl" - if you want Tk as well: the Tk library as loadable extension To build: - edit the "typcl.tcl" script to set the Python include paths right - create the beast, by doing "tclsh typcl.tcl" - the result is "typcl.so", this is a Python loadable module To try it out, do "python typcl.py" (note: it too has hardwired paths). As you will see, there is enough machinery here to run Tk scripts. The Typcl module implements a number of Python methods: typcl.config('path/to/libtcl8.?.so') this must be called first to make linkage work tcl = typcl.interp() returns a fresh Tcl interpreter object tcl.init() initialize the Tcl runtime, i.e. call Tcl_Init() print tcl.eval('info patchlevel') evaluate a script, and return the result print tcl.call('info', 'patchlevel') perform a call, passing arguments individually What the above does not show, is that the result is an object (which has a __str__, hence "print" does the right thing). Supported conversions: tcl.eval('expr {123+456}').s converts to a string (same as str()) tcl.eval('expr {123+456}').i converts to an int tcl.eval('expr {1.23+456}').f converts to a float tcl.eval('list 123 456').v converts to a vector, i.e. a Python list tcl.eval('list 123 456').v[0].i this shows that vector conversion is nested Lastly, there is a "link" method which lets Tcl call back into Python. It creates a command in the Tcl context which can call a Python object. The following example shows how to fetch Python info from Tcl: def fetch(key): return globals()[key.s] tcl.link('pyfetch', fetch) tcl.eval('puts "called from: [pyfetch __name__]"') Typcl is still young. Issues such as properly dealing with events and releasing the Python lock, as well as Unicode, have not been tackled.