ref(issues): Convert GroupType class config to ClassVar#113423
Merged
Conversation
`GroupType` is never instantiated in production: subclasses declare their configuration as class-level attributes (`type_id = 1001`, `slug = "foo"`, etc.) and the registry stores the classes themselves. Despite that, the base declared fields as `@dataclass(frozen=True)` instance attributes, so every access like `group_type.type_id` (where `group_type: type[GroupType]`) is really class-level access. Convert the fields to `ClassVar[T]`, preserving the same defaults. Also convert the `ReplayGroupTypeDefaults` mixin's `notification_config` for the same reason. Behavior change: `GroupType.__post_init__` was previously dead code (nothing instantiates `GroupType`), so category validation never actually ran. Move the same validation to `__init_subclass__` so it fires at subclass-definition time. Update the two tests that were constructing `GroupType(...)` (not a real production usage pattern) to define subclasses and the category-validation test to expect the failure at class-definition time. Prep work for the mypy 1.20 upgrade (#113419) — safe under 1.19.1. Agent transcript: https://claudescope.sentry.dev/share/8kuOPo_F7g0JelnKiHguToNZr0_y4AKNCOGNvydSZac
Isolate the TestBaseGroupTypeDetectorValidator registry. Defining `TestGroupType(GroupType)` subclasses inside the tests now triggers `__init_subclass__`, which registers on the global `grouptype.registry`. Two tests both use `type_id=1`, so the second collides with the first. Swap the module's registry for a fresh `GroupTypeRegistry()` in setUp, matching the pattern in `BaseGroupTypeTest` (tests/sentry/issues/ test_grouptype.py). Agent transcript: https://claudescope.sentry.dev/share/siSaRYJSAdoKlkFW34-zk5UlezkQnorq4y6bii-WA4o
Contributor
Author
|
@sentry review |
Contributor
Author
|
bugbot review |
Contributor
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 10acb03. Configure here.
evanpurkhiser
approved these changes
Apr 24, 2026
JoshFerge
added a commit
that referenced
this pull request
Apr 24, 2026
Bumps the mypy pin to 1.20.1. Prep PRs for the bulk class-level-attribute fixes have already merged; what remains here is the pin bump, the `GroupType` ClassVar + `__init_subclass__` migration, a handful of small typing cleanups, and targeted `# type: ignore` comments for cases that will get proper fixes in the follow-up PRs below. ### Prep PRs (already merged) - #113422 — `ref(typing)`: `RegressionDetector` + `AttributeHandler` → `ClassVar` - #113423 — `ref(issues)`: `GroupType` → `ClassVar` + move validation to `__init_subclass__` - #113424 — `fix(typing)`: `group_status` args are `int`, not `GroupStatus` - #113427 — `test(typing)`: refetch via queryset instead of `refresh_from_db` after narrowing - #113428 — `test(typing)`: misc test-side type hint fixes ### What's in this PR - Bump `mypy>=1.19.1` → `mypy>=1.20.1` (+ `uv.lock`) - Drop the now-stale `kombu.*` entry from `[[tool.mypy.overrides]]` - `src/sentry/issues/grouptype.py` — convert `GroupType` fields to `ClassVar` and move the category-validation check from the dead `__post_init__` into `__init_subclass__`. Plus the corresponding test updates in `tests/sentry/issues/test_grouptype.py` and `tests/sentry/workflow_engine/endpoints/test_validators.py`. - Small typing fixes that don't change runtime behavior: - `digests/backends/base.py` — widen options map to `Mapping[str, Any]` - `discover/compare_timeseries.py` — heterogeneous mismatches dict typed as `dict[int, dict[str, Any]]` - `explore/endpoints/explore_saved_queries.py` — drop unused `# type: ignore` - `tests/sentry/incidents/test_logic.py` — stringify both sides of the `target_identifier` comparison so mypy stops flagging it as unreachable - Targeted `# type: ignore` on six files where the proper fix is being landed as a follow-up PR (see below) - `.github/workflows/scripts/bootstrap-snuba.py` — `# type: ignore[arg-type]` on `int(workers_str)` (single-file mypy can't see through `sys.exit`; follow-up can replace with `NoReturn`) ### Follow-up PRs (drafts, based on this branch) Each one removes one of the `# type: ignore` comments and applies the proper fix. They'll merge after this lands. - #113927 — `fix(typing)`: narrow `organization_id` in `from_auth` - #113928 — `fix(typing)`: guard None options in `ReconnectingMemcache` - #113929 — `docs(typing)`: annotate the `save_team_assignments` ignore with a FIXME - #113930 — `ref(typing)`: drop redundant cast in trace metric search type - #113932 — `ref(typing)`: drop redundant cast on `TraceMetric.metric_type` - #113933 — `ref(typing)`: drop redundant casts in `resolve_measurement_value` ### Performance | version | cold | warm | |---|---|---| | 1.19.1 | 49.22s | 17.82s | | 1.20.1 | 39.04s | 5.73s | ### CI expectation - `mypy` job: clean (0 errors) - `uv` jobs: resolve against `pypi.devinfra.sentry.io` (1.20.1 has been published there)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GroupTypeis never instantiated in production — subclasses declare their configuration as class-level attributes (type_id = 1001,slug = "foo", etc.) and the registry stores the classes themselves. Despite that, the base declared fields as@dataclass(frozen=True)instance attributes, so every access likegroup_type.type_id(wheregroup_type: type[GroupType]) was really class-level access against an instance-level annotation.Convert the fields to
ClassVar[T], preserving the same defaults. Also convertReplayGroupTypeDefaults.notification_configfor the same reason.Small behavior change
GroupType.__post_init__was previously dead code — nothing instantiatesGroupType, so the category-value validation never actually ran. Move the same validation to__init_subclass__so it fires at subclass-definition time (earlier, and actually reachable). Updates:test_grouptype.py::test_category_validation— now expects theValueErrorat class-definition time.test_validators.py— two tests were instantiatingGroupType(...)to build a mock; since the base no longer takes fields as constructor args, convert them to subclasses ofGroupTypeinstead (which matches real production usage).Why this PR exists
Prep for the mypy upgrade to 1.20.1 in #113419. 1.20.1 adds a new
[misc]check, "Cannot access instance-only attribute on class object," andGroupTypeis the single biggest cluster of such errors in the repo. Splitting it out so the targeted reviewers only have to look at their own areas.Safe under the current mypy (1.19.1).
Agent transcript: https://claudescope.sentry.dev/share/JFAQdL9BKvw8zOc8RROKUSNl-Xpuu8y4p4URoIp7DDk