diff --git a/.github/workflows/backend-QA.yml b/.github/workflows/backend-QA.yml index 62176949..60bcafa6 100644 --- a/.github/workflows/backend-QA.yml +++ b/.github/workflows/backend-QA.yml @@ -26,16 +26,16 @@ jobs: working-directory: backend run: | pip install -U pip - pip install -e .[lint,scripts,test,check,maint-scripts] + pip install -e .[qa,scripts,test,maint-scripts] - - name: Check black formatting + - name: Check Linting working-directory: backend - run: inv lint-black + run: inv check-lint - - name: Check ruff + - name: Check Formatting working-directory: backend - run: inv lint-ruff + run: inv check-format - - name: Check pyright + - name: Check Type working-directory: backend - run: inv check-pyright + run: inv check-type diff --git a/.github/workflows/healthcheck-QA.yml b/.github/workflows/healthcheck-QA.yml index 0c88af89..3e3ccc06 100644 --- a/.github/workflows/healthcheck-QA.yml +++ b/.github/workflows/healthcheck-QA.yml @@ -26,16 +26,16 @@ jobs: working-directory: healthcheck run: | pip install -U pip - pip install -e .[lint,scripts,test,check] + pip install -e .[qa,scripts,test] - - name: Check black formatting + - name: Check Linting working-directory: healthcheck - run: inv lint-black + run: inv check-lint - - name: Check ruff + - name: Check Formatting working-directory: healthcheck - run: inv lint-ruff + run: inv check-format - - name: Check pyright + - name: Check Type working-directory: healthcheck - run: inv check-pyright + run: inv check-type diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71f27ccc..901b48af 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,18 +8,13 @@ repos: - id: mixed-line-ending - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.10 + rev: v0.15.12 hooks: - id: ruff-check - id: ruff-format - - repo: https://github.com/psf/black - rev: "25.1.0" - hooks: - - id: black - - repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.400 + rev: v1.1.409 hooks: - id: pyright name: pyright (system) @@ -30,22 +25,25 @@ repos: require_serial: true minimum_pre_commit_version: "2.9.2" - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.1.0 + - repo: local hooks: - id: prettier - files: frontend\/.*$ # files in javascript folder + name: prettier (system) + entry: prettier --write --ignore-unknown + language: node + files: frontend\/.*$ # files in frontend folder + minimum_pre_commit_version: "2.9.2" -# - repo: https://github.com/pre-commit/mirrors-eslint -# rev: v9.35.0 -# hooks: -# - id: eslint -# additional_dependencies: -# - eslint@9.29.0 -# - eslint-plugin-vue@10.2.0 -# - jiti@2.4.2 -# types: [file] -# files: frontend\/src\/.*(?:\.[jt]sx?|\.vue)$ # *.js, *.jsx, *.ts, *.tsx, *.vue in frontend-ui/src folder -# args: -# - --config -# - frontend/eslint.config.ts + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v9.35.0 + hooks: + - id: eslint + additional_dependencies: + - eslint@9.29.0 + - eslint-plugin-vue@10.2.0 + - jiti@2.4.2 + types: [file] + files: frontend\/src\/.*(?:\.[jt]sx?|\.vue)$ # *.js, *.jsx, *.ts, *.tsx, *.vue in frontend/src folder + args: + - --config + - frontend/eslint.config.ts diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c926dd05..97f33087 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -17,7 +17,7 @@ classifiers = [ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", ] dependencies = [ - "alembic == 1.15.2", + "alembic == 1.18.4", "pydantic == 2.11.4", # this is also a sub-dep of fastapi but we rely a lot on it "SQLAlchemy == 2.0.41", "pymongo == 4.13.0", @@ -38,12 +38,9 @@ api = [ scripts = [ "invoke == 2.2.0", ] -lint = [ - "black == 25.1.0", - "ruff == 0.11.10", -] -check = [ - "pyright == 1.1.400", +qa = [ + "ruff==0.15.12", + "pyright==1.1.409", "types-humanfriendly == 10.0.0" ] test = [ @@ -61,12 +58,10 @@ maint-scripts = [ ] dev = [ "pre-commit == 4.2.0", - "ipykernel == 6.29.5", "cms_backend[api]", "cms_backend[scripts]", - "cms_backend[lint]", "cms_backend[test]", - "cms_backend[check]", + "cms_backend[qa]", "cms_backend[maint-scripts]", ] @@ -105,29 +100,19 @@ report-cov = "inv report-cov" coverage = "inv coverage --args '{args}'" html = "inv coverage --html --args '{args}'" -[tool.hatch.envs.lint] -template = "lint" +[tool.hatch.envs.qa] +template = "qa" skip-install = false -features = ["scripts", "lint"] - -[tool.hatch.envs.lint.scripts] -black = "inv lint-black --args '{args}'" -ruff = "inv lint-ruff --args '{args}'" -all = "inv lintall --args '{args}'" -fix-black = "inv fix-black --args '{args}'" -fix-ruff = "inv fix-ruff --args '{args}'" -fixall = "inv fixall --args '{args}'" - -[tool.hatch.envs.check] -features = ["scripts", "test", "check"] - -[tool.hatch.envs.check.scripts] -pyright = "inv check-pyright --args '{args}'" -all = "inv checkall --args '{args}'" - -[tool.black] -line-length = 88 -target-version = ['py313'] +features = ["scripts", "qa", "test"] + +[tool.hatch.envs.qa.scripts] +check-lint = "inv check-lint --args '{args}'" +check-format = "inv check-format --args '{args}'" +check-type = "inv check-type --args '{args}'" +check-all = "inv check-all --args '{args}'" +fix-format = "inv fix-format --args '{args}'" +fix-lint = "inv fix-lint --args '{args}'" +fix-all = "inv fix-all --args '{args}'" [tool.ruff] line-length = 88 diff --git a/backend/src/cms_backend/api/routes/models.py b/backend/src/cms_backend/api/routes/models.py index 6277d1c7..46c46402 100644 --- a/backend/src/cms_backend/api/routes/models.py +++ b/backend/src/cms_backend/api/routes/models.py @@ -1,5 +1,5 @@ import math -from typing import Generic, TypeVar +from typing import TypeVar from pydantic import Field @@ -16,7 +16,7 @@ class Paginator(BaseModel): page: int -class ListResponse(BaseModel, Generic[T]): +class ListResponse[T](BaseModel): meta: Paginator items: list[T] diff --git a/backend/src/cms_backend/api/token.py b/backend/src/cms_backend/api/token.py index fbb55384..02e66d01 100644 --- a/backend/src/cms_backend/api/token.py +++ b/backend/src/cms_backend/api/token.py @@ -153,10 +153,9 @@ def can_decode(self, token: str) -> bool: except Exception: return False - if payload.get( - "iss" - ) != Context.oauth_issuer or Context.oauth_session_audience_id not in payload.get( - "aud", [] + if ( + payload.get("iss") != Context.oauth_issuer + or Context.oauth_session_audience_id not in payload.get("aud", []) ): return False return True diff --git a/backend/src/cms_backend/db/book.py b/backend/src/cms_backend/db/book.py index 473fc551..e94b70ba 100644 --- a/backend/src/cms_backend/db/book.py +++ b/backend/src/cms_backend/db/book.py @@ -302,9 +302,8 @@ def determine_current_location_kind( ): return "quarantine" - if ( - loc.warehouse_id == Context.staging_warehouse_id - and loc.path.is_relative_to(Context.staging_base_path) + if loc.warehouse_id == Context.staging_warehouse_id and loc.path.is_relative_to( + Context.staging_base_path ): return "staging" diff --git a/backend/src/cms_backend/schemas/orms.py b/backend/src/cms_backend/schemas/orms.py index 0b9139b1..0f417d4a 100644 --- a/backend/src/cms_backend/schemas/orms.py +++ b/backend/src/cms_backend/schemas/orms.py @@ -1,6 +1,6 @@ from datetime import datetime from pathlib import Path -from typing import Any, Generic, TypeVar +from typing import Any, TypeVar from uuid import UUID from cms_backend.api.routes.fields import NotEmptyString @@ -9,7 +9,7 @@ T = TypeVar("T") -class ListResult(BaseModel, Generic[T]): +class ListResult[T](BaseModel): nb_records: int records: list[T] diff --git a/backend/tasks.py b/backend/tasks.py index 87cd5529..91def6a3 100644 --- a/backend/tasks.py +++ b/backend/tasks.py @@ -42,69 +42,65 @@ def coverage(ctx: Context, args: str = "", *, html: bool = False): report_cov(ctx, html=html) -@task(optional=["args"], help={"args": "black additional arguments"}) -def lint_black(ctx: Context, args: str = "."): +def _lint(ctx: Context, args: str = "."): args = args or "." # needed for hatch script - ctx.run("black --version", pty=use_pty) - ctx.run(f"black --check --diff {args}", pty=use_pty) + ctx.run("ruff --version", pty=use_pty) + ctx.run(f"ruff check {args}", pty=use_pty) @task(optional=["args"], help={"args": "ruff additional arguments"}) -def lint_ruff(ctx: Context, args: str = "."): +def check_lint(ctx: Context, args: str = "."): + """check linting with ruff""" args = args or "." # needed for hatch script - ctx.run("ruff --version", pty=use_pty) - ctx.run(f"ruff check {args}", pty=use_pty) + _lint(ctx, args) -@task( - optional=["args"], - help={ - "args": "linting tools (black, ruff) additional arguments, typically a path", - }, -) -def lintall(ctx: Context, args: str = "."): - """Check linting""" +@task(optional=["args"], help={"args": "ruff additional arguments"}) +def fix_lint(ctx: Context, args: str = "."): + """fix linting issues with ruff""" args = args or "." # needed for hatch script - lint_black(ctx, args) - lint_ruff(ctx, args) + _lint(ctx, f"--fix {args}") @task(optional=["args"], help={"args": "check tools (pyright) additional arguments"}) -def check_pyright(ctx: Context, args: str = ""): +def check_type(ctx: Context, args: str = ""): """check static types with pyright""" ctx.run("pyright --version") ctx.run(f"pyright {args}", pty=use_pty) -@task(optional=["args"], help={"args": "check tools (pyright) additional arguments"}) -def checkall(ctx: Context, args: str = ""): - """check static types""" - check_pyright(ctx, args) +def _format(ctx: Context, args: str = "."): + args = args or "." # needed for hatch script + ctx.run("ruff --version", pty=use_pty) + ctx.run(f"ruff format {args}", pty=use_pty) -@task(optional=["args"], help={"args": "black additional arguments"}) -def fix_black(ctx: Context, args: str = "."): - """fix black formatting""" +@task(optional=["args"], help={"args": "ruff additional arguments"}) +def check_format(ctx: Context, args: str = "."): + """check formatting with ruff""" args = args or "." # needed for hatch script - ctx.run(f"black {args}", pty=use_pty) + _format(ctx, f"--check {args}") @task(optional=["args"], help={"args": "ruff additional arguments"}) -def fix_ruff(ctx: Context, args: str = "."): - """fix all ruff rules""" +def fix_format(ctx: Context, args: str = "."): + """fix formatting with ruff""" args = args or "." # needed for hatch script - ctx.run(f"ruff check --fix {args}", pty=use_pty) + _format(ctx, args) -@task( - optional=["args"], - help={ - "args": "linting tools (black, ruff) additional arguments, typically a path", - }, -) -def fixall(ctx: Context, args: str = "."): +@task(optional=["args"], help={"args": "additional arguments"}) +def check_all(ctx: Context, args: str = ""): + """check linting, formatting and static types""" + args = args or "." # needed for hatch script + check_lint(ctx, args) + check_format(ctx, args) + check_type(ctx, args) + + +@task(optional=["args"], help={"args": "additional arguments"}) +def fix_all(ctx: Context, args: str = ""): """Fix everything automatically""" args = args or "." # needed for hatch script - fix_black(ctx, args) - fix_ruff(ctx, args) - lintall(ctx, args) + fix_lint(ctx, args) + fix_format(ctx, args) diff --git a/backend/tests/api/routes/test_books.py b/backend/tests/api/routes/test_books.py index 07847cda..4d1dfdf3 100644 --- a/backend/tests/api/routes/test_books.py +++ b/backend/tests/api/routes/test_books.py @@ -1,6 +1,7 @@ import datetime from collections.abc import Callable from http import HTTPStatus +from uuid import UUID, uuid4 import pytest from fastapi.testclient import TestClient @@ -183,6 +184,7 @@ def test_get_books_combined_filters( ], ) def test_get_books_filter_by_needs_attention( + *, client: TestClient, create_book: Callable[..., Book], create_title: Callable[..., Title], @@ -243,7 +245,6 @@ def test_get_books_filter_by_id( create_book: Callable[..., Book], ): """Test get books endpoint passes id filter to database layer""" - from uuid import UUID # Create books with specific UUIDs for partial matching book1 = create_book( @@ -326,7 +327,6 @@ def test_get_book_by_id_not_found( book: Book, # noqa: ARG001 - needed for conftest ): """Test get book by ID endpoint when book doesn't exist""" - from uuid import uuid4 non_existent_id = uuid4() response = client.get(f"/v1/books/{non_existent_id}") diff --git a/backend/tests/api/routes/test_library.py b/backend/tests/api/routes/test_library.py index 196f428e..3c3a44b0 100644 --- a/backend/tests/api/routes/test_library.py +++ b/backend/tests/api/routes/test_library.py @@ -2,6 +2,7 @@ from collections.abc import Callable from datetime import timedelta from http import HTTPStatus +from pathlib import Path from uuid import uuid4 from xml.etree import ElementTree as ET @@ -92,7 +93,6 @@ def _add_title_to_collection( path: str, ) -> None: """Helper to add a title to a collection""" - from pathlib import Path ct = CollectionTitle(path=Path(path)) ct.title = title diff --git a/backend/tests/api/routes/test_zimfarm_notification.py b/backend/tests/api/routes/test_zimfarm_notification.py index 66bd311a..b189987f 100644 --- a/backend/tests/api/routes/test_zimfarm_notification.py +++ b/backend/tests/api/routes/test_zimfarm_notification.py @@ -2,6 +2,7 @@ from datetime import timedelta from http import HTTPStatus from typing import Any +from uuid import UUID import pytest from dateutil.parser import isoparse @@ -392,7 +393,6 @@ def test_get_zimfarm_notifications_filter_by_id( create_zimfarm_notification: Callable[..., ZimfarmNotification], ): """Test get zimfarm_notifications endpoint passes id filter to database layer""" - from uuid import UUID # Create notifications with specific UUIDs for partial matching notif1 = create_zimfarm_notification( diff --git a/backend/tests/api/test_token_decoder.py b/backend/tests/api/test_token_decoder.py index cc151eb2..e03e8428 100644 --- a/backend/tests/api/test_token_decoder.py +++ b/backend/tests/api/test_token_decoder.py @@ -36,9 +36,7 @@ def create_test_session_jwt_token( } # Create a test token (unsigned for testing purposes) - return jwt.encode( - payload, "test-secret", algorithm="HS256" - ) # pyright: ignore[reportUnknownMemberType] + return jwt.encode(payload, "test-secret", algorithm="HS256") # pyright: ignore[reportUnknownMemberType] def create_test_client_jwt_token( @@ -61,9 +59,7 @@ def create_test_client_jwt_token( } # Create a test token (unsigned for testing purposes) - return jwt.encode( - payload, "test-secret", algorithm="HS256" - ) # pyright: ignore[reportUnknownMemberType] + return jwt.encode(payload, "test-secret", algorithm="HS256") # pyright: ignore[reportUnknownMemberType] def test_verify_session_access_token_expired_token( diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 2f906523..74d63c9d 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,4 +1,3 @@ -# ruff: noqa: E501 from collections.abc import Callable, Generator from datetime import datetime from pathlib import Path diff --git a/backend/tests/db/test_account.py b/backend/tests/db/test_account.py index d2d73a12..f2e7ea96 100644 --- a/backend/tests/db/test_account.py +++ b/backend/tests/db/test_account.py @@ -130,7 +130,7 @@ def test_update_account_partial(dbsession: OrmSession, account: Account): def test_update_account_no_display_name(dbsession: OrmSession, account: Account): - with pytest.raises(ValueError, match="Account must have a display name."): + with pytest.raises(ValueError, match=r"Account must have a display name."): update_account( dbsession, account_id=account.id, diff --git a/backend/tests/db/test_books.py b/backend/tests/db/test_books.py index 8cb320d2..ba2dc521 100644 --- a/backend/tests/db/test_books.py +++ b/backend/tests/db/test_books.py @@ -1,7 +1,7 @@ import datetime from collections.abc import Callable from pathlib import Path -from uuid import uuid4 +from uuid import UUID, uuid4 import pytest from sqlalchemy.orm import Session as OrmSession @@ -120,6 +120,7 @@ def test_get_books_pagination( ], ) def test_get_books_filter_by_has_title( + *, dbsession: OrmSession, create_book: Callable[..., Book], create_title: Callable[..., Title], @@ -174,7 +175,6 @@ def test_get_books_book_id_filter( expected_ids: list[str], ): """Test that get_books works with book_id filter""" - from uuid import UUID # Create books with specific UUIDs book_mapping = { @@ -222,7 +222,6 @@ def test_get_books_book_id_combined_with_other_filters( create_title: Callable[..., Title], ): """Test book_id filter combined with other filters""" - from uuid import UUID title = create_title() @@ -274,6 +273,7 @@ def test_get_books_book_id_combined_with_other_filters( ], ) def test_get_books_filter_by_needs_attention( + *, dbsession: OrmSession, create_book: Callable[..., Book], create_title: Callable[..., Title], @@ -416,7 +416,6 @@ def test_get_zim_urls_book_with_subpath( create_book_location: Callable[..., BookLocation], ): """Download URL includes subpath when book is not at root""" - from pathlib import Path warehouse = create_warehouse() title = create_title(name="test_en_all") diff --git a/backend/tests/db/test_zimfarm_notification.py b/backend/tests/db/test_zimfarm_notification.py index b278d5f0..f84dd718 100644 --- a/backend/tests/db/test_zimfarm_notification.py +++ b/backend/tests/db/test_zimfarm_notification.py @@ -1,6 +1,6 @@ from collections.abc import Callable from datetime import timedelta -from uuid import uuid4 +from uuid import UUID, uuid4 import pytest from faker import Faker @@ -105,6 +105,7 @@ def test_get_next_notification_to_process_or_none( ], ) def test_get_zimfarm_notifications_filters( + *, dbsession: OrmSession, create_zimfarm_notification: Callable[..., ZimfarmNotification], create_book: Callable[..., Book], @@ -289,7 +290,6 @@ def test_get_zimfarm_notifications_notification_id_filter( expected_ids: list[str], ): """Test that get_zimfarm_notifications works with notification_id filter""" - from uuid import UUID # Create notifications with specific UUIDs notif_mapping = { @@ -343,7 +343,6 @@ def test_get_zimfarm_notifications_notification_id_combined_with_other_filters( create_book: Callable[..., Book], ): """Test notification_id filter combined with other filters""" - from uuid import UUID # Create notifications with specific characteristics notif1 = create_zimfarm_notification( diff --git a/backend/tests/test_schemas.py b/backend/tests/test_schemas.py index f25473b5..9fb936d3 100644 --- a/backend/tests/test_schemas.py +++ b/backend/tests/test_schemas.py @@ -2,7 +2,7 @@ import json from datetime import UTC, datetime -from enum import Enum +from enum import StrEnum from dateutil.parser import isoparse @@ -70,7 +70,7 @@ def __init__(self): def test_use_enum_values(self): """Test that enum values are used instead of enum instances""" - class Status(str, Enum): + class Status(StrEnum): ACTIVE = "active" INACTIVE = "inactive" diff --git a/healthcheck/pyproject.toml b/healthcheck/pyproject.toml index 758a21d9..e4fe0230 100644 --- a/healthcheck/pyproject.toml +++ b/healthcheck/pyproject.toml @@ -31,12 +31,9 @@ dynamic = ["version"] scripts = [ "invoke == 2.2.0", ] -lint = [ - "black == 25.1.0", - "ruff == 0.11.10", -] -check = [ - "pyright == 1.1.400", +qa = [ + "ruff==0.15.12", + "pyright==1.1.409", "types-humanfriendly == 10.0.0" ] test = [ @@ -49,11 +46,9 @@ test = [ ] dev = [ "pre-commit == 4.2.0", - "ipykernel == 6.29.5", "healthcheck[scripts]", - "healthcheck[lint]", + "healthcheck[qa]", "healthcheck[test]", - "healthcheck[check]", ] [project.urls] @@ -84,29 +79,19 @@ report-cov = "inv report-cov" coverage = "inv coverage --args '{args}'" html = "inv coverage --html --args '{args}'" -[tool.hatch.envs.lint] -template = "lint" +[tool.hatch.envs.qa] +template = "qa" skip-install = false -features = ["scripts", "lint"] - -[tool.hatch.envs.lint.scripts] -black = "inv lint-black --args '{args}'" -ruff = "inv lint-ruff --args '{args}'" -all = "inv lintall --args '{args}'" -fix-black = "inv fix-black --args '{args}'" -fix-ruff = "inv fix-ruff --args '{args}'" -fixall = "inv fixall --args '{args}'" - -[tool.hatch.envs.check] -features = ["scripts", "test", "check"] - -[tool.hatch.envs.check.scripts] -pyright = "inv check-pyright --args '{args}'" -all = "inv checkall --args '{args}'" - -[tool.black] -line-length = 88 -target-version = ['py313'] +features = ["scripts", "qa", "test"] + +[tool.hatch.envs.qa.scripts] +check-lint = "inv check-lint --args '{args}'" +check-format = "inv check-format --args '{args}'" +check-type = "inv check-type --args '{args}'" +check-all = "inv check-all --args '{args}'" +fix-format = "inv fix-format --args '{args}'" +fix-lint = "inv fix-lint --args '{args}'" +fix-all = "inv fix-all --args '{args}'" [tool.ruff] line-length = 88 diff --git a/healthcheck/src/healthcheck/status/__init__.py b/healthcheck/src/healthcheck/status/__init__.py index ef21956f..add81921 100644 --- a/healthcheck/src/healthcheck/status/__init__.py +++ b/healthcheck/src/healthcheck/status/__init__.py @@ -1,6 +1,6 @@ import logging from http import HTTPStatus -from typing import Generic, TypeVar +from typing import TypeVar from pydantic import BaseModel @@ -24,7 +24,7 @@ T = TypeVar("T") -class Result(BaseModel, Generic[T]): +class Result[T](BaseModel): status_code: HTTPStatus success: bool data: T | None = None diff --git a/healthcheck/src/healthcheck/status/zimfarm_notification.py b/healthcheck/src/healthcheck/status/zimfarm_notification.py index 35cb24ca..e683f597 100644 --- a/healthcheck/src/healthcheck/status/zimfarm_notification.py +++ b/healthcheck/src/healthcheck/status/zimfarm_notification.py @@ -16,9 +16,9 @@ class PendingZimfarmNotifications(BaseModel): ) -async def check_zimfarm_notifications_processed() -> ( - Result[PendingZimfarmNotifications] -): +async def check_zimfarm_notifications_processed() -> Result[ + PendingZimfarmNotifications +]: """Check that no zimfarm notifications stuck in pending state.""" check_name = "cms-pending-zimfarm-notifications" received_before = getnow() - Context.zimfarm_notification_pending_delay diff --git a/healthcheck/tasks.py b/healthcheck/tasks.py index 87cd5529..91def6a3 100644 --- a/healthcheck/tasks.py +++ b/healthcheck/tasks.py @@ -42,69 +42,65 @@ def coverage(ctx: Context, args: str = "", *, html: bool = False): report_cov(ctx, html=html) -@task(optional=["args"], help={"args": "black additional arguments"}) -def lint_black(ctx: Context, args: str = "."): +def _lint(ctx: Context, args: str = "."): args = args or "." # needed for hatch script - ctx.run("black --version", pty=use_pty) - ctx.run(f"black --check --diff {args}", pty=use_pty) + ctx.run("ruff --version", pty=use_pty) + ctx.run(f"ruff check {args}", pty=use_pty) @task(optional=["args"], help={"args": "ruff additional arguments"}) -def lint_ruff(ctx: Context, args: str = "."): +def check_lint(ctx: Context, args: str = "."): + """check linting with ruff""" args = args or "." # needed for hatch script - ctx.run("ruff --version", pty=use_pty) - ctx.run(f"ruff check {args}", pty=use_pty) + _lint(ctx, args) -@task( - optional=["args"], - help={ - "args": "linting tools (black, ruff) additional arguments, typically a path", - }, -) -def lintall(ctx: Context, args: str = "."): - """Check linting""" +@task(optional=["args"], help={"args": "ruff additional arguments"}) +def fix_lint(ctx: Context, args: str = "."): + """fix linting issues with ruff""" args = args or "." # needed for hatch script - lint_black(ctx, args) - lint_ruff(ctx, args) + _lint(ctx, f"--fix {args}") @task(optional=["args"], help={"args": "check tools (pyright) additional arguments"}) -def check_pyright(ctx: Context, args: str = ""): +def check_type(ctx: Context, args: str = ""): """check static types with pyright""" ctx.run("pyright --version") ctx.run(f"pyright {args}", pty=use_pty) -@task(optional=["args"], help={"args": "check tools (pyright) additional arguments"}) -def checkall(ctx: Context, args: str = ""): - """check static types""" - check_pyright(ctx, args) +def _format(ctx: Context, args: str = "."): + args = args or "." # needed for hatch script + ctx.run("ruff --version", pty=use_pty) + ctx.run(f"ruff format {args}", pty=use_pty) -@task(optional=["args"], help={"args": "black additional arguments"}) -def fix_black(ctx: Context, args: str = "."): - """fix black formatting""" +@task(optional=["args"], help={"args": "ruff additional arguments"}) +def check_format(ctx: Context, args: str = "."): + """check formatting with ruff""" args = args or "." # needed for hatch script - ctx.run(f"black {args}", pty=use_pty) + _format(ctx, f"--check {args}") @task(optional=["args"], help={"args": "ruff additional arguments"}) -def fix_ruff(ctx: Context, args: str = "."): - """fix all ruff rules""" +def fix_format(ctx: Context, args: str = "."): + """fix formatting with ruff""" args = args or "." # needed for hatch script - ctx.run(f"ruff check --fix {args}", pty=use_pty) + _format(ctx, args) -@task( - optional=["args"], - help={ - "args": "linting tools (black, ruff) additional arguments, typically a path", - }, -) -def fixall(ctx: Context, args: str = "."): +@task(optional=["args"], help={"args": "additional arguments"}) +def check_all(ctx: Context, args: str = ""): + """check linting, formatting and static types""" + args = args or "." # needed for hatch script + check_lint(ctx, args) + check_format(ctx, args) + check_type(ctx, args) + + +@task(optional=["args"], help={"args": "additional arguments"}) +def fix_all(ctx: Context, args: str = ""): """Fix everything automatically""" args = args or "." # needed for hatch script - fix_black(ctx, args) - fix_ruff(ctx, args) - lintall(ctx, args) + fix_lint(ctx, args) + fix_format(ctx, args)