I have an extension that I compile with cython into the related *.pyd
or *.so
files, which are then imported into my very basic main.py
:
if __name__ == "__main__":
from my_extension import Main
Main()
The issue is that I am trying to compile and package all the required modules into this single extension for use with pyinstaller.
By default, pyinstaller will look for any imports listed in main.py
, but it'll only see that it needs my_extension.Main
and will not look for what my_extension.Main
needs.
This leads to a situation where I need to include those external modules as hiddenimports
for pyinstaller, which works fine, but I want to gain the advantage of having the external modules compiled as well into the singular extension, or otherwise into their own extensions.
Is there an easy way to tell cython to include those external modules in it's compilation?
UPDATE
So I ended up finding a solution, but it doesn't exactly work. Here are the steps I followed:
- Create a virtual environment in the working directory with
python -m venv .venv
and enter it - Install related dependencies with:
python -m pip install -U pip wheel cython pyinstaller
python -m pip install -r requirements.txt
- Run the following
setup.py
file usingpython setup.py build_ext
:
from setuptools import Extension, setup
from Cython.Build import cythonize
from pathlib import Path
extensions = []
for directory in Path(".venv/Lib/site-packages").iterdir():
if directory.is_dir() and ("cython" not in str(directory.name).lower() and "pyinstaller" not in str(directory.name).lower()):
items = list(directory.glob("*[!__init__].py"))
for i in list(directory.glob("*[!__init__].pxd")):
items.append(i)
if len(items) > 0:
for i in range(len(items)):
items[i] = str(items[i])
extensions.append(
Extension(
str(directory.name),
items,
)
)
setup(
ext_modules=cythonize(
extensions,
language_level=3,
exclude_failures=True,
),
)
The problem comes in that last step - although the setup.py
is able to find any related *.py
files listed in the module's directory, it will often fail with certain libraries entirely, and the option exclude_failures=True
doesn't bypass this. One example of a problematic library is numpy
, which I could just explicitly remove from the list of modules to compile if it's found, but I'm trying to write this to be programmatically done instead of having any hard-coded sections.
I'm inclined to believe that this is a wasted effort, and that I could simply get along by compiling my source code, then packaging it alongside the dependencies into a single executable with pyinstaller
, but if someone out there has any ideas then I'm all ears.