Skip to content

Commit

Permalink
Use pep517 to load project metadata (#1311)
Browse files Browse the repository at this point in the history
Co-authored-by: Albert Tugushev <albert@tugushev.ru>
  • Loading branch information
astrojuanlu and atugushev committed Mar 6, 2021
1 parent 21d56e4 commit 9aeeed1
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 24 deletions.
36 changes: 24 additions & 12 deletions piptools/scripts/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
import click
from click.utils import safecall
from pip._internal.commands import create_command
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.constructors import (
install_req_from_line,
install_req_from_req_string,
)
from pip._internal.utils.misc import redact_auth_from_url
from pip._vendor.pep517 import meta

from .._compat import parse_requirements
from ..cache import DependencyCache
Expand Down Expand Up @@ -330,21 +334,14 @@ def cli(
constraints = []
for src_file in src_files:
is_setup_file = os.path.basename(src_file) == "setup.py"
if is_setup_file or src_file == "-":
if src_file == "-":
# pip requires filenames and not files. Since we want to support
# piping from stdin, we need to briefly save the input from stdin
# to a temporary file and have pip read that. also used for
# reading requirements from install_requires in setup.py.
tmpfile = tempfile.NamedTemporaryFile(mode="wt", delete=False)
if is_setup_file:
from distutils.core import run_setup

dist = run_setup(src_file)
tmpfile.write("\n".join(dist.install_requires))
comes_from = f"{dist.get_name()} ({src_file})"
else:
tmpfile.write(sys.stdin.read())
comes_from = "-r -"
tmpfile.write(sys.stdin.read())
comes_from = "-r -"
tmpfile.flush()
reqs = list(
parse_requirements(
Expand All @@ -357,6 +354,15 @@ def cli(
for req in reqs:
req.comes_from = comes_from
constraints.extend(reqs)
elif is_setup_file:
dist = meta.load(os.path.dirname(os.path.abspath(src_file)))
comes_from = f"{dist.metadata.get_all('Name')[0]} ({src_file})"
constraints.extend(
[
install_req_from_req_string(req, comes_from=comes_from)
for req in dist.requires or []
]
)
else:
constraints.extend(
parse_requirements(
Expand All @@ -378,7 +384,13 @@ def cli(

# Filter out pip environment markers which do not match (PEP496)
constraints = [
req for req in constraints if req.markers is None or req.markers.evaluate()
req
for req in constraints
if req.markers is None
# We explicitly set extra=None to filter out optional requirements
# since evaluating an extra marker with no environment raises UndefinedEnvironmentName
# (see https://packaging.pypa.io/en/latest/markers.html#usage)
or req.markers.evaluate({"extra": None})
]

log.debug("Using indexes:")
Expand Down
69 changes: 57 additions & 12 deletions tests/test_cli_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,17 @@ def test_command_line_overrides_pip_conf(pip_with_index_conf, runner):
assert "Using indexes:\n http://override.com" in out.stderr


def test_command_line_setuptools_read(pip_conf, runner):
@pytest.mark.network
def test_command_line_setuptools_read(runner, make_pip_conf):
make_pip_conf(
dedent(
"""\
[global]
disable-pip-version-check = True
"""
)
)

with open("setup.py", "w") as package:
package.write(
dedent(
Expand All @@ -51,16 +61,13 @@ def test_command_line_setuptools_read(pip_conf, runner):
"""
)
)
out = runner.invoke(cli, ["--no-emit-find-links"])
out = runner.invoke(
cli,
["--no-header", "--no-emit-find-links", "--find-links", MINIMAL_WHEELS_PATH],
)

assert out.stderr == dedent(
"""\
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --no-emit-find-links
#
small-fake-a==0.1
# via fake-setuptools-a (setup.py)
"""
Expand All @@ -70,6 +77,7 @@ def test_command_line_setuptools_read(pip_conf, runner):
assert os.path.exists("requirements.txt")


@pytest.mark.network
@pytest.mark.parametrize(
("options", "expected_output_file"),
(
Expand All @@ -85,12 +93,11 @@ def test_command_line_setuptools_read(pip_conf, runner):
(["setup.py", "--output-file", "output.txt"], "output.txt"),
),
)
def test_command_line_setuptools_output_file(
pip_conf, runner, options, expected_output_file
):
def test_command_line_setuptools_output_file(runner, options, expected_output_file):
"""
Test the output files for setup.py as a requirement file.
"""

with open("setup.py", "w") as package:
package.write(
dedent(
Expand All @@ -106,7 +113,8 @@ def test_command_line_setuptools_output_file(
assert os.path.exists(expected_output_file)


def test_command_line_setuptools_nested_output_file(pip_conf, tmpdir, runner):
@pytest.mark.network
def test_command_line_setuptools_nested_output_file(tmpdir, runner):
"""
Test the output file for setup.py in nested folder as a requirement file.
"""
Expand All @@ -127,6 +135,43 @@ def test_command_line_setuptools_nested_output_file(pip_conf, tmpdir, runner):
assert (proj_dir / "requirements.txt").exists()


@pytest.mark.network
def test_setuptools_preserves_environment_markers(
runner, make_package, make_wheel, make_pip_conf, tmpdir
):
make_pip_conf(
dedent(
"""\
[global]
disable-pip-version-check = True
"""
)
)

dists_dir = tmpdir / "dists"

foo_dir = make_package(name="foo", version="1.0")
make_wheel(foo_dir, dists_dir)

bar_dir = make_package(
name="bar", version="2.0", install_requires=['foo ; python_version >= "1"']
)
out = runner.invoke(
cli,
[
str(bar_dir / "setup.py"),
"--no-header",
"--no-annotate",
"--no-emit-find-links",
"--find-links",
str(dists_dir),
],
)

assert out.exit_code == 0, out.stderr
assert out.stderr == 'foo==1.0 ; python_version >= "1"\n'


def test_find_links_option(runner):
with open("requirements.in", "w") as req_in:
req_in.write("-f ./libs3")
Expand Down

0 comments on commit 9aeeed1

Please sign in to comment.