Bug 1899239 - update vendored mozilla_version to 3.1.0. r=mach-reviewers,releng-reviewers,bhearsum

Differential Revision: https://phabricator.services.mozilla.com/D211816
This commit is contained in:
Julien Cristau 2024-05-29 08:55:48 +00:00
parent 9c60f55dee
commit 7015137c0c
17 changed files with 950 additions and 526 deletions

View file

@ -1,22 +0,0 @@
mozilla_version/__init__.py,sha256=ro9IDUmjUco6GHJhqbgynResbswRnh6HL5Iv1ttDuWU,60
mozilla_version/balrog.py,sha256=p75Ln9W5IiEzO8C-HIDmKsgdpN4hc9zbvTEOMZodNhQ,4961
mozilla_version/errors.py,sha256=DvBsNaJdhpaT3wb4E3Rnl7KAuxnqXlElBllYOcijwbQ,2468
mozilla_version/fenix.py,sha256=zruk3WsTMCeaRaaNi5ezxaSAb8t_8CATXpLhbVryOPM,199
mozilla_version/gecko.py,sha256=t4JcuF7mehXqFhKIxvFM3hrEx-qZpCmF9YcwWTUuCHM,24783
mozilla_version/maven.py,sha256=jH0F-Rq3tJJ_N3KbNE1KBi0i_BlXGZCYyjZ7K_CRxoM,1988
mozilla_version/mobile.py,sha256=3VJgbC90NpQMUTfy75zWyK4kMKjb3E7MnE_cfdHZriM,9520
mozilla_version/parser.py,sha256=kwaw3UeAbWgUFtCmCheY9grKwabmq9tc64JyTlPrHS8,1335
mozilla_version/version.py,sha256=MNTbIWmRWlN4jofkt2wKmyq3z3MWGWFDqrJYN1nKxj0,7929
mozilla_version/test/__init__.py,sha256=r9z_NrSZeN6vCBiocNFI00XPm2bveSogzO-jsLa7Q-I,87
mozilla_version/test/test_balrog.py,sha256=olr3NBdF1wtsz2Rfnb1aT3-cD7YgWQlDMfszmgz-ZgM,7839
mozilla_version/test/test_errors.py,sha256=oR6PZorSCYDWDRrye560gz6MCXD2E4J-eyfIVCVoenw,933
mozilla_version/test/test_fenix.py,sha256=qs8sD39N_cM9rNEZxyCaLuxx53hIIeHZIrJe_EBpYoQ,193
mozilla_version/test/test_gecko.py,sha256=TbIoRzfvCqtbrdIOw8aeNi-eieuZBSCE9c7nNghsOps,24494
mozilla_version/test/test_maven.py,sha256=_KaMDq47nQNctmPfA8zbTSq35vUFtaHyLkjdP9HL0zk,3526
mozilla_version/test/test_mobile.py,sha256=uMNZhPE1Go4vJ7hxzIs23T9qBVbNYQVs6gjN32NTP4U,11948
mozilla_version/test/test_version.py,sha256=AeWRvkgW739mEbq3JBd1hlY9hQqHro4h9gaUuLAChqU,7441
mozilla_version-2.0.0.dist-info/LICENSE,sha256=YCIsKMGn9qksffmOXF9EWeYk5uKF4Lm5RGevX2qzND0,15922
mozilla_version-2.0.0.dist-info/METADATA,sha256=3ZeZKRMprBj6yz8xBbHQTvK5h3vk18joltdq29yj2gY,482
mozilla_version-2.0.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
mozilla_version-2.0.0.dist-info/top_level.txt,sha256=K1r8SXa4ny0i7OTfimG0Ct33oHkXtLjuU1E5_aHBe94,16
mozilla_version-2.0.0.dist-info/RECORD,,

View file

@ -360,4 +360,3 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

View file

@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: mozilla-version
Version: 2.0.0
Version: 3.1.0
Summary: Process Firefox versions numbers. Tells whether they are valid or not, whether they are nightlies or regular releases, whether this version precedes that other.
Home-page: https://github.com/mozilla-releng/mozilla-version
Author: Mozilla Release Engineering
@ -8,5 +8,5 @@ Author-email: release+python@mozilla.com
License: MPL2
Classifier: Programming Language :: Python :: 3
License-File: LICENSE
Requires-Dist: attrs (>=19.2)
Requires-Dist: attrs >=19.2

View file

