Loosen ListFilter.choices() return type to allow custom filter templates#3180
Open
alessio-b2c2 wants to merge 3 commits intotypeddjango:masterfrom
Open
Loosen ListFilter.choices() return type to allow custom filter templates#3180alessio-b2c2 wants to merge 3 commits intotypeddjango:masterfrom
alessio-b2c2 wants to merge 3 commits intotypeddjango:masterfrom
Conversation
ListFilter.choices() is the base class method that imposes no structure on the returned dicts — subclasses with custom templates can yield dicts with entirely different keys. Change its return type from Iterator[_ListFilterChoices] to Iterator[dict[str, object]]. Keep the specific _ListFilterChoices return type on SimpleListFilter and FieldListFilter where the shape is actually enforced by Django. This fixes spurious mypy [override] errors for ListFilter subclasses that use custom templates with different dict shapes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Neither mypy nor pyright consider TypedDict a structural subtype of dict (even dict[str, Any]), so using dict[str, ...] as the base class return type prevents subclasses from narrowing it to a TypedDict without triggering [override] errors. TypedDict IS recognized as a subtype of Mapping by both type checkers, so Iterator[Mapping[str, object]] is the correct choice: it allows the base class to accept any dict-like return while letting subclasses like SimpleListFilter and FieldListFilter narrow to Iterator[_ListFilterChoices]. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ilter Fixes mypy explicit-override errors in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
ListFilter.choices()return type fromIterator[_ListFilterChoices]toIterator[Mapping[str, object]]since the base class imposes no structure on the returned dictschoices()declarations onSimpleListFilterandFieldListFilterwith the specificIterator[_ListFilterChoices]return type, where Django actually enforces the dict shape[override]errors forListFiltersubclasses that use custom templates with different dict keys (e.g. multi-select filters yielding{"selected": bool, "value": str, "display": str}withoutquery_string)Why
Mapping[str, object]instead ofdict[str, Any]?Neither mypy nor pyright consider
TypedDicta structural subtype ofdict(evendict[str, Any]), so usingdict[str, ...]as the base return type prevents subclasses from narrowing to aTypedDictwithout triggering[override]errors.TypedDictIS recognized as a subtype ofMappingby both type checkers, soIterator[Mapping[str, object]]allows the base class to accept any dict-like return while letting subclasses narrow toIterator[_ListFilterChoices].Motivation
ListFilteris the base class and itschoices()method raisesNotImplementedError— it's meant to be overridden. The consuming code in Django (admin_list_filtertemplate tag) just passeslist(spec.choices(cl))to the template context without enforcing any specific shape. Subclasses with custom templates can return dicts with entirely different keys, but the current stub forces the_ListFilterChoicesTypedDict shape on all subclasses, causing spurious[override]errors.🤖 Generated with Claude Code