This is a Tcl extension to effortlessly to call bidirectionally between Tcl and Python, targeting Tcl >= 8.5 and Python 3.6+
The extension is available under the 3-clause BSD license (see "LICENSE").
Tcl users may also want to consider using pyman, a Tcl package that provides a higher level of abstraction on top of tclpy.
You can import the libtclpy in either a Tcl or Python parent interpreter. Doing so will initialise an interpreter for the other language and insert all libtclpy methods. This means you can call backwards and forwards between interpreters.
FROM TCL
General notes:
Reference:
py call ?obj.?func ?arg ...?
takes: name of a python function
returns: return value of function with the first appropriate conversion applied from the list below:
None is converted to an empty string
True is converted to 1
False is converted to 0
Python 'str' objects are considered to be byte arrays
Python 'unicode' objects are considered to be unicode strings
Python 'number' objects are converted to base 10 if necessary
Python mapping objects (supporting key-val mapping, e.g. python dicts) are converted to tcl dicts
Python sequence objects (supporting indexing, e.g. python lists) are converted to tcl lists
Otherwise, the str function is applied to the python object
side effects: executes function
func
may be a dot qualified name (i.e. object or module method)py eval evalString
takes: string of valid python code
returns: nothing
side effects: executes code in the python interpreter
evalString
may be any valid python code, including semicolons for single
line statements or (non-indented) multiline blockspy import module
takes: name of a python module
returns: nothing
side effects: imports named module into globals of the python interpreter
example tclsh session:
% load ./libtclpy.so
%
% py eval {def mk(dir): os.mkdir(dir)}
% py eval {def rm(dir): os.rmdir(dir); return 15}
% py import os
% set a [py eval {print "creating 'testdir'"; mk('testdir')}]
creating 'testdir'
% set b [py call rm testdir]
15
%
% py import StringIO
% py eval {sio = StringIO.StringIO()}
% py call sio.write someinput
% set c [py call sio.getvalue]
someinput
%
% py eval {divide = lambda x: 1.0/int(x)}
% set d [py call divide 16]
0.0625
% list [catch {py call divide 0} err] $err
1 {ZeroDivisionError: float division by zero
File "<string>", line 1, in <lambda>
----- tcl -> python interface -----}
%
% py import json
% py eval {
def jobj(*args):
d = {}
for i in range(len(args)/2):
d[args[2*i]] = args[2*i+1]
return json.dumps(d)
}
% set e [dict create]
% dict set e {t"est} "11{24"
t\"est 11\{24
% dict set e 6 5
t\"est 11\{24 6 5
% set e [py call jobj {*}$e]
{"t\"est": "11{24", "6": "5"}
%
% py import sqlite3
% py eval {b = sqlite3.connect(":memory:").cursor()}
% py eval {def exe(sql, *args): b.execute(sql, args)}
% py call exe "create table x(y integer, z integer)"
% py call exe "insert into x values (?,?)" 1 5
% py call exe "insert into x values (?,?)" 7 9
% py call exe "select avg(y), min(z) from x"
% py call b.fetchone
4.0 5
% py call exe "select * from x"
% set f [py call b.fetchall]
{1 5} {7 9}
%
% puts "a: $a, b: $b, c: $c, d: $d, e: $e, f: $f"
a: , b: 15, c: someinput, d: 0.0625, e: {"t\"est": "11{24", "6": "5"}, f: {1 5} {7 9}
FROM PYTHON
Reference:
tclpy.eval(evalstring)
takes: string of valid Tcl code
returns: the final return value
side effects: executes code in the Tcl interpreter
evalString
may be any valid Tcl code, including semicolons for single
line statements or multiline blocksexample python session:
>>> import tclpy
>>> a = tclpy.eval('list 1 [list 2 4 5] 3')
>>> print a
1 {2 4 5} 3
It is assumed that you
git clone
or a tar.gz from the releases page).The build process fairly simple:
make
and gcc
are installed.python-config
and have the Python headers available
(usually installed by the Python development package for your distro).make
/usr/lib/tclConfig.sh
(TCLCONFIG=/path/to/tclConfig.sh
).TCL_STUBS=0
). Note this then requires compilation per Tcl interpreter.On Ubuntu the default tclConfig.sh path is correct:
$ sudo apt-get install -y python-dev tcl-dev
$ cd libtclpy
$ make
For other distros you may need give the path of tclConfig.sh. E.g. CentOS 6.5:
$ sudo yum install -y python-devel tcl-devel make gcc
$ cd libtclpy
$ make TCLCONFIG=/usr/lib64/tclConfig.sh
Now try it out:
$ TCLLIBPATH=. tclsh
% package require tclpy
0.3
% py import random
% py call random.random
0.507094977417
Run the tests with
$ make test
py eval
call - they are decoded by the tcl parser and passed as literal bytes
to the python interpreter. So if we directly have the character "ಠ", it is
decoded to a utf-8 byte sequence and becomes u"\xe0\xb2\xa0" (where the \xXY are
literal bytes) as seen by the Python interpreter.\x00
) inside py eval may be interpreted by tcl - use
{} quoting to avoid this.In order of priority:
py call -types [list t1 ...] func ?arg ...? : ?t1 ...? -> multi
(polymorphic args, polymorphic return)py eval
work with indented multiline blockspy import ?-from module? module : -> nil
py call
look in the builtins module - http://stackoverflow.com/a/11181607