@ -0,0 +1,25 @@
mozilla_version/__init__.py,sha256=oITKXEftlJ9dVeCJgvhk39LIP3vnXuXoobkJj4mNAxU,391
mozilla_version/balrog.py,sha256=WgnQEtMa16JvKisD4JTFnP2gd5e6_K9QcHxkOqQ9mJk,5072
mozilla_version/errors.py,sha256=C8mutZqgu68eCLuD7sr4dggBym4QzsMsI1iKMNPOGQI,2401
mozilla_version/fenix.py,sha256=10Z7jbST3xVgmLiCuJZH8GqffwChPIfJdBu1hig3jP8,200
mozilla_version/gecko.py,sha256=8ifd1acKkanGOUn4Q0A841kr_68zAm6icrmouDZW6S8,31141
mozilla_version/maven.py,sha256=H5bZ-9QitMdT89o4mRXkTaeb7iQc8yVTJnmauyxSImU,1971
mozilla_version/mobile.py,sha256=09aPz1f1YyEnb4bGNeWF-rvPfnEIp4iK1DL5XK0EYU0,10146
mozilla_version/parser.py,sha256=kwaw3UeAbWgUFtCmCheY9grKwabmq9tc64JyTlPrHS8,1335
mozilla_version/version.py,sha256=wmB66af-0OkjPxZ-NRHraayY1ujAccIZ3q_8GAkTS-U,10212
mozilla_version/test/__init__.py,sha256=ui4glNH_cCoz4Ex7hcZhHTcstOPJb2wcojFiNvvIALI,88
mozilla_version/test/test_balrog.py,sha256=PY1b_q5Vhr6fhyrSB-sMwQC86CQQhLtAD2cIisbu51U,9431
mozilla_version/test/test_default_imports.py,sha256=gj9fuHANTOQq6isXmVIoE1sXDKO46xmM_H3jxgDE7W0,303
mozilla_version/test/test_errors.py,sha256=KLe6NiicS7aer7NURynsOWTZGZwKp8OQauX_nGJ_HdQ,1037
mozilla_version/test/test_fenix.py,sha256=qs8sD39N_cM9rNEZxyCaLuxx53hIIeHZIrJe_EBpYoQ,193
mozilla_version/test/test_gecko.py,sha256=vPwn1vo9iar8PD_2Mw-wi2tZaSVVzL5l3BYGOanUjqY,49787
mozilla_version/test/test_maven.py,sha256=JnBLgIngC83roeKM4U_PL2_op_VZD9_MQT0KIhAWtxw,3904
mozilla_version/test/test_mobile.py,sha256=2koHZxIf5hQpHcvWiuwiua7CmmBq0ADUPYhqJzpw1xM,12792
mozilla_version/test/test_version.py,sha256=7mkWoAViZ8pto9UU6_BSLSa8anKB29cNF7lcgurMyPk,8308
mozilla_version/test/integration/__init__.py,sha256=WbyyzEaWz3-1bSc9IOrFjBqzYJdVpLPcPy4PpJchpA8,345
mozilla_version/test/integration/test_product_details.py,sha256=oUdCi3dbkMx_8zJLo_pjy86LCcZLUPDnVq3TVPIkarw,1369
mozilla_version-3.1.0.dist-info/LICENSE,sha256=rxdbnZbuk8IaA2FS4bkFsLlTBNSujCySHHYJEAuo334,15921
mozilla_version-3.1.0.dist-info/METADATA,sha256=zjWD9Nide8MuhW010gJ0VJ-snESn3wdZGk7TvwtTZTs,480
mozilla_version-3.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
mozilla_version-3.1.0.dist-info/top_level.txt,sha256=K1r8SXa4ny0i7OTfimG0Ct33oHkXtLjuU1E5_aHBe94,16
mozilla_version-3.1.0.dist-info/RECORD,,

View file

@ -1,5 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.38.4)
Generator: bdist_wheel (0.43.0)
Root-Is-Purelib: true
Tag: py3-none-any

View file

@ -1 +1,17 @@
"""Defines characteristics of Mozilla's version numbers."""
from mozilla_version.gecko import (
DeveditionVersion,
FirefoxVersion,
ThunderbirdVersion,
)
from mozilla_version.maven import MavenVersion
from mozilla_version.mobile import MobileVersion
__all__ = [
"DeveditionVersion",
"FirefoxVersion",
"MavenVersion",
"MobileVersion",
"ThunderbirdVersion",
]

View file

