Skip to content

Commit 704fa64

Browse files
committed
add notes
1 parent 3267dad commit 704fa64

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

python/private/py_wheel_normalize_pep440.bzl

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ def _pad_zeros(release, n):
526526
release = list(release) + [0] * padding
527527
return tuple(release)
528528

529-
# TODO @aignas 2025-05-04: add tests for the comparison
530529
def _version_eq(left, right):
531530
if left.is_prefix and right.is_prefix:
532531
fail("Invalid comparison: both versions cannot be prefix matching")
@@ -568,7 +567,14 @@ def _version_lt(left, right):
568567
elif left_release < right_release:
569568
return True
570569

571-
return left.key() < right.key()
570+
# From PEP440, this is not a simple ordering check and we need to check the version
571+
# semantically:
572+
# * The exclusive ordered comparison <V MUST NOT allow a pre-release of the specified version
573+
# unless the specified version is itself a pre-release.
574+
if left.pre and right.pre:
575+
return left.pre < right.pre
576+
else:
577+
return False
572578

573579
def _version_gt(left, right):
574580
if left.epoch > right.epoch:
@@ -585,7 +591,30 @@ def _version_gt(left, right):
585591
elif left_release < right_release:
586592
return False
587593

588-
return left.key() > right.key()
594+
# From PEP440, this is not a simple ordering check and we need to check the version
595+
# semantically:
596+
# * The exclusive ordered comparison >V MUST NOT allow a post-release of the given version
597+
# unless V itself is a post release.
598+
#
599+
# * The exclusive ordered comparison >V MUST NOT match a local version of the specified
600+
# version.
601+
602+
if left.post and right.post:
603+
return left.post > right.post
604+
else:
605+
# ignore the left.post if right is not a post if right is a post, then this evaluates to
606+
# False anyway.
607+
return False
608+
609+
def _version_ge(left, right):
610+
# PEP440: simple order check
611+
# https://peps.python.org/pep-0440/#inclusive-ordered-comparison
612+
return left.key(local = False) >= right.key(local = False)
613+
614+
def _version_le(left, right):
615+
# PEP440: simple order check
616+
# https://peps.python.org/pep-0440/#inclusive-ordered-comparison
617+
return left.key(local = False) <= right.key(local = False)
589618

590619
def _first_non_none(*args):
591620
for arg in args:
@@ -594,11 +623,14 @@ def _first_non_none(*args):
594623

595624
return None
596625

