Skip to content

Conversation

@dcreager
Copy link
Member

You can now use subscript expressions in a type expression to explicitly specialize generic classes, just like you could already do in value expressions.

This still does not implement bidirectional checking, so a type annotation on an assignment does not influence how we infer a specialization for a (not explicitly specialized) constructor call. You might get an invalid-assignment error if (a) we cannot infer a class specialization from the constructor call (in which case you end up e.g. trying to assign C[Unknown] to C[int]) or if (b) we can infer a specialization, but it doesn't match the annotation.

Closes #17432

@dcreager dcreager added the ty Multi-file analysis & type inference label Apr 16, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Apr 16, 2025

mypy_primer results

Changes were detected when running on open source projects
bidict (https://github.com/jab/bidict)
- error[lint:type-assertion-failure] /tmp/mypy_primer/projects/bidict/tests/test_bidict.py:541:5: Actual type `frozenbidict` is not the same as asserted type `@Todo(generics)`
+ error[lint:type-assertion-failure] /tmp/mypy_primer/projects/bidict/tests/test_bidict.py:541:5: Actual type `frozenbidict` is not the same as asserted type `@Todo(specialized non-generic class)`

porcupine (https://github.com/Akuli/porcupine)
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/tests/test_docs.py:19:64: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/tests/test_docs.py:29:12: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/autocomplete.py:529:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `bound method AutoCompleter.on_tab(event: @Todo(specialized non-generic class), shifted: bool) -> str | None`
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/tests/test_docs.py:19:64: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/tests/test_docs.py:29:12: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/aboutdialog.py:154:46: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> (() -> object) | None`, found `def get_link_opener(match: @Todo(specialized non-generic class)) -> () -> object`
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/langserver.py:231:16: Attribute `value` on type `@Todo(specialized non-generic class) | Unknown | str` is possibly unbound
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/tabs2spaces.py:31:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `def on_tab_key(event: @Todo(generics), shift_pressed: bool) -> str`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/tabs2spaces.py:31:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `def on_tab_key(event: @Todo(specialized non-generic class), shift_pressed: bool) -> str`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/autocomplete.py:529:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `bound method AutoCompleter.on_tab(event: @Todo(generics), shifted: bool) -> str | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/indent_block.py:39:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `def on_tab_key(event: @Todo(generics), shifted: bool) -> None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/aboutdialog.py:154:46: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> (() -> object) | None`, found `def get_link_opener(match: @Todo(generics)) -> () -> object`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/pluginloader.py:298:55: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> None`, found `def _handle_circular_dependency(cycle: @Todo(generics)) -> None`
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/langserver.py:231:16: Attribute `value` on type `@Todo(generics) | Unknown | str` is possibly unbound
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/plugins/indent_block.py:39:40: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Any`, found `def on_tab_key(event: @Todo(specialized non-generic class), shifted: bool) -> None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/porcupine/porcupine/pluginloader.py:298:55: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> None`, found `def _handle_circular_dependency(cycle: @Todo(specialized non-generic class)) -> None`

pyinstrument (https://github.com/joerick/pyinstrument)
- error[lint:unsupported-operator] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:52:31: Operator `+` is unsupported between objects of type `@Todo(generics) | None` and `@Todo(generics) | None`
+ error[lint:unsupported-operator] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:52:31: Operator `+` is unsupported between objects of type `@Todo(specialized non-generic class) | None` and `@Todo(specialized non-generic class) | None`
- warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:53:9: Method `__getitem__` of type `@Todo(generics) | None` is possibly unbound
+ warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:53:9: Method `__getitem__` of type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:54:9: Method `__getitem__` of type `@Todo(generics) | None` is possibly unbound
+ warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:54:9: Method `__getitem__` of type `@Todo(specialized non-generic class) | None` is possibly unbound
- error[lint:not-iterable] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:487:32: Object of type `@Todo(generics) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
+ error[lint:not-iterable] /tmp/mypy_primer/projects/pyinstrument/pyinstrument/__main__.py:487:32: Object of type `@Todo(specialized non-generic class) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method

async-utils (https://github.com/mikeshardmind/async-utils)
- error[lint:invalid-assignment] /tmp/mypy_primer/projects/async-utils/src/async_utils/_paramkey.py:65:1: Object of type `def _make_key(args: @Todo(full tuple[...] support), kwds: @Todo(generics), *, /, _typ: (object, /) -> type = Literal[type], _fast_types: @Todo(generics) = set) -> Hashable` is not assignable to `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Hashable`
+ error[lint:invalid-assignment] /tmp/mypy_primer/projects/async-utils/src/async_utils/_paramkey.py:65:1: Object of type `def _make_key(args: @Todo(full tuple[...] support), kwds: @Todo(specialized non-generic class), *, /, _typ: (object, /) -> type = Literal[type], _fast_types: @Todo(specialized non-generic class) = set) -> Hashable` is not assignable to `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Hashable`

pyp (https://github.com/hauntsaninja/pyp)
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/pyp/tests/test_find_names.py:33:25: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/pyp/tests/test_find_names.py:33:25: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound

python-chess (https://github.com/niklasf/python-chess)
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/polyglot.py:256:44: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)]` is not callable on object of type `list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/polyglot.py:258:42: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)]` is not callable on object of type `list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:682:35: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:682:35: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:683:35: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:683:35: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:686:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:686:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:687:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:687:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:829:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:829:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:830:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:830:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:833:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:833:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:834:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/variant.py:834:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/polyglot.py:256:44: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)]` is not callable on object of type `list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/polyglot.py:258:42: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)]` is not callable on object of type `list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1551:46: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1553:46: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
- error[lint:invalid-return-type] /tmp/mypy_primer/projects/python-chess/chess/pgn.py:137:12: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> str`, found `def repl(match: @Todo(generics)) -> str`
+ error[lint:invalid-return-type] /tmp/mypy_primer/projects/python-chess/chess/pgn.py:137:12: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> str`, found `def repl(match: @Todo(specialized non-generic class)) -> str`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/syzygy.py:446:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1551:46: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/syzygy.py:447:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1553:46: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1899:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/gaviota.py:1910:52: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:880:20: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)]` is not callable on object of type `list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:880:20: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)]` is not callable on object of type `list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1437:44: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1437:44: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1557:27: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1557:27: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1558:27: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1558:27: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1577:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1577:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1578:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/__init__.py:1578:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/syzygy.py:446:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/chess/syzygy.py:447:9: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:919:25: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:919:25: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/test.py:4008:44: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(generics)`, found `def raise_expected_error(future) -> Unknown`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/test.py:4008:44: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(specialized non-generic class)`, found `def raise_expected_error(future) -> Unknown`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/test.py:4015:49: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(generics)`, found `def resolve(future) -> Unknown`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/test.py:4015:49: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(specialized non-generic class)`, found `def resolve(future) -> Unknown`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:4755:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:4755:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:4756:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(generics)])` is not callable on object of type `Unknown | list`
+ error[lint:call-non-callable] /tmp/mypy_primer/projects/python-chess/test.py:4756:26: Method `__getitem__` of type `Unknown | (Overload[(i: SupportsIndex, /) -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions), (s: slice, /) -> @Todo(specialized non-generic class)])` is not callable on object of type `Unknown | list`
- error[lint:not-iterable] /tmp/mypy_primer/projects/python-chess/chess/engine.py:2349:33: Object of type `@Todo(generics) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
+ error[lint:not-iterable] /tmp/mypy_primer/projects/python-chess/chess/engine.py:2349:33: Object of type `@Todo(specialized non-generic class) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
- error[lint:unsupported-operator] /tmp/mypy_primer/projects/python-chess/chess/engine.py:2497:40: Operator `in` is not supported for types `@Todo(generics)` and `None`, in comparing `@Todo(generics)` with `str | None`
+ error[lint:unsupported-operator] /tmp/mypy_primer/projects/python-chess/chess/engine.py:2497:40: Operator `in` is not supported for types `@Todo(specialized non-generic class)` and `None`, in comparing `@Todo(specialized non-generic class)` with `str | None`
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/chess/engine.py:3014:34: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(generics)`, found `def background(future: @Todo(generics)) -> @Todo(generic types.CoroutineType)`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/python-chess/chess/engine.py:3014:34: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(specialized non-generic class)`, found `def background(future: @Todo(specialized non-generic class)) -> @Todo(generic types.CoroutineType)`

psycopg (https://github.com/psycopg/psycopg)
+ error[lint:invalid-return-type] /tmp/mypy_primer/projects/psycopg/tests/typing_example.py:32:16: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Person`, found `def mkrow(values: @Todo(specialized non-generic class)) -> Person`
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:91:17: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:92:14: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:93:47: Attribute `groups` on type `@Todo(generics) | None` is possibly unbound
- error[lint:invalid-return-type] /tmp/mypy_primer/projects/psycopg/tests/typing_example.py:32:16: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> Person`, found `def mkrow(values: @Todo(generics)) -> Person`
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:91:17: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:92:14: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/psycopg/tests/utils.py:93:47: Attribute `groups` on type `@Todo(specialized non-generic class) | None` is possibly unbound

black (https://github.com/psf/black)
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/conv.py:77:34: Attribute `groups` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- error[lint:invalid-argument-type] /tmp/mypy_primer/projects/black/src/black/__init__.py:511:1: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(Support for `typing.TypeVar` instances in type expressions)`, found `def main(ctx: Context, code: str | None, line_length: int, target_version: @Todo(generics), check: bool, diff: bool, line_ranges: @Todo(generics), color: bool, fast: bool, pyi: bool, ipynb: bool, python_cell_magics: @Todo(generics), skip_source_first_line: bool, skip_string_normalization: bool, skip_magic_trailing_comma: bool, preview: bool, unstable: bool, enable_unstable_feature: @Todo(generics), quiet: bool, verbose: bool, required_version: str | None, include: @Todo(generics), exclude: @Todo(generics) | None, extend_exclude: @Todo(generics) | None, force_exclude: @Todo(generics) | None, stdin_filename: str | None, workers: int | None, src: @Todo(full tuple[...] support), config: str | None) -> None`
+ error[lint:invalid-argument-type] /tmp/mypy_primer/projects/black/src/black/__init__.py:511:1: Argument to this function is incorrect: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> @Todo(Support for `typing.TypeVar` instances in type expressions)`, found `def main(ctx: Context, code: str | None, line_length: int, target_version: @Todo(specialized non-generic class), check: bool, diff: bool, line_ranges: @Todo(specialized non-generic class), color: bool, fast: bool, pyi: bool, ipynb: bool, python_cell_magics: @Todo(specialized non-generic class), skip_source_first_line: bool, skip_string_normalization: bool, skip_magic_trailing_comma: bool, preview: bool, unstable: bool, enable_unstable_feature: @Todo(specialized non-generic class), quiet: bool, verbose: bool, required_version: str | None, include: @Todo(specialized non-generic class), exclude: @Todo(specialized non-generic class) | None, extend_exclude: @Todo(specialized non-generic class) | None, force_exclude: @Todo(specialized non-generic class) | None, stdin_filename: str | None, workers: int | None, src: @Todo(full tuple[...] support), config: str | None) -> None`
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/black/src/black/__init__.py:602:37: Attribute `items` on type `@Todo(specialized non-generic class) | None` is possibly unbound

rich (https://github.com/Textualize/rich)
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/rich/tests/test_console.py:1020:5: Attribute `close` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/rich/tests/test_console.py:1020:5: Attribute `close` on type `@Todo(specialized non-generic class) | None` is possibly unbound

werkzeug (https://github.com/pallets/werkzeug)
- error[lint:invalid-assignment] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/formparser.py:181:13: Object of type `def default_stream_factory(total_content_length: int | None, content_type: str | None, filename: str | None, content_length: int | None = None) -> @Todo(generics)` is not assignable to `TStreamFactory | None`
- error[lint:invalid-assignment] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/formparser.py:305:13: Object of type `def default_stream_factory(total_content_length: int | None, content_type: str | None, filename: str | None, content_length: int | None = None) -> @Todo(generics)` is not assignable to `TStreamFactory | None`
+ error[lint:invalid-assignment] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/formparser.py:181:13: Object of type `def default_stream_factory(total_content_length: int | None, content_type: str | None, filename: str | None, content_length: int | None = None) -> @Todo(specialized non-generic class)` is not assignable to `TStreamFactory | None`
+ error[lint:invalid-assignment] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/formparser.py:305:13: Object of type `def default_stream_factory(total_content_length: int | None, content_type: str | None, filename: str | None, content_length: int | None = None) -> @Todo(specialized non-generic class)` is not assignable to `TStreamFactory | None`
+ error[lint:unsupported-operator] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/routing/exceptions.py:102:50: Operator `in` is not supported for types `Unknown` and `None`, in comparing `Unknown` with `Unknown | @Todo(specialized non-generic class) & None | None | @Todo(set comprehension type)`
- error[lint:unsupported-operator] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/routing/exceptions.py:102:50: Operator `in` is not supported for types `Unknown` and `None`, in comparing `Unknown` with `Unknown | @Todo(generics) & None | None | @Todo(set comprehension type)`
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/test.py:1049:17: Attribute `close` on type `@Todo(specialized non-generic class) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/local.py:506:23: Attribute `top` on type `@Todo(generics) | Local | (() -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions))` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/local.py:506:23: Attribute `top` on type `@Todo(specialized non-generic class) | Local | (() -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions))` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/local.py:517:27: Attribute `get` on type `@Todo(generics) | Local | (() -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions))` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/local.py:517:27: Attribute `get` on type `@Todo(specialized non-generic class) | Local | (() -> Unknown | @Todo(Support for `typing.TypeVar` instances in type expressions))` is possibly unbound
- error[lint:invalid-return-type] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/debug/repr.py:117:12: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> str`, found `def proxy(self: DebugReprGenerator, obj: @Todo(generics), recursive: bool) -> str`
+ error[lint:invalid-return-type] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/debug/repr.py:117:12: Return type does not match returned value: Expected `(*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> str`, found `def proxy(self: DebugReprGenerator, obj: @Todo(specialized non-generic class), recursive: bool) -> str`
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_test.py:166:12: Attribute `getvalue` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_test.py:168:12: Attribute `getvalue` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:601:5: Attribute `add` on type `Unknown | @Todo(specialized non-generic class) & None | None | @Todo(set comprehension type)` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:603:5: Attribute `discard` on type `Unknown | @Todo(specialized non-generic class) & None | None | @Todo(set comprehension type)` is possibly unbound
+ warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:604:5: Method `__getitem__` of type `Unknown | @Todo(specialized non-generic class) | None` is possibly unbound
- error[lint:not-iterable] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/serving.py:273:35: Object of type `@Todo(generics) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
+ error[lint:not-iterable] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/serving.py:273:35: Object of type `@Todo(specialized non-generic class) | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/src/werkzeug/test.py:1049:17: Attribute `close` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:601:5: Attribute `add` on type `Unknown | @Todo(generics) & None | None | @Todo(set comprehension type)` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:603:5: Attribute `discard` on type `Unknown | @Todo(generics) & None | None | @Todo(set comprehension type)` is possibly unbound
- warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/werkzeug/tests/test_routing.py:604:5: Method `__getitem__` of type `Unknown | @Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_test.py:166:12: Attribute `getvalue` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/werkzeug/tests/test_test.py:168:12: Attribute `getvalue` on type `@Todo(generics) | None` is possibly unbound
- error[lint:unsupported-operator] /tmp/mypy_primer/projects/werkzeug/tests/test_local.py:208:12: Operator `in` is not supported for types `str` and `None`, in comparing `Literal["int("]` with `@Todo(generics) & Unknown & str | @Todo(generics) & Any & str | @Todo(generics) & str | @Todo(generics) & Unknown & None | @Todo(generics) & Any & None | @Todo(generics) & None`
+ error[lint:unsupported-operator] /tmp/mypy_primer/projects/werkzeug/tests/test_local.py:208:12: Operator `in` is not supported for types `str` and `None`, in comparing `Literal["int("]` with `@Todo(specialized non-generic class) & Unknown & str | @Todo(specialized non-generic class) & Any & str | @Todo(specialized non-generic class) & str | @Todo(specialized non-generic class) & Unknown & None | @Todo(specialized non-generic class) & Any & None | @Todo(specialized non-generic class) & None`

scrapy (https://github.com/scrapy/scrapy)
+ error[lint:unresolved-attribute] /tmp/mypy_primer/projects/scrapy/tests/test_command_check.py:198:9: Type `bound method CrawlerProcess.crawl(crawler_or_spidercls: type[Spider] | str | Crawler, *args: Any, **kwargs: Any) -> @Todo(unknown type subscript)` has no attribute `assert_not_called`
- warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/scrapy/tests/test_dependencies.py:38:41: Method `__getitem__` of type `@Todo(generics) | None` is possibly unbound
- error[lint:not-iterable] /tmp/mypy_primer/projects/scrapy/scrapy/http/response/text.py:277:24: Object of type `@Todo(generics) | Unknown | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
- error[lint:unsupported-operator] /tmp/mypy_primer/projects/scrapy/scrapy/spidermiddlewares/httperror.py:62:12: Operator `in` is not supported for types `int` and `None`, in comparing `int` with `@Todo(generics) | Any | None`
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/tests/test_proxy_connect.py:51:21: Attribute `group` on type `@Todo(generics) | None` is possibly unbound
- error[lint:unresolved-attribute] /tmp/mypy_primer/projects/scrapy/tests/test_command_check.py:198:9: Type `bound method CrawlerProcess.crawl(crawler_or_spidercls: type[Spider] | str | Crawler, *args: Any, **kwargs: Any) -> @Todo(generics)` has no attribute `assert_not_called`
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/scrapy/extensions/feedexport.py:542:17: Attribute `close` on type `@Todo(generics) | None` is possibly unbound
- warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/scrapy/extensions/feedexport.py:543:24: Attribute `file` on type `@Todo(generics) | None` is possibly unbound
+ warning[lint:call-possibly-unbound-method] /tmp/mypy_primer/projects/scrapy/tests/test_dependencies.py:38:41: Method `__getitem__` of type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/scrapy/extensions/feedexport.py:542:17: Attribute `close` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/scrapy/extensions/feedexport.py:543:24: Attribute `file` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ warning[lint:possibly-unbound-attribute] /tmp/mypy_primer/projects/scrapy/tests/test_proxy_connect.py:51:21: Attribute `group` on type `@Todo(specialized non-generic class) | None` is possibly unbound
+ error[lint:not-iterable] /tmp/mypy_primer/projects/scrapy/scrapy/http/response/text.py:277:24: Object of type `@Todo(specialized non-generic class) | Unknown | None` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
+ error[lint:unsupported-operator] /tmp/mypy_primer/projects/scrapy/scrapy/spidermiddlewares/httperror.py:62:12: Operator `in` is not supported for types `int` and `None`, in comparing `int` with `@Todo(specialized non-generic class) | Any | None`

@dcreager
Copy link
Member Author

6 new mypy_primer diagnostics:

  • 5 depend on bidirectional type-checking and/or supporting legacy TypeVars in typeshed
  • 1 looks like we're not narrowing int | None to int in a while statement?

@carljm
Copy link
Contributor

carljm commented Apr 16, 2025

  • 1 looks like we're not narrowing int | None to int in a while statement?

Yeah I don't think we support narrowing the type of x based on the use of (x := ...) in a predicate yet, but we should: #17437

@carljm
Copy link
Contributor

carljm commented Apr 16, 2025

  • 5 depend on bidirectional type-checking and/or supporting legacy TypeVars in typeshed

This may be involved in us inferring an unknown specialization when we shouldn't, but it seems to me that those false positives are also demonstrating a simpler issue, which is that we should in general consider C[Unknown] assignable to C[int].

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love how simple this was to implement!

Looks like the main follow-up this reveals is not in the code touched in this PR, but more like a knock-on effect of supporting this: we need a more sophisticated understanding of subtyping (variance) and assignability (dynamic types) for specialized generics. Both are important but the latter is probably higher priority and simpler to implement.

pass

static_assert(is_subtype_of(B, A[int]))
static_assert(not is_subtype_of(B, A[bool]))
Copy link
Contributor

@carljm carljm Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really a test of variance, which is opening another whole can of worms we'll have to handle soon. This subtype relation should be true if A is contravariant in T (because that means that A[bool] is a supertype of A[int]), false if it is invariant or covariant.

Variance for PEP 695 typevars is determined by inference, based on the positions in which the typevar occurs in the definition of the class. If the typevar occurs in covariant positions only (that is, method return types, including the implicit getter "method" of every normal mutable attribute) it is covariant, if it occurs in contravariant positions only (accepted as a method argument type, including the implicit setter "method" of every normal mutable attribute) then it is contravariant, and if it appears in both, it is invariant. (Note that this means a type variable used as the type of a normal mutable attribute must be invariant, since it appears in both covariant and contravariant position via the implicit getter and setter of that mutable attribute.) Also note that callable types can invert variance of a position: that is, a typevar occurring as argument to a Callable type accepted as a method argument is in covariant position. The method argument is a contravariant position, but the argument of the Callable type is also a contravariant position, so this reverses the variance and that typevar is actually occurring in a covariant position.

A class like A which never uses its typevar in any position at all can safely be considered "bivariant" (in plain English, the type of the typevar has no impact whatsoever on the class, so it just doesn't matter to subtyping at all!)

So all that said: technically, given the code we see here, this assertion is wrong: B should be a subtype of A[bool], because A is bivariant in T, which means that any specialization of A is a subtype of any other specialization of A -- that is, all specializations of A are equivalent.

As far as this PR is concerned, the only thing I would suggest is a TODO comment on this assertion :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a bunch of tests for each of the four variance cases, and for each of subtyping, assignability, equivalence, and gradual equivalence. All TODOs for now where they're not producing the right answer.

}
_ => {
self.infer_type_expression(slice);
todo_type!("generics")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might want an update?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I got rid of the todo type completely, and instead added new diagnostics for when (a) you try to specialize a non-generic class, and (b) when you use any other subscript expression in a type expression. Added quite a bit of churn in the mdtest suite, but I think it's worth it.

* main:
  [red-knot] Detect version-related syntax errors (#16379)
  [`pyflakes`] Add fix safety section (`F841`) (#17410)
  [red-knot] Add `KnownFunction` variants for `is_protocol`, `get_protocol_members` and `runtime_checkable` (#17450)
  Bump 0.11.6 (#17449)
  Auto generate `visit_source_order` (#17180)
  [red-knot] Initial tests for protocols (#17436)
  [red-knot] Dataclasses: synthesize `__init__` with proper signature (#17428)
  [red-knot] Dataclasses: support `order=True` (#17406)
Copy link
Member Author

@dcreager dcreager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I anticipate that there's about to a lot of new false positives on the ecosystem check, since we're now printing out diagnostics when you try to specialize a non-generic class — which will be (incorrectly) reported for basically every generic class in the typeshed, since we don't yet recognize them as generic since they use legacy typevars.

pass

static_assert(is_subtype_of(B, A[int]))
static_assert(not is_subtype_of(B, A[bool]))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a bunch of tests for each of the four variance cases, and for each of subtyping, assignability, equivalence, and gradual equivalence. All TODOs for now where they're not producing the right answer.

}
_ => {
self.infer_type_expression(slice);
todo_type!("generics")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I got rid of the todo type completely, and instead added new diagnostics for when (a) you try to specialize a non-generic class, and (b) when you use any other subscript expression in a type expression. Added quite a bit of churn in the mdtest suite, but I think it's worth it.

@github-actions
Copy link
Contributor

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@sharkdp
Copy link
Contributor

sharkdp commented Apr 17, 2025

I anticipate that there's about to a lot of new false positives on the ecosystem check

4153 new invalid-type-form false positives, to be precise 🙃

@dcreager
Copy link
Member Author

4153 new invalid-type-form false positives, to be precise

Yeahhhhh, I'm going to back that out and wait to add those diagnostics until we understand that list is generic... 😅

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a few assertions around non-fully-static generics that we need to fix up here, but otherwise looks great!

Comment on lines 7196 to 7201
Type::StringLiteral(_) => {
// Don't emit a diagnostic, since we haven't determined the type of the deferred
// string annotation yet.
self.infer_type_expression(slice);
Type::unknown()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to understand what case this is handling -- did it come up in ecosystem checks? It seems like it would be permitting an annotation like "list"[str], but AFAIK we don't need to support that, it isn't allowed. Pyright doesn't allow it. Am I misunderstand what this is for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this in our mdtests, e.g. here

class C[T]:
def __new__(cls, x: T) -> "C"[T]:
return object.__new__(cls)

Should we change that to "C[T]" per pyright's suggestion?

Copy link
Member Author

@dcreager dcreager Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to "C[T]" and verified that the noisy new diagnostics didn't fire after removing the StringLiteral match arm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like mypy does support this. But I don't feel like we should go out of our way to support it, IMO it's an odd thing to do, and the alternative (quote the entire annotation) is easy and the more obvious choice anyway. So I support what you've done here.

* main:
  [red-knot] class bases are not affected by __future__.annotations (#17456)
  [red-knot] Add support for overloaded functions (#17366)
  [`pyupgrade`] Add fix safety section to docs (`UP036`) (#17444)
  [red-knot] more type-narrowing in match statements (#17302)
  [red-knot] Add some narrowing for assignment expressions (#17448)
  [red-knot] Understand `typing.Protocol` and `typing_extensions.Protocol` as equivalent (#17446)
  Server: Use `min` instead of `max` to limit the number of threads (#17421)
* main:
  [red-knot] fix building unions with literals and AlwaysTruthy/AlwaysFalsy (#17451)
  [red-knot] Type narrowing for assertions (take 2) (#17345)
@dcreager dcreager merged commit 787bcd1 into main Apr 18, 2025
22 checks passed
@dcreager dcreager deleted the dcreager/subscript-types branch April 18, 2025 15:49
Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest additions here look good!

dcreager added a commit that referenced this pull request Apr 18, 2025
* main: (123 commits)
  [red-knot] Handle explicit class specialization in type expressions (#17434)
  [red-knot] allow assignment expression in call compare narrowing (#17461)
  [red-knot] fix building unions with literals and AlwaysTruthy/AlwaysFalsy (#17451)
  [red-knot] Type narrowing for assertions (take 2) (#17345)
  [red-knot] class bases are not affected by __future__.annotations (#17456)
  [red-knot] Add support for overloaded functions (#17366)
  [`pyupgrade`] Add fix safety section to docs (`UP036`) (#17444)
  [red-knot] more type-narrowing in match statements (#17302)
  [red-knot] Add some narrowing for assignment expressions (#17448)
  [red-knot] Understand `typing.Protocol` and `typing_extensions.Protocol` as equivalent (#17446)
  Server: Use `min` instead of `max` to limit the number of threads (#17421)
  [red-knot] Detect version-related syntax errors (#16379)
  [`pyflakes`] Add fix safety section (`F841`) (#17410)
  [red-knot] Add `KnownFunction` variants for `is_protocol`, `get_protocol_members` and `runtime_checkable` (#17450)
  Bump 0.11.6 (#17449)
  Auto generate `visit_source_order` (#17180)
  [red-knot] Initial tests for protocols (#17436)
  [red-knot] Dataclasses: synthesize `__init__` with proper signature (#17428)
  [red-knot] Dataclasses: support `order=True` (#17406)
  [red-knot] Super-basic generic inference at call sites (#17301)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[red-knot] understand equivalence of specialized generic classes

4 participants