9

I want to build a C extension for CPython. I could do it traditionally with a setup.py file. However, for the reasons mentioned in PEP 517, I would prefer a declarative approach using a pyproject.toml. I understand that setuptools is the only build backend that can build C extensions on all relevant platforms. In fact, I am unaware of any backend capable of building C extensions at all alongside the outdated distutils.

Against this background, a common setup.py would look like this:

from setuptools import setup, Extension
kwargs = dict(
    name='mypackage',
    # more metadata
    ext_modules=[
        Extension('mypackage.mymodule', ['lib/mymodule.c',
                                         'lib/mypackage.c',
                                         'lib/myalloc.c'],
                  include_dirs=['lib'],
                  py_limited_api=True)])

setup(**kwargs)

Now, the challenge is to put the above into a pyproject.toml plus a setup.cfg.

The setuptools docs suggest a pyproject.toml like this:

[build-system]
requires = [
    "setuptools >=52.0",
        'wheel >= 0.36']
build-backend = "setuptools.build_meta"

See

Further, the actual metadata should go into setup.cfg. However, I haven't found any explanation on how to translate the ext_modules kwarg, in particular the Extension() call, into setup.cfg syntax.

EvgenKo423
  • 976
  • 1
  • 13
  • 18
Dr Leo
  • 169
  • 3
  • So what is blocking? What is your current status? Any error message? – sinoroc Feb 11 '21 at 19:05
  • 1
    My status is: blocked by lack of documentation on how to declare the C extension in the setup.cfg file. I thought about a [build_ext] section, but didn't find any docs either. So I guess I am a few steps away from attempting a build. – Dr Leo Feb 13 '21 at 12:25
  • 1
    Looks like it is not supported (yet): https://github.com/pypa/setuptools/issues/2220 – sinoroc Feb 13 '21 at 15:44

1 Answers1

6

pyproject.toml is not strictly meant to replace setup.py, but rather to ensure its correct execution if it's still needed (see PEP 517 and my answer here):

Where the build-backend key exists, this takes precedence and the source tree follows the format and conventions of the specified backend (as such no setup.py is needed unless the backend requires it). Projects may still wish to include a setup.py for compatibility with tools that do not use this spec.

While setuptools plans to move everything from a script into a config file, it's not always possible:

There are two types of metadata: static and dynamic.

  • Static metadata (setup.cfg): guaranteed to be the same every time. This is simpler, easier to read, and avoids many common errors, like encoding errors.
  • Dynamic metadata (setup.py): possibly non-deterministic. Any items that are dynamic or determined at install-time, as well as extension modules or extensions to setuptools, need to go into setup.py.

Static metadata should be preferred and dynamic metadata should be used only as an escape hatch when absolutely necessary.

In fact, setuptools will still use an imaginary setup.py if it doesn't exist.

Note: Since version 61.0.0 setuptools allows to specify project metadata and other configuration options in pyproject.toml file. Its use looks more attractive as this file has another, more useful function and allows to specify most of the metadata in standardized, tool-agnostic way.


With that being said, if you want to stick as much as possible to a static way, you could move everything you can into pyproject.toml file and leave the rest in setup.py for now:

pyproject.toml
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
# more metadata
setup.py
from setuptools import setup, Extension

setup_args = dict(
    ext_modules = [
        Extension(
            'mypackage.mymodule',
            ['lib/mymodule.c', 'lib/mypackage.c', 'lib/myalloc.c'],
            include_dirs = ['lib'],
            py_limited_api = True
        )
    ]
)
setup(**setup_args)
EvgenKo423
  • 976
  • 1
  • 13
  • 18