diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 700ecb70b..000000000 --- a/.flake8 +++ /dev/null @@ -1,7 +0,0 @@ -[flake8] -ignore = E501, W503, E203, E231, F405, F403, E701, E704, E731, F821 -exclude = - .git, - __pycache__ - .venv - node_modules diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index b9fb3f3e8..000000000 --- a/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=black diff --git a/CLAUDE.md b/CLAUDE.md index de64bc0a1..90aebc4e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,6 +87,10 @@ When `manage.py runserver` runs, `reactivated/__init__.py` patches the process: - **syrupy**: Snapshot testing (snapshots in `tests/__snapshots__/`) - **Mypy plugin**: `reactivated/plugin.py` — hooks for `Pick` type analysis and `@template`/`@interface` decorator typing +## CI + +When monitoring CI after a push, only wait for "Code tests" and the ubuntu integration test to pass. The macOS integration tests (macos-14, macos-15) are slow and verified manually — don't block on them. + ## Environment Uses Nix (`shell.nix`) for dependency management. Key env vars: diff --git a/development/.flake8 b/development/.flake8 deleted file mode 100644 index 00ce8e865..000000000 --- a/development/.flake8 +++ /dev/null @@ -1,13 +0,0 @@ -[flake8] -exclude = - .git, - __pycache__, - .venv - node_modules -extend-ignore = - # Line too long: black handles this. - E501, - # mypy analyzes undefined names for wildcards. - F403, - # We use literals inside Pick that get flagged as missing variable/names. - F821 diff --git a/development/.isort.cfg b/development/.isort.cfg deleted file mode 100644 index b9fb3f3e8..000000000 --- a/development/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=black diff --git a/development/manage.py b/development/manage.py index 42be6c8ce..121a57c37 100755 --- a/development/manage.py +++ b/development/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys diff --git a/development/pyproject.toml b/development/pyproject.toml index 323196733..886c0ef0f 100644 --- a/development/pyproject.toml +++ b/development/pyproject.toml @@ -5,10 +5,7 @@ requires-python = ">=3.12" dependencies = [ "Django==5.0.2", "psycopg[binary]==3.1.18", - "flake8==7.0.0", - "isort==5.13.2", - "black==22.3.0", - "autoflake==2.3.0", + "ruff>=0.11", "pytest==8.0.1", "pytest-django==4.5.2", "gunicorn==22.0.0", @@ -18,3 +15,10 @@ dependencies = [ [tool.uv.sources] reactivated = { path = "..", editable = true } + +[tool.ruff.lint] +ignore = [ + "E501", # Line too long: formatter handles this. + "F403", # Wildcard imports: mypy analyzes these. + "F821", # Undefined names: used in Pick literals. +] diff --git a/development/scripts/fix.sh b/development/scripts/fix.sh index 080cc2c48..0f0f1874a 100755 --- a/development/scripts/fix.sh +++ b/development/scripts/fix.sh @@ -74,13 +74,10 @@ cd "$PROJECT_ROOT" if [[ -n "${CHANGED_PY_FILES// /}" ]]; then # shellcheck disable=SC2086 - autoflake --exclude node_modules,.venv -i -r --remove-all-unused-imports $CHANGED_PY_FILES + ruff check --force-exclude --fix $CHANGED_PY_FILES || true # shellcheck disable=SC2086 - isort $CHANGED_PY_FILES - - # shellcheck disable=SC2086 - black $CHANGED_PY_FILES + ruff format --force-exclude $CHANGED_PY_FILES fi if [[ -n "${CHANGED_TS_JS_FILES// /}" ]]; then diff --git a/development/scripts/test.sh b/development/scripts/test.sh index 3937ef03a..515e365b1 100755 --- a/development/scripts/test.sh +++ b/development/scripts/test.sh @@ -94,9 +94,8 @@ cd "$PROJECT_ROOT" if [[ $SERVER -eq 1 ]]; then capture_stdout_and_stderr_if_successful pytest - capture_stdout_and_stderr_if_successful flake8 . - capture_stdout_and_stderr_if_successful isort -c . - capture_stdout_and_stderr_if_successful black . --check + capture_stdout_and_stderr_if_successful ruff check --force-exclude . + capture_stdout_and_stderr_if_successful ruff format --force-exclude --check . capture_stdout_and_stderr_if_successful mypy --no-incremental . capture_stdout_and_stderr_if_successful python manage.py makemigrations --dry-run --check fi diff --git a/development/server/example/forms.py b/development/server/example/forms.py index b85792503..b9bc50eb8 100644 --- a/development/server/example/forms.py +++ b/development/server/example/forms.py @@ -1,7 +1,6 @@ from typing import Any from django import forms - from reactivated.forms import ModelFormSetFactory from . import models diff --git a/development/server/example/migrations/0001_initial.py b/development/server/example/migrations/0001_initial.py index a3401dbb2..8038283d2 100644 --- a/development/server/example/migrations/0001_initial.py +++ b/development/server/example/migrations/0001_initial.py @@ -5,7 +5,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/development/server/example/migrations/0002_question_status_and_more.py b/development/server/example/migrations/0002_question_status_and_more.py index 4bbad7840..6b60b79a2 100644 --- a/development/server/example/migrations/0002_question_status_and_more.py +++ b/development/server/example/migrations/0002_question_status_and_more.py @@ -1,14 +1,13 @@ # Generated by Django 5.0.2 on 2026-03-21 21:00 -import server.example.models -from django.db import migrations - import reactivated.constraints import reactivated.fields +from django.db import migrations +import server.example.models -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ ("example", "0001_initial"), ] diff --git a/development/server/example/models.py b/development/server/example/models.py index 929a3e3de..792e6af30 100644 --- a/development/server/example/models.py +++ b/development/server/example/models.py @@ -1,7 +1,6 @@ import enum from django.db import models - from reactivated.fields import EnumField diff --git a/pyproject.toml b/pyproject.toml index 3b0f674e7..03f6adf07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,11 +24,8 @@ dev = [ "django-extensions==3.2.3", "django-stubs-ext==5.1.1", "djangorestframework==3.14.0", - "flake8==7.1.1", "greenlet==3.1.1", - "isort==5.13.2", - "black==24.10.0", - "autoflake==2.3.1", + "ruff>=0.11", "jsonschema==3.1.1", "Jinja2==3.1.4", "playwright==1.48.0", @@ -59,3 +56,16 @@ include = ["reactivated*"] [tool.setuptools.package-data] reactivated = ["py.typed", "templates/**/*"] + +[tool.ruff.lint] +ignore = [ + "E501", # Line too long: formatter handles this. + "F405", # May be undefined from star import. + "F403", # Wildcard imports: mypy analyzes these. + "E731", # Do not assign a lambda. + "F821", # Undefined names: used in Pick literals. + "E721", # Type comparison: used in generic alias origin checks. +] + +[tool.ruff.lint.isort] +known-first-party = ["reactivated"] diff --git a/reactivated/__init__.py b/reactivated/__init__.py index ec8fd4bd3..8fbed1259 100644 --- a/reactivated/__init__.py +++ b/reactivated/__init__.py @@ -339,7 +339,9 @@ def ssr( ]: ... -def ssr(*, props: type[P], params: type[K] | None = None) -> ( +def ssr( + *, props: type[P], params: type[K] | None = None +) -> ( Callable[ [NoArgsView[P]], Callable[[Arg(HttpRequest, "request"), KwArg(Any)], HttpResponse], diff --git a/reactivated/backend.py b/reactivated/backend.py index 1da66575a..5eab152f5 100644 --- a/reactivated/backend.py +++ b/reactivated/backend.py @@ -81,9 +81,9 @@ def render(self, context: Any = None, request: HttpRequest | None = None) -> str return render_jsx_to_string(request, serialized_context, props) - assert ( - False - ), "At this time, only templates with the request object can be rendered with reactivated" + assert False, ( + "At this time, only templates with the request object can be rendered with reactivated" + ) class AdapterTemplate(JSXTemplate): diff --git a/reactivated/constraints.py b/reactivated/constraints.py index e859b26a8..9d305d869 100644 --- a/reactivated/constraints.py +++ b/reactivated/constraints.py @@ -37,7 +37,9 @@ def constraint_sql( columns=columns, ) - def create_sql(self, model: type[Model] | None, schema_editor: BaseDatabaseSchemaEditor | None) -> Statement: # type: ignore[override] + def create_sql( # type: ignore[override] + self, model: type[Model] | None, schema_editor: BaseDatabaseSchemaEditor | None + ) -> Statement: assert model is not None assert schema_editor is not None @@ -64,7 +66,9 @@ def create_sql(self, model: type[Model] | None, schema_editor: BaseDatabaseSchem columns=", ".join([f"'{column}'" for column in columns]), ) - def remove_sql(self, model: type[Model] | None, schema_editor: BaseDatabaseSchemaEditor | None) -> Statement: # type: ignore[override] + def remove_sql( # type: ignore[override] + self, model: type[Model] | None, schema_editor: BaseDatabaseSchemaEditor | None + ) -> Statement: assert model is not None assert schema_editor is not None diff --git a/reactivated/fields.py b/reactivated/fields.py index 3942a6fb1..60634136d 100644 --- a/reactivated/fields.py +++ b/reactivated/fields.py @@ -194,7 +194,9 @@ def deconstruct(self) -> Any: del kwargs["choices"] return name, path, args, kwargs - def contribute_to_class(self, cls: type[models.Model], name: str, **kwargs: Any) -> None: # type: ignore[override] + def contribute_to_class( # type: ignore[override] + self, cls: type[models.Model], name: str, **kwargs: Any + ) -> None: """ We don't store the enum in the constraint. Instead, we store the fields so the autodetection for changed enums works automatically. diff --git a/reactivated/models.py b/reactivated/models.py index 0e9b7a385..34bae5f95 100644 --- a/reactivated/models.py +++ b/reactivated/models.py @@ -98,7 +98,7 @@ def computed_foreign_key( ] ): def inner( - fget: Callable[[T], SOptionalInstance] + fget: Callable[[T], SOptionalInstance], ) -> ComputedRelation[T, S, SOptionalInstance]: return ComputedRelation( fget=fget, label=label, model=model, many=False, null=null diff --git a/reactivated/pick.py b/reactivated/pick.py index eb1e4d689..b3864d607 100644 --- a/reactivated/pick.py +++ b/reactivated/pick.py @@ -97,9 +97,9 @@ def get_field_descriptor( ): if len(remaining) == 0: if isinstance(field_descriptor.descriptor, ForeignObjectRel): - assert ( - False - ), f"You cannot Pick reverse relationships. Specify which fields from {field_name} you want, such as {field_name}.example_field" + assert False, ( + f"You cannot Pick reverse relationships. Specify which fields from {field_name} you want, such as {field_name}.example_field" + ) if ( isinstance(field_descriptor.descriptor, (models.ForeignKey)) @@ -118,12 +118,14 @@ def get_field_descriptor( (), ) - assert ( - False - ), f"Do not specify related fields directly for model {model_class} in {pick_name}. Use {field_name}_id if you just want the reference or {field_name}.subfield if you want fields inside the related model" + assert False, ( + f"Do not specify related fields directly for model {model_class} in {pick_name}. Use {field_name}_id if you just want the reference or {field_name}.subfield if you want fields inside the related model" + ) nested_descriptor, nested_field_names = get_field_descriptor( - pick_name, field_descriptor.annotation or field_descriptor.descriptor.related_model, remaining # type: ignore[arg-type] + pick_name, + field_descriptor.annotation or field_descriptor.descriptor.related_model, # type: ignore[arg-type] + remaining, ) # TODO: Maybe RelatedField replaces all of the above? diff --git a/reactivated/plugin.py b/reactivated/plugin.py index b995aeae0..594d2a290 100644 --- a/reactivated/plugin.py +++ b/reactivated/plugin.py @@ -60,7 +60,8 @@ def analyze_template(ctx: ClassDefContext) -> None: Instance(http_request.node, []), # type: ignore[union-attr, arg-type] ), type_annotation=Instance( - http_request.node, [] # type: ignore[union-attr, arg-type] + http_request.node, # type: ignore[union-attr, arg-type] + [], ), initializer=None, kind=ARG_POS, @@ -71,7 +72,8 @@ def analyze_template(ctx: ClassDefContext) -> None: "render", args=[request_arg], return_type=Instance( - template_response.node, [] # type: ignore[union-attr, arg-type] + template_response.node, # type: ignore[union-attr, arg-type] + [], ), ) diff --git a/reactivated/rpc.py b/reactivated/rpc.py index 5976fa3fe..ddb704e82 100644 --- a/reactivated/rpc.py +++ b/reactivated/rpc.py @@ -138,7 +138,9 @@ def context( def inner_context_provider( request: THttpRequest, *args: Any, **kwargs: Any ) -> Any: - return context_provider(request, self.context_provider(request, *args, **kwargs)) # type: ignore[call-arg] + return context_provider( + request, self.context_provider(request, *args, **kwargs) + ) # type: ignore[call-arg] inner_context_provider.__annotations__ = self.context_provider.__annotations__ inner_context_provider.__name__ = context_provider.__name__ @@ -155,9 +157,8 @@ def process( | Callable[[THttpRequest, None], TResponse] ), *, - context_provider: None | ( - Callable[[THttpRequest, TFirst, TSecond], TContext] - ) = None, + context_provider: None + | (Callable[[THttpRequest, TFirst, TSecond], TContext]) = None, ) -> URLPattern: ... @overload @@ -168,9 +169,8 @@ def process( | Callable[[THttpRequest, None, TForm], TResponse] ), *, - context_provider: None | ( - Callable[[THttpRequest, TFirst, TSecond], TContext] - ) = None, + context_provider: None + | (Callable[[THttpRequest, TFirst, TSecond], TContext]) = None, ) -> URLPattern: ... def process( @@ -182,9 +182,8 @@ def process( | Callable[[THttpRequest, None, TForm], TResponse] ), *, - context_provider: None | ( - Callable[[THttpRequest, TFirst, TSecond], TContext] - ) = None, + context_provider: None + | (Callable[[THttpRequest, TFirst, TSecond], TContext]) = None, ) -> URLPattern: return_type = get_type_hints(view)["return"] return_schema = create_schema(return_type, registry.definitions_registry) @@ -248,9 +247,12 @@ def wrapped_view(request: THttpRequest, *args: Any, **kwargs: Any) -> Any: if request.method == "POST": if form.is_valid(): try: - response = view(request, context, form) if form_type is not None else view(request, context) # type: ignore[arg-type, call-arg] + response = ( + view(request, context, form) # type: ignore[arg-type, call-arg] + if form_type is not None + else view(request, context) + ) except forms.ValidationError as error: - if hasattr(error, "error_dict"): return JsonResponse( error.message_dict, status=400, safe=False @@ -302,7 +304,9 @@ def wrapped_view(request: THttpRequest, *args: Any, **kwargs: Any) -> Any: "type": ( "form_group" if issubclass(form_class, FormGroup) - else "form" if issubclass(form_class, forms.BaseForm) else "form_set" + else "form" + if issubclass(form_class, forms.BaseForm) + else "form_set" ), } route = path(url_path, wrapped_view, name=url_name) @@ -480,7 +484,9 @@ def wrapped_view(request: THttpRequest, *args: Any, **kwargs: Any) -> Any: "type": ( "form_group" if issubclass(form_class, FormGroup) - else "form" if issubclass(form_class, forms.BaseForm) else "form_set" + else "form" + if issubclass(form_class, forms.BaseForm) + else "form_set" ), } route = path(url_path, wrapped_view, name=url_name) @@ -506,6 +512,6 @@ def context( def create_rpc( - authentication: Callable[[HttpRequest], THttpRequest | enum.Enum | Literal[False]] + authentication: Callable[[HttpRequest], THttpRequest | enum.Enum | Literal[False]], ) -> RPC[THttpRequest]: return RPC[THttpRequest](authentication) diff --git a/reactivated/serialization/__init__.py b/reactivated/serialization/__init__.py index d5aad577c..8e0fcd17a 100644 --- a/reactivated/serialization/__init__.py +++ b/reactivated/serialization/__init__.py @@ -357,9 +357,9 @@ def get_serialized_value( callable(field.field.initial) and schema.definitions.get("is_static_context") is True # type: ignore[comparison-overlap] ): - assert ( - False - ), f"Callables are not supported in initial/default values for field {field.name} in form {name}" + assert False, ( + f"Callables are not supported in initial/default values for field {field.name} in form {name}" + ) form = value context = form.get_context() @@ -675,9 +675,9 @@ def get_json_schema( } if len(typed_dicts) > 1 and len(subschemas) != len(typed_dicts): - assert ( - False - ), "Unions with TypedDict must have only TypedDict members and a discriminant" + assert False, ( + "Unions with TypedDict must have only TypedDict members and a discriminant" + ) elif len(typed_dicts) > 1: keys = set.intersection( *[ @@ -824,9 +824,9 @@ def generic_alias_schema( for arg in _Type.__args__: if not isinstance(arg, (type(None), int, str, float, bool)): - assert ( - False - ), f"Unsupported Literal {_Type}. Only simple members are supported." + assert False, ( + f"Unsupported Literal {_Type}. Only simple members are supported." + ) return Thing( schema={"enum": list(_Type.__args__)}, diff --git a/reactivated/serialization/context_processors.py b/reactivated/serialization/context_processors.py index a3a556ec4..f7f256944 100644 --- a/reactivated/serialization/context_processors.py +++ b/reactivated/serialization/context_processors.py @@ -84,9 +84,9 @@ def create_context_processor_type(context_processors: list[str]) -> Any: annotations = get_annotation_or_type_hints(definition) annotation = annotations.get("return", None) - assert ( - annotation - ), f"No annotations found for context processor {context_processor}" + assert annotation, ( + f"No annotations found for context processor {context_processor}" + ) types.append(annotation) return Intersection[types] # type: ignore[misc] diff --git a/reactivated/serialization/widgets.py b/reactivated/serialization/widgets.py index 85b12c539..910e1b4b8 100644 --- a/reactivated/serialization/widgets.py +++ b/reactivated/serialization/widgets.py @@ -164,9 +164,9 @@ def get_serialized_value( ) for index, subwidget_class in enumerate(subwidgets_to_enumerate): - serialized["subwidgets"][index][ - "tag" - ] = f"{subwidget_class.__module__}.{subwidget_class.__qualname__}" + serialized["subwidgets"][index]["tag"] = ( + f"{subwidget_class.__module__}.{subwidget_class.__qualname__}" + ) return serialized diff --git a/reactivated/utils.py b/reactivated/utils.py index 2514013ea..bb110ba7d 100644 --- a/reactivated/utils.py +++ b/reactivated/utils.py @@ -83,9 +83,9 @@ def __getitem__(self, key: Any) -> Any: elif getattr(base_class, "rpartition", False): base_class = str else: - assert ( - False - ), "Unsupported proxy / lazy promise type. See django/utils/functional.py" + assert False, ( + "Unsupported proxy / lazy promise type. See django/utils/functional.py" + ) # DRF uses this, we do not. # if hasattr(key, "_proxy_class"): diff --git a/reactivated/widgets.py b/reactivated/widgets.py index b079b18b6..ba574cc21 100644 --- a/reactivated/widgets.py +++ b/reactivated/widgets.py @@ -17,9 +17,9 @@ def get_context( choices = cast(ModelChoiceIterator, self.choices) assert choices.queryset is not None - assert hasattr( - choices.queryset, "autocomplete" - ), "Models marked for autocompletion must implement autocomplete(query: str) at the manager level" + assert hasattr(choices.queryset, "autocomplete"), ( + "Models marked for autocompletion must implement autocomplete(query: str) at the manager level" + ) to_field_name = choices.field.to_field_name or "pk" diff --git a/requirements.in b/requirements.in index b37a4dcba..e8e6ac84d 100644 --- a/requirements.in +++ b/requirements.in @@ -1,9 +1,6 @@ Django==5.1.3 psycopg[binary]==3.2.3 -flake8==7.1.1 -isort==5.13.2 -black==24.10.0 -autoflake==2.3.1 +ruff>=0.11 Werkzeug==3.0.1 django-extensions==3.2.3 diff --git a/sample/manage.py b/sample/manage.py index cad1d534c..ebb89b467 100755 --- a/sample/manage.py +++ b/sample/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys diff --git a/sample/server/apps/samples/migrations/0001_initial.py b/sample/server/apps/samples/migrations/0001_initial.py index 0b207546c..62905c988 100644 --- a/sample/server/apps/samples/migrations/0001_initial.py +++ b/sample/server/apps/samples/migrations/0001_initial.py @@ -9,7 +9,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/sample/server/apps/samples/migrations/0002_opera_uuid.py b/sample/server/apps/samples/migrations/0002_opera_uuid.py index 00ab6dc70..9d15eb7c6 100644 --- a/sample/server/apps/samples/migrations/0002_opera_uuid.py +++ b/sample/server/apps/samples/migrations/0002_opera_uuid.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("samples", "0001_initial"), ] diff --git a/scripts/fix.sh b/scripts/fix.sh index 89158a3c8..1edcaa42d 100755 --- a/scripts/fix.sh +++ b/scripts/fix.sh @@ -50,13 +50,10 @@ cd "$PROJECT_ROOT" if [[ -n "${CHANGED_PY_FILES// /}" ]]; then # shellcheck disable=SC2086 - autoflake --exclude node_modules,.venv -i -r --remove-all-unused-imports $CHANGED_PY_FILES + ruff check --force-exclude --fix $CHANGED_PY_FILES || true # shellcheck disable=SC2086 - isort $CHANGED_PY_FILES - - # shellcheck disable=SC2086 - black $CHANGED_PY_FILES + ruff format --force-exclude $CHANGED_PY_FILES fi if [[ -n "${CHANGED_PRETTIER_FILES// /}" ]]; then diff --git a/scripts/test.sh b/scripts/test.sh index 5be21ac20..e201c40a1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -69,9 +69,8 @@ if [[ $SERVER -eq 1 ]]; then # See: https://github.com/microsoft/playwright/issues/5501 PATH="$VIRTUAL_ENV/bin:/bin:/sbin/:$PATH" capture_stdout_and_stderr_if_successful pytest - capture_stdout_and_stderr_if_successful flake8 . - capture_stdout_and_stderr_if_successful isort -c . - capture_stdout_and_stderr_if_successful black . --check + capture_stdout_and_stderr_if_successful ruff check --force-exclude . + capture_stdout_and_stderr_if_successful ruff format --force-exclude --check . capture_stdout_and_stderr_if_successful mypy --no-incremental . PATH="$OLD_PATH" fi diff --git a/tests/serialization.py b/tests/serialization.py index 572156c10..129a5e239 100644 --- a/tests/serialization.py +++ b/tests/serialization.py @@ -372,9 +372,7 @@ class Meta: "tests.serialization.test_typed_choices_non_enum..TestForm" ]["properties"]["fields"]["properties"]["non_enum_typed_field"]["properties"][ "widget" - ] == { - "tsType": "widgets.Select" - } + ] == {"tsType": "widgets.Select"} def test_override_pick_types(settings, snapshot): diff --git a/uv.lock b/uv.lock index 4a2fc4d80..c92ecb79c 100644 --- a/uv.lock +++ b/uv.lock @@ -20,42 +20,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "autoflake" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2a/cb/486f912d6171bc5748c311a2984a301f4e2d054833a1da78485866c71522/autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e", size = 27642, upload-time = "2024-03-13T03:41:28.977Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/ee/3fd29bf416eb4f1c5579cf12bf393ae954099258abd7bde03c4f9716ef6b/autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840", size = 32483, upload-time = "2024-03-13T03:41:26.969Z" }, -] - -[[package]] -name = "black" -version = "24.10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813, upload-time = "2024-10-07T19:20:50.361Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256, upload-time = "2024-10-07T19:27:53.355Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534, upload-time = "2024-10-07T19:26:44.953Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892, upload-time = "2024-10-07T19:24:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796, upload-time = "2024-10-07T19:25:06.239Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986, upload-time = "2024-10-07T19:28:50.684Z" }, - { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085, upload-time = "2024-10-07T19:28:12.093Z" }, - { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928, upload-time = "2024-10-07T19:24:15.233Z" }, - { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875, upload-time = "2024-10-07T19:24:42.762Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898, upload-time = "2024-10-07T19:20:48.317Z" }, -] - [[package]] name = "certifi" version = "2026.2.25" @@ -138,18 +102,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -236,20 +188,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ff/4b/3b46c0914ba4b7546a758c35fdfa8e7f017fcbe7f23c878239e93623337a/djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08", size = 1062761, upload-time = "2022-09-22T11:38:41.825Z" }, ] -[[package]] -name = "flake8" -version = "7.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/37/72/e8d66150c4fcace3c0a450466aa3480506ba2cae7b61e100a2613afc3907/flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", size = 48054, upload-time = "2024-08-04T20:32:44.311Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/42/65004373ac4617464f35ed15931b30d764f53cdd30cc78d5aea349c8c050/flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213", size = 57731, upload-time = "2024-08-04T20:32:42.661Z" }, -] - [[package]] name = "greenlet" version = "3.1.1" @@ -313,15 +251,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "isort" -version = "5.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303, upload-time = "2023-12-13T20:37:26.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310, upload-time = "2023-12-13T20:37:23.244Z" }, -] - [[package]] name = "jinja2" version = "3.1.4" @@ -473,15 +402,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, -] - [[package]] name = "mypy" version = "1.19.1" @@ -542,15 +462,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] -[[package]] -name = "platformdirs" -version = "4.9.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, -] - [[package]] name = "playwright" version = "1.48.0" @@ -625,15 +536,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/20/b675af723b9a61d48abd6a3d64cbb9797697d330255d1f8105713d54ed8e/psycopg_binary-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:e90352d7b610b4693fad0feea48549d4315d10f1eba5605421c92bb834e90170", size = 2913413, upload-time = "2024-09-29T21:25:28.151Z" }, ] -[[package]] -name = "pycodestyle" -version = "2.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232, upload-time = "2024-08-04T20:26:54.576Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284, upload-time = "2024-08-04T20:26:53.173Z" }, -] - [[package]] name = "pyee" version = "12.0.0" @@ -646,15 +548,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1d/0d/95993c08c721ec68892547f2117e8f9dfbcef2ca71e098533541b4a54d5f/pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990", size = 14831, upload-time = "2024-08-30T19:40:42.132Z" }, ] -[[package]] -name = "pyflakes" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788, upload-time = "2024-01-05T00:28:47.703Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725, upload-time = "2024-01-05T00:28:45.903Z" }, -] - [[package]] name = "pyrsistent" version = "0.20.0" @@ -795,15 +688,11 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "autoflake" }, - { name = "black" }, { name = "django" }, { name = "django-extensions" }, { name = "django-stubs-ext" }, { name = "djangorestframework" }, - { name = "flake8" }, { name = "greenlet" }, - { name = "isort" }, { name = "jinja2" }, { name = "jsonschema" }, { name = "playwright" }, @@ -815,6 +704,7 @@ dev = [ { name = "pytest-playwright" }, { name = "python-slugify" }, { name = "pyyaml" }, + { name = "ruff" }, { name = "syrupy" }, { name = "types-pyyaml" }, { name = "types-requests" }, @@ -833,15 +723,11 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "autoflake", specifier = "==2.3.1" }, - { name = "black", specifier = "==24.10.0" }, { name = "django", specifier = "==5.1.3" }, { name = "django-extensions", specifier = "==3.2.3" }, { name = "django-stubs-ext", specifier = "==5.1.1" }, { name = "djangorestframework", specifier = "==3.14.0" }, - { name = "flake8", specifier = "==7.1.1" }, { name = "greenlet", specifier = "==3.1.1" }, - { name = "isort", specifier = "==5.13.2" }, { name = "jinja2", specifier = "==3.1.4" }, { name = "jsonschema", specifier = "==3.1.1" }, { name = "playwright", specifier = "==1.48.0" }, @@ -853,6 +739,7 @@ dev = [ { name = "pytest-playwright", specifier = "==0.5.2" }, { name = "python-slugify", specifier = "==8.0.4" }, { name = "pyyaml", specifier = "==6.0.1" }, + { name = "ruff", specifier = ">=0.11" }, { name = "syrupy", specifier = "==4.7.2" }, { name = "types-pyyaml", specifier = "==6.0.12.20240724" }, { name = "types-requests", specifier = "==2.32.0.20241016" }, @@ -976,6 +863,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/18/19221b6242e8135abef8f5859d8ea53e69c503042e6639852fa2865825e3/requests_unixsocket2-1.0.1-py3-none-any.whl", hash = "sha256:bdb2ba5bcd9d2f3c1dd1e099e5dba8e1d50b9eeef7985edd72f957da22304b92", size = 7731, upload-time = "2025-09-03T20:13:52.792Z" }, ] +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +] + [[package]] name = "setuptools" version = "82.0.1" diff --git a/website/.flake8 b/website/.flake8 deleted file mode 100644 index 00ce8e865..000000000 --- a/website/.flake8 +++ /dev/null @@ -1,13 +0,0 @@ -[flake8] -exclude = - .git, - __pycache__, - .venv - node_modules -extend-ignore = - # Line too long: black handles this. - E501, - # mypy analyzes undefined names for wildcards. - F403, - # We use literals inside Pick that get flagged as missing variable/names. - F821 diff --git a/website/.isort.cfg b/website/.isort.cfg deleted file mode 100644 index b9fb3f3e8..000000000 --- a/website/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -profile=black diff --git a/website/manage.py b/website/manage.py index 42be6c8ce..121a57c37 100755 --- a/website/manage.py +++ b/website/manage.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys diff --git a/website/pyproject.toml b/website/pyproject.toml index 2f1450c87..bb1640ed7 100644 --- a/website/pyproject.toml +++ b/website/pyproject.toml @@ -5,10 +5,7 @@ requires-python = ">=3.12" dependencies = [ "Django==5.0.2", "psycopg[binary]==3.1.18", - "flake8==7.0.0", - "isort==5.13.2", - "black==22.3.0", - "autoflake==2.3.0", + "ruff>=0.11", "pytest==8.0.1", "pytest-django==4.5.2", "types-requests==2.27.12", @@ -20,3 +17,10 @@ dependencies = [ [tool.uv.sources] reactivated = { path = "..", editable = true } + +[tool.ruff.lint] +ignore = [ + "E501", # Line too long: formatter handles this. + "F403", # Wildcard imports: mypy analyzes these. + "F821", # Undefined names: used in Pick literals. +] diff --git a/website/scripts/fix.sh b/website/scripts/fix.sh index 080cc2c48..0f0f1874a 100755 --- a/website/scripts/fix.sh +++ b/website/scripts/fix.sh @@ -74,13 +74,10 @@ cd "$PROJECT_ROOT" if [[ -n "${CHANGED_PY_FILES// /}" ]]; then # shellcheck disable=SC2086 - autoflake --exclude node_modules,.venv -i -r --remove-all-unused-imports $CHANGED_PY_FILES + ruff check --force-exclude --fix $CHANGED_PY_FILES || true # shellcheck disable=SC2086 - isort $CHANGED_PY_FILES - - # shellcheck disable=SC2086 - black $CHANGED_PY_FILES + ruff format --force-exclude $CHANGED_PY_FILES fi if [[ -n "${CHANGED_TS_JS_FILES// /}" ]]; then diff --git a/website/scripts/test.sh b/website/scripts/test.sh index 3937ef03a..515e365b1 100755 --- a/website/scripts/test.sh +++ b/website/scripts/test.sh @@ -94,9 +94,8 @@ cd "$PROJECT_ROOT" if [[ $SERVER -eq 1 ]]; then capture_stdout_and_stderr_if_successful pytest - capture_stdout_and_stderr_if_successful flake8 . - capture_stdout_and_stderr_if_successful isort -c . - capture_stdout_and_stderr_if_successful black . --check + capture_stdout_and_stderr_if_successful ruff check --force-exclude . + capture_stdout_and_stderr_if_successful ruff format --force-exclude --check . capture_stdout_and_stderr_if_successful mypy --no-incremental . capture_stdout_and_stderr_if_successful python manage.py makemigrations --dry-run --check fi