From fcad94915dbc3ff5927c300aebaa9f0928c86331 Mon Sep 17 00:00:00 2001 From: Stanislav Khlud Date: Thu, 23 May 2024 13:41:25 +0700 Subject: [PATCH] Migrate to ruff --- .pre-commit-config.yaml | 49 +-------- .vscode/recommended_settings.json | 45 +++++---- README.md | 3 +- pyproject.toml | 159 +++++++++++++++++++----------- saritasa_invocations/_config.py | 10 +- saritasa_invocations/alembic.py | 6 +- saritasa_invocations/cruft.py | 2 +- saritasa_invocations/db_k8s.py | 4 +- saritasa_invocations/django.py | 7 +- saritasa_invocations/docker.py | 10 +- saritasa_invocations/git.py | 4 +- saritasa_invocations/k8s.py | 4 +- saritasa_invocations/pip.py | 2 +- saritasa_invocations/poetry.py | 4 +- saritasa_invocations/secrets.py | 6 +- saritasa_invocations/system.py | 12 ++- tasks.py | 14 +-- 17 files changed, 180 insertions(+), 161 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c90110e..51520f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,51 +20,12 @@ repos: - id: poetry-check args: ["--lock"] - - repo: https://github.com/pycqa/isort - rev: 5.13.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.7 hooks: - - id: isort - name: isort (python) - - - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - exclude: .venv|__init__.py - additional_dependencies: [ - # A plugin for Flake8 finding likely bugs and design problems in your program. - # https://github.com/PyCQA/flake8-bugbear - flake8-bugbear, - # A flake8 plugin checking common style issues or inconsistencies with pytest-based tests. - # https://github.com/m-burst/flake8-pytest-style - flake8-pytest-style, - # A flake8 plugin that warn about backslashes usage. - # https://github.com/wemake-services/flake8-broken-line - flake8-broken-line, - # A simple module that adds an extension for the fantastic pydocstyle tool to flake8. - # https://github.com/PyCQA/flake8-docstrings - flake8-docstrings, - # A flake8 plugin loading the configuration from pyproject.toml - # https://github.com/john-hen/Flake8-pyproject - flake8-pyproject, - # flake8 plugin to validate type annotations according to modern practices. - # https://github.com/plinss/flake8-modern-annotations - flake8-modern-annotations, - # McCabe complexity checker. - # https://github.com/PyCQA/mccabe - mccabe, - ] - - - repo: https://github.com/asottile/add-trailing-comma - rev: v3.1.0 - hooks: - - id: add-trailing-comma - - - repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - language_version: python3 + - id: ruff + args: [ --fix ] + - id: ruff-format - repo: https://github.com/asottile/add-trailing-comma rev: v3.1.0 diff --git a/.vscode/recommended_settings.json b/.vscode/recommended_settings.json index 18b96e1..9b0757a 100644 --- a/.vscode/recommended_settings.json +++ b/.vscode/recommended_settings.json @@ -1,31 +1,30 @@ { - "files.exclude": { - "**/__pycache__": true, - "**/.mypy_cache": true - }, + "files.exclude": { + "**/__pycache__": true, + "**/.pytest_cache": true, + "**/.mypy_cache": true, + "**/.ruff_cache": true, + "**/localstack_volume": true, + "**/htmlcov": true, + }, - "editor.rulers": [79], + "editor.rulers": [79], - "editor.bracketPairColorization.enabled": true, + "editor.bracketPairColorization.enabled": true, - "python.analysis.typeCheckingMode": "basic", - "python.analysis.diagnosticSeverityOverrides": { - "reportPrivateImportUsage": "none" - }, + "python.analysis.typeCheckingMode": "basic", + "python.analysis.diagnosticSeverityOverrides": { + "reportPrivateImportUsage": "none" + }, - "python.analysis.inlayHints.functionReturnTypes": true, + "python.analysis.inlayHints.functionReturnTypes": true, + "mypy.enabled": false, - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "mypy.enabled": true, - "mypy.dmypyExecutable": "${workspaceFolder}/.venv/bin/dmypy", + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, - "[python]": { - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true - }, - "editor.defaultFormatter": "ms-python.black-formatter" - }, - "isort.args": ["--settings-path", "${workspaceFolder}/pyproject.toml"], + "[python]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "charliermarsh.ruff" + } } diff --git a/README.md b/README.md index bcff04a..b7563a6 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/saritasa-invocations) ![PyPI - License](https://img.shields.io/pypi/l/saritasa-invocations) ![PyPI - Downloads](https://img.shields.io/pypi/dm/saritasa-invocations) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) Collection of [invoke](https://www.pyinvoke.org/) commands used by Saritasa diff --git a/pyproject.toml b/pyproject.toml index 8a6180c..feb563c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,66 +89,115 @@ mypy = "^1.10.0" requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.isort] -profile = "black" -line_length = 79 -multi_line_output = 3 -skip = [ - "_tmp", - "src", - ".venv", -] -sections = [ - "FUTURE", - "STDLIB", - "THIRDPARTY", - "FIRSTPARTY", - "LOCALFOLDER", -] -include_trailing_comma = true -default_section = "THIRDPARTY" +[tool.ruff] +line-length = 79 +indent-width = 4 +target-version = "py310" -[tool.flake8] +[tool.ruff.lint] +extend-select = [ + # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w + "W", + "E", + # https://docs.astral.sh/ruff/rules/#mccabe-c90 + "C90", + # https://docs.astral.sh/ruff/rules/#isort-i + "I", + # https://docs.astral.sh/ruff/rules/#pep8-naming-n + "N", + # https://docs.astral.sh/ruff/rules/#pydocstyle-d + "D", + # https://docs.astral.sh/ruff/rules/#pyupgrade-up + "UP", + # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + "ANN", + # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + "S", + # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + "B", + # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + "A", + # https://docs.astral.sh/ruff/rules/#flake8-commas-com + "COM", + # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 + "C4", + # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + "DTZ", + # https://docs.astral.sh/ruff/rules/#flake8-debugger-t10 + "T10", + # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + "T20", + # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt + "PT", + # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + "SIM", + # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "PTH", + # https://docs.astral.sh/ruff/rules/#flake8-todos-td + "TD", + # https://docs.astral.sh/ruff/rules/#eradicate-era + "ERA", + # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + "RUF" +] ignore = [ - # https://www.flake8rules.com/rules/E126.html - "E126", - # https://www.flake8rules.com/rules/W503.html - "W503", - # https://www.flake8rules.com/rules/W504.html - "W504", - # https://github.com/m-burst/flake8-pytest-style/blob/master/docs/rules/PT004.md - "PT004", - # https://www.pydocstyle.org/en/latest/error_codes.html - # Missing docstring in public module - "D100", - # Missing docstring in public nested class - "D106", - # Missing docstring in __init__ - "D107", - # Use r””” if any backslashes in a docstring - "D301", + # https://docs.astral.sh/ruff/rules/ANN101 + "ANN101", + # https://docs.astral.sh/ruff/rules/ANN102 + "ANN102", + # https://docs.astral.sh/ruff/rules/ANN401 + "ANN401", + # https://docs.astral.sh/ruff/rules/ANN003 + "ANN003", + # https://docs.astral.sh/ruff/rules/D100 + "D100", + # https://docs.astral.sh/ruff/rules/D104 + "D104", + # https://docs.astral.sh/ruff/rules/D106 + "D106", + # https://docs.astral.sh/ruff/rules/D107 + "D107", ] -statistics = true -count = true -max-complexity = 10 -pytest-fixture-no-parentheses = true -pytest-parametrize-names-type = "list" -pytest-parametrize-values-type = "list" -pytest-parametrize-values-row-type = "list" -inline-quotes = "double" -docstring-quotes = "double" -docstring-convention = "pep257" -exclude = [ - ".venv", - "__init__.py", - "docs", +[tool.ruff.lint.per-file-ignores] +"__init__.py" = [ + # https://docs.astral.sh/ruff/rules/F401 + "F401", ] - -[tool.black] -line-length = 79 -target-version = [ - "py311", +"**/tests/*" = [ + # https://docs.astral.sh/ruff/rules/S101 + "S101", + # https://docs.astral.sh/ruff/rules/S106 + "S106", + # https://docs.astral.sh/ruff/rules/S311 + "S311", ] +"**/test_*" = [ + # https://docs.astral.sh/ruff/rules/ANN201 + "ANN201", +] +[tool.ruff.lint.isort] +force-wrap-aliases = true +split-on-trailing-comma = true +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +parametrize-names-type = "list" +parametrize-values-type = "list" +parametrize-values-row-type = "list" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + [tool.mypy] # https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports diff --git a/saritasa_invocations/_config.py b/saritasa_invocations/_config.py index 816c8c7..2642750 100644 --- a/saritasa_invocations/_config.py +++ b/saritasa_invocations/_config.py @@ -89,7 +89,7 @@ class DockerSettings: class GitHubActionsSettings: """Settings for github actions module.""" - hosts: collections.abc.Sequence[str] = tuple() + hosts: collections.abc.Sequence[str] = () @dataclasses.dataclass @@ -97,7 +97,7 @@ class DjangoSettings: """Settings for django module.""" runserver_command: str = "runserver_plus" - runserver_host: str = "0.0.0.0" + runserver_host: str = "0.0.0.0" # noqa: S104 runserver_port: str = "8000" runserver_params: str = "" runserver_docker_params: str = "--rm --service-ports" @@ -149,7 +149,7 @@ class FastAPISettings: uvicorn_command: str = "-m uvicorn" app: str = "config:fastapi_app" - host: str = "0.0.0.0" + host: str = "0.0.0.0" # noqa: S104 port: str = "8000" params: str = "--reload" docker_params: str = "--rm --service-ports" @@ -220,7 +220,7 @@ class DBSettings: class K8SSettingsMeta(type): """Meta class for K8SSettings.""" - def __call__(cls, *args, **kwargs) -> "K8SSettings": + def __call__(cls, *args, **kwargs) -> "K8SSettings": # noqa: ANN002 """Update mapping of environments.""" instance: K8SSettings = super().__call__(*args, **kwargs) if instance.name in _K8S_CONFIGS: @@ -243,7 +243,7 @@ class K8SDBSettings: "--output jsonpath='{{.items[0].metadata.name}}'" ) exec_command: str = ( - "kubectl exec -ti --namespace {db_pod_namespace} " "$({db_pod})" + "kubectl exec -ti --namespace {db_pod_namespace} $({db_pod})" ) dump_command: str = ( "pg_dump " diff --git a/saritasa_invocations/alembic.py b/saritasa_invocations/alembic.py index d256891..34e8ab6 100644 --- a/saritasa_invocations/alembic.py +++ b/saritasa_invocations/alembic.py @@ -52,12 +52,12 @@ def wait_for_database(context: invoke.Context) -> None: ) wait_for_database._called = True # type: ignore return - except invoke.UnexpectedExit: + except invoke.UnexpectedExit as error: printing.print_error( "Failed to connect to db, " f"after {config.alembic.connect_attempts} attempts", ) - raise invoke.Exit(code=1) + raise invoke.Exit(code=1) from error @invoke.task @@ -160,7 +160,7 @@ def check_for_adjust_messages( for filepath in _get_migration_files_paths( config.alembic.migrations_folder, ): - with open(filepath) as migration_file: + with pathlib.Path(filepath).open() as migration_file: file_text = migration_file.read() for adjust_message in config.alembic.adjust_messages: if adjust_message in file_text: diff --git a/saritasa_invocations/cruft.py b/saritasa_invocations/cruft.py index e0f1c93..c1752d4 100644 --- a/saritasa_invocations/cruft.py +++ b/saritasa_invocations/cruft.py @@ -12,7 +12,7 @@ def check_for_cruft_files(context: invoke.Context) -> None: found_files = tuple( filter( lambda filepath: not filepath.startswith(".venv"), - map(str, pathlib.Path(".").glob("**/*.rej")), + map(str, pathlib.Path().glob("**/*.rej")), ), ) if not found_files: diff --git a/saritasa_invocations/db_k8s.py b/saritasa_invocations/db_k8s.py index e3e673b..0ff9431 100644 --- a/saritasa_invocations/db_k8s.py +++ b/saritasa_invocations/db_k8s.py @@ -1,4 +1,4 @@ -import os +import pathlib import invoke @@ -55,7 +55,7 @@ def get_dump( pod_namespace=config.namespace, get_pod_name_command=_generate_get_pod_name_command(context), path_to_file_in_pod=f"tmp/{file}", - path_to_where_save_file=f"{os.getcwd()}/{file}", + path_to_where_save_file=f"{pathlib.Path.cwd()}/{file}", ) k8s.success(context, f"Downloaded dump ({file}) from pod. Clean up") context.run(f"{_generate_exec_command(context)} -- rm tmp/{file}") diff --git a/saritasa_invocations/django.py b/saritasa_invocations/django.py index 7d68e82..e35cf8c 100644 --- a/saritasa_invocations/django.py +++ b/saritasa_invocations/django.py @@ -42,6 +42,7 @@ def manage( be ready. Args: + ---- context: Invoke context command: Manage command docker_params: Params for docker run @@ -78,7 +79,7 @@ def check_new_migrations(context: invoke.Context) -> None: @invoke.task -def startapp(context: invoke.Context): +def startapp(context: invoke.Context) -> None: """Create new django app. Requires cookiecutter: @@ -178,7 +179,7 @@ def run(context: invoke.Context) -> None: manage( context, docker_params=config.django.runserver_docker_params, - command="{command} {host}:{port} {params}".format( + command="{command} {host}:{port} {params}".format( # noqa: UP032 command=config.django.runserver_command, host=config.django.runserver_host, port=config.django.runserver_port, @@ -300,7 +301,7 @@ def load_django_db_settings(context: invoke.Context) -> dict[str, str]: config = _config.Config.from_context(context) os.environ["DJANGO_SETTINGS_MODULE"] = config.django.settings_path - from django.conf import settings + from django.conf import settings # type: ignore db_settings = settings.DATABASES["default"] return { diff --git a/saritasa_invocations/docker.py b/saritasa_invocations/docker.py index 3768ea4..e2a46ce 100644 --- a/saritasa_invocations/docker.py +++ b/saritasa_invocations/docker.py @@ -1,5 +1,5 @@ import collections.abc -import os +import pathlib import invoke @@ -25,7 +25,7 @@ def buildpack( """Build app image using buildpacks.""" config = _config.Config.from_context(context) # Builder needs requirements.txt - if os.path.exists(config.docker.buildpack_requirements_path): + if pathlib.Path(config.docker.buildpack_requirements_path).exists(): context.run( f"cp {config.docker.buildpack_requirements_path}/{env}.txt " "requirements.txt", @@ -34,7 +34,7 @@ def buildpack( runner = runner or config.docker.buildpack_runner tag = tag or config.docker.build_image_tag context.run(f"pack build --builder={builder} --run-image={runner} {tag}") - if os.path.exists(config.docker.buildpack_requirements_path): + if pathlib.Path(config.docker.buildpack_requirements_path).exists(): context.run("rm requirements.txt") @@ -54,6 +54,7 @@ def docker_compose_run( files. Args: + ---- context: Invoke context params: Configuration params for docker compose container: Name of container to start @@ -82,6 +83,7 @@ def docker_compose_exec( files. Args: + ---- context: Invoke context service: Name of service to run command in command: Command to run in service container @@ -109,6 +111,7 @@ def up_containers( Add `d` kwarg to run them in background. Args: + ---- context: Invoke context containers: Name of containers to start detach: To run them in background @@ -117,6 +120,7 @@ def up_containers( same ports, for example, Postgres and redis. Raises: + ------ UnexpectedExit: when `up` command wasn't successful """ diff --git a/saritasa_invocations/git.py b/saritasa_invocations/git.py index 6107cc2..acea30d 100644 --- a/saritasa_invocations/git.py +++ b/saritasa_invocations/git.py @@ -245,7 +245,7 @@ def _move_file( from_path: str, to_path: str, message: str, - options: collections.abc.Sequence[str] = tuple(), + options: collections.abc.Sequence[str] = (), ) -> None: """Move `first_file `to `second_file` path using git.""" context.run(f"git mv {' '.join(options)} {from_path} {to_path}") @@ -316,7 +316,7 @@ def _get_project_task_from_current_branch( return task -def _display_continue_prompt(): +def _display_continue_prompt() -> None: """Display continue message. If `n` entered, then exit script. diff --git a/saritasa_invocations/k8s.py b/saritasa_invocations/k8s.py index cfd2b90..dbec69c 100644 --- a/saritasa_invocations/k8s.py +++ b/saritasa_invocations/k8s.py @@ -154,7 +154,9 @@ def logs( @invoke.task -def pods(context) -> None: +def pods( + context: invoke.Context, +) -> None: """Get pods from k8s.""" success(context, "Getting pods") context.run("kubectl get pods") diff --git a/saritasa_invocations/pip.py b/saritasa_invocations/pip.py index b60f669..c10b837 100644 --- a/saritasa_invocations/pip.py +++ b/saritasa_invocations/pip.py @@ -27,7 +27,7 @@ def install_dependencies( @invoke.task(aliases=["compile-dependencies"]) -def compile( +def compile( # noqa: A001 context: invoke.Context, upgrade: bool = False, ) -> None: diff --git a/saritasa_invocations/poetry.py b/saritasa_invocations/poetry.py index 643e7b5..5df6f78 100644 --- a/saritasa_invocations/poetry.py +++ b/saritasa_invocations/poetry.py @@ -15,7 +15,7 @@ def update( context: invoke.Context, groups: str = "", params: str = "", -): +) -> None: """Update dependencies with respect to version constraints using poetry up. https://python-poetry.org/docs/dependency-specification/ @@ -43,7 +43,7 @@ def update_to_latest( groups: str = "", params: str = "", fallback: bool = True, -): +) -> None: """Update dependencies to latest versions using poetry up plugin. By default fallbacks to `update` task in case of an error. diff --git a/saritasa_invocations/secrets.py b/saritasa_invocations/secrets.py index f729778..7d27757 100644 --- a/saritasa_invocations/secrets.py +++ b/saritasa_invocations/secrets.py @@ -1,5 +1,5 @@ -import collections import collections.abc +import pathlib import re import invoke @@ -36,9 +36,9 @@ def setup_env_credentials( def env_secret_replacer(env_file_path: str, **credentials) -> None: """Replace secret in env file.""" - with open(env_file_path, mode="r") as env_file: + with pathlib.Path(env_file_path).open() as env_file: env_data = env_file.read() - with open(env_file_path, mode="w") as env_file: + with pathlib.Path(env_file_path).open(mode="w") as env_file: for cred, value in credentials.items(): env_data = re.sub( rf"{cred}=.*\n", diff --git a/saritasa_invocations/system.py b/saritasa_invocations/system.py index 5154997..b22c7d1 100644 --- a/saritasa_invocations/system.py +++ b/saritasa_invocations/system.py @@ -1,4 +1,4 @@ -import os +import pathlib import invoke @@ -13,6 +13,8 @@ def copy_local_settings( """Copy local settings from template. Args: + ---- + context: invoke's context force_update: rewrite file if exists or not """ @@ -28,11 +30,13 @@ def copy_local_settings( @invoke.task def copy_vscode_settings( context: invoke.Context, - force_update=False, + force_update: bool = False, ) -> None: """Copy vscode settings from template. Args: + ---- + context: invoke's context force_update: rewrite file if exists or not """ @@ -49,10 +53,10 @@ def _rewrite_file( context: invoke.Context, from_path: str, to_path: str, - force_update=False, + force_update: bool = False, ) -> None: """Copy file to destination.""" - if force_update or not os.path.isfile(to_path): + if force_update or not pathlib.Path(to_path).is_file(): context.run(" ".join(("cp", from_path, to_path))) diff --git a/tasks.py b/tasks.py index 3f1aed2..7a7e80a 100644 --- a/tasks.py +++ b/tasks.py @@ -27,12 +27,12 @@ # Configurations for run command ns.configure( - dict( - run=dict( - pty=True, - echo=True, - ), - saritasa_invocations=saritasa_invocations.Config( + { + "run": { + "pty": True, + "echo": True, + }, + "saritasa_invocations": saritasa_invocations.Config( project_name="saritasa_invocations", pre_commit=saritasa_invocations.PreCommitSettings( hooks=( @@ -42,5 +42,5 @@ ), ), ), - ), + }, )