Skip to content

Commit 2d7d9e5

Browse files
committed
rework the code structure a little
1 parent 42c866f commit 2d7d9e5

1 file changed

Lines changed: 98 additions & 79 deletions

File tree

python/private/version.bzl

Lines changed: 98 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ def _parse(version_str, strict = True):
544544
parts[key] = parser.context()["norm"][start:]
545545
parts["norm"] = parser.context()["norm"]
546546

547+
# TODO @aignas 2025-05-09: move the `is_prefix` handling to `accept_release`
547548
if is_prefix and (parts["local"] or parts["post"] or parts["dev"] or parts["pre"]):
548549
if strict:
549550
fail("local version part has been obtained, but only public segments can have prefix matches")
@@ -582,65 +583,75 @@ def version(version_str, strict = False):
582583

583584
return _new_version(**parts)
584585

585-
def _new_version(
586-
*,
587-
norm,
588-
epoch = 0,
589-
release,
590-
pre = "",
591-
post = "",
592-
dev = "",
593-
local = "",
594-
is_prefix = False):
595-
epoch = epoch or 0
596-
_release = tuple([int(d) for d in release.split(".")])
586+
def _parse_epoch(value):
587+
if not value:
588+
return 0
597589

598-
if pre:
599-
if pre.startswith("rc"):
600-
prefix = "rc"
601-
else:
602-
prefix = pre[0]
590+
return int(value)
603591

604-
pre = (prefix, int(pre[len(prefix):]))
605-
else:
606-
pre = None
592+
def _parse_release(value):
593+
return tuple([int(d) for d in value.split(".")])
607594

608-
if post:
609-
if not post.startswith(".post"):
610-
fail("post release identifier must start with '.post', got: {}".format(post))
611-
post = int(post[len(".post"):])
595+
def _parse_local(value):
596+
if not value:
597+
return None
612598

613-
# We choose `~` since almost all of the ASCII characters will be before
614-
# it. Use `ord` and `chr` functions to find a good value.
615-
post = ("~", post)
616-
else:
617-
post = None
599+
local = value.lstrip("+")
618600

619-
if dev:
620-
if not dev.startswith(".dev"):
621-
fail("dev release identifier must start with '.dev', got: {}".format(dev))
622-
dev = int(dev[len(".dev"):])
601+
# If the part is numerical, handle it as a number
602+
return tuple([int(part) if part.isdigit() else part for part in local.split(".")])
623603

624-
# Empty string goes first when comparing
625-
dev = ("", dev)
626-
else:
627-
dev = None
604+
def _parse_dev(value):
605+
if not value:
606+
return None
628607

629-
if local:
630-
local = local.lstrip("+")
608+
if not value.startswith(".dev"):
609+
fail("dev release identifier must start with '.dev', got: {}".format(value))
610+
dev = int(value[len(".dev"):])
631611

632-
# If the part is numerical, handle it as a number
633-
local = tuple([int(part) if part.isdigit() else part for part in local.split(".")])
612+
# Empty string goes first when comparing
613+
return ("", dev)
614+
615+
def _parse_pre(value):
616+
if not value:
617+
return None
618+
619+
if value.startswith("rc"):
620+
prefix = "rc"
634621
else:
635-
local = None
622+
prefix = value[0]
623+
624+
return (prefix, int(value[len(prefix):]))
636625

626+
def _parse_post(value):
627+
if not value:
628+
return None
629+
630+
if not value.startswith(".post"):
631+
fail("post release identifier must start with '.post', got: {}".format(value))
632+
post = int(value[len(".post"):])
633+
634+
# We choose `~` since almost all of the ASCII characters will be before
635+
# it. Use `ord` and `chr` functions to find a good value.
636+
return ("~", post)
637+
638+
def _new_version(
639+
*,
640+
norm,
641+
epoch = 0,
642+
release,
643+
pre = "",
644+
post = "",
645+
dev = "",
646+
local = "",
647+
is_prefix = False):
637648
self = struct(
638-
epoch = epoch,
639-
release = _release,
640-
pre = pre,
641-
post = post,
642-
dev = dev,
643-
local = local,
649+
epoch = _parse_epoch(epoch),
650+
release = _parse_release(release),
651+
pre = _parse_pre(pre),
652+
post = _parse_post(post),
653+
dev = _parse_dev(dev),
654+
local = _parse_local(local),
644655
is_prefix = is_prefix,
645656
norm = norm,
646657
eq = lambda x: _version_eq(self, x), # buildifier: disable=uninitialized
@@ -652,7 +663,7 @@ def _new_version(
652663
ne = lambda x: _version_ne(self, x), # buildifier: disable=uninitialized
653664
compatible = lambda x: _version_compatible(self, x), # buildifier: disable=uninitialized
654665
str = lambda: norm,
655-
key = lambda *, local = True: _key(self, local = local), # buildifier: disable=uninitialized
666+
key = lambda *, local = True: _version_key(self, local = local), # buildifier: disable=uninitialized
656667
)
657668

658669
return self
@@ -665,15 +676,25 @@ def _pad_zeros(release, n):
665676
release = list(release) + [0] * padding
666677
return tuple(release)
667678

679+
def _prefix_err(left, op, right):
680+
if left.is_prefix or right.is_prefix:
681+
fail("PEP440: only '==' and '!=' operators can use prefix matching: {} {} {}".format(
682+
left.norm,
683+
op,
684+
right.norm,
685+
))
686+
668687
def _version_eqq(left, right):
688+
"""=== operator"""
669689
if left.is_prefix or right.is_prefix:
670-
fail(_prefix_err(left, "<", right))
690+
fail(_prefix_err(left, "===", right))
671691

672692
# https://peps.python.org/pep-0440/#arbitrary-equality
673693
# > simple string equality operations
674694
return left.norm == right.norm
675695

676696
def _version_eq(left, right):
697+
"""== operator"""
677698
if left.is_prefix and right.is_prefix:
678699
fail("Invalid comparison: both versions cannot be prefix matching")
679700
if left.is_prefix:
@@ -699,18 +720,24 @@ def _version_eq(left, right):
699720
##and left.local == right.local
700721
)
701722

