Skip to content

Commit 357064e

Browse files
authored
Fix constraints file parsing for URL requirements. (#3090)
Fixes #3089
1 parent 6b43715 commit 357064e

5 files changed

Lines changed: 82 additions & 6 deletions

File tree

CHANGES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Release Notes
22

3+
## 2.86.1
4+
5+
This release fixes a bug in constraints file requirement parsing. Previously, Pex tried to validate
6+
constraints beyond its own needs, anticipating Pip's needs, leading to a failure to handle direct
7+
reference URL requirements, including VCS requirements.
8+
9+
* Fix constraints file parsing for URL requirements. (#3090)
10+
311
## 2.86.0
412

513
This release adds support for Linux PEX scies installing themselves with a desktop entry on first

pex/requirements.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -778,12 +778,17 @@ def parse_requirements(
778778
logical_line, basepath=os.path.dirname(source.origin) if source.is_file else None
779779
)
780780
if source.is_constraints:
781-
if not isinstance(requirement, PyPIRequirement) or requirement.requirement.extras:
781+
if (
782+
not isinstance(requirement, (PyPIRequirement, URLRequirement, VCSRequirement))
783+
or requirement.requirement.extras
784+
):
782785
raise ParseError(
783786
logical_line,
784-
"Constraint files do not support VCS, URL or local project requirements"
785-
"and they do not support requirements with extras. Search for 'We are also "
786-
"changing our support for Constraints Files' here: "
787+
"Constraint files do not support local project requirements and they "
788+
"do not support requirements with extras; see:"
789+
"https://pip.pypa.io/en/stable/user_guide/#constraints-files. If you are "
790+
"using --pip-version vendored or a very old --pip-version, search for 'We "
791+
"are also changing our support for Constraints Files' here: "
787792
"https://pip.pypa.io/en/stable/user_guide/"
788793
"#changes-to-the-pip-dependency-resolver-in-20-3-2020.",
789794
)

pex/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright 2015 Pex project contributors.
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
__version__ = "2.86.0"
4+
__version__ = "2.86.1"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2026 Pex project contributors.
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
from __future__ import absolute_import, print_function
5+
6+
from pex.artifact_url import VCS, ArtifactURL
7+
from pex.dist_metadata import Requirement
8+
from pex.pep_440 import Version
9+
from pex.pep_503 import ProjectName
10+
from pex.resolve.locked_resolve import VCSArtifact
11+
from pex.resolve.lockfile import json_codec
12+
from testing.cli import run_pex3
13+
from testing.pytest_utils.tmp import Tempdir
14+
15+
16+
def test_url_requirement_constraints(tmpdir):
17+
# type: (Tempdir) -> None
18+
19+
lock_file = tmpdir.join("lock.json")
20+
21+
with open(tmpdir.join("constraint.txt"), "w") as fp:
22+
print("cowsay @ git+https://github.com/VaasuDevanS/cowsay-python@v5.0", file=fp)
23+
24+
run_pex3(
25+
"lock", "create", "--constraints", fp.name, "cowsay", "--indent", "2", "-o", lock_file
26+
).assert_success()
27+
28+
lock = json_codec.load(lock_file)
29+
assert [Requirement.parse("cowsay")] == list(lock.requirements)
30+
assert len(lock.locked_resolves) == 1
31+
32+
locked_resolve = lock.locked_resolves[0]
33+
locked_requirements_by_name = {
34+
locked_requirement.pin.project_name: locked_requirement
35+
for locked_requirement in locked_resolve.locked_requirements
36+
}
37+
locked_requirement = locked_requirements_by_name.pop(ProjectName("cowsay"))
38+
assert not locked_requirements_by_name
39+
40+
assert Version("5") == locked_requirement.pin.version
41+
assert isinstance(locked_requirement.artifact, VCSArtifact)
42+
assert locked_requirement.artifact.vcs is VCS.Git
43+
assert "v5.0" == locked_requirement.artifact.requested_revision
44+
assert (
45+
ArtifactURL.parse("git+https://github.com/VaasuDevanS/cowsay-python@v5.0")
46+
== locked_requirement.artifact.url
47+
)

tests/test_requirements.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2020 Pex project contributors.
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
from __future__ import absolute_import
4+
from __future__ import absolute_import, print_function
55

66
import os
77
from textwrap import dedent
@@ -612,3 +612,19 @@ def test_parse_requirements_from_url_no_fetcher():
612612
"Problem resolving requirements file: The source is a url but no fetcher was supplied to "
613613
"resolve its contents with.".format(EXAMPLE_PYTHON_REQUIREMENTS_URL)
614614
) == str(exec_info.value)
615+
616+
617+
def test_url_requirement_constraints_issue_3089(tmpdir):
618+
# type: (Tempdir) -> None
619+
620+
with open(tmpdir.join("constraint.txt"), "w") as fp:
621+
print("cowsay @ git+https://github.com/VaasuDevanS/cowsay-python@v5.0", file=fp)
622+
623+
assert [
624+
Constraint(
625+
line=DUMMY_LINE,
626+
requirement=Requirement.parse(
627+
"cowsay @ git+https://github.com/VaasuDevanS/cowsay-python@v5.0"
628+
),
629+
)
630+
] == normalize_results(parse_requirement_file(fp.name, is_constraints=True))

0 commit comments

Comments
 (0)