summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
authorAnderson Bravalheri <andersonbravalheri@gmail.com>2022-08-12 14:56:55 +0100
committerAnderson Bravalheri <andersonbravalheri@gmail.com>2022-08-12 14:56:55 +0100
commit8f2cc1f47f795de68a2a5a93e7e59fd91a6346d2 (patch)
treea1905241aebb3fb984fc072607f6ca29360962fa /setuptools
parent207a56d2109548168403455f2b57999d81355a0f (diff)
parent66994535aceb1d4865957ab7ec70762d15716c25 (diff)
downloadpython-setuptools-git-8f2cc1f47f795de68a2a5a93e7e59fd91a6346d2.tar.gz
Avoid crashing on build_py errors during editable installs (#3506)
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/command/build.py4
-rw-r--r--setuptools/command/editable_wheel.py45
-rw-r--r--setuptools/tests/test_editable_install.py51
3 files changed, 93 insertions, 7 deletions
diff --git a/setuptools/command/build.py b/setuptools/command/build.py
index 283999da..c0676d8e 100644
--- a/setuptools/command/build.py
+++ b/setuptools/command/build.py
@@ -20,7 +20,7 @@ class build(_build):
# copy to avoid sharing the object with parent class
sub_commands = _build.sub_commands[:]
- def run(self):
+ def get_sub_commands(self):
subcommands = {cmd[0] for cmd in _build.sub_commands}
if subcommands - _ORIGINAL_SUBCOMMANDS:
msg = """
@@ -30,7 +30,7 @@ class build(_build):
"""
warnings.warn(msg, SetuptoolsDeprecationWarning)
self.sub_commands = _build.sub_commands
- super().run()
+ return super().get_sub_commands()
class SubCommand(Protocol):
diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py
index 1bb7ddfb..8a53de65 100644
--- a/setuptools/command/editable_wheel.py
+++ b/setuptools/command/editable_wheel.py
@@ -37,6 +37,7 @@ from typing import (
)
from setuptools import Command, SetuptoolsDeprecationWarning, errors, namespaces
+from setuptools.command.build_py import build_py as build_py_cls
from setuptools.discovery import find_package_path
from setuptools.dist import Distribution
@@ -254,13 +255,55 @@ class editable_wheel(Command):
self, dist_name: str, unpacked_wheel: _Path, build_lib: _Path, tmp_dir: _Path
) -> Tuple[List[str], Dict[str, str]]:
self._configure_build(dist_name, unpacked_wheel, build_lib, tmp_dir)
- self.run_command("build")
+ self._run_build_subcommands()
files, mapping = self._collect_build_outputs()
self._run_install("headers")
self._run_install("scripts")
self._run_install("data")
return files, mapping
+ def _run_build_subcommands(self):
+ """
+ Issue #3501 indicates that some plugins/customizations might rely on:
+
+ 1. ``build_py`` not running
+ 2. ``build_py`` always copying files to ``build_lib``
+
+ However both these assumptions may be false in editable_wheel.
+ This method implements a temporary workaround to support the ecosystem
+ while the implementations catch up.
+ """
+ # TODO: Once plugins/customisations had the chance to catch up, replace
+ # `self._run_build_subcommands()` with `self.run_command("build")`.
+ # Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023.
+ build: Command = self.get_finalized_command("build")
+ for name in build.get_sub_commands():
+ cmd = self.distribution.get_command_obj(name)
+ if name == "build_py" and type(cmd) != build_py_cls:
+ self._safely_run(name)
+ else:
+ self.run_command(name)
+
+ def _safely_run(self, cmd_name: str):
+ try:
+ return self.run_command(cmd_name)
+ except Exception:
+ msg = f"""{traceback.format_exc()}\n
+ If you are seeing this warning it is very likely that a setuptools
+ plugin or customization overrides the `{cmd_name}` command, without
+ tacking into consideration how editable installs run build steps
+ starting from v64.0.0.
+
+ Plugin authors and developers relying on custom build steps are encouraged
+ to update their `{cmd_name}` implementation considering the information in
+ https://setuptools.pypa.io/en/latest/userguide/extension.html
+ about editable installs.
+
+ For the time being `setuptools` will silence this error and ignore
+ the faulty command, but this behaviour will change in future versions.\n
+ """
+ warnings.warn(msg, SetuptoolsDeprecationWarning, stacklevel=2)
+
def _create_wheel_file(self, bdist_wheel):
from wheel.wheelfile import WheelFile
diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py
index ea31cb46..f7612377 100644
--- a/setuptools/tests/test_editable_install.py
+++ b/setuptools/tests/test_editable_install.py
@@ -558,8 +558,9 @@ class TestOverallBehaviour:
@pytest.mark.parametrize("layout", EXAMPLES.keys())
def test_editable_install(self, tmp_path, venv, layout, editable_opts):
- opts = editable_opts
- project = install_project("mypkg", venv, tmp_path, self.EXAMPLES[layout], *opts)
+ project, _ = install_project(
+ "mypkg", venv, tmp_path, self.EXAMPLES[layout], *editable_opts
+ )
# Ensure stray files are not importable
cmd_import_error = """\
@@ -758,13 +759,55 @@ def test_pbr_integration(tmp_path, venv, editable_opts):
assert b"Hello world!" in out
+class TestCustomBuildPy:
+ """
+ Issue #3501 indicates that some plugins/customizations might rely on:
+
+ 1. ``build_py`` not running
+ 2. ``build_py`` always copying files to ``build_lib``
+
+ During the transition period setuptools should prevent potential errors from
+ happening due to those assumptions.
+ """
+ # TODO: Remove tests after _run_build_steps is removed.
+
+ FILES = {
+ **TestOverallBehaviour.EXAMPLES["flat-layout"],
+ "setup.py": dedent("""\
+ import pathlib
+ from setuptools import setup
+ from setuptools.command.build_py import build_py as orig
+
+ class my_build_py(orig):
+ def run(self):
+ super().run()
+ raise ValueError("TEST_RAISE")
+
+ setup(cmdclass={"build_py": my_build_py})
+ """),
+ }
+
+ def test_safeguarded_from_errors(self, tmp_path, venv):
+ """Ensure that errors in custom build_py are reported as warnings"""
+ # Warnings should show up
+ _, out = install_project("mypkg", venv, tmp_path, self.FILES)
+ assert b"SetuptoolsDeprecationWarning" in out
+ assert b"ValueError: TEST_RAISE" in out
+ # but installation should be successful
+ out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"])
+ assert b"42" in out
+
+
def install_project(name, venv, tmp_path, files, *opts):
project = tmp_path / name
project.mkdir()
jaraco.path.build(files, prefix=project)
opts = [*opts, "--no-build-isolation"] # force current version of setuptools
- venv.run(["python", "-m", "pip", "install", "-e", str(project), *opts])
- return project
+ out = venv.run(
+ ["python", "-m", "pip", "-v", "install", "-e", str(project), *opts],
+ stderr=subprocess.STDOUT,
+ )
+ return project, out
# ---- Assertion Helpers ----