Source code for php_version_compare.versioning

import sys
from collections import defaultdict
from itertools import zip_longest
from typing import Iterable, Optional, Union, overload

if sys.version_info >= (3, 8):
    from typing import Literal
else:
    from typing_extensions import Literal

Compare = Literal[-1, 0, 1]

Operator = Literal[
    "<", "lt", "<=", "le", ">", "gt", ">=", "ge", "==", "=", "eq", "!=", "<>", "ne"
]

_SUFFIX_WEIGHT = defaultdict(
    lambda: -1,
    {
        "dev": 0,
        "alpha": 1,
        "a": 1,
        "beta": 2,
        "b": 2,
        "rc": 3,
        "#": 4,
        "pl": 5,
        "p": 5,
    },
)


def _split_version(version: str) -> Iterable[str]:
    """
     Split a version string into its components. This function is used to split a
     version string into its major, minor, patch, and suffix components.

    Examples:
         >>> list(_split_version("1.0"))
         ['1', '0']
         >>> list(_split_version("1.0-DEV"))
         ['1', '0', 'DEV']
         >>> list(_split_version("1.0.1alpha"))
         ['1', '0', '1', 'alpha']

     Args:
         version: The version string to split.

     Yields:
         The components of the version string.
    """
    current_segment = ""

    for curr_char in version:
        if curr_char in "-+_.":
            yield current_segment
            current_segment = ""
        elif current_segment and (
            (current_segment[-1].isdigit() and curr_char.isalpha())
            or (current_segment[-1].isalpha() and curr_char.isdigit())
        ):
            yield current_segment
            current_segment = curr_char
        else:
            current_segment += curr_char

    yield current_segment


[docs] def canonicalize_version(version: str) -> str: """ Canonicalize a version string into a "PHP-style" version string. This function is used to normalize version strings before comparing them. Examples: >>> canonicalize_version("1.0") '1.0' >>> canonicalize_version("1.0-DEV") '1.0.DEV' >>> canonicalize_version("1.0.1alpha") '1.0.1.alpha' Args: version: The version string to canonicalize. Returns: The canonicalized version string. """ return ".".join(_split_version(version))
def _version_compare(version1: str, version2: str) -> Compare: def _compare_part(part1: str, part2: str) -> int: if part1.isdigit() and part2.isdigit(): return int(part1) - int(part2) if part1.isdigit(): return 1 if part2.isdigit(): return -1 return _SUFFIX_WEIGHT[part1] - _SUFFIX_WEIGHT[part2] version1_parts = map(str.lower, _split_version(version1)) version2_parts = map(str.lower, _split_version(version2)) for part1, part2 in zip_longest(version1_parts, version2_parts, fillvalue="#"): result = _compare_part(part1, part2) if result != 0: return 1 if result > 0 else -1 return 0 @overload def version_compare(version1: str, version2: str) -> Compare: ... @overload def version_compare(version1: str, version2: str, operator: Operator) -> bool: ...
[docs] def version_compare( version1: str, version2: str, operator: Optional[Operator] = None ) -> Union[Compare, bool]: """ Compare two version strings according to PHP's version_compare function. Examples: >>> version_compare("1.0", "1.0") 0 >>> version_compare("1.0", "1.1") -1 >>> version_compare("1.0-pl1", "1.0") 1 >>> version_compare("1.0", "1.0", "==") True >>> version_compare("1.0", "1.1", "<") True >>> version_compare("1.0", "1.1", ">") False Args: version1: The first version string. version2: The second version string. operator: The (optional) comparison operator. Must be one of "<", "lt", "<=", "le", ">", "gt", ">=", "ge", "==", "=", "eq", "!=", "<>", or "ne". Returns: If `operator` is None, returns -1 if `version1` is less than `version2`, 0 if they are equal, and 1 if `version1` is greater than `version2`. If `operator` is provided, returns True if the comparison is true, and False otherwise. """ result = _version_compare(version1, version2) if operator is None: return result elif operator in ("<", "lt"): return result < 0 elif operator in ("<=", "le"): return result <= 0 elif operator in (">", "gt"): return result > 0 elif operator in (">=", "ge"): return result >= 0 elif operator in ("==", "=", "eq"): return result == 0 elif operator in ("!=", "<>", "ne"): return result != 0 else: raise ValueError(f"Invalid operator: {operator}")