-
Notifications
You must be signed in to change notification settings - Fork 9
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
Fix PyPiVersionRange working with * and ~= #126
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -664,11 +664,6 @@ class PypiVersionRange(VersionRange): | |||||
">=": ">=", | ||||||
"<": "<", | ||||||
">": ">", | ||||||
# per https://www.python.org/dev/peps/pep-0440/#compatible-release | ||||||
# For a given release identifier V.N, the compatible release clause is | ||||||
# approximately equivalent to the pair of comparison clauses: | ||||||
# >= V.N, == V.* | ||||||
"~=": None, | ||||||
# 01.01.01 is NOT equal to 1.1.1 using === which is strict string | ||||||
# equality this is a rare and eventually non-suggested approach | ||||||
"===": None, | ||||||
|
@@ -681,7 +676,7 @@ def from_native(cls, string): | |||||
Raise an a univers.versions.InvalidVersion | ||||||
""" | ||||||
# TODO: environment markers are yet supported | ||||||
# TODO: handle .* version, ~= and === operators | ||||||
# TODO: handle === operators | ||||||
|
||||||
if ";" in string: | ||||||
raise InvalidVersionRange(f"Unsupported PyPI environment marker: {string!r}") | ||||||
|
@@ -694,6 +689,8 @@ def from_native(cls, string): | |||||
f"Unsupported character: {unsupported_chars!r} " f"in PyPI version: {string!r}" | ||||||
) | ||||||
|
||||||
string = cls.sanitize_constraints(string) | ||||||
|
||||||
try: | ||||||
specifiers = SpecifierSet(string) | ||||||
except InvalidSpecifier as e: | ||||||
|
@@ -707,14 +704,10 @@ def from_native(cls, string): | |||||
operator = spec.operator | ||||||
version = spec.version | ||||||
|
||||||
if operator == "~=" or operator == "===": | ||||||
if operator == "===": | ||||||
msg = f"Unsupported PyPI version constraint operator: {spec!r}" | ||||||
unsupported_messages.append(msg) | ||||||
|
||||||
if str(version).endswith(".*"): | ||||||
msg = f"Unsupported PyPI version: {spec!r}" | ||||||
unsupported_messages.append(msg) | ||||||
|
||||||
try: | ||||||
version = cls.version_class(version) | ||||||
comparator = cls.vers_by_native_comparators[operator] | ||||||
|
@@ -729,6 +722,35 @@ def from_native(cls, string): | |||||
|
||||||
return cls(constraints=constraints) | ||||||
|
||||||
@classmethod | ||||||
def sanitize_constraints(cls, string): | ||||||
constraints = [] | ||||||
try: | ||||||
specifiers = SpecifierSet(string) | ||||||
except InvalidSpecifier as e: | ||||||
raise InvalidVersionRange() from e | ||||||
for spec in specifiers: | ||||||
operator = spec.operator | ||||||
version = spec.version | ||||||
if "*" in version: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we limit this to trailing "*" and not at any position?
Suggested change
|
||||||
pos = version.find("*") | ||||||
version = version[: pos - 1] | ||||||
if "==" in operator: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about using a strict equality? otherwise this is true:
Suggested change
The same applies to the tests below |
||||||
constraints.extend( | ||||||
[f">= {version}", f"< {version[:pos - 2]}{str(int(version[pos - 2]) + 1)}"] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why keep a space between the operator and the version? Also |
||||||
) | ||||||
elif "!=" in operator: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
constraints.extend( | ||||||
[f"< {version}", f">= {version[:pos - 2]}{str(int(version[pos - 2]) + 1)}"] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same nit as above: there must be a more readable way to express this. |
||||||
) | ||||||
elif "~=" in operator: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
parts = version.split(".") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if there is no dot in the version? Will the code below crash? |
||||||
parts[-2] = str(int(parts[-2]) + 1) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a more obvious way to write this? This is really hard for me to understand :] |
||||||
constraints.extend([f">= {version}", f"< {'.'.join(parts[:-1])}"]) | ||||||
else: | ||||||
constraints.append(f"{operator} {version}") | ||||||
return ", ".join(constraints) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the benefit of just sanitizing constraints and returning a string, vs. actually integrating this in the parsing of constraints proper? Here the code ends up parsing the same constraints set twice: |
||||||
|
||||||
|
||||||
class MavenVersionRange(VersionRange): | ||||||
""" | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -357,13 +357,14 @@ def test_from_native_and_from_string_round_trip(scheme, native_ranges): | |
@pytest.mark.parametrize( | ||
"range, will_pass, expected", | ||
[ | ||
(" ~= 0.9", False, "Unsupported PyPI version constraint operator"), | ||
("~= 1.3", False, "Unsupported PyPI version constraint operator"), | ||
(" ~= 0.9", True, "vers:pypi/>=0.9|<1"), | ||
("~= 1.3", True, "vers:pypi/>=1.3|<2"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about these added tests:
|
||
(" >= 1.0", True, "vers:pypi/>=1.0"), | ||
(" != 1.3.4.*", False, "Unsupported PyPI version"), | ||
(" != 1.3.4.*", True, "vers:pypi/<1.3.4|>=1.3.5"), | ||
("< 2.0", True, "vers:pypi/<2.0"), | ||
("~= 1.3.4.*", False, ""), | ||
("==1. *", False, "Unsupported PyPI version"), | ||
("==1. *", True, "vers:pypi/>=1|<2"), | ||
("!=1.2.*", True, "vers:pypi/<1.2|>=1.3"), | ||
("==1.3.4 ) (", False, "Unsupported character"), | ||
("===1.0", False, "Unsupported PyPI version"), | ||
], | ||
|
@@ -375,6 +376,7 @@ def test_PypiVersionRange_raises_ivr_for_unsupported_and_invalid_ranges(range, w | |
raise Exception("Exception not raised") | ||
except InvalidVersionRange as ivre: | ||
assert expected in str(ivre) | ||
|
||
else: | ||
assert expected == str(PypiVersionRange.from_native(range)) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add tests specifically for this function? And some doc string?