723+
def _version_compatible(left, right):
724+
"""~= operator"""
725+
if left.is_prefix or right.is_prefix:
726+
fail(_prefix_err(left, "~=", right))
727+
728+
# https://peps.python.org/pep-0440/#compatible-release
729+
# Note, the ~= operator can be also expressed as:
730+
# >= V.N, == V.*
731+
head, _, _ = right.norm.partition(".")
732+
right_star = version("{}.*".format(head))
733+
return _version_ge(left, right) and _version_eq(left, right_star)
734+
702735
def _version_ne(left, right):
736+
"""!= operator"""
703737
return not _version_eq(left, right)
704738

705-
def _prefix_err(left, op, right):
706-
if left.is_prefix or right.is_prefix:
707-
fail("PEP440: only '==' and '!=' operators can use prefix matching: {} {} {}".format(
708-
left.str(),
709-
op,
710-
right.str(),
711-
))
712-
713739
def _version_lt(left, right):
740+
"""< operator"""
714741
if left.is_prefix or right.is_prefix:
715742
fail(_prefix_err(left, "<", right))
716743

@@ -738,6 +765,7 @@ def _version_lt(left, right):
738765
return False
739766

740767
def _version_gt(left, right):
768+
"""> operator"""
741769
if left.is_prefix or right.is_prefix:
742770
fail(_prefix_err(left, ">", right))
743771

@@ -770,38 +798,29 @@ def _version_gt(left, right):
770798
# False anyway.
771799
return False
772800

773-
def _version_ge(left, right):
774-
if left.is_prefix or right.is_prefix:
775-
fail(_prefix_err(left, ">=", right))
776-
777-
# PEP440: simple order check
778-
# https://peps.python.org/pep-0440/#inclusive-ordered-comparison
779-
_left = left.key(local = False)
780-
_right = right.key(local = False)
781-
return _left > _right or _version_eq(left, right)
782-
783801
def _version_le(left, right):
802+
"""<= operator"""
784803
if left.is_prefix or right.is_prefix:
785804
fail(_prefix_err(left, "<=", right))
786805

787806
# PEP440: simple order check
788807
# https://peps.python.org/pep-0440/#inclusive-ordered-comparison
789-
_left = left.key(local = False)
790-
_right = right.key(local = False)
808+
_left = _version_key(left, local = False)
809+
_right = _version_key(right, local = False)
791810
return _left < _right or _version_eq(left, right)
792811

793-
def _version_compatible(left, right):
812+
def _version_ge(left, right):
813+
""">= operator"""
794814
if left.is_prefix or right.is_prefix:
795-
fail(_prefix_err(left, "~=", right))
815+
fail(_prefix_err(left, ">=", right))
796816

797-
# https://peps.python.org/pep-0440/#compatible-release
798-
# Note, the ~= operator can be also expressed as:
799-
# >= V.N, == V.*
800-
head, _, _ = right.norm.partition(".")
801-
right_star = version("{}.*".format(head))
802-
return left.ge(right) and left.eq(right_star)
817+
# PEP440: simple order check
818+
# https://peps.python.org/pep-0440/#inclusive-ordered-comparison
819+
_left = _version_key(left, local = False)
820+
_right = _version_key(right, local = False)
821+
return _left > _right or _version_eq(left, right)
803822

804-
def _key(self, *, local, release_key = ("z",)):
823+
def _version_key(self, *, local, release_key = ("z",)):
805824
"""This function returns a tuple that can be used in 'sorted' calls.
806825
807826
This implements the PEP440 version sorting.

0 commit comments

Comments
 (0)