Is there a way to register a function within an Cython extenstion module such that this function is called when the Python garbage collector destroys the module object ?
Something like
def __dealloc__(...)
on module level ?
I'm not sure there is an explicit/official way to achieve that. However, you could add a global variable which will be destroyed, when the cython module is deallocated:
# foo.pyx:
class Observer:
def __init__(self):
print("module initialized")
def __del__(self):
print ("module deleted")
_o=Observer()
However, cythonizing it via cythonize foo.pyx -i
will not lead to the desired effect:
[$] python -c `import foo`
module initialized
there is no "module deleted" printed to the console!
The problem: one cannot reload a cython-extension anyway (for example via importlib.reload()
), so it gets only deallocated when the Python interpreter is shut down. But then there is no need to deallocate the resources ...
Cython keeps all global Python-variables in a global C-variable which is called static PyObject *__pyx_d;
in the cythonized C-source; this is just a Python-dictionary. However, because there is a reference to the global _o
in this not destroyed dictionary, the reference count of _o
will never become 0 an thus the object doesn't get destroyed.
By setting generate_cleanup_code=True
one can force Cython to generate a clean-up function which will be put into the m_free
-slot of the PyModuleDef definition struct. For example with the following setup-file:
from distutils.core import setup
from Cython.Build import cythonize
from Cython.Compiler import Options
Options.generate_cleanup_code = True # this is important!
setup(
name = "foo",
ext_modules = cythonize("foo.pyx"),
)
And now after python setup.py build_ext -i
:
[$] python -c "import foo"
module initialized
module deleted
it works as expected.
Because there is no reload
for Cython-extensions, "module deleted" will be only seen when module_dealloc
is called.
However, when the first listing were pure-Python, we would also see module deleted
if importlib.reload(foo)
were called. In this case "module_dealloc" isn't used, but all references to global _o
would be cleared and object would be destroyed.
Depending on what one want, it might be either the expected or unexpected behavior.