Skip to content

Commit d551f66

Browse files
authored
Provide traceback on setuptools import failure in legacy paths (#13291)
In the legacy `setup.py` wrapper importer, failures to import setuptools might not be because setuptools itself is absent, but because it failed to import successfully. This can currently arise (as of `setuptools==77.0.1`) in seemingly valid environments due to limitations in the setuptools vendoring. This commit exposes more of the failure up to users, to help with debugging failed environments, and makes it possible for setuptools to expose a better error message itself through the import system. The above situation is more likely than setuptools being wholly absent as the legacy code paths invoke setup.py will check that setuptools is installed (and install setuptools in a temporary build env. if not) beforehand. For some more context, see pypa/setuptools#4894.
1 parent a5c50f3 commit d551f66

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

news/13290.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Include traceback on failure to import ``setuptools`` when ``setup.py`` is being invoked directly.

src/pip/_internal/utils/setuptools_build.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@
1717
# setuptools doesn't think the script is `-c`. This avoids the following warning:
1818
# manifest_maker: standard file '-c' not found".
1919
# - It generates a shim setup.py, for handling setup.cfg-only projects.
20-
import os, sys, tokenize
20+
import os, sys, tokenize, traceback
2121
2222
try:
2323
import setuptools
24-
except ImportError as error:
24+
except ImportError:
2525
print(
26-
"ERROR: Can not execute `setup.py` since setuptools is not available in "
27-
"the build environment.",
26+
"ERROR: Can not execute `setup.py` since setuptools failed to import in "
27+
"the build environment with exception:",
2828
file=sys.stderr,
2929
)
30+
traceback.print_exc()
3031
sys.exit(1)
3132
3233
__file__ = %r

tests/functional/test_install_reqs.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
_create_test_package_with_subdirectory,
1414
create_basic_sdist_for_package,
1515
create_basic_wheel_for_package,
16+
make_wheel,
1617
need_svn,
1718
requirements_file,
1819
)
@@ -917,3 +918,26 @@ def test_config_settings_local_to_package(
917918
assert "--verbose" not in simple3_args
918919
simple2_args = simple2_sdist.args()
919920
assert "--verbose" not in simple2_args
921+
922+
923+
def test_nonpep517_setuptools_import_failure(script: PipTestEnvironment) -> None:
924+
"""Any import failures of `setuptools` should inform the user both that it's
925+
not pip's fault, but also exactly what went wrong in the import."""
926+
# Install a poisoned version of 'setuptools' that fails to import.
927+
name = "setuptools_poisoned"
928+
module = """\
929+
raise ImportError("this 'setuptools' was intentionally poisoned")
930+
"""
931+
path = make_wheel(name, "0.1.0", extra_files={"setuptools.py": module}).save_to_dir(
932+
script.scratch_path
933+
)
934+
script.pip("install", "--no-index", path)
935+
936+
result = script.pip_install_local("--no-use-pep517", "simple", expect_error=True)
937+
nice_message = (
938+
"ERROR: Can not execute `setup.py`"
939+
" since setuptools failed to import in the build environment"
940+
)
941+
exc_message = "ImportError: this 'setuptools' was intentionally poisoned"
942+
assert nice_message in result.stderr
943+
assert exc_message in result.stderr

0 commit comments

Comments
 (0)