From 488ccae363bb1b8862ccf1d10a9f777a051bc19e Mon Sep 17 00:00:00 2001 From: Tobias Messner Date: Wed, 18 Dec 2024 11:55:46 +0100 Subject: [PATCH] ci: Replace isort, black and pylint with Ruff --- .pre-commit-config.yaml | 42 +---- README.md | 4 - capella/install_dropins.py | 2 +- .../disable_semantic_browser_auto_refresh.py | 2 +- pyproject.toml | 168 ++++++++--------- remote/tests/test_metrics.py | 11 +- t4c/pyproject.toml | 172 +++++++++--------- t4c/setup_workspace_t4c.py | 2 +- t4c/t4c_cli/export.py | 4 +- t4c/t4c_cli/util/capella.py | 9 +- t4c/t4c_cli/util/config.py | 6 +- tests/test_backups.py | 2 +- tests/test_exporter_git.py | 4 +- 13 files changed, 188 insertions(+), 240 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9cc3a517..1a44d668 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,39 +27,12 @@ repos: - id: end-of-file-fixer - id: fix-byte-order-marker - id: trailing-whitespace - - repo: https://github.com/pylint-dev/pylint - rev: v3.3.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.2 hooks: - - id: pylint - additional_dependencies: - - mccabe - require_serial: false - args: - - -rn - - -sn - - -dfixme - - -dduplicate-code - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - - repo: https://github.com/PyCQA/docformatter - rev: eb1df347edd128b30cd3368dddc3aa65edcfac38 - hooks: - - id: docformatter - additional_dependencies: - - docformatter[tomli] - - repo: https://github.com/PyCQA/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle - exclude: '^tests/' - additional_dependencies: - - pydocstyle[toml] + - id: ruff + args: [--extend-ignore=FIX, --fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.11.2 hooks: @@ -131,11 +104,6 @@ repos: stages: [commit-msg] additional_dependencies: - '@commitlint/config-conventional' - - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 - hooks: - - id: pyupgrade - args: ['--py311-plus'] - repo: https://github.com/hadolint/hadolint rev: v2.12.0 hooks: diff --git a/README.md b/README.md index 2110fd9e..8ea08913 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,6 @@ ~ SPDX-License-Identifier: Apache-2.0 --> - # MBSE Docker images diff --git a/capella/install_dropins.py b/capella/install_dropins.py index ceb74a72..00cbd494 100644 --- a/capella/install_dropins.py +++ b/capella/install_dropins.py @@ -21,7 +21,7 @@ def extract_repositories_and_install_ius(dropins: dict[str, t.Any]) -> None: if not dropin_slug: continue - if not dropin_slug in dropins: + if dropin_slug not in dropins: raise KeyError( f"Dropin '{dropin_slug}' not found in list of supported dropins." ) diff --git a/capella/setup/disable_semantic_browser_auto_refresh.py b/capella/setup/disable_semantic_browser_auto_refresh.py index 7f4836cd..a2d534df 100644 --- a/capella/setup/disable_semantic_browser_auto_refresh.py +++ b/capella/setup/disable_semantic_browser_auto_refresh.py @@ -141,7 +141,7 @@ def _replace_content_in_line_of_file( sys.stdout.write(line) continue if old in line: - line = line.replace(old, new) + line = line.replace(old, new) # noqa: PLW2901 logger.info( "Replaced `%s` with `%s` in line number: `%d`", old, diff --git a/pyproject.toml b/pyproject.toml index 7e200047..068a99a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" dynamic = ["version"] name = "capella-dockerimages" -requires-python = ">=3.11, <3.12" +requires-python = ">=3.11, <3.13" license = { text = "Apache-2.0" } authors = [{ name = "DB InfraGO AG" }] dependencies = ["PyYAML", "requests", "prometheus_client"] @@ -20,18 +20,91 @@ Homepage = "https://github.com/DSD-DBS/capella-dockerimages" [project.optional-dependencies] test = ["docker>=6.1.0", "pytest", "capellambse[decl]", "chardet"] dev = [ - "black", - "isort", + "ruff", "mypy", - "pylint", "types-PyYAML", "types-lxml", "types-requests", ] -[tool.black] +[tool.ruff] line-length = 79 -target-version = ["py311"] + +[tool.ruff.lint] +extend-select = [ + "ARG", # flake8-unused-arguments + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C90", # mccabe + "D", # pydocstyle + "DTZ", # flake8-datetimez + "ERA", # eradicate + "FA", # flake8-future-annotations + "FBT", # flake8-boolean-trap + "FIX", # flake8-fixme + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "ISC001", # "Implicitly concatenated string literals on one line" + "ISC003", # "Explicitly concatenated string should be implicitly concatenated" + "LOG", # flake8-logging + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "RET", # flake8-return + "RUF", # ruff + "SIM", # flake8-simplify + "TC005", # "Found empty type-checking block" + "T1", # flake8-debugger + "UP", # pyupgrade + "YTT", # flake8-2020 +] +extend-ignore = [ + "D1", # Missing docstring in _ + "D201", # No blank lines allowed before function docstring # auto-formatting + "D202", # No blank lines allowed after function docstring # auto-formatting + "D203", # 1 blank line required before class docstring # auto-formatting + "D204", # 1 blank line required after class docstring # auto-formatting + "D211", # No blank lines allowed before class docstring # auto-formatting + "D213", # Multi-line docstring summary should start at the second line + "DTZ001", # `tzinfo=None` passed to `datetime.datetime()` + "DTZ005", # `tz=None` passed to `datetime.datetime.now()` + "E402", # Module level import not at top of file + "ERA001", # Remove commented-out code + "F403", # `from _ import *` used; unable to detect undefined names + "F405", # `_` may be undefined, or defined from star imports + "FBT001", # Boolean-typed positional argument in function definition + "FBT002", # Boolean default positional argument in function definition + "LOG002", # Use `__name__` with `logging.getLogger()` + "PLC0414", # Import alias does not rename original package # used for explicit reexports + "PLR0904", # Too many public methods + "PLR0911", # Too many return statements + "PLR0912", # Too many branches + "PLR0913", # Too many arguments in function definition + "PLR0914", # Too many local variables + "PLR0915", # Too many statements + "PLR0916", # Too many Boolean expressions + "PLR0917", # Too many positional arguments + "PLR2004", # Magic value used in comparison + "SIM108", # Use ternary operator instead of `if`-`else`-block +] + +[tool.ruff.lint.extend-per-file-ignores] +"__init__.py" = [ + "PLE0604", # Invalid object in `__all__`, must contain only strings # false-positive when unpacking imported submodule __all__ +] +"tests/test_*.py" = [ + "F811", # Redefinition of unused `_` from line _ + "PLR2004", # Magic value used in comparison, consider replacing `_` with a constant variable +] + +[tool.ruff.lint.pydocstyle] +convention = "numpy" +ignore-decorators = ["typing.overload"] + +[tool.ruff.lint.mccabe] +max-complexity = 14 [tool.coverage.run] branch = true @@ -46,13 +119,6 @@ exclude_also = [ ] skip_covered = true -[tool.docformatter] -wrap-descriptions = 72 -wrap-summaries = 79 - -[tool.isort] -profile = 'black' -line_length = 79 [tool.mypy] check_untyped_defs = true @@ -89,82 +155,6 @@ add-ignore = [ "D213", # Multi-line docstring summary should start at the second line ] -[tool.pylint.format] -ignore-long-lines = '^\s*(?:(?:__ |\.\. __: )?https?://[^ ]+$|def test_.*|[A-Za-z0-9_\.]+(?: ?:)?$)' - -[tool.pylint.master] -init-import = "yes" -load-plugins = ["pylint.extensions.mccabe", "pylint.extensions.bad_builtin"] -max-complexity = 14 -max-line-length = 79 -extension-pkg-allow-list = ["lxml.builder", "lxml.etree"] - - -[tool.pylint.messages_control] -disable = [ - "broad-except", - "global-statement", - "import-outside-toplevel", - "missing-class-docstring", - "missing-function-docstring", - "missing-module-docstring", - "no-else-break", - "no-else-continue", - "no-else-raise", - "no-else-return", - "protected-access", - "redefined-builtin", - "too-few-public-methods", - "too-many-ancestors", - "too-many-arguments", - "too-many-positional-arguments", - "too-many-boolean-expressions", - "too-many-branches", - "too-many-instance-attributes", - "too-many-lines", - "too-many-locals", - "too-many-public-methods", - "too-many-return-statements", - "too-many-statements", - - # Auto-formatting - "bad-indentation", - "inconsistent-quotes", - "line-too-long", - "missing-final-newline", - "mixed-line-endings", - "multiple-imports", - "multiple-statements", - "trailing-newlines", - "trailing-whitespace", - "unexpected-line-ending-format", - "ungrouped-imports", - "wrong-import-order", - "wrong-import-position", - - # Handled by mypy - "arguments-differ", - "assignment-from-no-return", - "import-error", - "missing-kwoa", - "no-member", - "no-value-for-parameter", - "redundant-keyword-arg", - "signature-differs", - "syntax-error", - "too-many-function-args", - "unbalanced-tuple-unpacking", - "undefined-variable", - "unexpected-keyword-arg", -] -enable = [ - "c-extension-no-member", - "deprecated-pragma", - "use-symbolic-message-instead", - "useless-suppression", -] - - [tool.pytest.ini_options] addopts = """ --strict-config diff --git a/remote/tests/test_metrics.py b/remote/tests/test_metrics.py index 2b78a486..279794be 100644 --- a/remote/tests/test_metrics.py +++ b/remote/tests/test_metrics.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors # SPDX-License-Identifier: Apache-2.0 """Unit tests for metrics.""" + import time import typing as t from unittest import mock @@ -11,15 +12,12 @@ @pytest.fixture(name="idler") def fixture_idler() -> metrics.IdleTimer: - """Return an instance of `metrics.IdleTime`""" + """Return an instance of `metrics.IdleTime`.""" return metrics.IdleTimer() def print_no_display(*__: t.Any, **_: t.Any) -> mock.MagicMock: - """ - Just print: 'could not open display', the response of xprintidle - when there is no X server. - """ + """Just print: 'could not open display', the response of xprintidle when there is no X server.""" return mock.MagicMock(stdout="couldn't open display") @@ -64,5 +62,6 @@ def test_get_idletime_increases_after_display_is_closed( second_idletime = idler.get_idletime() - assert isinstance(idletime, float) and isinstance(second_idletime, float) + assert isinstance(idletime, float) + assert isinstance(second_idletime, float) assert idletime < second_idletime diff --git a/t4c/pyproject.toml b/t4c/pyproject.toml index 461bbea2..0fb3069e 100644 --- a/t4c/pyproject.toml +++ b/t4c/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" dynamic = ["version"] name = "t4c-cli" -requires-python = ">=3.11, <3.12" +requires-python = ">=3.11, <3.13" license = { text = "Apache-2.0" } authors = [{ name = "DB InfraGO AG" }] keywords = [] @@ -20,6 +20,7 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] dependencies = ["PyYAML", "click", "lxml"] @@ -28,23 +29,96 @@ Homepage = "https://github.com/DSD-DBS/capella-dockerimages" [project.optional-dependencies] dev = [ - "black", - "isort", + "ruff", "mypy", - "pylint", "pytest", "types-PyYAML", "types-lxml", ] +[tool.ruff] +line-length = 79 + +[tool.ruff.lint] +extend-select = [ + "ARG", # flake8-unused-arguments + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C90", # mccabe + "D", # pydocstyle + "DTZ", # flake8-datetimez + "ERA", # eradicate + "FA", # flake8-future-annotations + "FBT", # flake8-boolean-trap + "FIX", # flake8-fixme + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "ISC001", # "Implicitly concatenated string literals on one line" + "ISC003", # "Explicitly concatenated string should be implicitly concatenated" + "LOG", # flake8-logging + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "RET", # flake8-return + "RUF", # ruff + "SIM", # flake8-simplify + "TC005", # "Found empty type-checking block" + "T1", # flake8-debugger + "UP", # pyupgrade + "YTT", # flake8-2020 +] +extend-ignore = [ + "D1", # Missing docstring in _ + "D201", # No blank lines allowed before function docstring # auto-formatting + "D202", # No blank lines allowed after function docstring # auto-formatting + "D203", # 1 blank line required before class docstring # auto-formatting + "D204", # 1 blank line required after class docstring # auto-formatting + "D211", # No blank lines allowed before class docstring # auto-formatting + "D213", # Multi-line docstring summary should start at the second line + "DTZ001", # `tzinfo=None` passed to `datetime.datetime()` + "DTZ005", # `tz=None` passed to `datetime.datetime.now()` + "E402", # Module level import not at top of file + "ERA001", # Remove commented-out code + "F403", # `from _ import *` used; unable to detect undefined names + "F405", # `_` may be undefined, or defined from star imports + "FBT001", # Boolean-typed positional argument in function definition + "FBT002", # Boolean default positional argument in function definition + "LOG002", # Use `__name__` with `logging.getLogger()` + "PLC0414", # Import alias does not rename original package # used for explicit reexports + "PLR0904", # Too many public methods + "PLR0911", # Too many return statements + "PLR0912", # Too many branches + "PLR0913", # Too many arguments in function definition + "PLR0914", # Too many local variables + "PLR0915", # Too many statements + "PLR0916", # Too many Boolean expressions + "PLR0917", # Too many positional arguments + "PLR2004", # Magic value used in comparison + "SIM108", # Use ternary operator instead of `if`-`else`-block +] + +[tool.ruff.lint.extend-per-file-ignores] +"__init__.py" = [ + "PLE0604", # Invalid object in `__all__`, must contain only strings # false-positive when unpacking imported submodule __all__ +] +"tests/test_*.py" = [ + "F811", # Redefinition of unused `_` from line _ + "PLR2004", # Magic value used in comparison, consider replacing `_` with a constant variable +] + +[tool.ruff.lint.pydocstyle] +convention = "numpy" +ignore-decorators = ["typing.overload"] + +[tool.ruff.lint.mccabe] +max-complexity = 14 + [project.scripts] backup = "t4c_cli.backup:backup" exporter = "t4c_cli.export:export" -[tool.black] -line-length = 79 -target-version = ["py311"] - [tool.coverage.run] branch = true command_line = "-m pytest" @@ -59,13 +133,6 @@ exclude_also = [ ] skip_covered = true -[tool.docformatter] -wrap-descriptions = 72 -wrap-summaries = 79 - -[tool.isort] -profile = 'black' -line_length = 79 [tool.mypy] check_untyped_defs = true @@ -104,81 +171,6 @@ add-ignore = [ "D213", # Multi-line docstring summary should start at the second line ] -[tool.pylint.format] -ignore-long-lines = '^\s*(?:(?:__ |\.\. __: )?https?://[^ ]+$|def test_.*|[A-Za-z0-9_\.]+(?: ?:)?$)' - -[tool.pylint.master] -init-import = "yes" -load-plugins = ["pylint.extensions.mccabe", "pylint.extensions.bad_builtin"] -max-complexity = 14 -max-line-length = 79 -extension-pkg-allow-list = ["lxml.builder", "lxml.etree"] - - -[tool.pylint.messages_control] -disable = [ - "broad-except", - "global-statement", - "import-outside-toplevel", - "missing-class-docstring", - "missing-function-docstring", - "missing-module-docstring", - "no-else-break", - "no-else-continue", - "no-else-raise", - "no-else-return", - "protected-access", - "redefined-builtin", - "too-few-public-methods", - "too-many-ancestors", - "too-many-arguments", - "too-many-boolean-expressions", - "too-many-branches", - "too-many-instance-attributes", - "too-many-lines", - "too-many-locals", - "too-many-public-methods", - "too-many-return-statements", - "too-many-statements", - - # Auto-formatting - "bad-indentation", - "inconsistent-quotes", - "line-too-long", - "missing-final-newline", - "mixed-line-endings", - "multiple-imports", - "multiple-statements", - "trailing-newlines", - "trailing-whitespace", - "unexpected-line-ending-format", - "ungrouped-imports", - "wrong-import-order", - "wrong-import-position", - - # Handled by mypy - "arguments-differ", - "assignment-from-no-return", - "import-error", - "missing-kwoa", - "no-member", - "no-value-for-parameter", - "redundant-keyword-arg", - "signature-differs", - "syntax-error", - "too-many-function-args", - "unbalanced-tuple-unpacking", - "undefined-variable", - "unexpected-keyword-arg", -] -enable = [ - "c-extension-no-member", - "deprecated-pragma", - "use-symbolic-message-instead", - "useless-suppression", -] - - [tool.pytest.ini_options] addopts = """ --import-mode=importlib diff --git a/t4c/setup_workspace_t4c.py b/t4c/setup_workspace_t4c.py index 299b0d19..7fea1961 100644 --- a/t4c/setup_workspace_t4c.py +++ b/t4c/setup_workspace_t4c.py @@ -78,7 +78,7 @@ def setup_repositories() -> None: if count > 1 ] for repo in t4c_repos: - protocol = repo["protocol"] if "protocol" in repo else None + protocol = repo.get("protocol", None) if protocol: assert protocol in ["tcp", "ssl", "ws", "wss"] inject_t4c_connection_details( diff --git a/t4c/t4c_cli/export.py b/t4c/t4c_cli/export.py index ba362b3a..7c9bdcd3 100644 --- a/t4c/t4c_cli/export.py +++ b/t4c/t4c_cli/export.py @@ -51,9 +51,9 @@ def _build_export_command(model_dir: pathlib.Path) -> list[str]: def _validate_exporter_stdout(line: str) -> None: if "No address associated with hostname" in line: raise RuntimeError("Unknown host") - elif "No such user:" in line: + if "No such user:" in line: raise RuntimeError("Unknown user") - elif re.search(r"[1-9][0-9]* projects? exports? failed", line): + if re.search(r"[1-9][0-9]* projects? exports? failed", line): raise RuntimeError("Export failed") diff --git a/t4c/t4c_cli/util/capella.py b/t4c/t4c_cli/util/capella.py index a09de678..e4178b86 100644 --- a/t4c/t4c_cli/util/capella.py +++ b/t4c/t4c_cli/util/capella.py @@ -41,9 +41,12 @@ def validate_line(line: str): ``` """ - command = ( - DEFAULT_CAPELLA_COMMAND + ["-application", application] + arguments - ) + command = [ + *DEFAULT_CAPELLA_COMMAND, + "-application", + application, + *arguments, + ] log.info("Executing the following command: %s", " ".join(command)) stderr = "" diff --git a/t4c/t4c_cli/util/config.py b/t4c/t4c_cli/util/config.py index df369027..d165ca8a 100644 --- a/t4c/t4c_cli/util/config.py +++ b/t4c/t4c_cli/util/config.py @@ -54,9 +54,9 @@ class FileHandler(enum.Enum): class GeneralConfig: file_handler = FileHandler(os.getenv("FILE_HANDLER", "GIT").upper()) - git: GitConfig = GitConfig() - t4c: T4CConfig = T4CConfig() - capella: CapellaConfig = CapellaConfig() + git: GitConfig = dataclasses.field(default_factory=GitConfig) + t4c: T4CConfig = dataclasses.field(default_factory=T4CConfig) + capella: CapellaConfig = dataclasses.field(default_factory=CapellaConfig) config = GeneralConfig() diff --git a/tests/test_backups.py b/tests/test_backups.py index fc7ea45d..c6cff9ea 100644 --- a/tests/test_backups.py +++ b/tests/test_backups.py @@ -33,7 +33,7 @@ def fixture_t4c_backup_local_env( @pytest.fixture(name="t4c_backup_container") def fixture_t4c_backup_container( - t4c_backup_local_env: dict[str, str] + t4c_backup_local_env: dict[str, str], ) -> t.Generator[containers.Container, None, None]: with conftest.get_container( image="t4c/client/base", diff --git a/tests/test_exporter_git.py b/tests/test_exporter_git.py index 99f5134e..0b9f0e70 100644 --- a/tests/test_exporter_git.py +++ b/tests/test_exporter_git.py @@ -41,8 +41,8 @@ def fixture_t4c_exporter_container( t4c_exporter_git_env: dict[str, str], t4c_ip_addr: str, t4c_http_port: str, - init_t4c_server_repo: None, # pylint: disable=unused-argument - init_git_server: None, # pylint: disable=unused-argument + init_t4c_server_repo: None, # noqa: ARG001 + init_git_server: None, # noqa: ARG001 ) -> t.Generator[containers.Container, None, None]: if conftest.is_capella_6_x_x(): assert not conftest.get_projects_of_t4c_repository(