597-
def _key(self, release_key = ("z",)):
626+
def _key(self, *, local, release_key = ("z",)):
598627
"""This function returns a tuple that can be used in 'sorted' calls.
599628
600629
This implements the PEP440 version sorting.
601630
"""
631+
local = self.local if local else []
632+
local = local or []
633+
602634
return (
603635
self.epoch,
604636
self.release,
@@ -609,7 +641,7 @@ def _key(self, release_key = ("z",)):
609641
# then stable
610642
_first_non_none(self.pre, self.post, self.dev, release_key),
611643
# PEP440 local versions go before post versions
612-
tuple([(type(item) == "int", item) for item in self.local or []]),
644+
tuple([(type(item) == "int", item) for item in local]),
613645
# PEP440 - pre-release ordering: .devN, <no suffix>, .postN
614646
_first_non_none(
615647
self.post,
@@ -672,23 +704,22 @@ def _new_version(*, epoch = 0, release, pre = "", post = "", dev = "", local = "
672704
local = local,
673705
is_prefix = is_prefix,
674706
norm = norm,
675-
# TODO @aignas 2025-05-04: add tests for the comparison
676707
eq = lambda x: _version_eq(self, x), # buildifier: disable=uninitialized
677708
ne = lambda x: not _version_eq(self, x), # buildifier: disable=uninitialized
678709
lt = lambda x: _version_lt(self, x), # buildifier: disable=uninitialized
679710
gt = lambda x: _version_gt(self, x), # buildifier: disable=uninitialized
680-
le = lambda x: not _version_gt(self, x), # buildifier: disable=uninitialized
681-
ge = lambda x: not _version_lt(self, x), # buildifier: disable=uninitialized
711+
le = lambda x: _version_le(self, x), # buildifier: disable=uninitialized
712+
ge = lambda x: _version_ge(self, x), # buildifier: disable=uninitialized
682713
str = lambda: norm,
683-
key = lambda: _key(self), # buildifier: disable=uninitialized
714+
key = lambda *, local = True: _key(self, local = local), # buildifier: disable=uninitialized
684715
)
685716

686717
return self
687718

688719
def parse_version(version, strict = False):
689720
"""Parse a PEP4408 compliant version
690721
691-
TODO: finish
722+
TODO @aignas 2025-05-06: where should this go?
692723
693724
See https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
694725
and https://peps.python.org/pep-0440/
@@ -702,6 +733,9 @@ def parse_version(version, strict = False):
702733
"""
703734

704735
parser = _new(version.strip(" " if strict else " .*")) # PEP 440: Leading and Trailing Whitespace and .*
736+
737+
# TODO @aignas 2025-05-06: Remove this usage of a second parser just to get the normalized
738+
# version.
705739
parser_2 = _new(version.strip(" " if strict else " .*")) # PEP 440: Leading and Trailing Whitespace and .*
706740
accept(parser, _is("v"), "") # PEP 440: Preceding v character
707741
accept(parser_2, _is("v"), "") # PEP 440: Preceding v character

tests/pypi/pep508/evaluate_tests.bzl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ _MISC_EXPRESSIONS = [
275275
_expr_case('python_version != "3.11.*"', True, {"python_version": "3.10.1"}),
276276
_expr_case('python_version != "3.10"', False, {"python_version": "3.10.0"}),
277277
_expr_case('python_version == "3.10"', True, {"python_version": "3.10.0"}),
278+
# Cases for the '>' operator
278279
# Taken from spec: https://peps.python.org/pep-0440/#exclusive-ordered-comparison
279280
_expr_case('python_version > "1.7"', True, {"python_version": "1.7.1"}),
280281
_expr_case('python_version > "1.7"', False, {"python_version": "1.7.0.post0"}),
@@ -284,14 +285,13 @@ _MISC_EXPRESSIONS = [
284285
_expr_case('python_version > "1.7.post2"', False, {"python_version": "1.7.0"}),
285286
_expr_case('python_version > "1.7.1+local"', False, {"python_version": "1.7.1"}),
286287
_expr_case('python_version > "1.7.1+local"', True, {"python_version": "1.7.2"}),
288+
# Extra cases for the '<' operator
287289
_expr_case('python_version < "1.7.1"', False, {"python_version": "1.7.2"}),
288290
_expr_case('python_version < "1.7.3"', True, {"python_version": "1.7.2"}),
289291
_expr_case('python_version < "1.7.1"', True, {"python_version": "1.7"}),
290-
_expr_case('python_version < "1.7.1-rc2"', True, {"python_version": "1.7"}),
291-
_expr_case('python_version < "1.7-rc2"', False, {"python_version": "1.7"}),
292-
_expr_case('python_version < "1.7-rc2"', True, {"python_version": "1.7-rc1"}),
293-
_expr_case('python_version < "1.7-rc2"', False, {"python_version": "1.7-rc3"}),
294-
_expr_case('python_version < "1.7-rc12"', True, {"python_version": "1.7-rc3"}),
292+
_expr_case('python_version < "1.7.1"', False, {"python_version": "1.7.1-rc2"}),
293+
_expr_case('python_version < "1.7.1-rc3"', True, {"python_version": "1.7.1-rc2"}),
294+
_expr_case('python_version < "1.7.1-rc1"', False, {"python_version": "1.7.1-rc2"}),
295295
]
296296

297297
def _misc_expressions(env):

0 commit comments

Comments
 (0)