@ -17,23 +17,27 @@ Examples:
previous_release = BalrogReleaseName.parse('firefox-60.0-build2')
previous_release < balrog_release # True
invalid = BalrogReleaseName.parse('60.0.1') # raises PatternNotMatchedError
invalid = BalrogReleaseName.parse('firefox-60.0.1') # raises PatternNotMatchedError
BalrogReleaseName.parse('60.0.1') # raises PatternNotMatchedError
BalrogReleaseName.parse('firefox-60.0.1') # raises PatternNotMatchedError
# Releases can be built thanks to version classes like FirefoxVersion
BalrogReleaseName('firefox', FirefoxVersion(60, 0, 1, 1)) # 'firefox-60.0.1-build1'
BalrogReleaseName('firefox', FirefoxVersion(60, 0, 1)) # 'firefox-60.0-build1'
"""
import attr
import re
from mozilla_version.errors import PatternNotMatchedError
from mozilla_version.parser import get_value_matched_by_regex
from mozilla_version.gecko import (
GeckoVersion, FirefoxVersion, DeveditionVersion, FennecVersion, ThunderbirdVersion
)
import attr
from mozilla_version.errors import PatternNotMatchedError
from mozilla_version.gecko import (
DeveditionVersion,
FennecVersion,
FirefoxVersion,
GeckoVersion,
ThunderbirdVersion,
)
from mozilla_version.parser import get_value_matched_by_regex
_VALID_ENOUGH_BALROG_RELEASE_PATTERN = re.compile(
r"^(?P<product>[a-z]+)-(?P<version>.+)$", re.IGNORECASE
@ -41,17 +45,17 @@ _VALID_ENOUGH_BALROG_RELEASE_PATTERN = re.compile(
_SUPPORTED_PRODUCTS = {
'firefox': FirefoxVersion,
'devedition': DeveditionVersion,
'fennec': FennecVersion,
'thunderbird': ThunderbirdVersion,
"firefox": FirefoxVersion,
"devedition": DeveditionVersion,
"fennec": FennecVersion,
"thunderbird": ThunderbirdVersion,
}
def _supported_product(string):
product = string.lower()
if product not in _SUPPORTED_PRODUCTS:
raise PatternNotMatchedError(string, patterns=('unknown product',))
raise PatternNotMatchedError(string, patterns=("unknown product",))
return product
@ -60,6 +64,7 @@ def _products_must_be_identical(method):
if this.product != other.product:
raise ValueError(f'Cannot compare "{this.product}" and "{other.product}"')
return method(this, other)
return checker
@ -68,8 +73,10 @@ class BalrogReleaseName:
"""Class that validates and handles Balrog release names.
Raises:
PatternNotMatchedError: if a parsed string doesn't match the pattern of a valid release
MissingFieldError: if a mandatory field is missing in the string. Mandatory fields are
PatternNotMatchedError: if a parsed string doesn't match the pattern of a valid
release
MissingFieldError: if a mandatory field is missing in the string. Mandatory
fields are
`product`, `major_number`, `minor_number`, and `build_number`
ValueError: if an integer can't be cast or is not (strictly) positive
TooManyTypesError: if the string matches more than 1 `VersionType`
@ -83,23 +90,29 @@ class BalrogReleaseName:
def __attrs_post_init__(self):
"""Ensure attributes are sane all together."""
if self.version.build_number is None:
raise PatternNotMatchedError(self, patterns=('build_number must exist',))
raise PatternNotMatchedError(self, patterns=("build_number must exist",))
@classmethod
def parse(cls, release_string):
"""Construct an object representing a valid Firefox version number."""
regex_matches = _VALID_ENOUGH_BALROG_RELEASE_PATTERN.match(release_string)
if regex_matches is None:
raise PatternNotMatchedError(release_string, (_VALID_ENOUGH_BALROG_RELEASE_PATTERN,))
raise PatternNotMatchedError(
release_string, (_VALID_ENOUGH_BALROG_RELEASE_PATTERN,)
)
product = get_value_matched_by_regex('product', regex_matches, release_string)
product = get_value_matched_by_regex("product", regex_matches, release_string)
try:
VersionClass = _SUPPORTED_PRODUCTS[product.lower()]
version_class = _SUPPORTED_PRODUCTS[product.lower()]
except KeyError:
raise PatternNotMatchedError(release_string, patterns=('unknown product',))
raise PatternNotMatchedError(
release_string, patterns=("unknown product",)
) from None
version_string = get_value_matched_by_regex('version', regex_matches, release_string)
version = VersionClass.parse(version_string)
version_string = get_value_matched_by_regex(
"version", regex_matches, release_string
)
version = version_class.parse(version_string)
return cls(product, version)
@ -108,8 +121,8 @@ class BalrogReleaseName:
Computes a new string based on the given attributes.
"""
version_string = str(self.version).replace('build', '-build')
return f'{self.product}-{version_string}'
version_string = str(self.version).replace("build", "-build")
return f"{self.product}-{version_string}"
@_products_must_be_identical
def __eq__(self, other):

View file

@ -13,13 +13,12 @@ class PatternNotMatchedError(ValueError):
"""Initialize error."""
number_of_patterns = len(patterns)
if number_of_patterns == 0:
raise ValueError('At least one pattern must be provided')
elif number_of_patterns == 1:
raise ValueError("At least one pattern must be provided")
if number_of_patterns == 1:
message = f'"{string}" does not match the pattern: {patterns[0]}'
else:
message = '"{}" does not match the patterns:\n - {}'.format(
string,
'\n - '.join(patterns)
string, "\n - ".join(patterns)
)
super().__init__(message)
@ -35,10 +34,9 @@ class NoVersionTypeError(ValueError):
def __init__(self, version_string):
"""Initialize error."""
super().__init__(
'Version "{}" matched the pattern of a valid version, but it is unable to '
'find what type it is. This is likely a bug in mozilla-version'.format(
version_string
)
f'Version "{version_string}" matched the pattern of a valid version, but '
"it is unable to find what type it is. This is likely a bug in "
"mozilla-version"
)
@ -69,7 +67,6 @@ class TooManyTypesError(ValueError):
def __init__(self, version_string, first_matched_type, second_matched_type):
"""Initialize error."""
super().__init__(
'Release "{}" cannot match types "{}" and "{}"'.format(
version_string, first_matched_type, second_matched_type
)
f'Release "{version_string}" cannot match types "{first_matched_type}" and '
f'"{second_matched_type}"'
)

View file

@ -1,3 +1,4 @@
"""Deprecated module for backwards compatibility."""
# TODO remove in a future release - deprecated in favor of MobileVersion
from mozilla_version.mobile import MobileVersion as FenixVersion # noqa

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,9 @@
"""Defines characteristics of a Maven version at Mozilla."""
import attr
import re
import attr
from mozilla_version.version import BaseVersion
@ -10,23 +11,27 @@ from mozilla_version.version import BaseVersion
class MavenVersion(BaseVersion):
"""Class that validates and handles Maven version numbers.
At Mozilla, Maven packages are used in projects like "GeckoView" or "Android-Components".
At Mozilla, Maven packages are used in projects like "GeckoView" or
"Android-Components".
"""
is_snapshot = attr.ib(type=bool, default=False)
is_beta = attr.ib(type=bool, default=False, init=False)
is_release_candidate = attr.ib(type=bool, default=False, init=False)
_VALID_ENOUGH_VERSION_PATTERN = re.compile(r"""
_VALID_ENOUGH_VERSION_PATTERN = re.compile(
r"""
^(?P<major_number>\d+)
\.(?P<minor_number>\d+)
(\.(?P<patch_number>\d+))?
(?P<is_snapshot>-SNAPSHOT)?$""", re.VERBOSE)
(?P<is_snapshot>-SNAPSHOT)?$""",
re.VERBOSE,
)
@classmethod
def parse(cls, version_string):
"""Construct an object representing a valid Maven version number."""
return super().parse(version_string, regex_groups=('is_snapshot', ))
return super().parse(version_string, regex_groups=("is_snapshot",))
def __str__(self):
"""Implement string representation.
@ -36,14 +41,14 @@ class MavenVersion(BaseVersion):
string = super().__str__()
if self.is_snapshot:
string = f'{string}-SNAPSHOT'
string = f"{string}-SNAPSHOT"
return string
def _compare(self, other):
if isinstance(other, str):
other = MavenVersion.parse(other)
elif not isinstance(other, MavenVersion):
if not isinstance(other, MavenVersion):
raise ValueError(f'Cannot compare "{other}", type not supported!')
difference = super()._compare(other)
@ -52,14 +57,11 @@ class MavenVersion(BaseVersion):
if not self.is_snapshot and other.is_snapshot:
return 1
elif self.is_snapshot and not other.is_snapshot:
if self.is_snapshot and not other.is_snapshot:
return -1
else:
return 0
return 0
@property
def is_release(self):
"""Return `True` if the others are both False."""
return not any((
self.is_beta, self.is_release_candidate, self.is_snapshot
))
return not any((self.is_beta, self.is_release_candidate, self.is_snapshot))

