From 24277a72236df234564eaeafbfa11d1d91991840 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 20 Dec 2023 12:08:04 +0100 Subject: [PATCH 1/3] Remove global version variable --- Lib/test/test_clinic.py | 8 ++++---- Tools/clinic/clinic.py | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 6c6bd4e75a0b02..04c32096fde087 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -267,13 +267,13 @@ def converter_init(self): @staticmethod @contextlib.contextmanager def _clinic_version(new_version): - """Helper for test_version_*() tests""" - _saved = clinic.version - clinic.version = new_version + """Safely mutate the DSL VERSION (for version directive tests).""" + _saved = clinic.DSLParser.VERSION + clinic.DSLParser.VERSION = new_version try: yield finally: - clinic.version = _saved + clinic.DSLParser.VERSION = _saved def test_version_directive(self): dataset = ( diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 87feef1b82ca39..30d08105009a84 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -61,8 +61,6 @@ # and keyword-only # -version = '1' - NO_VARARG = "PY_SSIZE_T_MAX" CLINIC_PREFIX = "__clinic_" CLINIC_PREFIXED_ARGS = { @@ -5206,6 +5204,8 @@ class DSLParser: target_critical_section: list[str] from_version_re = re.compile(r'([*/]) +\[from +(.+)\]') + VERSION: Final[str] = '1' # DSL version. + def __init__(self, clinic: Clinic) -> None: self.clinic = clinic @@ -5242,10 +5242,9 @@ def reset(self) -> None: self.target_critical_section = [] def directive_version(self, required: str) -> None: - global version - if version_comparator(version, required) < 0: + if version_comparator(self.VERSION, required) < 0: fail("Insufficient Clinic version!\n" - f" Version: {version}\n" + f" Version: {self.VERSION}\n" f" Required: {required}") def directive_module(self, name: str) -> None: From 420b8e582716dc8bc9b6d7f6960b3ab6cb009364 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 20 Dec 2023 13:19:10 +0100 Subject: [PATCH 2/3] Quick-and-dirty: separate out version helpers --- Tools/clinic/clinic.py | 59 +++++++++--------------------------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 30d08105009a84..77a9e44d4ac0df 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -50,6 +50,11 @@ overload, ) + +# Local includes. +import libclinic + + # TODO: # # soon: @@ -373,49 +378,6 @@ def pprint_words(items: list[str]) -> str: return ", ".join(items[:-1]) + " and " + items[-1] -def version_splitter(s: str) -> tuple[int, ...]: - """Splits a version string into a tuple of integers. - - The following ASCII characters are allowed, and employ - the following conversions: - a -> -3 - b -> -2 - c -> -1 - (This permits Python-style version strings such as "1.4b3".) - """ - version: list[int] = [] - accumulator: list[str] = [] - def flush() -> None: - if not accumulator: - fail(f'Unsupported version string: {s!r}') - version.append(int(''.join(accumulator))) - accumulator.clear() - - for c in s: - if c.isdigit(): - accumulator.append(c) - elif c == '.': - flush() - elif c in 'abc': - flush() - version.append('abc'.index(c) - 3) - else: - fail(f'Illegal character {c!r} in version string {s!r}') - flush() - return tuple(version) - -def version_comparator(version1: str, version2: str) -> Literal[-1, 0, 1]: - iterator = itertools.zip_longest( - version_splitter(version1), version_splitter(version2), fillvalue=0 - ) - for a, b in iterator: - if a < b: - return -1 - if a > b: - return 1 - return 0 - - class CRenderData: def __init__(self) -> None: @@ -5242,10 +5204,13 @@ def reset(self) -> None: self.target_critical_section = [] def directive_version(self, required: str) -> None: - if version_comparator(self.VERSION, required) < 0: - fail("Insufficient Clinic version!\n" - f" Version: {self.VERSION}\n" - f" Required: {required}") + try: + if libclinic.version_comparator(self.VERSION, required) < 0: + fail("Insufficient Clinic version!\n" + f" Version: {self.VERSION}\n" + f" Required: {required}") + except libclinic.ParseVersionError as err: + fail(str(err)) def directive_module(self, name: str) -> None: fields = name.split('.')[:-1] From 0a716d3fc1aefd84311926a9e46c281f33602b0f Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 20 Dec 2023 13:41:47 +0100 Subject: [PATCH 3/3] git add --- Tools/clinic/libclinic/__init__.py | 7 ++++ Tools/clinic/libclinic/version.py | 53 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 Tools/clinic/libclinic/__init__.py create mode 100644 Tools/clinic/libclinic/version.py diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py new file mode 100644 index 00000000000000..47456050e987c1 --- /dev/null +++ b/Tools/clinic/libclinic/__init__.py @@ -0,0 +1,7 @@ +from .version import version_comparator, ParseVersionError + + +__all__ = [ + "version_comparator", + "ParseVersionError", +] diff --git a/Tools/clinic/libclinic/version.py b/Tools/clinic/libclinic/version.py new file mode 100644 index 00000000000000..3cbf658ef56ddb --- /dev/null +++ b/Tools/clinic/libclinic/version.py @@ -0,0 +1,53 @@ +import itertools + +from typing import Literal + + +class ParseVersionError(BaseException): + pass + + +def _version_splitter(s: str) -> tuple[int, ...]: + """Splits a version string into a tuple of integers. + + The following ASCII characters are allowed, and employ + the following conversions: + a -> -3 + b -> -2 + c -> -1 + (This permits Python-style version strings such as "1.4b3".) + """ + version: list[int] = [] + accumulator: list[str] = [] + def flush() -> None: + if not accumulator: + raise ParseVersionError(f'Unsupported version string: {s!r}') + version.append(int(''.join(accumulator))) + accumulator.clear() + + for c in s: + if c.isdigit(): + accumulator.append(c) + elif c == '.': + flush() + elif c in 'abc': + flush() + version.append('abc'.index(c) - 3) + else: + raise ParseVersionError( + f'Illegal character {c!r} in version string {s!r}' + ) + flush() + return tuple(version) + + +def version_comparator(version1: str, version2: str) -> Literal[-1, 0, 1]: + iterator = itertools.zip_longest( + _version_splitter(version1), _version_splitter(version2), fillvalue=0 + ) + for a, b in iterator: + if a < b: + return -1 + if a > b: + return 1 + return 0