Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve our setuptools shim #10723

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions src/pip/_internal/utils/setuptools_build.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
import sys
import textwrap
from typing import List, Optional, Sequence

# Shim to wrap setup.py invocation with setuptools
#
# We set sys.argv[0] to the path to the underlying setup.py file so
# setuptools / distutils don't take the path to the setup.py to be "-c" when
# invoking via the shim. This avoids e.g. the following manifest_maker
# warning: "warning: manifest_maker: standard file '-c' not found".
_SETUPTOOLS_SHIM = (
"import io, os, sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};"
"f = getattr(tokenize, 'open', open)(__file__) "
"if os.path.exists(__file__) "
"else io.StringIO('from setuptools import setup; setup()');"
"code = f.read().replace('\\r\\n', '\\n');"
"f.close();"
"exec(compile(code, __file__, 'exec'))"
)
_SETUPTOOLS_SHIM = textwrap.dedent(
"""
exec(compile('''
# This is <pip-setuptools-shim> -- a shim that pip uses to run setup.py
#
# - It imports setuptools before invoking setup.py, to enable projects that directly
# import from `distutils.core` to work with newer packaging standards.
# - Provides a clearer error message when setuptools is not installed.
# - It sets `sys.argv[0]` to the underlying `setup.py`, when invoking `setup.py` so
# setuptools doesn't think the script is `-c`. This avoids the following warning:
# manifest_maker: standard file '-c' not found".
# - It generates a shim setup.py, for handling setup.cfg-only projects.
import os, sys, tokenize

try:
import setuptools
except ImportError as error:
raise RuntimeError(
"setuptools is not available in the build environment, but is required "
"to use setup.py-based projects with pip."
) from error

__file__ = {!r}
sys.argv[0] = __file__

if os.path.exists(__file__):
filename = __file__
with tokenize.open(__file__) as f:
setup_py_code = f.read()
else:
filename = "<auto-generated setuptools caller>"
setup_py_code = "from setuptools import setup; setup()"

exec(compile(setup_py_code, filename, "exec"))
''', "<pip-setuptools-shim>", "exec"))
"""
).rstrip()


def make_setuptools_shim_args(
Expand Down
9 changes: 6 additions & 3 deletions tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,11 +943,14 @@ def test_make_setuptools_shim_args() -> None:
)

assert args[1:3] == ["-u", "-c"]
# Spot-check key aspects of the command string.
assert "sys.argv[0] = '/dir/path/setup.py'" in args[3]
assert "__file__='/dir/path/setup.py'" in args[3]
assert args[4:] == ["--some", "--option", "--no-user-cfg"]

shim = args[3]
# Spot-check key aspects of the command string.
assert "import setuptools" in shim
assert "__file__ = '/dir/path/setup.py'" in args[3]
assert "sys.argv[0] = __file__" in args[3]


@pytest.mark.parametrize("global_options", [None, [], ["--some", "--option"]])
def test_make_setuptools_shim_args__global_options(
Expand Down