View file

@ -1,12 +1,17 @@
"""Defines characteristics of a Mobile version at Mozilla."""
import attr
import re
from mozilla_version.errors import PatternNotMatchedError, TooManyTypesError, NoVersionTypeError
import attr
from mozilla_version.errors import (
NoVersionTypeError,
PatternNotMatchedError,
TooManyTypesError,
)
from mozilla_version.gecko import GeckoVersion
from mozilla_version.version import BaseVersion, VersionType
from mozilla_version.parser import strictly_positive_int_or_none
from mozilla_version.version import BaseVersion, ShipItVersion, VersionType
def _find_type(version):
@ -14,9 +19,7 @@ def _find_type(version):
def ensure_version_type_is_not_already_defined(previous_type, candidate_type):
if previous_type is not None:
raise TooManyTypesError(
str(version), previous_type, candidate_type
)
raise TooManyTypesError(str(version), previous_type, candidate_type)
if version.is_nightly:
version_type = VersionType.NIGHTLY
@ -24,7 +27,9 @@ def _find_type(version):
ensure_version_type_is_not_already_defined(version_type, VersionType.BETA)
version_type = VersionType.BETA
if version.is_release_candidate:
ensure_version_type_is_not_already_defined(version_type, VersionType.RELEASE_CANDIDATE)
ensure_version_type_is_not_already_defined(
version_type, VersionType.RELEASE_CANDIDATE
)
version_type = VersionType.RELEASE_CANDIDATE
if version.is_release:
ensure_version_type_is_not_already_defined(version_type, VersionType.RELEASE)
@ -37,13 +42,14 @@ def _find_type(version):
@attr.s(frozen=True, eq=False, hash=True)
class MobileVersion(BaseVersion):
class MobileVersion(ShipItVersion):
"""Validate and handle version numbers for mobile products.
This covers applications such as Fenix and Focus for Android.
"""
_VALID_ENOUGH_VERSION_PATTERN = re.compile(r"""
_VALID_ENOUGH_VERSION_PATTERN = re.compile(
r"""
^(?P<major_number>\d+)
\.(?P<minor_number>\d+)
(\.(?P<patch_number>\d+))?
@ -52,10 +58,15 @@ class MobileVersion(BaseVersion):
|(-beta\.|b)(?P<beta_number>\d+)
|-rc\.(?P<release_candidate_number>\d+)
)?
-?(build(?P<build_number>\d+))?$""", re.VERBOSE)
-?(build(?P<build_number>\d+))?$""",
re.VERBOSE,
)
_OPTIONAL_NUMBERS = (
'patch_number', 'beta_number', 'release_candidate_number', 'build_number'
"patch_number",
"beta_number",
"release_candidate_number",
"build_number",
)
_ALL_NUMBERS = BaseVersion._MANDATORY_NUMBERS + _OPTIONAL_NUMBERS
@ -66,87 +77,103 @@ class MobileVersion(BaseVersion):
# Android-Components later (bug 1800611)
_LAST_VERSION_TO_FOLLOW_MAVEN_PATTERN = 108
build_number = attr.ib(type=int, converter=strictly_positive_int_or_none, default=None)
beta_number = attr.ib(type=int, converter=strictly_positive_int_or_none, default=None)
build_number = attr.ib(
type=int, converter=strictly_positive_int_or_none, default=None
)
beta_number = attr.ib(
type=int, converter=strictly_positive_int_or_none, default=None
)
is_nightly = attr.ib(type=bool, default=False)
release_candidate_number = attr.ib(
type=int, converter=strictly_positive_int_or_none, default=None
)
version_type = attr.ib(init=False, default=attr.Factory(_find_type, takes_self=True))
version_type = attr.ib(
init=False, default=attr.Factory(_find_type, takes_self=True)
)
def __attrs_post_init__(self):
"""Ensure attributes are sane all together."""
error_messages = []
def _get_all_error_messages_for_attributes(self):
error_messages = super()._get_all_error_messages_for_attributes()
if self.is_gecko_pattern:
error_messages.extend([
pattern_message
for condition, pattern_message in ((
self.beta_number is not None and self.patch_number is not None,
'Beta number and patch number cannot be both defined',
), (
self.release_candidate_number is not None,
'Release candidate number cannot be defined after Mobile v{}'.format(
self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
),
), (
self.major_number > self._LAST_VERSION_TO_FOLLOW_MAVEN_PATTERN and
self.minor_number == 0 and
self.patch_number == 0,
'Minor number and patch number cannot be both equal to 0 past '
'Mobile v{}'.format(
self._LAST_VERSION_TO_FOLLOW_MAVEN_PATTERN
),
), (
self.minor_number != 0 and self.patch_number is None,
'Patch number cannot be undefined if minor number is greater than 0',
))
if condition
])
error_messages.extend(
[
pattern_message
for condition, pattern_message in (
(
self.beta_number is not None
and self.patch_number is not None,
"Beta number and patch number cannot be both defined",
),
(
self.release_candidate_number is not None,
"Release candidate number cannot be defined after Mobile "
f"v{self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN}",
),
(
self.major_number
> self._LAST_VERSION_TO_FOLLOW_MAVEN_PATTERN
and self.minor_number == 0
and self.patch_number == 0,
"Minor number and patch number cannot be both equal to 0 "
f"past Mobile {self._LAST_VERSION_TO_FOLLOW_MAVEN_PATTERN}",
),
(
self.minor_number != 0 and self.patch_number is None,
"Patch number cannot be undefined if minor number is "
"greater than 0",
),
)
if condition
]
)
else:
error_messages.extend([
pattern_message
for condition, pattern_message in ((
self.patch_number is None,
'Patch number must be defined before Mobile v{}'.format(
self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
),
), (
self.is_nightly,
'Nightlies are not supported until Mobile v{}'.format(
self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
),
))
if condition
])
error_messages.extend(
[
pattern_message
for condition, pattern_message in (
(
self.patch_number is None,
"Patch number must be defined before Mobile "
f"v{self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN}",
),
(
self.is_nightly,
"Nightlies are not supported until Mobile "
f"v{self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN}",
),
)
if condition
]
)
if error_messages:
raise PatternNotMatchedError(self, patterns=error_messages)
return error_messages
@classmethod
def parse(cls, version_string):
"""Construct an object representing a valid Firefox version number."""
mobile_version = super().parse(
version_string, regex_groups=('is_nightly',)
)
mobile_version = super().parse(version_string, regex_groups=("is_nightly",))
# Betas are supported in both the old and the gecko pattern. Let's make sure
# the string we got follows the right rules
if mobile_version.is_beta:
if mobile_version.is_gecko_pattern and '-beta.' in version_string:
if mobile_version.is_gecko_pattern and "-beta." in version_string:
raise PatternNotMatchedError(
mobile_version, ['"-beta." can only be used before Mobile v{}'.format(
cls._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
)]
mobile_version,
[
'"-beta." can only be used before Mobile '
f"v{cls._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN}"
],
)
if not mobile_version.is_gecko_pattern and re.search(r"\db\d", version_string):
if not mobile_version.is_gecko_pattern and re.search(
r"\db\d", version_string
):
raise PatternNotMatchedError(
mobile_version, [
'"b" cannot be used before Mobile v{} to define a '
'beta version'.format(
cls._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
)
]
mobile_version,
[
'"b" cannot be used before Mobile '
f"v{cls._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN} to define a "
"beta version"
],
)
return mobile_version
@ -158,20 +185,24 @@ class MobileVersion(BaseVersion):
@property
def is_beta(self):
"""Return `True` if `MobileVersion` was built with a string matching a beta version."""
"""Return `True` if `MobileVersion` was built with a `beta_number`."""
return self.beta_number is not None
@property
def is_release_candidate(self):
"""Return `True` if `MobileVersion` was built with a string matching an RC version."""
"""Return `True` if `MobilVersion` was built with `release_candidate_number`."""
return self.release_candidate_number is not None
@property
def is_release(self):
"""Return `True` if `MobileVersion` was built with a string matching a release version."""
return not any((
self.is_nightly, self.is_beta, self.is_release_candidate,
))
"""Return `True` if `MobileVersion` was built as a release version."""
return not any(
(
self.is_nightly,
self.is_beta,
self.is_release_candidate,
)
)
def __str__(self):
"""Implement string representation.
@ -179,20 +210,22 @@ class MobileVersion(BaseVersion):
Computes a new string based on the given attributes.
"""
if self.is_gecko_pattern:
string = str(GeckoVersion(
major_number=self.major_number,
minor_number=self.minor_number,
patch_number=self.patch_number,
build_number=self.build_number,
beta_number=self.beta_number,
is_nightly=self.is_nightly,
))
string = str(
GeckoVersion(
major_number=self.major_number,
minor_number=self.minor_number,
patch_number=self.patch_number,
build_number=self.build_number,
beta_number=self.beta_number,
is_nightly=self.is_nightly,
)
)
else:
string = super().__str__()
if self.is_beta:
string = f'{string}-beta.{self.beta_number}'
string = f"{string}-beta.{self.beta_number}"
elif self.is_release_candidate:
string = f'{string}-rc.{self.release_candidate_number}'
string = f"{string}-rc.{self.release_candidate_number}"
return string
@ -216,7 +249,9 @@ class MobileVersion(BaseVersion):
return beta_difference
if self.is_release_candidate and other.is_release_candidate:
rc_difference = self.release_candidate_number - other.release_candidate_number
rc_difference = (
self.release_candidate_number - other.release_candidate_number
)
if rc_difference != 0:
return rc_difference
@ -228,23 +263,24 @@ class MobileVersion(BaseVersion):
def _create_bump_kwargs(self, field):
bump_kwargs = super()._create_bump_kwargs(field)
if field != 'build_number' and bump_kwargs.get('build_number') == 0:
del bump_kwargs['build_number']
if bump_kwargs.get('beta_number') == 0:
if field != "build_number" and bump_kwargs.get("build_number") == 0:
del bump_kwargs["build_number"]
if bump_kwargs.get("beta_number") == 0:
if self.is_beta:
bump_kwargs['beta_number'] = 1
bump_kwargs["beta_number"] = 1
else:
del bump_kwargs['beta_number']
del bump_kwargs["beta_number"]
if field != 'release_candidate_number':
del bump_kwargs['release_candidate_number']
if field != "release_candidate_number":
del bump_kwargs["release_candidate_number"]
if (
field == 'major_number'
and bump_kwargs.get('major_number') == self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
field == "major_number"
and bump_kwargs.get("major_number")
== self._FIRST_VERSION_TO_FOLLOW_GECKO_PATTERN
):
del bump_kwargs['patch_number']
del bump_kwargs["patch_number"]
bump_kwargs['is_nightly'] = self.is_nightly
bump_kwargs["is_nightly"] = self.is_nightly
return bump_kwargs

