Cythonized Python.Net code cannot find system assemblies

Question:

When I compile with Cython the Python code which uses Python.NET to access .NET assemblies, it can’t find those assemblies:

ModuleNotFoundError: No module named 'System'

Without compilation it works ok.

For demo code, I used https://github.com/pythonnet/pythonnet/blob/master/demo/helloform.py

My setup.py file:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

ext_modules = [
    Extension(
        'helloform',
        sources = ['helloform.py'],
        language = 'c++'
      )
]

setup(
  name = 'helloform',
  ext_modules = cythonize(ext_modules),
)

Then I build it with python setup.py build_ext --inplace.

I wanted to load compiled module from Python prompt with import helloform but it failed with

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "helloform.py", line 8, in init helloform
ModuleNotFoundError: No module named 'System'
Asked By: crayxt

||

Answers:

This answer is untested – I don’t think I can easily set up an environment to test so it’s a bit of a guess. If it doesn’t work I’ll remove it.

This probably is a bug, and if you want it fixed in the longer term you should report it. Cython does try to be compatible with Python wherever possible…. A quick investigate suggests that Python.NET overrides the built-in __import__ function. Cython looks to lookup and use this function in Python 2, but not in Python 3. This is no longer the preferred way of customizing import behaviour (but is still supported). I’d guess it would work in Cython + Python 2?

As a workaround you should probably just run the import statements in Python. There’s two obvious ways to do it:

  1. Write a small separate module just containing the import statements, then in Cython import from that module:

    from import_module import WinForms, Size, Point
    
  2. Run the import statements in exec; extract the values out of the global dict you pass it:

    import_dict = {}
    exec("""import clr
    # etc...
    """, import_dict) # pass a dict in as `globals`
    WinForms = import_dict['WinForms']
    # etc.
    
Answered By: DavidW
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.