Hi all,
Does pip install -e .
– editable installation with pip
for a project available locally with pyproject.toml
– not run setup.py
? Because I have a project I am developing, with a pyproject.toml
that installs with some custom steps expressed with setup.py
which works with pip install .
and python -m build
(as per PyPa, IIRC), but the custom steps seem to be entirely omitted when installing in “editable” mode.
The pyproject.toml
, edited for brevity:
[build-system]
requires = [ "setuptools", "setuptools-scm", "wheel" ]
build-backend = "setuptools.build_meta"
[project]
name = "foobar"
dynamic = [ "version" ]
requires-python = ">=3.11"
[tool.setuptools_scm]
The reason I have a setup.py
is because there’s an additional step it expresses before the wheel is built – I run make
to pre-process some Python code. Shouldn’t really matter how, but I need said additional step, and setup.py
ended up being the way I could enable running the step:
import setuptools.command.build
from setuptools import Command, setup
import os
import os.path
import subprocess
class MakeCommand(Command):
"""Class of `setuptools`/`distutils` commands which invoke a `make` program.
GNU Make (http://www.gnu.org/software/make) is currently assumed for providing `make`. The program is invoked in a manner where it changes the working directory to the build directory advertised for the command (utilizing `self.set_undefined_options` as hinted at by [the documentation](http://setuptools.pypa.io/en/latest/userguide/extension.html) which defers to `help(setuptools.command.build.SubCommand)`).
The command is expected to produce build artefacts which will be added to the wheel.
"""
build_lib: str | None
def finalize_options(self) -> None:
self.set_undefined_options('build_py', ('build_lib', 'build_lib'))
def initialize_options(self) -> None:
self.build_lib = None
def run(self, *args, **kwargs) -> None:
os.makedirs(self.build_lib, exist_ok=True)
subprocess.check_call(('make', '-C', self.build_lib, '-f', os.path.realpath('Makefile')))
class BuildCommand(setuptools.command.build.build):
sub_commands = [ ('build_make', None) ] + setuptools.command.build.build.sub_commands # Makes the `build_make` command a sub-command of the `build` command, which has the effect of the former being invoked when the latter is invoked (which is invoked in turn when the wheel must be built, through the `bdist_wheel` command)
setup(cmdclass={ 'build': BuildCommand, 'build_make': MakeCommand })
I am not against abandoning setup.py
– I am not sure if my particular set-up (in the broad(er) sense of the term) is “deprecated”. It was only an accident I noticed while doing local development that pip install -e .
doesn’t end up invoking make
as expressed with setup.py
, which is essential for the project. Everything works exactly as intended with pip install .
, in comparison. I am not sure I am doing this all correctly anymore – I want to declare what can be declared with pyproject.toml
while make
is run when building the wheel – my sdist
should be the original (before make
artifacts) source code.