Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions python/private/version.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ def _in(reference):
return lambda token: token in reference

def _ctx(start):
return {"norm": "", "start": start}
"""Creates a context, which is state for parsing (or sub-parsing)."""
return {
# The result value from parsing
"norm": "",
# Where in the parser's input string this context starts.
"start": start,
}

def _open_context(self):
"""Open an new parsing ctx.
Expand All @@ -60,7 +66,16 @@ def _open_context(self):
return self.contexts[-1]

def _accept(self, key = None):
"""Close the current ctx successfully and merge the results."""
"""Close the current ctx successfully and merge the results.

Args:
self: {type}`Parser}
key: {type}`str | None` the key to store the result in
the most recent context. If not set, the key is "norm".

Returns:
{type}`bool` always True
"""
finished = self.contexts.pop()
self.contexts[-1]["norm"] += finished["norm"]
if key:
Expand All @@ -79,7 +94,14 @@ def _discard(self, key = None):
return False

def _new(input):
"""Create a new normalizer"""
"""Create a new parser

Args:
input: {type}`str` input to parse

Returns:
{type}`Parser` a struct for a parser object.
"""
self = struct(
input = input,
contexts = [_ctx(0)],
Expand Down Expand Up @@ -167,7 +189,7 @@ def accept_placeholder(parser):
return parser.accept()

def accept_digits(parser):
"""Accept multiple digits (or placeholders).
"""Accept multiple digits (or placeholders), up to a non-digit/placeholder.

Args:
parser: The normalizer.
Expand Down Expand Up @@ -275,13 +297,20 @@ def accept_separator_alnum(parser):
Returns:
whether a separator and an alphanumeric string were accepted.
"""
parser.open_context()
ctx = parser.open_context()

# PEP 440: Local version segments
if (
accept(parser, _in([".", "-", "_"]), ".") and
(accept_digits(parser) or accept_alnum(parser))
):
if not accept(parser, _in([".", "-", "_"]), "."):
return parser.discard()

if accept_alnum(parser):
# First character is separator; skip it.
value = ctx["norm"][1:]

# PEP 440: Integer Normalization
if value.isdigit():
value = str(int(value))
ctx["norm"] = ctx["norm"][0] + value
return parser.accept()

return parser.discard()
Expand Down
8 changes: 8 additions & 0 deletions tests/version/version_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ def _test_normalization(env):

_tests.append(_test_normalization)

def _test_normalize_local(env):
# Verify a local with a [digit][non-digit] sequence parses ok
in_str = "0.1.0+brt.9e"
actual = version.normalize(in_str)
env.expect.that_str(actual).equals(in_str)

_tests.append(_test_normalize_local)

def _test_ordering(env):
want = [
# Taken from https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering
Expand Down