Skip to content

[ty] Preserve inferred types for unknown class-decorator results - #28067

Merged
AlexWaygood merged 1 commit into
mainfrom
alex/simplify-class-decorator-unknown-results
Aug 27, 2026
Merged

[ty] Preserve inferred types for unknown class-decorator results#28067
AlexWaygood merged 1 commit into
mainfrom
alex/simplify-class-decorator-unknown-results

Conversation

@AlexWaygood

@AlexWaygood AlexWaygood commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

Currently we attempt to distinguish between "unannotated class decorators" and class decorators which, for any other reason, evaluate to Unknown when the class is passed in. This creates a strange inconsistency between these two cases:

from uninstalled_module import unknown_decorator  # ty: ignore[unresolved-import]

def decorator(cls): ...

@decorator
class Foo: ...

@unknown_decorator
class Bar: ...

reveal_type(Foo)  # <class 'Foo'>
reveal_type(Bar)  # Unknown

decorator(Foo) and unknown_decorator(Bar) are both inferred as returning Unknown in a call expression because in both cases, we encountered an error and had to fallback to a gradual type. (The only difference between them is that in one case, the error is reported in a diagnostic, whereas we currently do not complain about entirely missing type annotations in any diagnostic.) So why do we treat them differently at the moment when they're used as decorators?

This PR radically simplifies our heuristic so that we apply the same treatment for any class decorator that evaluates to Unknown or type[Unknown] when it's implicitly called on the class. This removes the strange inconsistency above, is much easier to explain to users in my opinion, and is also more similar to the logic we now use for dynamic-function-decorator-return. Codex's ecosystem analysis indicates that this has 266 diagnostic improvements on the ecosystem, at the cost of 46 new false positives. I think that's a pretty good tradeoff.

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Aug 26, 2026
@astral-sh-bot

astral-sh-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 97.79%. The percentage of expected errors that received a diagnostic held steady at 94.33%. The number of fully passing files held steady at 112/136.

@astral-sh-bot

astral-sh-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-type-form 195 63 0
invalid-argument-type 77 0 4
unresolved-attribute 63 0 18
unsound-return-statement 46 28 3
unsound-assignment 22 7 0
invalid-base 26 0 0
missing-argument 17 0 0
unknown-argument 16 0 0
unsupported-operator 9 0 7
invalid-assignment 7 0 7
no-matching-overload 4 0 0
unsound-yield 3 0 0
unsupported-base 2 0 0
call-non-callable 1 0 0
invalid-method-override 1 0 0
invalid-return-type 1 0 0
unused-ignore-comment 0 1 0
unused-type-ignore-comment 0 1 0
Total 490 100 39

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Showing a random sample of 394 of 629 changes. See the HTML report for the full diff.

Raw diff sample (394 of 629 changes)
dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ tests/contrib/ray/jobs/actor_without_init.py:18:15 error[unresolved-attribute] Class `Counter` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:139:25 error[unresolved-attribute] Class `Counter` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:188:29 error[unresolved-attribute] Class `IgnoredCounter` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:282:19 error[unresolved-attribute] Class `ComputationManager` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:319:22 error[unresolved-attribute] Class `Sender` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:320:24 error[unresolved-attribute] Class `Receiver` has no attribute `remote`
+ tests/contrib/ray/test_ray.py:402:29 error[unresolved-attribute] Class `Counter` has no attribute `remote`
+ tests/contrib/ray_serve/test_ray_serve.py:142:21 error[unresolved-attribute] Class `Ingress` has no attribute `bind`
+ tests/contrib/ray_serve/test_ray_serve.py:180:19 error[unresolved-attribute] Class `TraceIDDeployment` has no attribute `bind`

