Skip to content

Commit

Permalink
Support poetry wildcard requirements. (Cherry-pick of #15213) (#15214)
Browse files Browse the repository at this point in the history
See https://python-poetry.org/docs/dependency-specification/#wildcard-requirements

[ci skip-rust]

[ci skip-build-wheels]

Co-authored-by: Benjy Weinberger <[email protected]>
  • Loading branch information
Eric-Arellano and benjyw authored Apr 21, 2022
1 parent f1187f4 commit de437a9
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 7 deletions.
39 changes: 32 additions & 7 deletions src/python/pants/backend/python/macros/poetry_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ def get_max_tilde(parsed_version: Version) -> str:
return f"{major}.{minor}.0"


def get_max_wildcard(parsed_version: Version) -> str:
# Note: Assumes this is not a global wildcard, so parsed_version.release has
# at least two components.
release = list(parsed_version.release)
release[-2] += 1
major = release[0]
minor = release[1]
return f"{major}.{minor}.0"


def parse_str_version(attributes: str, **kwargs: str) -> str:
valid_specifiers = "<>!~="
pep440_reqs = []
Expand All @@ -114,12 +124,10 @@ def parse_str_version(attributes: str, **kwargs: str) -> str:
extras_str = kwargs["extras_str"]
comma_split_reqs = (i.strip() for i in attributes.split(","))
for req in comma_split_reqs:
is_caret = req[0] == "^"
# ~= is an acceptable default operator; however, ~ is not, and IS NOT the same as ~=
is_tilde = req[0] == "~" and req[1] != "="
if is_caret or is_tilde:

def parse_version(version_str: str) -> Version:
try:
parsed_version = Version(req[1:])
return Version(version_str)
except InvalidVersion:
raise InvalidVersion(
f'Failed to parse requirement {proj_name} = "{req}" in {fp} loaded by the '
Expand All @@ -128,12 +136,29 @@ def parse_str_version(attributes: str, **kwargs: str) -> str:
"that we can update Pants' Poetry macro to support this."
)

max_ver = get_max_caret(parsed_version) if is_caret else get_max_tilde(parsed_version)
if not req:
continue
if req[0] == "^":
parsed_version = parse_version(req[1:])
max_ver = get_max_caret(parsed_version)
min_ver = f"{parsed_version.public}"
pep440_reqs.append(f">={min_ver},<{max_ver}")
elif req[0] == "~" and req[1] != "=":
# ~= is an acceptable default operator; however, ~ is not, and IS NOT the same as ~=
parsed_version = parse_version(req[1:])
max_ver = get_max_tilde(parsed_version)
min_ver = f"{parsed_version.public}"
pep440_reqs.append(f">={min_ver},<{max_ver}")
elif req[-1] == "*":
if req != "*": # This is not a global wildcard.
# To parse we replace the * with a 0.
parsed_version = parse_version(f"{req[:-1]}0")
max_ver = get_max_wildcard(parsed_version)
min_ver = f"{parsed_version.public}"
pep440_reqs.append(f">={min_ver},<{max_ver}")
else:
pep440_reqs.append(req if req[0] in valid_specifiers else f"=={req}")
return f"{proj_name}{extras_str} {','.join(pep440_reqs)}"
return f"{proj_name}{extras_str} {','.join(pep440_reqs)}".rstrip()


def parse_python_constraint(constr: str | None, fp: str) -> str:
Expand Down
21 changes: 21 additions & 0 deletions src/python/pants/backend/python/macros/poetry_requirements_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ def test_max_tilde(test, exp) -> None:
assert get_max_tilde(version) == exp


@pytest.mark.parametrize(
"test, exp",
[
("*", ""),
("1.*", ">=1.0,<2.0.0"),
("1.2.*", ">=1.2.0,<1.3.0"),
],
)
def test_wildcard(test, exp) -> None:
assert (
parse_str_version(test, proj_name="foo", file_path="", extras_str="")
== f"foo {exp}".rstrip()
)


@pytest.mark.parametrize(
"test, exp",
[
Expand Down Expand Up @@ -363,6 +378,9 @@ def test_parse_multi_reqs() -> None:
[tool.poetry.group.mygroup.dependencies]
myrequirement = "1.2.3"
awildcard = "6.7.*"
anotherwildcard = "44.*"
aglobalwildcard = "*"
[tool.poetry.group.mygroup2.dependencies]
myrequirement2 = "1.2.3"
Expand All @@ -379,6 +397,9 @@ def test_parse_multi_reqs() -> None:
actual_reqs = {
PipRequirement.parse("junk[security]@ https://github.com/myrepo/junk.whl"),
PipRequirement.parse("myrequirement==1.2.3"),
PipRequirement.parse("awildcard>=6.7.0,<6.8.0"),
PipRequirement.parse("anotherwildcard>=44.0,<45.0.0"),
PipRequirement.parse("aglobalwildcard"),
PipRequirement.parse("myrequirement2==1.2.3"),
PipRequirement.parse("poetry@ git+https://github.com/python-poetry/[email protected]"),
PipRequirement.parse('requests[security, random]<3.0.0,>=2.25.1; python_version > "2.7"'),
Expand Down

0 comments on commit de437a9

Please sign in to comment.