diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index ceb608e2..5b3d3b6b 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -26,6 +26,30 @@ env: TESTS_REPORTS_ARTIFACT: tests-reports jobs: + pyupgrade: + name: Find Upgradable CodingStandards + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@v4 + - name: Setup Python Environment + # see https://github.com/actions/setup-python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION_DEFAULT }} + architecture: 'x64' + - name: Install poetry + # see https://github.com/marketplace/actions/setup-poetry + uses: Gr1N/setup-poetry@v9 + with: + poetry-version: ${{ env.POETRY_VERSION }} + - name: Install dependencies + run: poetry install --no-root + - name: Run tox + run: poetry run tox run -e pyupgrade -s false + coding-standards: name: Linting & CodingStandards runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95e47ac2..a2fdfa1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,13 +21,16 @@ poetry install --all-extras ## Code style +THis project loves latest python features. This project uses [PEP8] Style Guide for Python Code. -This project loves sorted imports. +This project loves sorted imports. + Get it all applied via: ```shell -poetry run isort . -poetry run autopep8 -ir cyclonedx/ tests/ typings/ examples/ +poetry run -- tox r -e pyupgrade -- --exit-zero-even-if-changed +poetry run -- tox r -e isort +poetry run -- tox r -e autopep8 ``` This project prefers `f'strings'` over `'string'.format()`. diff --git a/cyclonedx/_internal/compare.py b/cyclonedx/_internal/compare.py index e7544b3b..756260ef 100644 --- a/cyclonedx/_internal/compare.py +++ b/cyclonedx/_internal/compare.py @@ -64,7 +64,7 @@ class ComparableDict(ComparableTuple): """ def __new__(cls, d: dict[Any, Any]) -> 'ComparableDict': - return super(ComparableDict, cls).__new__(cls, sorted(d.items())) + return super().__new__(cls, sorted(d.items())) class ComparablePackageURL(ComparableTuple): @@ -73,7 +73,7 @@ class ComparablePackageURL(ComparableTuple): """ def __new__(cls, p: 'PackageURL') -> 'ComparablePackageURL': - return super(ComparablePackageURL, cls).__new__(cls, ( + return super().__new__(cls, ( p.type, p.namespace, p.version, diff --git a/cyclonedx/spdx.py b/cyclonedx/spdx.py index c87d9a19..7178d3e4 100644 --- a/cyclonedx/spdx.py +++ b/cyclonedx/spdx.py @@ -39,7 +39,7 @@ __IDS: set[str] = set(json_load(schema).get('enum', [])) assert len(__IDS) > 0, 'known SPDX-IDs should be non-empty set' -__IDS_LOWER_MAP: dict[str, str] = dict((id_.lower(), id_) for id_ in __IDS) +__IDS_LOWER_MAP: dict[str, str] = {id_.lower(): id_ for id_ in __IDS} __SPDX_EXPRESSION_LICENSING: 'Licensing' = get_spdx_licensing() diff --git a/cyclonedx/validation/__init__.py b/cyclonedx/validation/__init__.py index b1e973c1..4f5c775f 100644 --- a/cyclonedx/validation/__init__.py +++ b/cyclonedx/validation/__init__.py @@ -110,8 +110,7 @@ def make_schemabased_validator(output_format: OutputFormat, schema_version: 'Sch Raises error when no instance could be made. """ if TYPE_CHECKING: # pragma: no cover - from typing import Type - Validator: Type[BaseSchemabasedValidator] # noqa:N806 + Validator: type[BaseSchemabasedValidator] # noqa:N806 if OutputFormat.JSON is output_format: from .json import JsonValidator as Validator elif OutputFormat.XML is output_format: diff --git a/pyproject.toml b/pyproject.toml index 6e6b40d4..21bc376c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,6 +98,7 @@ tomli = { version = "2.2.1", python = "<3.11" } tox = "4.26.0" xmldiff = "2.7.0" bandit = "1.8.3" +pyupgrade = "3.20.0" [tool.semantic_release] # see https://python-semantic-release.readthedocs.io/en/latest/configuration.html diff --git a/tests/__init__.py b/tests/__init__.py index 9c664cc3..e58c6aa2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -58,7 +58,7 @@ def writeSnapshot(cls, snapshot_name: str, data: str) -> None: # noqa: N802 @classmethod def readSnapshot(cls, snapshot_name: str) -> str: # noqa: N802 - with open(cls.getSnapshotFile(snapshot_name), 'r') as s: + with open(cls.getSnapshotFile(snapshot_name)) as s: return s.read() def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802 diff --git a/tests/test_deserialize_xml.py b/tests/test_deserialize_xml.py index 3f213aef..157c0d31 100644 --- a/tests/test_deserialize_xml.py +++ b/tests/test_deserialize_xml.py @@ -42,7 +42,7 @@ def test_prepared(self, get_bom: Callable[[], Bom], *_: Any, **__: Any) -> None: # only latest schema will have all data populated in serialized form snapshot_name = mksname(get_bom, SchemaVersion.V1_6, OutputFormat.XML) expected = get_bom() - with open(self.getSnapshotFile(snapshot_name), 'r') as s: + with open(self.getSnapshotFile(snapshot_name)) as s: bom = Bom.from_xml(s) self.assertBomDeepEqual(expected, bom, fuzzy_deps=get_bom in all_get_bom_funct_with_incomplete_deps) diff --git a/tests/test_enums.py b/tests/test_enums.py index 48138876..f769388e 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -103,8 +103,7 @@ def dp_cases_from_json_schema(sf: str, jsonpointer: Iterable[str]) -> Generator[ data = data[pp] except KeyError: return - for value in data['enum']: - yield value + yield from data['enum'] def dp_cases_from_json_schemas(*jsonpointer: str) -> Generator[str, None, None]: diff --git a/tests/test_validation_json.py b/tests/test_validation_json.py index d4283d61..9bc1a7cc 100644 --- a/tests/test_validation_json.py +++ b/tests/test_validation_json.py @@ -15,10 +15,10 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. +from collections.abc import Generator from glob import iglob from itertools import chain from os.path import join -from typing import Generator from unittest import TestCase from ddt import data, ddt, idata, unpack @@ -69,7 +69,7 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) @@ -84,7 +84,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) @@ -109,7 +109,7 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonStrictValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) @@ -124,7 +124,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonStrictValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) diff --git a/tests/test_validation_xml.py b/tests/test_validation_xml.py index d5fcbb0f..81a56cce 100644 --- a/tests/test_validation_xml.py +++ b/tests/test_validation_xml.py @@ -69,7 +69,7 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = XmlValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) @@ -84,7 +84,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = XmlValidator(schema_version) - with open(join(test_data_file), 'r') as tdfh: + with open(join(test_data_file)) as tdfh: test_data = tdfh.read() try: validation_error = validator.validate_str(test_data) diff --git a/tools/schema-downloader.py b/tools/schema-downloader.py index d795f474..8eb1be14 100755 --- a/tools/schema-downloader.py +++ b/tools/schema-downloader.py @@ -102,7 +102,7 @@ target = dspec['targetPattern'].replace('%s', version) tempfile, _ = urlretrieve(source) # nosec B310 print(source, '->', target) - with open(tempfile, 'r') as tmpf: + with open(tempfile) as tmpf: text = tmpf.read() with open(target, 'w', newline='\n') as tarf: for search, replace in dspec['replace']: diff --git a/tox.ini b/tox.ini index 88c3d0de..20b36023 100644 --- a/tox.ini +++ b/tox.ini @@ -44,4 +44,14 @@ commands = commands = poetry run bandit -c bandit.yml -v -r cyclonedx tests examples tools +[testenv:pyupgrade] +allowlist_externals = poetry, sh +commands = sh -c "\ + find cyclonedx typings tests tools examples -type f \( -name '*.py' -or -name '*.pyi' \) -print0 \ + | xargs -0 poetry run pyupgrade --py39-plus {posargs} " +[testenv:isort] +commands = poetry run isort . + +[testenv:autopep8] +commands = poetry run autopep8 --in-place -r cyclonedx typings tests tools examples diff --git a/typings/sortedcontainers.pyi b/typings/sortedcontainers.pyi index 5959205f..3c7924b3 100644 --- a/typings/sortedcontainers.pyi +++ b/typings/sortedcontainers.pyi @@ -3,8 +3,8 @@ # The contents of this file were obtained from # https://github.com/althonos/python-sortedcontainers/blob/d0a225d7fd0fb4c54532b8798af3cbeebf97e2d5/sortedcontainers/sortedset.pyi -from collections.abc import Callable, Iterable, MutableSet, Sequence -from typing import Any, Hashable, Optional, TypeVar, Union, overload # Iterator,; Tuple,; Type, Set +from collections.abc import Callable, Hashable, Iterable, MutableSet, Sequence +from typing import Any, Optional, TypeVar, Union, overload # Iterator,; Tuple,; Type, Set # --- Global