Coverage for r11k/version.py: 100%
37 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-13 21:48 +0100
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-13 21:48 +0100
1"""Extension of `semver` for parsing Puppet version specifications."""
3import operator
4import re
6from typing import (
7 Callable,
8 Tuple,
9)
10try: # pragma: nocover
11 from typing import TypeAlias # type: ignore
12except ImportError: # pragma: nocover
13 from typing import Any as TypeAlias # type: ignore
15from semver import VersionInfo
17Comp: TypeAlias = Callable[[VersionInfo, VersionInfo], bool]
19min_version: VersionInfo = VersionInfo(major=0)
20max_version: VersionInfo = VersionInfo(major=2**63)
23def parse_version(version: str) -> Tuple[Comp, VersionInfo, Comp, VersionInfo]:
24 """
25 Parse a puppet version.
27 Specification for the format can be found at, but is also
28 sumarized in the version parameter.
29 https://puppet.com/docs/puppet/7/modules_metadata.html#metadata-version
31 The returning procedures min_comperator and max_comperator are
32 both procedures taking two arguments: the value to compare
33 against, and their coresponding returning version. So to test a
34 version v, the correct usage would be
35 min_comperator(v, min_version) and max_comperator(v, max_version)
37 :param version: should be on off
38 - `<a>.<b>.<c>` Exact version
39 - `<a>.x` Exact major
40 - `<a>.<b>.x` Exact minor
41 - `>= <a>.<b>.<c>` GE specified
42 - `<= <a>.<b>.<c>` LE specifiec
43 - `> <a>.<b>.<c>` GT specified
44 - `< <a>.<b>.<c>` LT specifiec
45 - `>= <a>.<b>.<c> < <d>.<e>.<f>` GE *a*.*b*.*c*, LT *d*.*e*.*f*
47 :return: A 4-tuple containing
48 - min_comperator
49 - min_version
50 - max_comperator
51 - max_version
52 """
53 if m := re.match(r'^(\d+)[.](\d+)[.](\d+)$', version):
54 min = VersionInfo(m[1], m[2], m[3])
55 max = min
56 return (operator.ge, min, operator.le, max)
58 elif m := re.match(r'^(\d+)[.]x$', version):
59 min = VersionInfo(m[1], 0, 0)
60 max = min.bump_major()
61 return (operator.ge, min, operator.lt, max)
63 elif m := re.match(r'^(\d+)[.](\d+)[.]x$', version):
64 min = VersionInfo(m[1], m[2], 0)
65 max = min.bump_minor()
66 return (operator.ge, min, operator.lt, max)
68 elif m := re.match(r'^([<>]=?) (\d+[.]\d+[.]\d+)$', version):
69 if m[1] == '<':
70 v = VersionInfo.parse(m[2])
71 return (operator.gt, min_version, operator.lt, v)
72 elif m[1] == '<=':
73 v = VersionInfo.parse(m[2])
74 return (operator.gt, min_version, operator.le, v)
75 elif m[1] == '>':
76 v = VersionInfo.parse(m[2])
77 return (operator.gt, v, operator.lt, max_version)
78 else: # m[1] == '>=':
79 v = VersionInfo.parse(m[2])
80 return (operator.ge, v, operator.lt, max_version)
82 elif m := re.match(r'^>= ?(\d+[.]\d+[.]\d+) ?< ?(\d+[.]\d+[.]\d+)$',
83 version):
84 min = VersionInfo.parse(m[1])
85 max = VersionInfo.parse(m[2])
86 return (operator.ge, min, operator.lt, max)
88 else:
89 raise ValueError('Invalid version:', version)