[Cython] Bug with inlined functions that access globals
Erik Bray
erik.m.bray at gmail.com
Fri Nov 24 07:44:04 EST 2017
Hi,
I think maybe I've seen this brought up once or twice before in the
past, but with no real discussion. There is a slightly unpythonic
problem with using cpdef inline functions defined in a .pxd file that
happen to access global variables. For example:
$ cat foo.pxd
FOO_A = 1
cpdef inline foo():
return FOO_A
$ cat bar.pyx
from foo cimport foo
print(foo())
Running this like python -c 'import bar' results in:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "bar.pyx", line 3, in init bar
print(foo())
File "foo.pxd", line 4, in foo.foo
return FOO_A
NameError: name 'FOO_A' is not defined
Of course, if the function "foo" were not inlined this would work
fine. But the inlining really breaks things, I think, in an
unpythonic way. I could be misunderstanding how Cython's "inline"
should be interpreted but I feel like it's an implementation detail
that should not fundamentally change how the code in that function
works.
There are a couple issues here. First of all, if a .pxd file exists
but not a .pyx, no actual module will be created. This is by design
of course, but in this case either, inlined functions in a .pxd module
should be prohibited from accessing globals, or a trivial module
should be created to store the globals for that module.
The second problem boils down to the global variable lookup being
compiled to something like:
__pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_FOO_A)
Perhaps, when a function from one module gets inlined in another
module, at least in the case where it needs to access globals, the
originating module should be imported first and
__Pyx_GetModuleGlobalName replaced with an equivalent that uses the
originating module's globals.
So have a global variable like
static PyObject* __pyx_d_foo
and in the module init code something like:
_pyx_t_1 = __Pyx_Import("foo")
__pyx_d_foo = PyModule_GetDict(__pyx_t_1)
then in the inlined function, replace the call to
__Pyx_GetModuleGlobalName to an equivalent that uses __pyx_d_foo
instead of __pyx_d.
This all assumes, of course, that "foo" is an importable module. I'm
not sure this makes sense otherwise... If not then maybe inlining a
function should also "inline" any global variables it uses.
Thoughts?
Erik
More information about the cython-devel
mailing list