There are at least four solutions to the Cythonization error (these results are with cython == 0.29.24
):
adding the file example_package/__init__.pxd
and changing the names of the Extension
s being built to be submodules of the module being built, i.e., example_package.other
and example_package.driver
(in the question these would be Test.other
and Test.driver
).
This change is anyway necessary for importing the installed submodules driver
and other
, as described below. Note that the installed package is actually a namespace package in this case, due to absence of the keyword parameter and argument packages=['example_package']
, as discussed below.
adding the file example_package/__init__.py
and changing the names of the Extension
s being built to be submodules of the module being built, i.e., example_package.other
and example_package.driver
. Even in this case, where an __init__.py
is present, the installed package example_package
will be a namespace package. Turning it into a regular package requires passing packages=['example_package']
to the function setuptools.setup
.
Likewise to adding an __init__.pxd
, this change is necessary for importing the installed submodules.
adding the file example_package/__init__.pxd
and changing the cimport
statement to an absolute cimport
inside the file example_package/driver.pyx
(The package builds and installs with this alternative, but does not import, due to the need to also change the names of Extension
s):
from . import other
from example_package cimport other
adding the file example_package/__init__.py
and changing the cimport
statement to an absolute cimport
inside the file example_package/driver.pyx
, as done in the previous item. The package builds and installs with this, but does not import.
The question is explicitly asking for relative imports, so in that sense, the first two alternatives are the answers to the question, because they do work with relative imports.
Either of the four changes listed above avoids the following error:
Error compiling Cython file:
------------------------------------------------------------
...
from . import other
from . cimport other
^
------------------------------------------------------------
example_package/driver.pyx:2:0: relative cimport beyond main package is not allowed
but as already noted above, and also discussed below, the change of Extension
names of the first or second alternatives is necessary for importing the installed submodules (additionally passing the parameter and keyword argument packages=[PACKAGE_NAME]
in the fourth alternative allows the Python package example_package
to import, but not its submodules driver
and other
).
Modified setup.py
The file setup.py
that I recommend, with all additional changes (not only those changes necessary for building and installing, listed above) is:
"""Installation script."""
import os
import setuptools
try:
from Cython.Build import cythonize
cy_ext = f'{os.extsep}pyx'
except ImportError:
# this case is intended for use when installing from
# a source distribution (produced with `sdist`),
# which, as recommended by Cython documentation,
# should include the generated `*.c` files,
# in order to enable installation in absence of `cython`
print('`import cython` failed')
cy_ext = f'{os.extsep}c'
PACKAGE_NAME = 'example_package'
def run_setup():
"""Build and install package."""
ext_modules = extensions()
setuptools.setup(
name=PACKAGE_NAME,
ext_modules=ext_modules,
packages=[PACKAGE_NAME],
package_dir={PACKAGE_NAME: PACKAGE_NAME})
def extensions():
"""Return C extensions, cythonize as needed."""
extensions = dict(
other=setuptools.extension.Extension(
f'{PACKAGE_NAME}.other',
sources=[f'{PACKAGE_NAME}/other{cy_ext}'],),
driver=setuptools.extension.Extension(
f'{PACKAGE_NAME}.driver',
sources=[f'{PACKAGE_NAME}/driver{cy_ext}'],))
if cy_ext == f'{os.extsep}pyx':
ext_modules = list()
for k, v in extensions.items():
c = cythonize(
[v],
# show_all_warnings=True # this line requires `cython >= 3.0`
)
ext_modules.append(c[0])
else:
ext_modules = list(extensions.values())
return ext_modules
if __name__ == '__main__':
run_setup()
Other changes
The other changes in this answer are not necessary for successfully building and installing the package, but recommended for other reasons. For some of the other changes, I describe the motivation below.
Note that:
- only adding
example_package/__init__.pxd
or example_package/__init__.py
is insufficient, and
- only changing the
Extension
names is insuffient, and
- only changing the
cimport
statement to from example_package cimport other
is insufficient.
Two of these changes are needed together for building and installing, i.e., one of the four alternatives listed earlier.
For being able to import the extension modules built from the Cython sources driver.pyx
and other.pyx
, it is also necessary to change the names of the extensions to:
Extension('example_package.other', ...)
Extension('example_package.driver', ...)
Note that this makes import
work because now example_package
has become a namespace package (CPython glossary entry):
>>>
<module 'example_package' (namespace)>
>>> import example_package.driver
>>> import example_package.other
(Also, I have omitted the parameter include_dirs
of setuptools.setup
in the setup.py
file that I used, and which I include below.)
These changes are needed for building and installing the package, and for importing the extension modules. For importing the installed package from Python in case it did not include any extensions modules (and thus had not become a namespace package):
- a file
__init__.py
needs to be added in the directory example_package/
(which in the question is the directory Test/
), and
- the keyword argument
packages=[
example_package],
needs to be passed to the function setuptools.setup
.
Otherwise, the statement import example_package
will raise a ModuleNotFoundError
. The addition of an __init__.py
file is also necessary to make the package a regular package (CPython glossary entry), which is usually what is intended, instead of a namespace package.
Whether to use an __init__.pxd
A regular Python package includes an __init__.py
file. An __init__.pxd
file is only relevant in case other packages need the *.pxd
headers. If this is not the case, it seems that the file example_package/__init__.py
suffices, since the four solutions above are essentially two solutions, each with either __init__.py
or __init__.pxd
as alternatives.
So my recommendation for files and their arrangement is:
.
├── example_package
│ ├── __init__.py
│ ├── driver.pyx
│ ├── other.pxd
│ └── other.pyx
└── setup.py
Both changes are needed
Only adding the __init__.pxd
file raises the cythonization error:
Error compiling Cython file:
------------------------------------------------------------
...
from . import other
from . cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: relative cimport beyond main package is not allowed
and only changing the cimport
statement (without an __init__.pxd
) raises the cythonization error:
Error compiling Cython file:
------------------------------------------------------------
...
#!/usr/bin/env python
from . import other
from example_package cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: 'example_package.pxd' not found
Error compiling Cython file:
------------------------------------------------------------
...
#!/usr/bin/env python
from . import other
from example_package cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: 'example_package/other.pxd' not found
Naming the package
Above I write example_package
as the package's name, though I did build and install the example also with the name Test/
as it is named in the question, in order to ensure that this indeed works, and so that the minimal changes needed are the __init__.pxd
file and the from example_package cimport other
.
For uniformity, I actually also renamed the directory to Test/
when running setup.py
with this name of the package, but I am not on a case-sensitive filesystem at the moment, so I do not know whether a directory named test/
together with the keyword argument name='Test',
in setup.py
, as in the question, would have caused issues on a case-sensitive filesystem.
So:
- using
Test
as package name and Test
as directory name worked for me for building and installing, and
- using
test
as package name and test
as directory name worked for me for building and installing.
I recommend using another package name. Also, for reasons described below:
- Importing when the package is named
Test
is done with the statement import Test
. Writing import test
will import another package (see below).
- using
test
as package name does not import the installed test
package, for reasons explained below, even if an __init__.py
file is added.
In any case, for reasons explained below, my recommendation is to change the package name, even if it is intended to be an auxiliary package that is intended to be used only as a test harness for the main package.
Also, lowercase package naming is mandated by PEP 8, thus leading to test
, which may be understood to be a directory of tests, which is not the case if this actually is intended to be an example of a main package.
The error that happens after building and installing, when the package and directory are named test
is (the dots ... are the result of editing the actual output):
>>> import test
>>> test
<module 'test' from '.../lib/python3.9/test/__init__.py'>
In other words, CPython includes a package called test
:
The test
package contains all regression tests for Python as well as the modules test.support
and test.regrtest
.
Therefore, the name test
cannot be used for an example package that is intended to be imported after installation (though the package does get built and installed, and even uninstalled by pip uninstall -y test
, fine).
Another detail is that from test cimport other
is actually wrong, even though it compiles, because had the built test
package actually been magically imported somehow (in the presence of CPython's test
package), at runtime this cimport
statement would have defaulted to CPython's test
package. Nonetheless, Cython's translation may transform this cimport
to some other form that would have actually imported from test.other
of the built package. Since the import of the installed test
package appears to be impossible in the presence of CPython's test
package, it is unknown whether this cimport
would have raised a runtime error.
Also, note that:
Note: The test
package is meant for internal use by Python only. It is documented for the benefit of the core developers of Python. Any use of this package outside of Python’s standard library is discouraged as code mentioned here can change or be removed without notice between releases of Python.
Between all experiments, I run rm -rf build dist *.egg-info test/*.c
.
So before changing the file arrangement to the one shown earlier, what I used is the same with the question.
Renaming the package to example_package
I changed the name of the package to example_package
, assuming that test/
contains the actual package to be installed, based on the argument given to the parameter name=
within the file setup.py
of the question.
The motivation for this renaming is that "test" or "tests" is usually used to name the directory of tests that accompany a Python package. There are many arrangements for such directories, and for how the tests are used. In the next section, I discuss my suggestion for arranging tests.
Regarding possibilities, arrangements other than what I describe in the next section have been used in general, including placing the tests in directories within the package itself. Given that the question writes myProject/
, and has a file myProject/__init__.py
, I am not sure whether the question actually uses such an arrangement.
In that case, though, driver
and other
would actually be test modules. Though installing the tests as a separate package (called Test
in the question), which is what the module myProject/setup.py
does, suggests that driver
and other
are the main package's modules, and thus that the main package is called "Test".
If not, i.e. if driver
and other
are actually test modules, and setup.py
is not the main package's setup script, but instead a setup script that builds and installs an "auxiliary" package that is intended for only testing the main package (which may in this case be named "myProject", with a setup.py
present in the directory that contains the directory myProject/
of the question), then my renaming of Test
to example_package/
would not correspond to this being the main package. (It is also interesting to have a test-harness package that includes Cython code and thus requires to be compiled--and possibly installed.)
In that case, perhaps Test
could be renamed instead to tests_of_example_package
. In other words, in that case it is relevant to include the word "test" in the package's name, though it seems that qualifying the package as auxiliary to example_package
is explicit. Explicit is better than implicit (PEP 20).
(Tests sometimes are arranged as packages (using __init__.py
), even when not installing this as an auxiliary Python package (intended as only a test harness of the main Python package it accompanies). The motivation is to enable importing common modules of the test suite that are used by multiple test modules, but are not themselves modules that are run directly by the test runner.)
If this is the main package, then I assume that "Test" was used for the purpose of writing an example in the example. If so, then my only reason for renaming (lowercase aside) is to distinguish the main package itself from its tests.
Lowercase names for Python packages are mandated by PEP 8:
Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.
The underscore in example_package
is only for the sake of example.
Arranging tests
Tests might be placed in a test/
directory that is in the same directory with the directory that contains the Python package, and is named after the package. I strongly recommend this approach, for example (this tree was created with the program tree
):
.
├── example_package
│ └── __init__.py
├── setup.py
└── tests
└── module_name_test.py
For testing without accidentally importing the package example_package
from its source directory, but from where it is installed (usually under site-packages/
), I recommend in all cases first cd
ing to the directory tests/
before running any tests. This is the most reliable approach to testing, does not rely on how each testing framework works, how the testing framework's various configuration options work, how the options interact with each other, nor how bugs in the testing framework itself affect testing.
In this way, the package source can be placed inside the directory example_package
, without any reason to use any other directory arrangement.
Shebangs in Python modules
The shebang inside the *.pyx
files can be removed, because it has no effect. The shebang line is treated by Cython as a Python comment line that is moved to inside a C comment somewhere later inside the *.c
files that Cython generates from the *.pyx
files. So it has no effect. I am not aware of any use of shebang lines in C sources that are intended to be compiled by directly calling gcc
(or another C compiler), as Cython does (whether Cython calls gcc
, or another compiler depends on the system, environment path, environment variables, and other information).
Also, the shebang is only relevant to Python modules that might be executed as executables. This is not intended to be the case for modules inside a Python package, so shebang lines are almost never used there.
An exception might be a package module that might be infrequently be run directly during development, e.g., for experimentation or debugging purposes. Nonetheless, such a module would be expected to have a __main__
stanza.
So Python modules to which a shebang is relevant, also typically have a __main__
stanza.
For completeness, setup.py
is intended to be run as __main__
, and does have a __main__
stanza, but the way setup scripts are run (when not using pip
--using pip
is strongly recommended) is by python setup.py
, so there is no need for a shebang in setup.py
(no shebang appears there in the question--I just mention this for completeness).
The distutils
module]() is deprecated as of Python 3.10, as specified in PEP 632, and will be removed in Python 3.12.
Switching when Cython is absent to the extension .c
, instead of .pyx
This is in accord with Cython recommendations:
It is strongly recommended that you distribute the generated .c
files as well as your Cython sources, so that users can install your module without needing to have Cython available.
Uppercase names for module-scope variables in setup.py
that remain unchanged ("constants")
Module-scope Python variables that are intended to be used as constants, i.e., remain unchanged after the initial assignment, are mandated by PEP 8 to have identifiers that are uppercase with underscores:
Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW
and TOTAL
.
Hence the identifier PACKAGE_NAME
.
I used formatted string literals, which require Python >= 3.6.
Arranging code as functions within the module setup.py
This is good practice in general, enabling naming different sections of code via function names, executing the code only when run as __main__
, by including a __main__
stanza, and thus enabling importing setup.py
and using specific functionality that may be relevant to outside code (e.g., an installation framework) without necessarily running all code--e.g., without running the function setuptools.setup
.
The question presents a minimal working example, so a small setup.py
is relevant in the question. I am writing this section as recommendation for what to do in actual packages, not in questions.
The same observation applies to module and function docstrings inside setup.py
.
Also, I recommend the top-down arrangement of functions: callers above callees, because this layout is more readable.
I used os.extsep
for generality, though using a dot I think will still work, and is more readable.
Arrangement of package
As I noted earlier, the only change to the question's example that was needed to avoid the build error "relative cimport beyond main package is not allowed" was the addition of either an __init__.py
or an __init__.pxd
, and either an absolute cimport
inside driver.pyx
or the renaming of the Extensions
.
Removing the file __init__.py
In the final version, I removed the file __init__.py
that is in the same directory with setup.py
. My understanding is that this file has no effect in this example. If the example is intended to have test/
as the main package's directory, then any __init__.py
would appear inside test/
.
If test/
is actually an auxiliary package of tests for the main package, then the __init__.py
would be part of the main package, and unrelated to the test/
package. However, in that case, it seems that there would be a setup.py
file above myProject/
, which would be responsible for building both the main package, and the test-harness package.
Using absolute imports
The default language_level
in cython < 3.0.0
is 2, even on Python 3:
language_level
(2/3/3str)
Globally set the Python language level to be used for module
compilation. Default is compatibility with Python 2.
To enable Python 3 source code semantics, set this to 3 (or 3str)
at the start of a module or pass the "-3" or "--3str"
command line options to the compiler.
The question uses Python 3.5 and cython == 0.23.4
, so this is the case.
The default Cython semantics are changing in cython >= 3.0.0
:
The default language level was changed to 3str
, i.e. Python 3 semantics, ...
With both Python 2 and Python 3 semantics (passing compiler_directives=dict(language_level=3)
, or installing the pre-release cython == 3.0.0a8
), the first two solutions (which use relative imports) do work.
Nonetheless, absolute imports are recommended by PEP 8:
Absolute imports are recommended, as they are usually more readable and tend to be better behaved (or at least give better error messages) if the import system is incorrectly configured ...
Absolute imports are also robust to refactoring a package's structure. They are explicit, and explicit is better than implicit (PEP 20).
The resulting module driver.pyx
after this change would be:
from example_package import other
from example_package cimport other
The setup.py
code in this answer is based on what I have written in the file download.py
of the Python package dd
.