You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-`frontend/tests/CorpusDescriptionEditor.ct.tsx` (+7 tests): save failure (`ok: false`), save network-error path, reapply of snapshot-less version, twice-click collapse, Cancel Version Edit reset, fetch-md URL failure, and version-count pluralization.
27
+
-**Return-type annotations across core models and import/export pipeline** (Issue #1334, follow-up to #1331): The mypy gate wired in by #1331 recorded a 7208-error baseline frozen across 357 files. This PR pays down the annotation deficit on the core domain models and the bulk import/export tasks without touching runtime behavior or adding validators. Coverage jumped from the pre-issue numbers to:
-`tasks/import_tasks.py`, `tasks/import_tasks_v2.py`, `tasks/export_tasks.py`, `tasks/export_tasks_v2.py` all at 100% function-signature coverage.
33
+
-**Files touched** (annotations only — zero behavior changes, zero new comments, zero renames): `annotations/{models,signals,admin}.py`, `corpuses/{models,signals,managers}.py`, `documents/{models,signals}.py`, `extracts/{models,signals}.py`, `tasks/{import_tasks,import_tasks_v2,export_tasks,export_tasks_v2}.py`.
34
+
- Each file adopts `from __future__ import annotations` so forward references work without string quoting, and uses a `TYPE_CHECKING` block for the handful of cross-app imports (`AbstractBaseUser`, `Corpus`, `Document`, `Annotation`, etc.) that would otherwise be circular at runtime.
35
+
-**TypedDict adoption** — the import/export task signatures now consume existing TypedDicts from `opencontractserver/types/dicts.py` directly instead of bare `dict`: `OpenContractsExportDataJsonPythonType` / `OpenContractsExportDataJsonV2Type` for the data.json payloads, `OpenContractDocExport` for per-document payloads, `OpenContractsAnnotationPythonType` / `OpenContractsRelationshipPythonType` for annotation/relationship lists, `IngestionSourceExport` / `DocumentPathExport` / `StructuralAnnotationSetExport` / `CorpusFolderExport` / `AgentConfigExport` / `DescriptionRevisionExport` / `ConversationExport` / `ChatMessageExport` / `MessageVoteExport` for V2-specific shapes, and `OpenContractsAnnotatedDocumentImportType` for single-document imports. No new TypedDicts were introduced — all were already defined and previously unused in callers.
36
+
- Signal handlers (`annotations/signals.py`, `corpuses/signals.py`, `documents/signals.py`, `extracts/signals.py`) now have typed `sender` / `instance` / `created` / `**kwargs` parameters with `TYPE_CHECKING` imports of the sender model classes.
37
+
-**Manager methods**: `CorpusActionExecutionManager.visible_to_user` now types the optional `user` param as `Optional[AbstractBaseUser]` (previously bare default), and `summary_by_status` / `summary_by_action` tightened from bare `dict` / `QuerySet` to `dict[str, int]` / `QuerySet[Any]`.
38
+
-**Model method signatures**: `save()`/`clean()`/`delete()` overrides are now `-> None` (Django's `Model.save` returns None) or `-> tuple[int, dict[str, int]]` (Django's `Model.delete` signature); `__str__` returns `str`; `@property` accessors have explicit scalar returns; classmethod factories return `"ClassName"`; tree/versioning helpers on `CorpusFolder` type their descendant queries as `QuerySet[CorpusFolder]` / `list[CorpusFolder]` where applicable (falling back to `Any` where the CTE queryset class isn't easily importable).
39
+
-**Graduation from the mypy baseline is deferred.** The `mypy.ini``[mypy-…] ignore_errors = True` sections for these modules still contain pre-existing Django-plugin-specific errors (field descriptor `assignment` mismatches, `misc` lambdas in model field declarations, base-class variable override warnings on `embedder_path`/`creator`) that are not caused by missing annotations and therefore cannot be fixed under this issue's "annotations only, no bugfixes" constraint. The annotations landed here make those modules ready for a follow-up PR that either `# type: ignore`s or refactors the remaining Django-model type issues and then removes the baseline entries.
27
40
-**Mypy type-checking wired into pre-commit and CI** (Issue #1331): The existing `[mypy]` block in `setup.cfg` and the `mypy==1.20.1` / `django-stubs==6.0.2` / `djangorestframework-stubs==3.16.9` pins in `requirements/local.txt` were never actually enforced, so the investment was drifting (48 pre-existing `# type: ignore` markers, many modules at 0% annotation coverage). This PR turns on the gate without requiring the 7208 existing errors across 357 files to be fixed first — `mypy.ini` lists each of those files under its own `[mypy-<module>] ignore_errors = True` section, so new modules added outside the baseline **are** type-checked and CI / the hook fails on their errors.
28
41
-`mypy.ini` (split out of `setup.cfg` because the per-module baseline is ~1000 lines): `python_version` bumped `3.9` → `3.11` to match the runtime, plugins kept, `django_settings_module` pointed at the new `config/settings/mypy.py` (dummy `DATABASE_URL` default so contributors don't need env vars).
29
42
-`.pre-commit-config.yaml`: new `mirrors-mypy@v1.20.1` hook with `pass_filenames: false` and fully pinned stubs + Django runtime in `additional_dependencies` (pre-commit autoupdate only bumps `rev`, so unpinned stubs would drift).
30
43
-`.github/workflows/backend.yml`: `Run mypy` step added to the `linter` job; the preceding `pip install -r requirements/local.txt` step satisfies the django-stubs plugin.
31
44
-`docs/typing/README.md`: how to run locally / via pre-commit / in the test container, plus the per-file graduation workflow.
32
45
-`docs/typing/mypy_baseline.txt`: frozen error list (sorted for stable diffs) so follow-up issues can measure progress.
46
+
-**Mypy: typed the auth, users, and notifications packages** (Issue #1333, follow-up to #1331): Brought `config/admin_auth`, `config/graphql_api_token_auth`, `opencontractserver/users`, and `opencontractserver/notifications` to full return-annotation coverage and graduated them out of the mypy baseline. These packages sit on hot paths for authentication, token verification, session handling, and notification dispatch; typing them documents invariants that used to live only in `CLAUDE.md`.
47
+
- Signal handlers (`opencontractserver/users/signals.py`, `opencontractserver/notifications/signals.py`) now declare `sender: type[Model]`, `instance: Model`, `created: bool`, `**kwargs: Any`, and `-> None` so drive-by edits can't silently change the signal contract.
48
+
-`opencontractserver/notifications/__init__.py` gained a module-level docstring calling out that the app deliberately diverges from `AnnotatePermissionsForReadMixin` — a single `recipient` FK is the authoritative visibility gate. The design note in `CLAUDE.md` is referenced so changes to one file flag the other.
49
+
-`mypy.ini`: dropped `[mypy-config.admin_auth.*]`, `[mypy-config.graphql_api_token_auth.backends]`, and `[mypy-opencontractserver.users.models]``ignore_errors` sections. Added a narrow `disable_error_code = django-manager-missing` on `opencontractserver.users.models` only, documented inline: `django-tree-queries`' `as_manager(with_tree_fields=True)` pattern used by `Corpus`, `CorpusFolder`, and `DocumentPath` creates manager classes at runtime that the `mypy_django_plugin` can't introspect, so reverse `_set` accessors on `User` can't resolve — a known plugin limitation, not a code error. Graduating those three models with explicit manager typing will let us delete the disable.
50
+
-`docs/typing/mypy_baseline.txt`: pruned all 20 entries for the graduated modules.
51
+
- Bug fixes uncovered by typing:
52
+
-`config/graphql_api_token_auth/backends.py`: `ApiKeyBackend.authenticate_header` referenced `self.keyword` but the attribute was never declared. DRF calls this on 401 responses to build the `WWW-Authenticate` header, so an unauthenticated request would have hit `AttributeError` at response time. Added `keyword: str = "Token"` as a class attribute.
53
+
-`opencontractserver/notifications/signals.py`: `create_moderation_notification` dereferenced `action.moderator.username` unconditionally, but the `ModerationAction.moderator` FK permits `NULL`. A moderation action created without a moderator would crash the signal handler (and therefore the originating save). Added an explicit early return with a debug log when `action.moderator is None`.
0 commit comments