-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
Description
Bug report
Bug description:
Hello,
I am using Python's Tcl interpreter roughly as suggested in https://wiki.tcl-lang.org/page/Python-Tcl-Interactions
import tkinter
interpreter = tkinter.Tcl()
interpreter.eval('puts "hello from Tcl"')
And everything works fine. However, this is a large Tcl legacy codebase, and some procedures are provided as shared objects that are loaded with Tcl's load
procedure:
load -global -- /path/to/shared_object.so myso
This works for some shared objects, but not all: some imports fail with
E _tkinter.TclError: couldn't load file "/path/to/shared_object.so": /path/to/shared_object.so: undefined symbol: Tcl_NewStringObj
I have reproduced this error by compiling a sample shared object and omitting -ltcl
when compiling it:
// Example inspired from https://www.tcl-lang.org/man/tcl/TclCmd/load.htm
// Compile with: gcc -shared -o tclext.so -fPIC tclext.c -ltcl
// load in tclsh with: load -- tclext.so Foo
// Then you can call the command foo
#include <tcl.h>
static int fooCmd(void *clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
// Returns a Tcl string "called with %d arguments\n"
Tcl_Obj *result = Tcl_NewStringObj("called with ", -1);
Tcl_AppendPrintfToObj(result, "%d", objc);
Tcl_AppendToObj(result, " arguments\n", -1);
Tcl_SetObjResult(interp, result);
return TCL_OK;
}
int Foo_Init(Tcl_Interp *interp) {
if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}
// Create foo command
Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
return TCL_OK;
}
In python, the above can be loaded just fine with:
import tkinter as tk
tcl = tk.Tcl()
tcl.eval('''
load ./tclext_str.so Foo
set abc [foo]
puts "abc contains ``$abc''"
''')
After compiling the .so with:
gcc -shared -o tclext_str.so -fPIC tclext_str.c -ltcl
However, Python fails to load the .so (with the aforementioned error) when it is compiled without -ltcl
:
gcc -shared -o tclext_str.so -fPIC tclext_str.c
The same load
statement works perfectly from tclsh
.
My suspicion is that tclsh implicitly links everything with the tcl library, while this is not the case for Python's Tcl interpreter. I think this should be fixed, if possible.
As a workaround, I can patchelf --add-needed libtcl.so tclext_str.so
but I am unsure about the drawbacks, and this difference in behavior remains problematic for sharing a unified codebase.
CPython versions tested on:
3.9, 3.10, 3.11, 3.12
Operating systems tested on:
Linux