mwiebe
mwiebe

Reputation: 275

How do I control the module/name of a cython cdef class?

I'm using cython to expose a C++ library to python, by putting all the wrapper objects and functions in an internal module _pydynd, then exposing these through a different python module.
I would like to control the name of the module and class that appear in these extension classes, to look like dynd.nd.array, for example, instead of _pydynd.w_array, which is the internal name of the wrapper class. Does cython have a mechanism to do this?

I hoped to find something similar to how you can rename C/C++ functions when writing their definitions, but my searches have come up dry. The generated C++ code which should be different is the tp_name line here:

static PyTypeObject __pyx_type_7_pydynd_w_array = {
  PyVarObject_HEAD_INIT(0, 0)
  __Pyx_NAMESTR("_pydynd.w_array"), /*tp_name*/
  sizeof(struct __pyx_obj_7_pydynd_w_array), /*tp_basicsize*/

UPDATE:

If I try to directly rename the objects, here is what happens:

In [103]: nd.array.__name__
Out[103]: 'w_array'

In [104]: nd.array.__module__
Out[104]: 'dynd._pydynd'

In [105]: nd.array.__name__ = "array"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-105-73d9172a0216> in <module>()
----> 1 nd.array.__name__ = "array"

TypeError: can't set attributes of built-in/extension type 'dynd._pydynd.w_array'

In [106]: nd.array.__module__ = "dynd.nd"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-106-37f990658ede> in <module>()
----> 1 nd.array.__module__ = "dynd.nd"

TypeError: can't set attributes of built-in/extension type 'dynd._pydynd.w_array'

Upvotes: 9

Views: 1556

Answers (2)

FDS
FDS

Reputation: 5387

Cython-0.21 seems to use the location of your .pyx file relative to an __init__.py file to set the module name.

For file layout

jmap/__init__.py
jmap/client.pyx

the output of cython's compiler includes

#define __Pyx_MODULE_NAME "jmap.client"

moving the location of __init__.py changes this value. This information may be in the Cython documentation, but I could not find it.

DISCUSSION

This is important to get right to e.g. correctly pickle classes. Specifically a module's concept of its name should be symmetrical with how you import it. This seems obvious, but is easy to break in cython.

Good

>>> from jmap.client import JMapError
>>> JMapError
<class 'jmap.client.JMapError'>

Bad

>>> import jmap
>>> jmap.JMapError
<class 'Client.client.JMapError'>

Upvotes: 2

Sven
Sven

Reputation: 720

I have a partly solution for your problem, you can move or should we say better import things to a dedicated namespace in your __ init__.py file.

Lets start, you have a lib, which has the following things:

module.libname.A (A is a class for e.g. or anything else)
module.libname.B
...

Now I created my own lib out of this by copying the tings over. In your __ init__.py found in your newmodule you can do something like this:

__import__("module."+libname) # this import will import the library in the current context
globals().update(vars(module.libname))

# or use this
library = import_("PathToLibAndFilename", "module.")
globals().update(vars(library))

If you iterate over the things from vars(library) you can also rename the things itself, but I have not tried this so far. update() needs a dictionary and you can pass a dictionary which is of your choice in layout.

You can experiment with it on the console using (I think this is a solution for you):

 import(libName)
 globals().update({"testMe":vars(libName)["A"]}) # A is your class name from the library
 # then you can do:
 testMe
 # in my case this outputs something like this: <class 'libName.A'>

PS: I am using boost::python to expose my classes, but it is just a wrapper to cpython stuff.

Upvotes: 0

Related Questions