I have a simple package that includes a C extension. I'm managing my dependencies and build process with Poetry.
When I run poetry build
, the extension is compiled and included in the .tar.gz archive but not in the .whl and I don't understand why. Pip installing from the tar.gz archive works as expected but since the wheel is lacking the .so, pip installing the wheel results in an unusable package.
I've lifted the build machinery from here: https://github.com/python-poetry/poetry/issues/2740
pyproject.toml
[tool.poetry]
name = "python_ctypes"
version = "0.1.0"
description = ""
authors = ["Me"]
include = [
{path = "_extensions/*.so", format = "wheel"}
]
[tool.poetry.dependencies]
python = "^3.9"
numpy = "^1.22.1"
[tool.poetry.dev-dependencies]
[tool.poetry.build]
generate-setup-file = false
script = "build.py"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
build.py
"""Poetry build script for python_ctypes"""
import os
import shutil
from distutils.command.build_ext import build_ext
from distutils.core import Distribution
from distutils.core import Extension
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
extensions = [
Extension("python_ctypes._extensions.arraysum", ["python_ctypes/_extensions/arraysum.c"]),
]
class ExtBuilder(build_ext):
# This class allows C extension building to fail.
built_extensions = []
def run(self):
try:
build_ext.run(self)
except (DistutilsPlatformError, FileNotFoundError):
print("Unable to build the C extensions")
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
print('Unable to build the "{}" C extension, '
"python_ctypes will use the pure python version of the extension.".format(ext.name))
def build(setup_kwargs):
"""
This function is mandatory in order to build the extensions.
"""
print(setup_kwargs)
distribution = Distribution({"name": "python_ctypes", "ext_modules": extensions})
distribution.package_dir = "python_ctypes"
cmd = ExtBuilder(distribution)
cmd.ensure_finalized()
cmd.run()
# Copy built extensions back to the project
for output in cmd.get_outputs():
relative_extension = os.path.relpath(output, cmd.build_lib)
if not os.path.exists(output):
continue
shutil.copyfile(output, relative_extension)
mode = os.stat(relative_extension).st_mode
mode |= (mode & 0o444) >> 2
os.chmod(relative_extension, mode)
return setup_kwargs
if __name__ == "__main__":
build({})