View file

@ -1,16 +1,17 @@
"""Defines common characteristics of a version at Mozilla."""
import attr
import re
from contextlib import suppress
from enum import Enum
import attr
from mozilla_version.errors import MissingFieldError, PatternNotMatchedError
from mozilla_version.parser import (
get_value_matched_by_regex,
does_regex_have_group,
get_value_matched_by_regex,
positive_int,
positive_int_or_none
positive_int_or_none,
)
@ -22,14 +23,17 @@ class BaseVersion:
minor_number = attr.ib(type=int, converter=positive_int)
patch_number = attr.ib(type=int, converter=positive_int_or_none, default=None)
_MANDATORY_NUMBERS = ('major_number', 'minor_number')
_OPTIONAL_NUMBERS = ('patch_number', )
_MANDATORY_NUMBERS = ("major_number", "minor_number")
_OPTIONAL_NUMBERS = ("patch_number",)
_ALL_NUMBERS = _MANDATORY_NUMBERS + _OPTIONAL_NUMBERS
_VALID_ENOUGH_VERSION_PATTERN = re.compile(r"""
_VALID_ENOUGH_VERSION_PATTERN = re.compile(
r"""
^(?P<major_number>\d+)
\.(?P<minor_number>\d+)
(\.(?P<patch_number>\d+))?$""", re.VERBOSE)
(\.(?P<patch_number>\d+))?$""",
re.VERBOSE,
)
@classmethod
def parse(cls, version_string, regex_groups=()):
@ -37,17 +41,21 @@ class BaseVersion:
regex_matches = cls._VALID_ENOUGH_VERSION_PATTERN.match(version_string)
if regex_matches is None:
raise PatternNotMatchedError(version_string, (cls._VALID_ENOUGH_VERSION_PATTERN,))
raise PatternNotMatchedError(
version_string, (cls._VALID_ENOUGH_VERSION_PATTERN,)
)
kwargs = {}
for field in cls._MANDATORY_NUMBERS:
kwargs[field] = get_value_matched_by_regex(field, regex_matches, version_string)
kwargs[field] = get_value_matched_by_regex(
field, regex_matches, version_string
)
for field in cls._OPTIONAL_NUMBERS:
try:
kwargs[field] = get_value_matched_by_regex(field, regex_matches, version_string)
except MissingFieldError:
pass
with suppress(MissingFieldError):
kwargs[field] = get_value_matched_by_regex(
field, regex_matches, version_string
)
for regex_group in regex_groups:
kwargs[regex_group] = does_regex_have_group(regex_matches, regex_group)
@ -63,7 +71,7 @@ class BaseVersion:
if self.patch_number is not None:
semvers.append(str(self.patch_number))
return '.'.join(semvers)
return ".".join(semvers)
def __eq__(self, other):
"""Implement `==` operator."""
@ -103,7 +111,7 @@ class BaseVersion:
elif not isinstance(other, BaseVersion):
raise ValueError(f'Cannot compare "{other}", type not supported!')
for field in ('major_number', 'minor_number', 'patch_number'):
for field in ("major_number", "minor_number", "patch_number"):
difference = self._substract_other_number_from_this_number(other, field)
if difference != 0:
return difference
@ -111,10 +119,10 @@ class BaseVersion:
return 0
def _substract_other_number_from_this_number(self, other, field):
# BaseVersion sets unmatched numbers to None. E.g.: "32.0" sets the patch_number to None.
# Because of this behavior, `getattr(self, 'patch_number')` returns None too. That's why
# we can't call `getattr(self, field, 0)` directly, it will return None for all unmatched
# numbers
# BaseVersion sets unmatched numbers to None. E.g.: "32.0" sets the patch_number
# to None. Because of this behavior, `getattr(self, 'patch_number')` returns
# None too. That's why we can't call `getattr(self, field, 0)` directly, it will
# return None for all unmatched numbers
this_number = getattr(self, field, None)
this_number = 0 if this_number is None else this_number
other_number = getattr(other, field, None)
@ -126,14 +134,14 @@ class BaseVersion:
"""Bump the number defined `field`.
Returns:
A new BaseVersion with the right field bumped and the following ones set to 0,
if they exist or if they need to be set.
A new BaseVersion with the right field bumped and the following ones set to
0, if they exist or if they need to be set.
For instance:
* 32.0 is bumped to 33.0, because the patch number does not exist
* 32.0.1 is bumped to 33.0.0, because the patch number exists
* 32.0 is bumped to 32.1.0, because patch number must be defined if the minor number
is not 0.
* 32.0 is bumped to 32.1.0, because patch number must be defined if the
minor number is not 0.
"""
try:
@ -155,17 +163,14 @@ class BaseVersion:
if current_field == field:
has_requested_field_been_met = True
new_number = 1 if current_number is None else current_number + 1
if new_number == 1 and current_field == 'minor_number':
if new_number == 1 and current_field == "minor_number":
should_set_optional_numbers = True
kwargs[current_field] = new_number
else:
if (
has_requested_field_been_met and
(
current_field not in self._OPTIONAL_NUMBERS or
should_set_optional_numbers or
current_number is not None
)
if has_requested_field_been_met and (
current_field not in self._OPTIONAL_NUMBERS
or should_set_optional_numbers
or current_number is not None
):
new_number = 0
else:
@ -178,10 +183,11 @@ class BaseVersion:
class VersionType(Enum):
"""Enum that sorts types of versions (e.g.: nightly, beta, release, esr).
Supports comparison. `ESR` is considered higher than `RELEASE` (even if they technically have
the same codebase). For instance: 60.0.1 < 60.0.1esr but 61.0 > 60.0.1esr.
This choice has a practical use case: if you have a list of Release and ESR version, you can
easily extract one kind or the other thanks to the VersionType.
Supports comparison. `ESR` is considered higher than `RELEASE` (even if they
technically have the same codebase). For instance: 60.0.1 < 60.0.1esr but
61.0 > 60.0.1esr. This choice has a practical use case: if you have a list of
Release and ESR version, you can easily extract one kind or the other thanks to the
VersionType.
Examples:
.. code-block:: python
@ -234,3 +240,67 @@ class VersionType(Enum):
return self.value - other.value
__hash__ = Enum.__hash__
@attr.s(frozen=True, eq=False, hash=True)
class ShipItVersion(BaseVersion):
"""Class that represent a version in the way ShipIt intends it to be.
ShipIt is Release Engineering's https://github.com/mozilla-releng/shipit/
"""
def __attrs_post_init__(self):
"""Ensure attributes are sane all together."""
error_messages = self._get_all_error_messages_for_attributes()
if error_messages:
raise PatternNotMatchedError(self, patterns=error_messages)
def _get_all_error_messages_for_attributes(self):
return [
pattern_message
for condition, pattern_message in (
(
self.is_major and self.is_stability,
"Version cannot be both a major and a stability one",
),
(
self.is_major and self.is_development,
"Version cannot be both a major and a development one",
),
(
self.is_stability and self.is_development,
"Version cannot be both a stability and a development one",
),
)
if condition
]
@property
def is_major(self):
"""Return `True` if `ShipItVersion` is considered to be a major version.
It's usually the .0 release but some exceptions may occur.
"""
return all(
(not self.is_development, self.minor_number == 0, self.patch_number is None)
)
@property
def is_stability(self):
"""Return `True` if `ShipItVersion` is a version that fixed a major one."""
return all(
(
not self.is_development,
not self.is_major,
self.minor_number != 0 or self.patch_number != 0,
)
)
@property
def is_development(self):
"""Return `True` if `ShipItVersion` was known to require further development.
It's usually a beta or before the rapid release scheme, a release candidate.
"""
# TODO Bubble up beta_number and release_candidate number in ShipItVersion
return self.is_beta or self.is_release_candidate

18
third_party/python/poetry.lock generated vendored
View file

@ -883,13 +883,13 @@ giturlparse = "*"
[[package]]
name = "mozilla-version"
version = "2.0.0"
version = "3.1.0"
description = "Process Firefox versions numbers. Tells whether they are valid or not, whether they are nightlies or regular releases, whether this version precedes that other."
optional = false
python-versions = "*"
files = [
{file = "mozilla-version-2.0.0.tar.gz", hash = "sha256:09697ddc5f55ad8d76521bf3e37aaec4d5bfd7fd4c9018a1cbb0e8cf6c536538"},
{file = "mozilla_version-2.0.0-py3-none-any.whl", hash = "sha256:50807a1f4000a7db6bfe95b0ffb1bade429cd8e56cbab70fd3eff5dd46ebb794"},
{file = "mozilla-version-3.1.0.tar.gz", hash = "sha256:3a9463ebcf2249dc8bcf504e246b6b5977c902dfa819de31602e10bce032ed93"},
{file = "mozilla_version-3.1.0-py3-none-any.whl", hash = "sha256:f798e716da9063608a0b49ca1ec0a51b73ac810c3cc8a4bcc2c461df902b147c"},
]
[package.dependencies]
@ -1131,6 +1131,7 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@ -1138,8 +1139,15 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@ -1156,6 +1164,7 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@ -1163,6 +1172,7 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@ -1584,4 +1594,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "55e903bf1ca5b8f0a61312d34696ab18281f4f31399366b4b9ee911333e49d1d"
content-hash = "9d5a7fe02c75289433a471dfd831a195ff604744f787c6b2b91d4af159c98475"

View file

@ -32,7 +32,7 @@ jsonschema==4.17.3
looseversion==1.0.1
mako==1.1.2
mozilla-repo-urls==0.1.1
mozilla-version==2.0.0
mozilla-version==3.1.0
packaging==23.1
pathspec==0.9.0
pip==24.0

View file

@ -389,9 +389,9 @@ mohawk==0.3.4 ; python_version >= "3.8" and python_version < "4.0" \
mozilla-repo-urls==0.1.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:30510d3519479aa70211145d0ac9cf6e2fadcb8d30fa3b196bb957bd773502ba \
--hash=sha256:7364da790751db2a060eb45adbf1d7db89a145ed279ba235f3425db9dd255915
mozilla-version==2.0.0 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:09697ddc5f55ad8d76521bf3e37aaec4d5bfd7fd4c9018a1cbb0e8cf6c536538 \
--hash=sha256:50807a1f4000a7db6bfe95b0ffb1bade429cd8e56cbab70fd3eff5dd46ebb794
mozilla-version==3.1.0 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:3a9463ebcf2249dc8bcf504e246b6b5977c902dfa819de31602e10bce032ed93 \
--hash=sha256:f798e716da9063608a0b49ca1ec0a51b73ac810c3cc8a4bcc2c461df902b147c
multidict==5.1.0 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a \
--hash=sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93 \
@ -469,7 +469,9 @@ python-slugify==8.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395 \
--hash=sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
@ -477,7 +479,10 @@ pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
@ -485,9 +490,12 @@ pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
@ -502,7 +510,9 @@ pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0" \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \