Skip to content

Commit

Permalink
fix: tolerate invalid python requires
Browse files Browse the repository at this point in the history
Signed-off-by: Frost Ming <[email protected]>
  • Loading branch information
frostming committed Jul 17, 2023
1 parent 992e637 commit 5530275
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 2 deletions.
11 changes: 9 additions & 2 deletions src/unearth/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@

from unearth.link import Link
from unearth.pep425tags import get_supported
from unearth.utils import ARCHIVE_EXTENSIONS, splitext, strip_extras
from unearth.utils import (
ARCHIVE_EXTENSIONS,
fix_legacy_specifier,
splitext,
strip_extras,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -151,7 +156,9 @@ def _check_requires_python(self, link: Link) -> None:
py_ver = self.target_python.py_ver or sys.version_info[:2]
py_version = ".".join(str(v) for v in py_ver)
try:
requires_python = SpecifierSet(link.requires_python)
requires_python = SpecifierSet(
fix_legacy_specifier(link.requires_python)
)
except InvalidSpecifier:
raise LinkMismatchError(
f"Invalid requires-python: {link.requires_python}"
Expand Down
38 changes: 38 additions & 0 deletions src/unearth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import functools
import itertools
import os
import re
import sys
import urllib.parse as parse
import warnings
from pathlib import Path
from typing import Iterable, Iterator, Sequence, TypeVar
from urllib.request import pathname2url, url2pathname
Expand Down Expand Up @@ -217,3 +219,39 @@ def __getitem__(self, index: int) -> T: # type: ignore[override]
if i == index:
return item
raise IndexError("Index out of range")


_legacy_specifier_re = re.compile(r"(==|!=|<=|>=|<|>)(\s*)([^,;\s)]*)")


@functools.lru_cache()
def fix_legacy_specifier(specifier: str) -> str:
"""Since packaging 22.0, legacy specifiers like '>=4.*' are no longer
supported. We try to normalize them to the new format.
"""

def fix_wildcard(match: re.Match[str]) -> str:
operator, _, version = match.groups()
if operator in ("==", "!="):
return match.group(0)
if ".*" in version:
warnings.warn(
".* suffix can only be used with `==` or `!=` operators",
FutureWarning,
stacklevel=4,
)
version = version.replace(".*", ".0")
if operator in ("<", "<="): # <4.* and <=4.* are equivalent to <4.0
operator = "<"
elif operator in (">", ">="): # >4.* and >=4.* are equivalent to >=4.0
operator = ">="
elif "+" in version: # Drop the local version
warnings.warn(
"Local version label can only be used with `==` or `!=` operators",
FutureWarning,
stacklevel=4,
)
version = version.split("+")[0]
return f"{operator}{version}"

return _legacy_specifier_re.sub(fix_wildcard, specifier)
8 changes: 8 additions & 0 deletions tests/test_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def test_retrieve_hash_from_internet(pypi, session, url):
)


@pytest.mark.filterwarnings("ignore::FutureWarning")
@pytest.mark.parametrize(
"link,expected",
[
Expand All @@ -226,6 +227,13 @@ def test_retrieve_hash_from_internet(pypi, session, url):
),
False,
),
(
Link(
"https://test.pypi.org/files/click-8.1.3-py3-none-any.whl",
requires_python=">3.6.*",
),
True,
),
],
)
@pytest.mark.parametrize("ignore_compatibility", (True, False))
Expand Down

0 comments on commit 5530275

Please sign in to comment.