mypy (https://github.com/python/mypy)
- mypy/typeshed/stdlib/typing.pyi:1063:77 warning[unused-ignore-comment] Unused `ty: ignore` directive
+ mypy/typeshed/stdlib/typing.pyi:157:14 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:314:15 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:326:15 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:400:31 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:400:38 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:407:20 error[invalid-argument-type] Argument to `TypeVar.__init__` is incorrect: Expected `Any | None`, found `object`
+ mypy/typeshed/stdlib/typing.pyi:411:28 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `tuple[EllipsisType, <class 'Any'>]` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:420:22 error[invalid-argument-type] Argument to `TypeVar.__init__` is incorrect: Expected `Any | None`, found `<special-form 'type[object]'>`
+ mypy/typeshed/stdlib/typing.pyi:422:20 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:423:24 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:423:31 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:427:65 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:433:42 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:463:33 error[invalid-argument-type] Argument to `TypeVar.__init__` is incorrect: Expected `Any`, found `<class 'bytes'>`
+ mypy/typeshed/stdlib/typing.pyi:481:28 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:484:19 error[invalid-base] Invalid class base with type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:490:21 error[invalid-base] Invalid class base with type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:508:21 error[invalid-base] Invalid class base with type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:514:19 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:517:26 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:520:21 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:520:21 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:531:13 error[invalid-base] Invalid class base with type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:544:16 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:544:16 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:546:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:549:33 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:549:33 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:551:27 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:552:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:555:35 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:557:31 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:561:54 error[unknown-argument] Argument `default` does not match any known parameter of `TypeVar.__init__`
+ mypy/typeshed/stdlib/typing.pyi:564:39 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:565:27 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:567:48 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:576:98 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:592:49 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a type expression
+ mypy/typeshed/stdlib/typing.pyi:592:70 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:595:59 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a type expression
+ mypy/typeshed/stdlib/typing.pyi:595:80 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:595:80 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:598:17 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:598:17 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:600:28 error[invalid-type-form] Non-generic class `Generator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:606:44 error[invalid-base] Invalid class base with type `_Final`
+ mypy/typeshed/stdlib/typing.pyi:611:27 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:617:10 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:620:98 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:632:5 error[invalid-base] Invalid class base with type `_Final`
+ mypy/typeshed/stdlib/typing.pyi:637:21 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:637:21 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:642:43 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:644:28 error[invalid-type-form] Non-generic class `Awaitable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:645:28 error[invalid-type-form] Non-generic class `AsyncIterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:648:49 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:667:17 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:673:53 error[invalid-base] Invalid class base with type `object`
+ mypy/typeshed/stdlib/typing.pyi:691:31 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:695:41 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:699:45 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:706:46 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:709:60 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:719:29 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:721:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:723:42 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:724:29 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:725:32 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:747:26 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:749:30 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:764:48 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a type expression
+ mypy/typeshed/stdlib/typing.pyi:768:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:768:61 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:771:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:772:29 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:772:59 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:773:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:773:78 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:774:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:774:69 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:776:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:776:68 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:777:31 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:777:61 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:780:42 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:782:57 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:783:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:784:31 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:787:29 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:787:53 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:787:62 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:788:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:788:54 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:789:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:789:55 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:790:31 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:791:30 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:791:54 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:791:63 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:792:55 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:792:64 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:795:62 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:797:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:804:32 error[invalid-base] Invalid class base with type `_Final`
+ mypy/typeshed/stdlib/typing.pyi:808:43 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:812:24 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:814:52 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:816:38 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:826:32 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:832:24 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:836:24 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:836:38 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:838:37 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:849:31 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:849:45 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:849:56 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:872:48 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:874:43 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:874:80 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:874:99 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:876:40 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:876:45 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:878:43 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:878:52 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:878:72 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:880:43 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:880:62 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:911:39 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:915:47 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:917:52 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:941:33 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:944:27 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
+ mypy/typeshed/stdlib/typing.pyi:946:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:981:7 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type alias value
+ mypy/typeshed/stdlib/typing.pyi:981:7 error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: Any) -> object` cannot be called with key of type `tuple[EllipsisType, <class 'Any'>]` on object of type `_SpecialForm`
+ mypy/typeshed/stdlib/typing.pyi:1003:14 error[invalid-type-form] Variable of type `object` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:1021:20 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:1047:22 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1048:14 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1049:21 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1056:47 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1076:16 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1078:24 error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
+ mypy/typeshed/stdlib/typing.pyi:1096:39 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:651:28 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypy/typeshed/stdlib/typing.pyi:739:64 error[unsupported-operator] Operator `|` is not supported between two objects of type `TypeVar`
+ mypy/typeshed/stdlib/typing.pyi:741:65 error[unsupported-operator] Operator `|` is not supported between two objects of type `TypeVar`
+ mypy/typeshed/stdlib/typing.pyi:847:46 error[unsupported-operator] Operator `|` is not supported between objects of type `TypeVar` and `None`
+ mypy/typeshed/stdlib/typing.pyi:847:63 error[invalid-type-form] Variable of type `TypeVar` is not allowed in a parameter annotation
+ mypyc/test-data/fixtures/typing-full.pyi:64:28 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:86:28 error[invalid-type-form] Non-generic class `Awaitable` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:95:25 error[invalid-type-form] Non-generic class `Awaitable` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:121:29 error[invalid-type-form] Non-generic class `AsyncIterator` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:125:29 error[invalid-type-form] Non-generic class `AsyncIterator` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:127:28 error[invalid-type-form] Non-generic class `Awaitable` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:168:27 error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:92:64 error[invalid-type-form] Non-generic class `Awaitable` cannot be specialized in a type expression
+ mypyc/test-data/fixtures/typing-full.pyi:141:24 error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression

pandera (https://github.com/pandera-dev/pandera)
+ tests/pandas/test_numpy_engine.py:45:30 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
- pandera/engines/pandas_engine.py:240:24 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `DataType`
- pandera/typing/common.py:52:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:53:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:56:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:59:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:60:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:61:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:65:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:66:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:68:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:69:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/common.py:70:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:89:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:90:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:92:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:95:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:96:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:97:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:99:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:101:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:102:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:103:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:104:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:105:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:106:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- pandera/typing/pandas.py:117:9 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
+ tests/geopandas/test_engine.py:119:20 error[no-matching-overload] No overload of function `any` matches arguments
+ tests/geopandas/test_geopandas.py:174:30 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Geometry`
+ tests/geopandas/test_geopandas.py:190:30 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Geometry`
+ tests/geopandas/test_geopandas.py:191:28 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Geometry`
+ tests/geopandas/test_geopandas.py:213:30 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Geometry`
- tests/io/test_pandas_io.py:1714:23 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/io/test_pandas_io.py:2361:27 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/io/test_pandas_io.py:2362:27 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/io/test_pandas_io.py:2381:27 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
+ tests/io/test_pandas_io.py:2381:40 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Series[Int64]`
+ tests/narwhals/test_pandas_e2e.py:828:21 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
- tests/pandas/test_dtypes.py:158:74 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pandas/test_dtypes.py:176:43 error[invalid-assignment] Object of type `list[tuple[dict[Unknown, Unknown], list[Unknown]] | tuple[dict[Unknown | <class 'datetime'> | <class 'datetime64'> | type[Unknown] | DatetimeTZDtype, Unknown | str], Series[Timestamp]] | tuple[dict[Unknown | PeriodDtype, Unknown | str], Series[Period]] | tuple[dict[Unknown | <class 'SparseDtype'> | SparseDtype, Unknown | SparseDtype], Series[Any]] | tuple[dict[Unknown | IntervalDtype, Unknown | str], Series[Interval[float]]]]` is not assignable to `list[tuple[dict[Unknown, Unknown], list[Unknown]]]`
+ tests/pandas/test_dtypes.py:176:43 error[invalid-assignment] Object of type `list[tuple[dict[Unknown, Unknown], list[Unknown]] | tuple[dict[Unknown | <class 'datetime'> | <class 'datetime64'> | ... omitted 4 union elements, Unknown | str], Series[pandas._libs.tslibs.timestamps.Timestamp]] | tuple[dict[Unknown | PeriodDtype, Unknown | str], Series[Period]] | tuple[dict[Unknown | <class 'SparseDtype'> | SparseDtype, Unknown | SparseDtype], Series[Any]] | tuple[dict[Unknown | IntervalDtype, Unknown | str], Series[Interval[float]]]]` is not assignable to `list[tuple[dict[Unknown, Unknown], list[Unknown]]]`
+ tests/pandas/test_logical_dtypes.py:212:12 error[unresolved-attribute] Object of type `bool | Iterable[bool]` has no attribute `all`
+ tests/pandas/test_logical_dtypes.py:235:16 error[unresolved-attribute] Object of type `bool | Iterable[bool]` has no attribute `any`
+ tests/pandas/test_logical_dtypes.py:243:31 error[invalid-argument-type] Argument to bound method `Decimal.check` is incorrect: Expected `DataType`, found `dtype[generic[Any]] | ExtensionDtype`
+ tests/pandas/test_logical_dtypes.py:245:12 error[unresolved-attribute] Object of type `bool | Iterable[bool]` has no attribute `dtype`
+ tests/pandas/test_logical_dtypes.py:269:9 error[invalid-argument-type] Argument to bound method `Decimal.check` is incorrect: Expected `pandera.engines.pandas_engine.DataType`, found `pandera.dtypes.DataType`
+ tests/pandas/test_logical_dtypes.py:269:52 error[invalid-argument-type] Argument to bound method `Decimal.check` is incorrect: Expected `Series[Any] | None`, found `Series[Any] | DataFrame`
+ tests/pandas/test_pandas_engine.py:130:24 error[invalid-argument-type] Argument to bound method `pandera.engines.pandas_engine.DataType.check` is incorrect: Expected `pandera.dtypes.DataType`, found `dtype[generic[Any]] | ExtensionDtype | Series[Any]`
+ tests/pandas/test_pandas_engine.py:249:9 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:413:38 error[unknown-argument] Argument `tz` does not match any known parameter of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:467:13 error[unknown-argument] Argument `time_zone_agnostic` does not match any known parameter of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:467:38 error[unknown-argument] Argument `tz` does not match any known parameter of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:490:13 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:507:13 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:601:12 error[call-non-callable] Object of type `Series[Any]` is not callable
+ tests/pandas/test_pandas_engine.py:606:13 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:624:9 error[invalid-argument-type] Argument to function `DateTime._rebuild_data_container` is incorrect: Expected `Series[Any] | DataFrame`, found `Index[str]`
+ tests/pandas/test_pandas_engine.py:640:13 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:640:36 error[unknown-argument] Argument `time_zone_agnostic` does not match any known parameter of `DataType.__init__`
+ tests/pandas/test_pandas_engine.py:466:20 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
- tests/pandas/test_typing.py:22:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:38:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:42:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:54:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:58:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:70:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:74:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:82:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:90:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:94:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
- tests/pandas/test_typing.py:515:17 error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
+ tests/pandas/test_typing.py:515:35 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Series[Bool]`
+ tests/pandas/test_typing.py:529:36 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Series[Int64]`
+ tests/polars/test_polars_model.py:401:37 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `DateTime`
+ tests/polars/test_polars_model.py:406:37 warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `DateTime`
+ tests/pyspark/test_schemas_on_pyspark_pandas.py:280:13 error[missing-argument] No argument provided for required parameter `dtype` of `DataType.__init__`
- tests/strategies/test_strategies.py:391:13 error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `pandera.engines.numpy_engine.DataType | pandera.engines.pandas_engine.DataType`, found `type[Unknown]`
+ tests/strategies/test_strategies.py:391:13 error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `pandera.engines.numpy_engine.DataType | pandera.engines.pandas_engine.DataType`, found `<class 'String'>`
- tests/strategies/test_strategies.py:405:44 error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `pandera.engines.numpy_engine.DataType | pandera.engines.pandas_engine.DataType`, found `type[Unknown]`
+ tests/strategies/test_strategies.py:405:44 error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `pandera.engines.numpy_engine.DataType | pandera.engines.pandas_engine.DataType`, found `<class 'String'>`

spack (https://github.com/spack/spack)
- lib/spack/spack/dependency.py:69:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
- lib/spack/spack/hash_types.py:43:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
- lib/spack/spack/solver/reuse.py:69:12 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
+ lib/spack/spack/cmd/create.py:1074:45 error[invalid-argument-type] Argument to bound method `RepoPath.get_repo` is incorrect: Expected `str`, found `(str & ~AlwaysFalsy) | Literal[True] | (Unknown & ~AlwaysFalsy)`
+ lib/spack/spack/concretize.py:264:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
+ lib/spack/spack/hash_lookup.py:77:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
+ lib/spack/spack/installer/schedule.py:258:16 warning[unsound-return-statement] Unsound return statement: `list[Spec | Unknown]` is not a subtype of `list[Spec]`
+ lib/spack/spack/test/architecture.py:66:12 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/directory_layout.py:132:37 error[invalid-argument-type] Argument to bound method `Spec.copy` is incorrect: Expected `int | str | list[str] | tuple[str, ...]`, found `SpecHashDescriptor`
+ lib/spack/spack/test/error.py:55:61 error[invalid-argument-type] Argument to `OutputDoesNotSatisfyInputError.__init__` is incorrect: Expected `list[tuple[Spec, Spec | None]]`, found `list[tuple[Spec, Spec] | tuple[Spec, None]]`
+ lib/spack/spack/test/spec_format.py:174:53 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_format.py:184:43 error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/binary_distribution.py:1117:12 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
+ lib/spack/spack/binary_distribution.py:962:21 error[invalid-argument-type] Argument to bound method `list.append` is incorrect: Expected `Prefix`, found `str`
- lib/spack/spack/builder.py:225:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
+ lib/spack/spack/cmd/__init__.py:272:16 warning[unsound-return-statement] Unsound return statement: `Spec | Unknown` is not a subtype of `Spec`
+ lib/spack/spack/cmd/__init__.py:276:12 warning[unsound-return-statement] Unsound return statement: `Spec | Unknown` is not a subtype of `Spec`
+ lib/spack/spack/cmd/compiler.py:246:12 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `list[Spec]`
- lib/spack/spack/compilers/config.py:252:12 warning[unsound-return-statement] Unsound return statement: `tuple[Unknown, str, str]` is not a subtype of `tuple[str, str, str]`
+ lib/spack/spack/compilers/config.py:178:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `list[Spec]`
+ lib/spack/spack/cray_manifest.py:182:5 error[unresolved-attribute] Unresolved attribute `_hashes_final` on type `Spec`
+ lib/spack/spack/cray_manifest.py:184:5 error[unresolved-attribute] Unresolved attribute `origin` on type `Spec`
+ lib/spack/spack/database.py:1229:25 error[unresolved-attribute] Object of type `Spec` has no attribute `_package_hash`
+ lib/spack/spack/database.py:1283:17 error[invalid-argument-type] Argument to `InstallRecord.__init__` is incorrect: Expected `str | None`, found `object`
- lib/spack/spack/database.py:382:16 warning[unsound-return-statement] Unsound return statement: `tuple[Unknown, Unknown]` is not a subtype of `tuple[str, str]`
+ lib/spack/spack/database.py:382:16 warning[unsound-return-statement] Unsound return statement: `tuple[Unknown, str]` is not a subtype of `tuple[str, str]`
+ lib/spack/spack/detection/path.py:333:16 warning[unsound-return-statement] Unsound return statement: `list[Unknown | Spec]` is not a subtype of `list[Spec]`
+ lib/spack/spack/detection/path.py:477:12 warning[unsound-return-statement] Unsound return statement: `defaultdict[Unknown, Unknown]` is not a subtype of `dict[str, list[Spec]]`
+ lib/spack/spack/directives.py:794:24 error[no-matching-overload] No overload of bound method `MutableMapping.setdefault` matches arguments
+ lib/spack/spack/directory_layout.py:37:20 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec | None`
- lib/spack/spack/environment/environment.py:1085:13 warning[unsound-return-statement] Unsound return statement: `bool | (Unknown & ~AlwaysTruthy)` is not a subtype of `bool`
- lib/spack/spack/environment/environment.py:2337:42 error[invalid-argument-type] Argument to function `traverse_nodes` is incorrect: Expected `Sequence[Unknown]`, found `dict_values[str, Unknown]`
+ lib/spack/spack/environment/environment.py:2337:42 error[invalid-argument-type] Argument to function `traverse_nodes` is incorrect: Expected `Sequence[Spec]`, found `dict_values[str, Spec]`
- lib/spack/spack/environment/environment.py:2604:26 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `str`
+ lib/spack/spack/environment/environment.py:2604:26 error[invalid-assignment] Object of type `None | str | bool | Unknown` is not assignable to `str`
+ lib/spack/spack/externals.py:89:9 error[invalid-assignment] Object of type `Microarchitecture` is not assignable to attribute `target` on type `None | (ArchSpec & ~AlwaysTruthy) | (Unknown & ~AlwaysTruthy)`
- lib/spack/spack/install_test.py:1013:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
- lib/spack/spack/modules/common.py:777:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
+ lib/spack/spack/modules/common.py:681:16 warning[unsound-return-statement] Unsound return statement: `dict[str, Unknown | Spec]` is not a subtype of `dict[str, Spec]`
+ lib/spack/spack/modules/common.py:692:16 warning[unsound-return-statement] Unsound return statement: `Any | dict[str, Spec]` is not a subtype of `dict[str, Spec]`
+ lib/spack/spack/modules/common.py:757:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
+ lib/spack/spack/old_installer.py:555:32 error[no-matching-overload] No overload of function `join` matches arguments
+ lib/spack/spack/old_installer.py:577:44 error[invalid-argument-type] Argument to bound method `RepoPath.get_repo` is incorrect: Expected `str`, found `None | str | bool | Unknown`
+ lib/spack/spack/old_installer.py:578:21 error[no-matching-overload] No overload of function `join` matches arguments
+ lib/spack/spack/old_installer.py:580:24 error[invalid-argument-type] Argument to function `create_or_construct` is incorrect: Expected `str | None`, found `None | str | bool | Unknown`
- lib/spack/spack/package_base.py:1461:16 warning[unsound-return-statement] Unsound return statement: `Literal[False] | Unknown` is not a subtype of `bool`
+ lib/spack/spack/platforms/__init__.py:35:32 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `() -> Platform`
- lib/spack/spack/solver/asp.py:1477:16 warning[unsound-assignment] Unsound assignment: `(Unknown & ~AlwaysFalsy) | str | None` is not a subtype of `str | None`
- lib/spack/spack/solver/asp.py:1493:16 warning[unsound-assignment] Unsound assignment: `(Unknown & ~AlwaysFalsy) | str | None` is not a subtype of `str | None`
- lib/spack/spack/solver/asp.py:1646:30 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `tuple[bool | str, ...]`
- lib/spack/spack/solver/asp.py:1780:25 warning[unsound-assignment] Unsound assignment: `(Unknown & ~AlwaysFalsy) | str | None` is not a subtype of `str | None`
- lib/spack/spack/solver/asp.py:1806:24 warning[unsound-assignment] Unsound assignment: `(Unknown & ~AlwaysFalsy) | str | None` is not a subtype of `str | None`
- lib/spack/spack/solver/asp.py:2274:16 warning[unsound-assignment] Unsound assignment: `(Unknown & ~AlwaysFalsy) | (str & ~AlwaysFalsy) | Literal[""]` is not a subtype of `str | None`
+ lib/spack/spack/solver/asp.py:2196:48 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/solver/asp.py:2571:13 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[GitVersion | StandardVersion, list[Provenance]].__getitem__(key: GitVersion | StandardVersion, /) -> list[Provenance]` cannot be called with key of type `ConcreteVersion` on object of type `dict[GitVersion | StandardVersion, list[Provenance]]`
+ lib/spack/spack/solver/asp.py:3067:17 error[invalid-assignment] Invalid subscript assignment with key of type `str` and value of type `str | bool | (Unknown & ~None)` on object of type `dict[str, str]`
+ lib/spack/spack/solver/asp.py:3544:12 warning[unsound-return-statement] Unsound return statement: `tuple[set[Unknown], set[Unknown]]` is not a subtype of `tuple[set[Spec], set[Spec]]`
- lib/spack/spack/spec.py:1022:16 warning[unsound-return-statement] Unsound return statement: `Unknown | Literal[True]` is not a subtype of `bool`
- lib/spack/spack/spec.py:1834:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
- lib/spack/spack/spec.py:1848:12 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
- lib/spack/spack/spec.py:3896:16 warning[unsound-return-statement] Unsound return statement: `bool | Unknown` is not a subtype of `bool`
- lib/spack/spack/spec.py:5413:16 warning[unsound-return-statement] Unsound return statement: `int | Unknown` is not a subtype of `int`
- lib/spack/spack/spec.py:5498:16 warning[unsound-return-statement] Unsound return statement: `tuple[list[Unknown], list[Unknown]]` is not a subtype of `tuple[list[str], list[str]]`
- lib/spack/spack/spec.py:6371:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
+ lib/spack/spack/spec.py:703:17 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `ArchSpec`
+ lib/spack/spack/spec.py:766:17 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `ArchSpec`
+ lib/spack/spack/spec.py:1727:23 warning[unsound-yield] Unsound `yield`: `Unknown` is not a subtype of `DependencySpec`
+ lib/spack/spack/spec.py:2337:35 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `DependencySpec | None`
+ lib/spack/spack/spec.py:2800:29 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["propagation"]` and value of type `Literal["PREFERENCE"]` on object of type `dict[str, tuple[Literal["build", "link", "run", "test"], ...] | tuple[Divergent, ...]]`
+ lib/spack/spack/spec.py:2815:13 error[invalid-assignment] Invalid subscript assignment with key of type `Literal["compiler"]` and value of type `str` on object of type `dict[str, int]`
+ lib/spack/spack/spec.py:3101:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
+ lib/spack/spack/spec.py:3547:17 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `str | Spec`
+ lib/spack/spack/spec.py:3755:17 warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `str | Spec`
+ lib/spack/spack/spec.py:5425:9 error[invalid-method-override] Invalid override of method `__setitem__`: Definition is incompatible with `HashableMap.__setitem__`
+ lib/spack/spack/spec.py:5914:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
+ lib/spack/spack/spec.py:6049:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
- lib/spack/spack/spec.py:1655:12 warning[unsound-return-statement] Unsound return statement: `Literal[""] | Unknown` is not a subtype of `str`
+ lib/spack/spack/spec.py:1655:12 warning[unsound-return-statement] Unsound return statement: `str | Unknown` is not a subtype of `str`
- lib/spack/spack/spec.py:3475:24 error[unresolved-attribute] Attribute `constrain` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:3475:24 error[unresolved-attribute] Attribute `constrain` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:4959:16 error[unresolved-attribute] Attribute `platform` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:4959:16 error[unresolved-attribute] Attribute `platform` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:4967:16 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:4967:16 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:5296:57 error[unresolved-attribute] Attribute `platform` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:5296:57 error[unresolved-attribute] Attribute `platform` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:5297:17 error[invalid-assignment] Object of type `Unknown & ~AlwaysFalsy` is not assignable to attribute `platform` on type `None | Unknown`
+ lib/spack/spack/spec.py:5297:17 error[invalid-assignment] Object of type `Unknown & ~AlwaysFalsy` is not assignable to attribute `platform` on type `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:5299:45 error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:5299:45 error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:5302:53 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | Unknown`
+ lib/spack/spack/spec.py:5302:53 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/spec.py:5303:17 error[invalid-assignment] Object of type `Unknown & ~AlwaysFalsy` is not assignable to attribute `target` on type `None | Unknown`
+ lib/spack/spack/spec.py:5303:17 error[invalid-assignment] Object of type `Unknown & ~AlwaysFalsy` is not assignable to attribute `target` on type `None | ArchSpec | Unknown`
- lib/spack/spack/spec_parser.py:365:27 error[invalid-assignment] Object of type `tuple[Unknown, Unknown | dict[str, tuple[str] | int], None | Token | Unknown | type[Token]]` is not assignable to `tuple[Unknown, dict[Unknown, Unknown], Token] | None`
+ lib/spack/spack/spec_parser.py:365:27 error[invalid-assignment] Object of type `tuple[Unknown, Unknown | dict[str, tuple[str] | int], None | Token | Unknown | type[Token]]` is not assignable to `tuple[Spec, dict[Unknown, Unknown], Token] | None`
+ lib/spack/spack/test/ci.py:475:29 error[invalid-argument-type] Argument to function `run_standalone_tests` is incorrect: Expected `CDashHandler | None`, found `Spec`
+ lib/spack/spack/test/cmd/env.py:3018:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/cmd/env.py:3044:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/cmd/env.py:3061:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/cmd/env.py:3081:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/cmd/env.py:3163:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/cmd/env.py:3302:41 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/concretization/core.py:652:49 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/concretization/core.py:1163:20 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/concretization/core.py:1198:20 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/concretization/core.py:1671:20 error[invalid-argument-type] Argument to `set.__init__` is incorrect: Expected `Iterable[bool | str]`, found `tuple[bool | str, ...] | bool | str`
+ lib/spack/spack/test/concretization/core.py:3451:37 error[invalid-argument-type] Argument to bound method `ReusableSpecsSelector.reusable_specs` is incorrect: Expected `list[Spec]`, found `list[Spec | str]`
+ lib/spack/spack/test/concretization/core.py:4618:12 error[unresolved-attribute] Object of type `Spec` has no attribute `_hash`
+ lib/spack/spack/test/concretization/core.py:4619:12 error[unresolved-attribute] Object of type `Spec` has no attribute `_hash`
+ lib/spack/spack/test/concretization/core.py:5759:12 error[unresolved-attribute] Attribute `target_concrete` is not defined on `None` in union `None | ArchSpec | Unknown`
- lib/spack/spack/test/cray_manifest.py:204:12 error[unresolved-attribute] Attribute `extra_attributes` is not defined on `None` in union `Unknown | None`
+ lib/spack/spack/test/cray_manifest.py:204:12 error[unresolved-attribute] Attribute `extra_attributes` is not defined on `None` in union `Spec | None`
- lib/spack/spack/test/directives.py:138:5 error[invalid-assignment] Object of type `dict[Unknown, str]` is not assignable to attribute `licenses` of type `property`
+ lib/spack/spack/test/directives.py:138:5 error[invalid-assignment] Object of type `dict[Spec, str]` is not assignable to attribute `licenses` of type `property`
+ lib/spack/spack/test/env.py:66:5 error[unresolved-attribute] Unresolved attribute `_hash` on type `Spec`
+ lib/spack/spack/test/modules/common.py:132:41 error[invalid-argument-type] Argument to bound method `UpstreamModuleIndex.upstream_module` is incorrect: Expected `Spec`, found `MockSpec`
+ lib/spack/spack/test/modules/common.py:148:40 error[invalid-argument-type] Argument to bound method `UpstreamModuleIndex.upstream_module` is incorrect: Expected `Spec`, found `MockSpec`
+ lib/spack/spack/test/modules/common.py:173:51 error[invalid-argument-type] Argument to function `get_module` is incorrect: Expected `Spec`, found `MockSpec`
+ lib/spack/spack/test/patch.py:214:12 error[unsupported-operator] Operator `not in` is not supported between objects of type `Literal["a69b288d7393261e613c276c6d38a01461028291f6e381623acc58139d01f54d"]` and `tuple[bool | str, ...] | bool | str`
+ lib/spack/spack/test/spec_semantics.py:1171:16 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["test"]` and `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_semantics.py:1172:16 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["debian"]` and `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_semantics.py:1173:16 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["x86_64"]` and `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_semantics.py:1378:9 error[unresolved-attribute] Unresolved attribute `_hash` on type `Spec`
+ lib/spack/spack/test/spec_semantics.py:1856:9 error[unresolved-attribute] Unresolved attribute `_package_hash` on type `Spec`
+ lib/spack/spack/test/spec_semantics.py:1888:5 error[unresolved-attribute] Unresolved attribute `_hash` on type `Spec`
+ lib/spack/spack/test/spec_semantics.py:1920:20 error[unresolved-attribute] Object of type `Spec` has no attribute `_package_hash`
+ lib/spack/spack/test/spec_semantics.py:2257:16 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_semantics.py:2265:16 error[unresolved-attribute] Attribute `target` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/test/spec_syntax.py:1302:27 error[invalid-argument-type] Argument to function `expand_toolchains` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1360:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1365:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1375:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1389:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1396:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1403:36 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1451:5 error[unresolved-attribute] Unresolved attribute `_hash` on type `Spec`
+ lib/spack/spack/test/spec_syntax.py:1462:39 error[invalid-argument-type] Argument to function `lookup_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1467:39 error[invalid-argument-type] Argument to function `lookup_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1478:40 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1481:40 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1486:40 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1509:40 error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1390:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1390:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1392:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["zmpi"]` and `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1392:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["zmpi"]` and `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1404:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["zmpi"]` and `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1404:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["zmpi"]` and `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1407:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1407:12 error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1838:12 error[unresolved-attribute] Attribute `intersects` is not defined on `None` in union `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1838:12 error[unresolved-attribute] Attribute `intersects` is not defined on `None` in union `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1838:27 error[invalid-argument-type] Argument to bound method `Spec.intersects` is incorrect: Expected `str | Spec`, found `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1839:12 error[unresolved-attribute] Attribute `intersects` is not defined on `None` in union `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1839:12 error[unresolved-attribute] Attribute `intersects` is not defined on `None` in union `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1839:27 error[invalid-argument-type] Argument to bound method `Spec.intersects` is incorrect: Expected `str | Spec`, found `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1841:12 error[unresolved-attribute] Attribute `satisfies` is not defined on `None` in union `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1841:12 error[unresolved-attribute] Attribute `satisfies` is not defined on `None` in union `Spec | None`
- lib/spack/spack/test/spec_syntax.py:1848:12 error[unresolved-attribute] Attribute `architecture` is not defined on `None` in union `Unknown | None`
+ lib/spack/spack/test/spec_syntax.py:1848:12 error[unresolved-attribute] Attribute `architecture` is not defined on `None` in union `Spec | None`
+ lib/spack/spack/test/spec_syntax.py:1848:12 error[unresolved-attribute] Attribute `platform` is not defined on `None` in union `None | ArchSpec | Unknown`
+ lib/spack/spack/traverse.py:234:42 error[invalid-argument-type] Argument to `DependencySpec.__init__` is incorrect: Expected `Spec`, found `None`
+ lib/spack/spack/traverse.py:548:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Iterable[DependencySpec | tuple[int, DependencySpec]]`
+ lib/spack/spack/traverse.py:554:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Iterable[DependencySpec | tuple[int, DependencySpec]]`
+ lib/spack/spack/traverse.py:693:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Iterable[tuple[int, DependencySpec]]`
+ lib/spack/spack/traverse.py:696:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Iterable[tuple[int, DependencySpec]]`
+ lib/spack/spack/util/libc.py:66:16 warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec | None`
- lib/spack/spack/variant.py:422:20 warning[unsound-return-statement] Unsound return statement: `tuple[bool | str | Unknown, ...]` is not a subtype of `tuple[str | bool, ...]`
+ lib/spack/spack/variant.py:536:55 error[invalid-argument-type] Argument to `VariantValue.__init__` is incorrect: Expected `tuple[bool | str, ...]`, found `tuple[None]`

steam.py (https://github.com/Gobot1234/steam.py)
+ steam/chat.py:586:16 warning[unsound-return-statement] Unsound return statement: `Any` is not a subtype of `tuple[type[MemberT@ChatGroup], type[ChatT@ChatGroup], type[ChatGroupTypeT@ChatGroup]]`

Full report with detailed diff (timing results)

@AlexWaygood

Copy link
Copy Markdown
Member Author

Codex ecosystem summary

Preserving classes when a decorator returns Unknown or type[Unknown] produces 266 improvements under the ecosystem configuration: 98 removed diagnostics, 98 useful new errors, and 70 new unsoundness warnings. It also adds 46 false positives where decorators really change the class: 29 in Pandera, 15 in dd-trace-py, and 2 in setuptools.

The remaining changes are 28 new diagnostics exposing other existing ty limitations, 39 message-only refinements, 249 changes in mypy's copied typing definitions, and one Pandera ignore becoming used because it now suppresses a false positive. Together these account for all 629 stable changes. The unsound-* warnings are opt-in checks enabled by the ecosystem configuration, not necessarily evidence of runtime bugs.

The examples annotate lines whose diagnostics differ between revisions; diagnostics on unaffected lines are omitted from the comments. Save examples as case.py unless another filename is shown, preserving the .pyi extension for stub examples.

Full report

Affected projects

1. mypy: Copied typing definitions are checked as ordinary modules (249 changes)

These 249 changes all concern copied typing definitions: 237 in mypy's vendored typeshed typing.pyi, and 12 in mypyc's typing-full.pyi fixture. ty checks these files as ordinary modules, without the special semantics it gives its own typing stubs. Preserving their decorated classes exposes errors involving the local TypeVar, _SpecialForm, generic classes, and protocol definitions. The result is 248 added errors and one unused-ignore warning disappearing. These are fixture-checking noise, not useful new diagnostics for mypy.

Report entries (249)
  • mypy mypy/typeshed/stdlib/typing.pyi:157–1096 — lines 157 (2 changes), 314, 326, 400 (2 changes), 407 (2 changes), 411 (2 changes), 420, 422 (2 changes), 423 (2 changes), 427 (2 changes), 433 (2 changes), 463 (2 changes), 481 (2 changes), 484, 490, 496, 502, 508, 514 (2 changes), 517, 520 (2 changes), 528, 531, 536, 544 (2 changes), 546, 549 (2 changes), 551, 552, 555 (2 changes), 557, 560, 561, 564 (2 changes), 565, 567 (2 changes), 573, 576, 583, 592 (3 changes), 595 (3 changes), 598 (2 changes), 600, 606, 611 (2 changes), 617, 620, 632, 637 (2 changes), 639, 642 (2 changes), 644, 645, 648 (2 changes), 651, 667 (2 changes), 673 (2 changes), 681, 690, 691, 695, 699, 706, 709, 719, 721, 723, 724, 725, 733, 739, 741, 743, 747, 749, 752, 753, 764 (3 changes), 765 (2 changes), 767 (2 changes), 768 (3 changes), 769 (2 changes), 771 (3 changes), 772 (4 changes), 773 (4 changes), 774 (3 changes), 775 (2 changes), 776 (4 changes), 777 (4 changes), 780, 782 (2 changes), 783 (2 changes), 784 (2 changes), 786, 787 (3 changes), 788 (3 changes), 789 (2 changes), 790 (2 changes), 791 (3 changes), 792 (3 changes), 795, 797, 804, 808 (2 changes), 812 (2 changes), 814 (3 changes), 816 (4 changes), 826 (2 changes), 828, 832 (2 changes), 834 (3 changes), 836 (4 changes), 838 (2 changes), 847 (3 changes), 849 (3 changes), 872 (2 changes), 874 (3 changes), 876 (3 changes), 878 (4 changes), 880 (2 changes), 884, 889, 911, 915, 917, 934, 938, 941, 944, 946, 981 (2 changes), 1003, 1021 (2 changes), 1047, 1048, 1049, 1056, 1063, 1076, 1077, 1078, 1096.
  • mypy mypyc/test-data/fixtures/typing-full.pyi:64–168 — lines 64, 86, 89, 92, 95, 121, 125, 127, 134, 140, 141, 168.
Representative typing-fixture cases

Ordinary TypeVar instances are rejected in annotations.

repro.pyi:

# Merge base: no diagnostic
# PR: error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
def final(f) -> _T:
    pass

@final
class TypeVar:
    pass
_T = TypeVar()

Decorated fixture classes are rejected when specialized as generics.

case.pyi:

def runtime_checkable(cls) -> 0:
    pass

@runtime_checkable
class Iterable:
    pass
# Merge base: no diagnostic
# PR: error[invalid-type-form] Non-generic class `Iterable` cannot be specialized in a type expression
value: Iterable[int]

Preserving both fixture classes exposes incompatible subscription keys.

case.pyi:

# Merge base: no diagnostic
# PR: error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
def final(f) -> _T:
    pass

@final
class TypeVar:
    pass

@final
class _SpecialForm:

    def __getitem__(self, parameters: int):
        pass
_T = TypeVar()

# Merge base: no diagnostic
# PR: error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: int) -> Unknown` cannot be called with key of type `TypeVar` on object of type `_SpecialForm`
_SpecialForm()[_T]

Special-form subscription results are rejected as class bases.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:

    def __getitem__(self, parameters) -> object:
        pass

# Merge base: no diagnostic
# PR: error[invalid-base] Invalid class base with type `object`
class Container(_SpecialForm()[None]):
    pass

Ordinary special-form instances are rejected in type expressions.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:
    pass
ClassVar: _SpecialForm
# Merge base: no diagnostic
# PR: error[invalid-type-form] Invalid subscript of object of type `_SpecialForm` in a type expression
__hash__: ClassVar[None]

Unresolved decorators no longer hide nongeneric fixture classes.

repro.pyi:

# Merge base: no diagnostic
# PR: error[invalid-type-form] Non-generic class `Iterator` cannot be specialized in a type expression
__iter__: Iterator[int]

@runtime_checkable
class Iterator:
    pass

Special-form instances are rejected as class bases.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:
    pass

# Merge base: no diagnostic
# PR: error[invalid-base] Invalid class base with type `_SpecialForm`
class SupportsBytes(_SpecialForm()):
    pass

The local Generic helper returns instances as class bases.

case.pyi:

def type_check_only(func_or_cls) -> 0:
    pass

@type_check_only
class _Generic:

    def __class_getitem__(cls, args) -> object:
        pass

# Merge base: no diagnostic
# PR: error[invalid-base] Invalid class base with type `object`
class Coroutine(_Generic[int]):
    pass

Constructor arguments are checked against ordinary nominal types.

case.pyi:

def final(f) -> 0:
    pass

@final
class TypeVar:

    def __init__(self, bound: int):
        pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to `TypeVar.__init__` is incorrect: Expected `int`, found `<class 'object'>`
TypeVar(object)

Special-form subscriptions reject tuple keys.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:

    def __getitem__(self, parameters: int):
        pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Method `__getitem__` of type `bound method _SpecialForm.__getitem__(parameters: int) -> Unknown` cannot be called with key of type `None` on object of type `_SpecialForm`
_SpecialForm()[None]

TypeVar operator annotations reject the supplied operands.

case.pyi:

# Merge base: no diagnostic
# PR: error[invalid-type-form] Variable of type `TypeVar` is not allowed in a return type annotation
def final(f) -> _T:
    pass

@final
class TypeVar:

    def __or__(self, right: int, /):
        pass
_T = TypeVar()
# Merge base: no diagnostic
# PR: error[unsupported-operator] Operator `|` is not supported between two objects of type `TypeVar`
_T | _T

The Python 3.10 TypeVar constructor rejects default keywords.

case.pyi:

def final(f) -> 0:
    pass

@final
class TypeVar:
    pass
# Merge base: no diagnostic
# PR: error[unknown-argument] Argument `default` does not match any known parameter of `object.__init__`
TypeVar(default=None)

A known subscription result fails a known constructor’s bound check.

case.pyi:

def final(f) -> 0:
    pass

@final
class TypeVar:

    def __init__(self, bound: int):
        pass

@final
class _SpecialForm:

    def __getitem__(self, parameters) -> object:
        pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to `TypeVar.__init__` is incorrect: Expected `int`, found `object`
TypeVar(_SpecialForm()[None])

A subscription result is rejected as a union operand.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:

    def __getitem__(self, parameters) -> object:
        pass
# Merge base: no diagnostic
# PR: error[unsupported-operator] Operator `|` is not supported between objects of type `object` and `<class 'type'>`
_SpecialForm()[None] | type

A subscription result is rejected in a parameter annotation.

case.pyi:

def final(f) -> 0:
    pass

@final
class _SpecialForm:

    def __getitem__(self, parameters) -> object:
        pass
Allowed = _SpecialForm()[None]

# Merge base: no diagnostic
# PR: error[invalid-type-form] Variable of type `object` is not allowed in a parameter annotation
def get_type_hints(obj: Allowed):
    pass

An existing ignore now suppresses a specialization error.

case.pyi:

def runtime_checkable(cls) -> 0:
    pass

@runtime_checkable
class Iterable:
    pass
# Merge base: warning[unused-ignore-comment] Unused `ty: ignore` directive
# PR: no diagnostic
iterable: Iterable[int]  # ty:ignore[invalid-type-form]

2. pandera: Decorated dtype classes become valid annotations (59 changes)

immutable uses a generic class alias without type arguments in its return annotation. The merge base consequently treats the decorated names as type[Unknown] and rejects them in annotations, including generic arguments and type-variable bounds. The PR preserves the original classes, removing these errors.

Report entries (59)
from typing import TypeVar

_Dtype = TypeVar("_Dtype")

_DataTypeClass = type[_Dtype]

def immutable(pandera_dtype_cls) -> _DataTypeClass:
    return pandera_dtype_cls

@immutable
class Bool:
    pass

# Merge base: error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
# PR: no diagnostic
value: Bool

3. spack: Restored class annotations expose gradual return values (41 changes)

Spack's lazy_lexicographic_ordering is itself decorated by the unannotated decorator_with_or_without_args, leaving it with an unknown return type. The merge base loses classes decorated by it; the PR retains them. Return annotations containing Spec or DependencySpec now retain those classes. Functions that return Unknown, Any, or containers containing gradual values consequently gain unsound-return-statement warnings. The previously erased class annotations hid the missing return-type information in unannotated helpers and dynamically populated collections.

Report entries (41)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def spec(conf) -> Spec:
    # Merge base: no diagnostic
    # PR: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `Spec`
    return conf

4. pandera: Registered dtype constructors expose incomplete decorator annotations (30 changes)

The merge base loses registered dtype classes through register_dtype's unparameterized Callable result. The PR retains their visible constructor signatures, adding missing-argument and unknown-keyword errors and making an existing ignore effective. Pandera's immutable decorator creates dataclass constructors or installs a zero-argument __init__ at runtime, but its annotations do not expose those transformations; these diagnostics reveal that annotation gap.

Report entries (30)
from collections.abc import Callable

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class DateTime:
    def __init__(self, dtype):
        pass

# Merge base: no diagnostic
# PR: error[missing-argument] No argument provided for required parameter `dtype` of `DateTime.__init__`
# PR: error[unknown-argument] Argument `tz` does not match any known parameter of `DateTime.__init__`
DateTime(tz=None)
# Merge base: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
# PR: no diagnostic
DateTime()  # type: ignore

5. spack: Optional architecture fields become visible to callers (28 changes)

Spec.architecture is initialized to None and may later hold an ArchSpec. Callers previously saw only Unknown because Spec was erased. They now see the optional field and gain errors when reading its os, target or platform members without excluding None.

Report entries (28)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class ArchSpec:
    os: str

@lazy_lexicographic_ordering
class Spec:
    def __init__(self):
        self.architecture = None
    def _set_architecture(self):
        self.architecture = ArchSpec()

def test_user_input_combination(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | ArchSpec`
    spec.architecture.os

6. spack: Recovered members remove unsound return warnings (26 changes)

Preserving the decorated classes recovers the types of their fields, properties, methods and comparison results, removing these return warnings. This also recovers inherited methods on subclasses of Spec and HashableMap. Some warnings occur inside the decorated class itself: copying a field from another annotated instance previously introduced Unknown into the field’s inferred type. The examples cover direct fields, method calls, comparisons, this recursive copy, and inherited calls.

Report entries (26)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    name: str

def name(spec: Spec) -> str:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
    # PR: no diagnostic
    return spec.name
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def cformat(self) -> str:
        pass

def _format_spec(spec: Spec) -> str:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
    # PR: no diagnostic
    return spec.cformat()
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def __eq__(self, other) -> bool:
        pass

def equal(left: Spec, right: Spec) -> bool:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
    # PR: no diagnostic
    return left == right
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def __init__(self):
        self.original_spec_format = int()
    def _dup(self, other: "Spec"):
        self.original_spec_format = other.original_spec_format
    def get_original_spec_format(self) -> int:
        # Merge base: warning[unsound-return-statement] Unsound return statement: `int | Unknown` is not a subtype of `int`
        # PR: no diagnostic
        return self.original_spec_format
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def constrain(self) -> bool:
        pass

class _ImmutableSpec(Spec):

    def constrain(self) -> bool:
        # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
        # PR: no diagnostic
        return super().constrain()

7. spack: Existing optional-member errors name the recovered class (18 changes)

These accesses already reported that None has no requested member. Preserving ArchSpec or Spec refines the other arm of the receiver union, so the existing error now names that class instead of reporting only Unknown alongside None. The nullability problem itself is unchanged.

Report entries (18)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class ArchSpec:
    os: str

class Spec:
    def __init__(self):
        self.architecture = None
    def _set_architecture(self):
        self.architecture = ArchSpec()
    def os(self):
        # Merge base: error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | Unknown`
        # PR: error[unresolved-attribute] Attribute `os` is not defined on `None` in union `None | ArchSpec`
        return self.architecture.os

8. spack: Optional parser results no longer satisfy required Spec parameters (18 changes)

The parser’s Spec | None result and parameters that require Spec are now both preserved. Passing an unchecked parser result to operations that require a spec gains invalid-argument-type errors because the result may be None.

Report entries (18)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def next_spec() -> Spec | None:
    pass

def replace_hash(spec: Spec):
    pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to function `replace_hash` is incorrect: Expected `Spec`, found `Spec | None`
replace_hash(next_spec())

9. dd-trace-py: Unresolved Ray decorators no longer hide missing class members (15 changes)

Ray is unresolved in the analyzed environment. The tests expect its decorators to add .remote or .bind, but those members are absent from the undecorated classes visible to ty. The merge base replaces each class with Unknown; the PR retains the class and reports 15 missing-member errors. The undefined decorator in the example preserves that unresolved input.

Report entries (15)
@ray
class Counter:
    pass

# Merge base: no diagnostic
# PR: error[unresolved-attribute] Class `Counter` has no attribute `remote`
Counter.remote

10. spack: Dynamically installed hash fields are no longer hidden by Unknown (14 changes)

Spack installs its hash-cache fields with setattr, including _hash, _dag_hash, _package_hash and _process_hash. Once Spec is preserved, ordinary reads of those fields gain missing-attribute errors because ty does not add the dynamically assigned names to the class.

Report entries (14)

Existing ty issues: ty#2016

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def __init__(self):
        setattr(self, "_hash", None)

def test_hash_change_no_rehash_concrete(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[unresolved-attribute] Object of type `Spec` has no attribute `_hash`
    spec._hash

11. spack: Required class parameters reject incompatible arguments (10 changes)

Parameters annotated with Spec, or containers of Spec, no longer degrade to Unknown. ty now rejects the existing strings, containers of strings, and None values passed to these parameters. The same recovery also makes the required Spec parent of DependencySpec visible.

Report entries (10)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def unreachable(when_spec: Spec):
    pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to function `unreachable` is incorrect: Expected `Spec`, found `Literal["target="]`
unreachable(when_spec='target=')

12. pandera: Restored dtype annotations expose Field assignment warnings (8 changes)

For four fields, restoring the dtype class replaces an invalid-type-expression error with an unsound-assignment warning. The annotation is now understood, so ty checks the Any returned by Field against the declared field type. This also applies when the restored dtype is a type argument to Series.

Report entries (8)
from typing import Any, TypeVar

_Dtype = TypeVar('_Dtype')
_DataTypeClass = type[_Dtype]

def immutable(pandera_dtype_cls) -> _DataTypeClass:
    return pandera_dtype_cls

@immutable
class Date:
    pass

def check_field(field: Any):
    # Merge base: error[invalid-type-form] Variable of type `type[Unknown]` is not allowed in a type expression
    # PR: warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Date`
    confirmation_date: Date = field

13. spack: Restored class annotations expose gradual assignments (8 changes)

Assignments into variables annotated with ArchSpec, Spec, DependencySpec, or containers of those classes gain unsound-assignment warnings. Their right-hand sides still contain Unknown, usually from an unannotated helper such as _autospec, while the destination annotation now retains its class.

Report entries (8)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class ArchSpec:

    def _autospec(self, spec_like):
        pass

    def satisfies(self, other: 'ArchSpec'):
        # Merge base: no diagnostic
        # PR: warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `ArchSpec`
        other = self._autospec(other)

14. spack: The inferred namespace union exposes incompatible uses (8 changes)

Spec._add_flag assigns a str | bool value to namespace; other assignments also contribute None and Unknown in the full project. Preserving Spec exposes this union. Assigning it to a string now produces an assignment error instead of an unsound-assignment warning, and string parameters, path-joining overloads and string-valued dictionaries reject the incompatible arms. The truthiness guard in one caller removes None but still permits True.

Report entries (8)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def _add_flag(self, value: str | bool):
        self.namespace = value

def _add_to_environment_repository(spec_node: Spec):
    # Merge base: warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `str`
    # PR: error[invalid-assignment] Object of type `str | bool` is not assignable to `str`
    namespace: str = spec_node.namespace
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def _add_flag(self, value: str | bool):
        self.namespace = value

def get_repo(namespace: str):
    pass

def get_repository(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to function `get_repo` is incorrect: Expected `str`, found `str | bool`
    get_repo(spec.namespace)
from typing import overload

@overload
def join(a: str) -> str:
    pass

@overload
def join(a: bytes) -> bytes:
    pass

def join(a):
    pass

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def _add_flag(self, value: str | bool):
        self.namespace = value

def dump_packages(node: Spec):
    # Merge base: no diagnostic
    # PR: error[no-matching-overload] No overload of function `join` matches arguments
    join(node.namespace)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def _add_flag(self, value: str | bool):
        self.namespace = value

def setup(node: Spec, explicitly_required_namespaces: dict[str, str]):
    # Merge base: no diagnostic
    # PR: error[invalid-assignment] Invalid subscript assignment with key of type `Literal[""]` and value of type `str | bool` on object of type `dict[str, str]`
    explicitly_required_namespaces[""] = node.namespace

15. spack: Variant values retain their scalar-or-tuple union (8 changes)

VariantValue.value can be a string, a boolean or a tuple of strings and booleans. Preserving that class changes a return from an unsound warning to an invalid-return error where a string is required. It also exposes the boolean arm at set/frozenset construction and membership tests, which require an iterable or a membership-capable value.

Report entries (8)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class VariantValue:

    @property
    def value(self) -> tuple[bool | str, ...] | bool | str:
        pass

def buildsystem_name(variant: VariantValue) -> str:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `str`
    # PR: error[invalid-return-type] Return type does not match returned value: expected `str`, found `tuple[bool | str, ...] | bool | str`
    return variant.value
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class VariantValue:

    @property
    def value(self) -> tuple[bool | str, ...] | bool | str:
        pass

def test_mv_variants_disjoint_sets_from_spec(variant: VariantValue):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to `set.__init__` is incorrect: Expected `Iterable[bool | str]`, found `tuple[bool | str, ...] | bool | str`
    set(variant.value)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class VariantValue:

    @property
    def value(self) -> tuple[bool | str, ...] | bool | str:
        pass

def test_patch_mixed_versions_subset_constraint(variant: VariantValue):
    # Merge base: no diagnostic
    # PR: error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["a"]` and `tuple[bool | str, ...] | bool | str`
    'a' in variant.value

16. pandera: Registered dtype fields receive Any assignment checks (7 changes)

Geometry and Polars datetime classes previously became Unknown through their registration decorator. Their annotations now retain the concrete classes, exposing seven warnings for Field values annotated as returning Any.

Report entries (7)
from typing import Any
from collections.abc import Callable

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class Geometry:
    pass

def check_field(field: Any):
    # Merge base: no diagnostic
    # PR: warning[unsound-assignment] Unsound assignment: `Any` is not a subtype of `Geometry`
    geometry: Geometry = field

17. spack: Recovered field types remove unsound assignment warnings (6 changes)

Reading Spec.name now produces str, and reading VariantValue.values now preserves its tuple element types. Copying these values into the corresponding annotated destinations no longer introduces Unknown, so the unsound-assignment warnings disappear.

Report entries (6)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    name: str

def spec_versions(spec: Spec, name: str):
    # Merge base: warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `str`
    # PR: no diagnostic
    name = spec.name

18. spack: A kwargs dictionary loses the association between its keys and values (5 changes)

A dictionary containing the job_spec argument is inferred with Spec in its value type. When it is unpacked, ty considers that value a possible argument to unrelated boolean parameters such as fail_fast, producing five errors. A regular dictionary type does not associate individual literal keys with their value types. The related issue below discusses the diagnostic noise from checking each parameter separately.

Report entries (5)

Existing ty issues: ty#4160

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def run_standalone_tests(*, fail_fast: bool=False, job_spec: Spec | None=None):
    pass
args = {'job_spec': Spec()}
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to function `run_standalone_tests` is incorrect: Expected `bool`, found `Spec`
run_standalone_tests(**args)

19. spack: Existing membership errors name optional Spec results (5 changes)

Membership tests on an unchecked parser result already failed because the result can be None. Their diagnostic now displays Spec | None instead of Unknown | None. Spec supplies the membership operation; the unsupported None arm remains the reason for the error.

Report entries (5)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def __contains__(self, spec):
        pass

def test_dep_spec_by_hash(mpileaks_hash_fake: Spec | None):
    # Merge base: error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Unknown | None`
    # PR: error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["fake"]` and `Spec | None`
    "fake" in mpileaks_hash_fake

20. pandera: Declared check results expose unsupported attribute accesses (4 changes)

Restoring Decimal exposes the declared bool | Iterable[bool] return type of check. That type does not provide the .all(), .any(), and .dtype attributes used by these tests, so four previously hidden attribute errors are now reported.

Report entries (4)
from collections.abc import Callable

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class Decimal:
    def check(self) -> bool:
        return False

# Merge base: no diagnostic
# PR: error[unresolved-attribute] Object of type `bool` has no attribute `all`
Decimal().check().all()

21. spack: Narrowed nested dictionaries reject later heterogeneous values (4 changes)

The outer mappings are explicitly annotated as dict[str, Any], but ty narrows a particular subscript to the more specific dictionary assigned there. Recovered member types make those inner dictionaries tuple-valued or integer-valued instead of gradual. Later boolean and string writes are then rejected. The integer case additionally depends on preserving the type of a field copied from another Spec; both paths are shown below.

Report entries (4)
from typing import Any

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class DependencySpec:
    virtuals: tuple[str, ...]

def to_node_dict(dspec: DependencySpec):
    dep_attrs: dict[str, Any] = {"parameters": {"virtuals": dspec.virtuals}}
    # Merge base: no diagnostic
    # PR: error[invalid-assignment] Invalid subscript assignment with key of type `Literal["direct"]` and value of type `Literal[True]` on object of type `dict[str, tuple[str, ...]]`
    dep_attrs["parameters"]["direct"] = True
from typing import Any
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def __init__(self):
        self.original_spec_format = int()
    def _dup(self, other: "Spec"):
        self.original_spec_format = other.original_spec_format
    def to_node_dict(self):
        d: dict[str, Any] = {}
        d["annotations"] = {"original_specfile_version": self.original_spec_format}
        # Merge base: no diagnostic
        # PR: error[invalid-assignment] Invalid subscript assignment with key of type `Literal["compiler"]` and value of type `str` on object of type `dict[str, int]`
        d["annotations"]["compiler"] = str()

22. spack: Recovered methods and base constructors check their arguments (4 changes)

Calls through a preserved Spec receiver now check the copy method’s deps argument, exposing the SpecHashDescriptor passed where a dependency flag or dependency-type description is expected. Preserving VariantValue as a base class also enables checking its constructor, exposing the tuple containing None passed by VariantValueRemoval.

Report entries (4)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class SpecHashDescriptor:
    pass

@lazy_lexicographic_ordering
class Spec:

    def copy(self, deps: int):
        pass

def test_read_and_write_spec(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to bound method `Spec.copy` is incorrect: Expected `int`, found `SpecHashDescriptor`
    spec.copy(deps=SpecHashDescriptor())
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class VariantValue:

    def __init__(self, value: bool):
        pass

class VariantValueRemoval(VariantValue):

    def __init__(self):
        # Merge base: no diagnostic
        # PR: error[invalid-argument-type] Argument to `VariantValue.__init__` is incorrect: Expected `bool`, found `None`
        super().__init__(None)

23. spack: Writes through optional architecture objects are checked or refined (4 changes)

A write through a recovered Spec.architecture field now sees its possible None value and gains an error. Three existing errors inside Spec also change their receiver display from a union containing Unknown to one that names ArchSpec; the writes remain invalid on the None arm.

Report entries (4)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class ArchSpec:
    pass

@lazy_lexicographic_ordering
class Spec:

    def __init__(self):
        self.architecture = None

    def _set_architecture(self):
        self.architecture = ArchSpec()

def complete_architecture(node: Spec, target):
    # Merge base: no diagnostic
    # PR: error[invalid-assignment] Object of type `Unknown` is not assignable to attribute `target` on type `None | ArchSpec`
    node.architecture.target = target
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class ArchSpec:
    pass

class Spec:

    def __init__(self):
        self.architecture = None

    def _set_architecture(self):
        self.architecture = ArchSpec()

    def mutate(self, mutator):
        # Merge base: error[invalid-assignment] Object of type `Unknown` is not assignable to attribute `platform` on type `None | Unknown`
        # PR: error[invalid-assignment] Object of type `Unknown` is not assignable to attribute `platform` on type `None | ArchSpec`
        self.architecture.platform = mutator

24. pandera: Existing strategy errors identify the concrete class argument (3 changes)

The three str_length_strategy errors remain, but now identify the supplied String class instead of displaying type[Unknown]. The function's annotation still expects a dtype instance, so preserving the class improves the existing message rather than adding an error.

Report entries (3)
from typing import TypeVar

class NumpyDataType:
    pass

_Dtype = TypeVar("_Dtype")

_DataTypeClass = type[_Dtype]

def immutable(pandera_dtype_cls) -> _DataTypeClass:
    return pandera_dtype_cls

def str_length_strategy(pandera_dtype: NumpyDataType):
    pass

@immutable
class String:
    pass

# Merge base: error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `NumpyDataType`, found `type[Unknown]`
# PR: error[invalid-argument-type] Argument to function `str_length_strategy` is incorrect: Expected `NumpyDataType`, found `<class 'String'>`
str_length_strategy(String)

25. pandera: Known method signatures reject incompatible dtype arguments (3 changes)

The PR can now check the dtype arguments passed to registered Decimal and Category methods. These calls supply pandas-native dtypes or the general Pandera DataType where the visible parameter annotation requires a different or more specific Pandera dtype. The implementations normalize dtype inputs at runtime, so the new errors expose existing annotation mismatches.

Report entries (3)
from collections.abc import Callable

class SemanticDataType:
    pass

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

class DataType:
    pass

@register_dtype()
class Decimal:
    def check(self, pandera_dtype: DataType):
        pass
# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to bound method `Decimal.check` is incorrect: Expected `DataType`, found `SemanticDataType`
Decimal().check(SemanticDataType())

26. spack: Membership checks see an optional architecture object (3 changes)

The architecture field now exposes None | ArchSpec instead of Unknown. ArchSpec supports membership, but None does not, so membership tests without a guard gain unsupported-operator errors.

Report entries (3)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class ArchSpec:
    def __contains__(self, string):
        pass

@lazy_lexicographic_ordering
class Spec:
    def __init__(self):
        self.architecture = None
    def _set_architecture(self):
        self.architecture = ArchSpec()

def test_forwarding_of_architecture_attributes(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[unsupported-operator] Operator `in` is not supported between objects of type `Literal["test"]` and `None | ArchSpec`
    "test" in spec.architecture

27. spack: Restored iterator annotations expose gradual yielded values (3 changes)

Iterator element annotations now retain Spec or DependencySpec, including when nested in a tuple. The yielded values still contain Unknown, so these generators gain unsound-yield warnings.

Report entries (3)
from typing import Iterator

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class DependencySpec:
    pass

def _satisfying_edges(lhs_edge) -> Iterator[DependencySpec]:
    # Merge base: no diagnostic
    # PR: warning[unsound-yield] Unsound `yield`: `Unknown` is not a subtype of `DependencySpec`
    yield lhs_edge

28. spack: Return warnings retain residual Unknown with more precise other arms (3 changes)

These returns remain unsound because part of each expression is still gradual. The recovered class changes a tuple element from Unknown to str, makes the first comparison in a boolean expression precise enough to expose its False result, and lets tree formatting produce str while an untyped formatting branch still contributes Unknown.

Report entries (3)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    name: str

    def dag_hash(self):
        pass

def _lock_key(spec: Spec) -> tuple[str, str]:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `tuple[Unknown, Unknown]` is not a subtype of `tuple[str, str]`
    # PR: warning[unsound-return-statement] Unsound return statement: `tuple[Unknown, str]` is not a subtype of `tuple[str, str]`
    return (spec.dag_hash(), spec.name)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    name: str

    @property
    def external_path(self):
        pass

def libc_is_compatible(lhs: Spec, rhs: Spec) -> bool:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `bool`
    # PR: warning[unsound-return-statement] Unsound return statement: `Literal[False] | Unknown` is not a subtype of `bool`
    return lhs.name == rhs.name and lhs.external_path
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:

    def format(self) -> str:
        pass

def tree(specs: list[Spec], colorize) -> str:
    out = ''
    for node in specs:
        if colorize:
            out += colorize()
        out += node.format()
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Literal[""] | Unknown` is not a subtype of `str`
    # PR: warning[unsound-return-statement] Unsound return statement: `str | Unknown` is not a subtype of `str`
    return out

29. pandera: Conditional NumPy dtype definitions expose unsupported union bases (2 changes)

The PR retains the distinct classes defined by the NumPy availability branches. Their subclasses therefore expose ty's existing limitation for union-valued class bases: both possible bases are valid classes, but ty cannot determine one MRO for the union.

Report entries (2)

Existing ty issues: ty#529

from collections.abc import Callable

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

def check(FLOAT_128_AVAILABLE):
    if FLOAT_128_AVAILABLE:
        class Float64:
            pass
    else:
        @register_dtype()
        class Float64:
            pass

    # Merge base: no diagnostic
    # PR: warning[unsupported-base] Unsupported class base with type `<class 'case.<locals of function 'check'>.Float64 @ case.py:8:15'> | <class 'case.<locals of function 'check'>.Float64 @ case.py:12:15'>`
    class Float32(Float64):
        pass

30. setuptools: An unresolved decorator no longer hides constructor keywords (2 changes)

ty resolves Extension to its standard-library stub but cannot resolve distutils._dataclass.lenient_dataclass in this environment. The merge base loses the decorated subclass. The PR retains it and checks the inherited constructor, which does not accept py_limited_api or world. The test expects the decorator to change that constructor, so these errors reflect the missing decorator information.

Report entries (2)
@lenient_dataclass
class setuptools_Extension:
    pass
# Merge base: no diagnostic
# PR: error[unknown-argument] Argument `py_limited_api` does not match any known parameter of `object.__init__`
setuptools_Extension(py_limited_api=True)

31. spack: A concrete-version base type is too broad for the version map key (2 changes)

Spec.versions.concrete now exposes its ConcreteVersion result. The destination map accepts only the more specific GitVersion | StandardVersion union, so indexing it with the base-class type gains an invalid-argument error. A base-class instance need not be an instance of either permitted subclass.

Report entries (2)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class ConcreteVersion:
    pass

class StandardVersion(ConcreteVersion):
    pass

@lazy_lexicographic_ordering
class Spec:
    concrete: ConcreteVersion

def define_versions(s: Spec, possible_versions: dict[StandardVersion, object]):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Method `__getitem__` of type `bound method dict[StandardVersion, object].__getitem__(key: StandardVersion, /) -> object` cannot be called with key of type `ConcreteVersion` on object of type `dict[StandardVersion, object]`
    possible_versions[s.concrete]

32. spack: Equivalent tuple-union forms are rejected inside an invariant list (2 changes)

The inferred list contains a union of tuples, while the parameter places the union inside a tuple element. Once Spec is preserved, ty rejects these equivalent tuple forms inside the invariant list.

Report entries (2)

Existing ty issues: ty#493

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def OutputDoesNotSatisfyInputError(input_to_output: list[tuple[Spec | None]]):
    pass

def test_output_does_not_satisfy_input_keeps_its_specs():
    unsolved = [(Spec(),), (None,)]
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to function `OutputDoesNotSatisfyInputError` is incorrect: Expected `list[tuple[Spec | None]]`, found `list[tuple[Spec] | tuple[None]]`
    OutputDoesNotSatisfyInputError(unsolved)

33. spack: Existing namedtuple field-assignment errors show Spec keys (2 changes)

These tests assign a dictionary to a field descriptor on a class created by namedtuple. That assignment was already invalid. The diagnostic’s dictionary type now shows Spec keys instead of Unknown because the constructor expression retains the class.

Report entries (2)
from collections import namedtuple

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass
package = namedtuple('package', ['licenses'])
# Merge base: error[invalid-assignment] Object of type `dict[Unknown, str]` is not assignable to attribute `licenses` of type `property`
# PR: error[invalid-assignment] Object of type `dict[Spec, str]` is not assignable to attribute `licenses` of type `property`
package.licenses = {Spec(): ''}

34. spack: Existing ordering errors name optional Spec operands (2 changes)

The optional parser results are still unsafe operands for < and <= because either may be None. Preserving Spec changes the displayed operand types from Unknown | None to Spec | None; it does not remove the unsupported None combinations.

Report entries (2)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    def __lt__(self, other) -> bool: ...
    def __le__(self, other) -> bool: ...

def test_compare_abstract_specs(a: Spec | None, b: Spec | None):
    # Merge base: error[unsupported-operator] Operator `<=` is not supported between two objects of type `Unknown | None`
    # PR: error[unsupported-operator] Operator `<=` is not supported between two objects of type `Spec | None`
    a <= b
    # Merge base: error[unsupported-operator] Operator `<` is not supported between two objects of type `Unknown | None`
    # PR: error[unsupported-operator] Operator `<` is not supported between two objects of type `Spec | None`
    b < a

35. spack: External attribute writes are checked against Spec (2 changes)

Assignments to Spec._hashes_final and Spec.origin were previously writes to Unknown. Preserving Spec makes ty check those names against the class and report that they have not been defined there.

Report entries (2)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def spec_from_entry(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[unresolved-attribute] Unresolved attribute `_hashes_final` on type `Spec`
    spec._hashes_final = True

36. spack: Platform factory annotations expose gradual callable values (2 changes)

Callable[[], Platform] now retains its result class. Assigning the unknown imported factory gains an unsound-assignment warning, as does assigning a _PickleableCallable whose unannotated __call__ result is Unknown.

Report entries (2)
from typing import Callable

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Platform:
    pass

def install(_host):
    # Merge base: no diagnostic
    # PR: warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `() -> Platform`
    host: Callable[[], Platform] = _host
from typing import Callable

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Platform:
    pass

class _PickleableCallable:

    def __call__(self):
        pass
# Merge base: no diagnostic
# PR: warning[unsound-assignment] Unsound assignment: `_PickleableCallable` is not a subtype of `() -> Platform`
host: Callable[[], Platform] = _PickleableCallable()

37. spack: Unannotated callback parameters no longer satisfy a precise callable subtype (2 changes)

The required callback type now includes Spec rather than an erased class. The returned view_hardlink and view_symlink functions accept the relevant arguments through unannotated variadic parameters. They remain assignable to the callback type but are not strict subtypes of it, so returning them gains unsound-return warnings.

Report entries (2)
from typing import Callable

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def view_hardlink(*args) -> None:
    pass

def function_for_link_type() -> Callable[[Spec], None]:
    # Merge base: no diagnostic
    # PR: warning[unsound-return-statement] Unsound return statement: `def view_hardlink(*args) -> None` is not a subtype of `(Spec, /) -> None`
    return view_hardlink

38. jax: A retained operation type exposes an unknown reassignment (1 change)

The MLIR registration decorator has an unknown result. The merge base therefore loses WarpMapOp, while the PR preserves it. In the lowering function, a loop reuses the annotated op parameter for operations whose type is Unknown, producing a new unsound-assignment warning. The example reduces that loop to an assignment while retaining the unknown base and member lookup.

Report entries (1)
@_cext
class WarpMapOp(_mosaic_gpu_gen_ops):
    pass

def lower(op: WarpMapOp):
    # Merge base: no diagnostic
    # PR: warning[unsound-assignment] Unsound assignment: `Unknown` is not a subtype of `WarpMapOp`
    op = op.body

39. pandera: A datetime helper rejects an Index missing from its annotation (1 change)

DateTime._rebuild_data_container explicitly handles an Index at runtime, but its PandasObject parameter annotation includes only Series and DataFrame. The PR now reports this mismatch at the call site.

Report entries (1)
from collections.abc import Callable

class Series:
    ...
class Index:
    ...

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class DateTime:
    def _rebuild_data_container(data_container: Series):
        pass

# Merge base: no diagnostic
# PR: error[invalid-argument-type] Argument to function `DateTime._rebuild_data_container` is incorrect: Expected `Series`, found `Index`
DateTime._rebuild_data_container(Index())

40. pandera: A general iterable does not satisfy NumPy any overloads (1 change)

Geometry.check returns bool | Iterable[bool]. Preserving the decorated Geometry class exposes that return type to np.any, whose ArrayLike overloads do not accept an arbitrary iterable. In particular, the relevant sequence protocol requires __len__, which Iterable does not guarantee. The example isolates the missing __len__ requirement.

Report entries (1)
from collections.abc import Callable
from typing import Protocol, overload

def register_dtype() -> Callable:
    ...

@register_dtype()
class Geometry:

    def check(self) -> object:
        ...

class _NestedSequence(Protocol):

    def __len__(self, /):
        ...

@overload
def any(a: _NestedSequence):
    ...

@overload
def any(a: _NestedSequence):
    ...
# Merge base: no diagnostic
# PR: error[no-matching-overload] No overload of function `any` matches arguments
any(Geometry().check())

41. pandera: A possible DataFrame result has no callable tolist method (1 change)

Preserving DateTime exposes the declared DataFrame | Series result of _call_to_datetime. The test calls .tolist() on that result, but DataFrame has no such method: pandas' dynamic attribute fallback instead gives the attribute type Series[Any], which is not callable. The reduced example preserves this fallback with a simpler non-callable return type.

Report entries (1)
from collections.abc import Callable

def register_dtype() -> Callable:
    ...

class DataFrame:

    def __getattr__(self, name) -> int:
        ...

@register_dtype()
class DateTime:

    def _call_to_datetime(self) -> DataFrame:
        ...
# Merge base: no diagnostic
# PR: error[call-non-callable] Object of type `int` is not callable
DateTime()._call_to_datetime().tolist()

42. pandera: A possibly-DataFrame argument is rejected where Series is required (1 change)

Decimal.coerce is annotated as returning Series | DataFrame, while Decimal.check accepts only Series | None for its data-container argument. Preserving the registered class exposes the incompatible DataFrame alternative.

Report entries (1)
from collections.abc import Callable

class Series:
    ...
class DataFrame:
    ...

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class Decimal:
    def check(self, data_container: Series):
        pass

def check_coerced(coerced: Series | DataFrame):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to bound method `Decimal.check` is incorrect: Expected `Series`, found `Series | DataFrame`
    Decimal().check(coerced)

43. pandera: More precise dtype classes disambiguate Timestamp in an existing error (1 change)

This assignment is rejected on both revisions: the fixture annotation expects lists, but some fixtures contain pandas Series. The PR preserves the Pandera dtype classes instead of displaying them as type[Unknown]. Their distinct types make the original union longer, and the presence of Pandera’s Timestamp means the pandas Timestamp must also be qualified. The reduced example keeps both same-named classes in the existing assignment error.

Report entries (1)
from typing import TypeVar
_Dtype = TypeVar('_Dtype')
_DataTypeClass = type[_Dtype]

def immutable(pandera_dtype_cls) -> _DataTypeClass:
    ...

@immutable
class Timestamp:
    pass

class pd:

    class Timestamp:
        pass
# Merge base: error[invalid-assignment] Object of type `tuple[type[Unknown], Timestamp]` is not assignable to `int`
# PR: error[invalid-assignment] Object of type `tuple[<class 'case.Timestamp'>, case.pd.Timestamp]` is not assignable to `int`
dtype_fixtures: int = (Timestamp, pd.Timestamp())

44. pandera: Returning a registered dtype retains its base type (1 change)

The merge base treats the registered Category constructor's result as Unknown, producing an unsound-return warning. The PR retains Category as a subclass of DataType, so the return statement satisfies its declared result type.

Report entries (1)
from collections.abc import Callable

class DataType:
    pass

def register_dtype() -> Callable:
    return lambda pandera_dtype_cls: pandera_dtype_cls

@register_dtype()
class Category(DataType):
    pass

def dtype() -> DataType:
    # Merge base: warning[unsound-return-statement] Unsound return statement: `Unknown` is not a subtype of `DataType`
    # PR: no diagnostic
    return Category()

45. spack: A Spec value no longer passes as a string (1 change)

The PR retains the Spec argument at a call to a function that expects str, exposing a type mismatch previously hidden by Unknown.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def msg(args: str):
    pass

def solve(spec: Spec):
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to function `msg` is incorrect: Expected `str`, found `Spec`
    msg(spec)

46. spack: A hasattr guard establishes existence without establishing a string type (1 change)

Spec.origin is not declared on the class. After hasattr(spec, "origin"), ty knows the attribute exists but gives it type object, which is too broad for the str | None parameter of InstallRecord. Preserving Spec therefore exposes an invalid-argument error that was hidden when the receiver was Unknown.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def InstallRecord(origin: str):
    pass

def _add(spec: Spec):
    if hasattr(spec, 'origin'):
        # Merge base: no diagnostic
        # PR: error[invalid-argument-type] Argument to function `InstallRecord` is incorrect: Expected `str`, found `object`
        InstallRecord(origin=spec.origin)

47. spack: A pre-existing sequence mismatch reports the recovered element type (1 change)

dict.values() returns a dictionary view rather than the Sequence required by traverse_nodes, so this call already failed. The existing error now carries Spec instead of Unknown in the argument and parameter element types. The example retains the nominal mismatch between a dictionary view and Sequence.

Report entries (1)
class Sequence:
    pass

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def traverse_nodes(specs: Sequence):
    pass

def _concrete_specs_dict(specs_by_hash: dict[str, Spec]):
    # Merge base: error[invalid-argument-type] Argument to function `traverse_nodes` is incorrect: Expected `Sequence`, found `dict_values[str, Unknown]`
    # PR: error[invalid-argument-type] Argument to function `traverse_nodes` is incorrect: Expected `Sequence`, found `dict_values[str, Spec]`
    traverse_nodes(specs_by_hash.values())

48. spack: A recovered base class exposes incompatible keyword parameter names (1 change)

VariantMap.__setitem__ overrides HashableMap.__setitem__ with different positional-or-keyword parameter names (name/vspec rather than key/value). Preserving the base class enables override checking and exposes that the override does not accept the base method’s keyword arguments.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class HashableMap:

    def __setitem__(self, key, value):
        pass

class VariantMap(HashableMap):

    # Merge base: no diagnostic
    # PR: error[invalid-method-override] Invalid override of method `__setitem__`: Definition is incompatible with `HashableMap.__setitem__`
    def __setitem__(self, name, vspec):
        pass

49. spack: An existing tuple assignment error retains its Spec element (1 change)

The parser assigns a tuple containing None to a variable whose second element is a Token. That assignment already failed. Its target type now preserves the first Spec element instead of showing Unknown; the invalid None token is unchanged.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

class Token:
    pass

def next_spec(dependency):
    pending: tuple[Spec, Token]
    # Merge base: error[invalid-assignment] Object of type `tuple[Unknown, None]` is not assignable to `tuple[Unknown, Token]`
    # PR: error[invalid-assignment] Object of type `tuple[Unknown, None]` is not assignable to `tuple[Spec, Token]`
    pending = (dependency, None)

50. spack: An optional Spec key no longer matches setdefault overloads (1 change)

The variant mapping requires Spec keys, but when_spec is optional. Preserving the key class means None is no longer absorbed by a gradual key annotation, and no setdefault overload accepts the unchecked optional key.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

@lazy_lexicographic_ordering
class Spec:
    pass

def _execute_variant(variants: dict[Spec, dict[str, object]], when_spec: Spec | None):
    # Merge base: no diagnostic
    # PR: error[no-matching-overload] No overload of bound method `MutableMapping.setdefault` matches arguments
    variants.setdefault(when_spec, {})

51. spack: An uninferred callback type variable becomes visible in a return warning (1 change)

The generic stable_partition call cannot determine its type variable from the unannotated predicate. Its result retains UnspecializedTypeVar, which was hidden by the erased Spec return annotation and now appears in an unsound-return warning. Assigning the call result to a temporary matters: returning the call directly lets the return annotation provide inference context and removes the warning.

Report entries (1)
from typing import TypeVar, Callable

def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls
T = TypeVar('T')

@lazy_lexicographic_ordering
class Spec:
    pass

def stable_partition(predicate_fn: Callable[[T], bool]) -> T:
    pass

def _partition_user_specs() -> Spec:
    new_user_specs = stable_partition(lambda x: True)
    # Merge base: no diagnostic
    # PR: warning[unsound-return-statement] Unsound return statement: `UnspecializedTypeVar` is not a subtype of `Spec`
    return new_user_specs

52. spack: Recovered prefix values narrow a list comprehension (1 change)

Spec.prefix is a Prefix, a subclass of str. The list comprehension now infers list[Prefix] instead of a gradual element type, so appending a plain string is rejected. The comprehension matters: replacing it with a simple one-element list widens this example to str and removes the difference.

Report entries (1)
def decorator_with_or_without_args(decorator):
    return decorator

@decorator_with_or_without_args
def lazy_lexicographic_ordering(cls):
    return cls

class Prefix(str):
    pass

@lazy_lexicographic_ordering
class Spec:
    prefix: Prefix

def prefixes_to_relocate(specs: list[Spec]):
    prefixes = [s.prefix for s in specs]
    # Merge base: no diagnostic
    # PR: error[invalid-argument-type] Argument to bound method `list.append` is incorrect: Expected `Prefix`, found `str`
    prefixes.append(str())

53. steam.py: A preserved bound exposes a value lost through a cyclic wildcard import (1 change)

Steam’s wildcard re-exports leave utils, and consequently the member-construction expression, typed as Divergent on both revisions. Retaining Member makes the MemberT return annotation eligible for the unsound-return check, exposing that pre-existing inference failure. The import cycle is the behavior tracked in ty#113.

Report entries (1)

Existing ty issues: ty#113

steam/app.py:

from typing import TypeVar
from . import utils
MemberT = TypeVar('MemberT', bound='Member')

@PartialMember
class Member:
    pass


def add_member() -> MemberT:
    # Merge base: no diagnostic
    # PR: warning[unsound-return-statement] Unsound return statement: `Divergent` is not a subtype of `MemberT@add_member`
    return utils

steam/__init__.py:

from .app import *

54. steam.py: An Any return becomes visible after preserving a type-variable bound (1 change)

get_original_bases(cls)[0].__args__ is typed as Any. Retaining the registered Member class gives MemberT a concrete bound, enabling the unsound-return check for the declared tuple of class objects. The introspection result remains Any on both revisions; the reduced example keeps the same return-value and bound relationship.

Report entries (1)

steam/chat.py:

from typing import Any, TypeVar

def get_original_bases() -> Any:
    pass
MemberT = TypeVar('MemberT', bound='Member')

@PartialMember
class Member:
    pass

def _type_args() -> MemberT:
    # Merge base: no diagnostic
    # PR: warning[unsound-return-statement] Unsound return statement: `Any` is not a subtype of `MemberT@_type_args`
    return get_original_bases()

Reproduction

  • Detailed report: ecosystem-analyzer report
  • Actions run: run 32978994476, attempt 1
  • Ruff comparison: d5a639442529 to 794d3116ae72
  • ecosystem-analyzer: 74af2b661a70
  • mypy-primer: 3058720299b8
  • Dependency cutoff: 2026-08-26T14:13:54Z
  • Project Python: Python 3.11 environments for all seven projects. The preserved project configurations select analysis targets 3.9 for dd-trace-py, 3.10 for mypy/Pandera/setuptools, 3.7 for Spack, and 3.11 for JAX/steam.py.
  • Project analysis mode: Pandera and setuptools are strict; dd-trace-py, JAX, mypy, Spack, and steam.py are non-strict.
  • Comparison method: run each exact-revision profiling binary from the freshly prepared project with check <paths> --python .venv --python-platform linux --output-format concise, with TY_CONFIG_FILE unset and the shared ecosystem configuration installed as $XDG_CONFIG_HOME/ty/ty.toml. Add --config analysis.strict-equality-semantics=true --config analysis.strict-generic-narrowing=true for both strict projects. Standalone examples additionally specify the corresponding --python-version listed above.
  • Checked paths: the whole dd-trace-py project, excluding tests/appsec/iast/fixtures/aspects/str/non_utf8_content.py and tests/appsec/iast/fixtures/ast/str/non_utf8_content.py; jax for JAX; mypy mypyc for mypy; the whole Pandera project; setuptools for setuptools; the whole Spack project; steam for steam.py.

@AlexWaygood
AlexWaygood marked this pull request as ready for review August 26, 2026 16:18
@AlexWaygood
AlexWaygood requested a review from a team as a code owner August 26, 2026 16:18
@astral-sh-bot
astral-sh-bot Bot requested a review from sharkdp August 26, 2026 16:18

@sharkdp sharkdp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you

I agree that those cases should be handled consistently.

The direction of change seems like it goes against the gradual guarantee, so whether or not this is a good idea really depends on the ecosystem impact. If all of these new diagnostics are really improvements, i.e. new true positives, then I'm all for it, but I haven't looked at the impact closely.

Great that we can simplify the code a lot!

@AlexWaygood AlexWaygood reopened this Aug 27, 2026
@astral-sh-bot
astral-sh-bot Bot requested a review from carljm August 27, 2026 07:20
@AlexWaygood
AlexWaygood removed the request for review from carljm August 27, 2026 07:24
@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 5.04%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
✅ 124 untouched benchmarks
⏩ 84 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation ty_micro[inherited_recursive_protocol_diagnostic] 10.3 s 9.8 s +5.04%
Simulation ty_micro[inherited_recursive_protocol_constructor] 10.3 s 9.8 s +5.04%

Tip

Curious why performance improved? Use the CodSpeed MCP and ask your agent.


Comparing alex/simplify-class-decorator-unknown-results (2891955) with main (b453116)

Open in CodSpeed

Footnotes

  1. 84 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@AlexWaygood

Copy link
Copy Markdown
Member Author

The direction of change seems like it goes against the gradual guarantee

That's correct, yes -- but we already made the decision when originally implementing support for class decorators in #25091 that it was better to set aside the gradual guarantee in many cases and preserve the original class. So I don't see this as a change in philosophy, just a more consistent application of the decision we made then.

I think codex's analysis is solid, and I think 266 ecosystem improvements in exchange for 46 new false positives is a good trade-off. And if you really do want your class to be inferred as Any after it's been decorated with foo, there's an easy way out: just explicitly annotate the return type of foo as Any.

@AlexWaygood
AlexWaygood merged commit 97060ba into main Aug 27, 2026
132 of 134 checks passed
@AlexWaygood
AlexWaygood deleted the alex/simplify-class-decorator-unknown-results branch August 27, 2026 10:24
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.

2 participants