From 2153e7c9006b8f1a9f71b602d459b1bda6effd12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Gr=C3=BCbel?= Date: Mon, 12 Dec 2022 22:45:00 +0100 Subject: [PATCH] chore: replace deprecated LegacyVersion with own one (#4043) * replace depreacted LegacyVersion with own one * upgrade to latest packaging version * fix mypy * fix linting * add ignorecase flag --- Pipfile | 2 +- Pipfile.lock | 30 +--- checkov/common/packaging/__init__.py | 0 checkov/common/packaging/version.py | 141 ++++++++++++++++++ checkov/common/sca/output.py | 3 +- checkov/common/util/json_utils.py | 2 +- checkov/sca_package/output.py | 2 +- checkov/sca_package_2/output.py | 2 +- .../module_loading/loaders/versions_parser.py | 2 +- setup.py | 2 +- 10 files changed, 155 insertions(+), 31 deletions(-) create mode 100644 checkov/common/packaging/__init__.py create mode 100644 checkov/common/packaging/version.py diff --git a/Pipfile b/Pipfile index 0a11b49b319..a1cf2dff4b4 100644 --- a/Pipfile +++ b/Pipfile @@ -56,7 +56,7 @@ jmespath = "*" tqdm = "*" update-checker = "*" semantic-version = "*" -packaging = "==21.3" +packaging = "*" cloudsplaining = ">=0.4.3" networkx = "<2.7" dockerfile-parse ="*" diff --git a/Pipfile.lock b/Pipfile.lock index f9d8285e05d..a12bceb8c91 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "17259b870634278a8de8e6bab2174e2943072ebf2a6dd2a2334b32e4dda3c2f8" + "sha256": "0f99c070a68f8db9411ed227c8cd70f581452dcd97b822d8b137908b3cb65c3e" }, "pipfile-spec": 6, "requires": { @@ -718,11 +718,11 @@ }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], "index": "pypi", - "version": "==21.3" + "version": "==22.0" }, "ply": { "hashes": [ @@ -827,14 +827,6 @@ ], "version": "==2.21" }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, "pyrsistent": { "hashes": [ "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed", @@ -1864,11 +1856,11 @@ }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], "index": "pypi", - "version": "==21.3" + "version": "==22.0" }, "pbr": { "hashes": [ @@ -1918,14 +1910,6 @@ "markers": "python_version >= '3.6'", "version": "==2.5.0" }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, "pyrsistent": { "hashes": [ "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed", diff --git a/checkov/common/packaging/__init__.py b/checkov/common/packaging/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/checkov/common/packaging/version.py b/checkov/common/packaging/version.py new file mode 100644 index 00000000000..d7fac030250 --- /dev/null +++ b/checkov/common/packaging/version.py @@ -0,0 +1,141 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +# Partial copy of https://github.com/pypa/packaging/blob/21.3/packaging/version.py + +from __future__ import annotations + +import re +from typing import Iterator, TYPE_CHECKING + +from packaging import version as packaging_version + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +LegacyCmpKey: TypeAlias = "tuple[int, tuple[str, ...]]" +Version = packaging_version.Version + + +def parse(version: str) -> packaging_version.Version | LegacyVersion: + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return packaging_version.parse(version) + except packaging_version.InvalidVersion: + return LegacyVersion(version) + + +class LegacyVersion(packaging_version._BaseVersion): + def __init__(self, version: str) -> None: + self._version = str(version) + self._key = _legacy_cmpkey(self._version) # type:ignore[assignment] + + def __str__(self) -> str: + return self._version + + def __repr__(self) -> str: + return f"" + + @property + def public(self) -> str: + return self._version + + @property + def base_version(self) -> str: + return self._version + + @property + def epoch(self) -> int: + return -1 + + @property + def release(self) -> None: + return None + + @property + def pre(self) -> None: + return None + + @property + def post(self) -> None: + return None + + @property + def dev(self) -> None: + return None + + @property + def local(self) -> None: + return None + + @property + def is_prerelease(self) -> bool: + return False + + @property + def is_postrelease(self) -> bool: + return False + + @property + def is_devrelease(self) -> bool: + return False + + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE | re.IGNORECASE) + +_legacy_version_replacement_map = { + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", +} + + +def _parse_version_parts(s: str) -> Iterator[str]: + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version: str) -> LegacyCmpKey: + + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts: list[str] = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + + return epoch, tuple(parts) diff --git a/checkov/common/sca/output.py b/checkov/common/sca/output.py index 1c97d28c136..3c1f28213ab 100644 --- a/checkov/common/sca/output.py +++ b/checkov/common/sca/output.py @@ -5,8 +5,6 @@ from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any, Optional, Dict, List -from packaging import version as packaging_version - from checkov.common.bridgecrew.integration_features.features.policy_metadata_integration import ( integration as metadata_integration, ) @@ -15,6 +13,7 @@ from checkov.common.models.enums import CheckResult, ScanDataFormat from checkov.common.output.extra_resource import ExtraResource from checkov.common.output.record import Record, DEFAULT_SEVERITY, SCA_PACKAGE_SCAN_CHECK_NAME, SCA_LICENSE_CHECK_NAME +from checkov.common.packaging import version as packaging_version from checkov.common.sca.commons import ( get_file_path_for_record, get_resource_for_record, diff --git a/checkov/common/util/json_utils.py b/checkov/common/util/json_utils.py index 5e96d9165c3..e93b20f856c 100644 --- a/checkov/common/util/json_utils.py +++ b/checkov/common/util/json_utils.py @@ -3,10 +3,10 @@ from typing import Any from lark import Tree -from packaging.version import LegacyVersion, Version from checkov.common.bridgecrew.severities import Severity from checkov.common.output.common import ImageDetails +from checkov.common.packaging.version import LegacyVersion, Version class CustomJSONEncoder(json.JSONEncoder): diff --git a/checkov/sca_package/output.py b/checkov/sca_package/output.py index 8466ce66aab..25f49850778 100644 --- a/checkov/sca_package/output.py +++ b/checkov/sca_package/output.py @@ -6,12 +6,12 @@ from dataclasses import dataclass from typing import List, Union, Dict, Any -from packaging import version as packaging_version from prettytable import PrettyTable, SINGLE_BORDER from checkov.common.bridgecrew.severities import Severities, BcSeverities from checkov.common.models.enums import CheckResult from checkov.common.output.record import Record, DEFAULT_SEVERITY, SCA_PACKAGE_SCAN_CHECK_NAME, SCA_LICENSE_CHECK_NAME +from checkov.common.packaging import version as packaging_version from checkov.common.sca.commons import UNFIXABLE_VERSION from checkov.common.typing import _LicenseStatus diff --git a/checkov/sca_package_2/output.py b/checkov/sca_package_2/output.py index cbd40522822..9e3fe965f17 100644 --- a/checkov/sca_package_2/output.py +++ b/checkov/sca_package_2/output.py @@ -6,12 +6,12 @@ from dataclasses import dataclass from typing import List, Union, Dict, Any -from packaging import version as packaging_version from prettytable import PrettyTable, SINGLE_BORDER from checkov.common.bridgecrew.severities import Severities, BcSeverities from checkov.common.models.enums import CheckResult from checkov.common.output.record import Record, DEFAULT_SEVERITY, SCA_PACKAGE_SCAN_CHECK_NAME, SCA_LICENSE_CHECK_NAME +from checkov.common.packaging import version as packaging_version from checkov.common.sca.commons import UNFIXABLE_VERSION, get_package_alias from checkov.common.typing import _LicenseStatus diff --git a/checkov/terraform/module_loading/loaders/versions_parser.py b/checkov/terraform/module_loading/loaders/versions_parser.py index 029cccbe0dd..becf3dd51ee 100644 --- a/checkov/terraform/module_loading/loaders/versions_parser.py +++ b/checkov/terraform/module_loading/loaders/versions_parser.py @@ -1,7 +1,7 @@ import re from typing import List, Dict, Optional -from packaging import version +from checkov.common.packaging import version VERSION_REGEX = re.compile(r"^(?P=|!=|>=|>|<=|<|~>)?\s*(?P[\d.]+-?\w*)$") diff --git a/setup.py b/setup.py index 94a5f8eed83..e0f477fe616 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ "tqdm", "update-checker", "semantic-version", - "packaging==21.3", + "packaging", "cloudsplaining>=0.4.3", "networkx<2.7", "dockerfile-parse",