Skip to content

Conversation

@dcreager
Copy link
Member

@dcreager dcreager commented Nov 20, 2025

This is a first stab at solving astral-sh/ty#500, at least in part, with the old solver. We add a new TypeRelation that lets us opt into using constraint sets to describe when a typevar is assignability to some type, and then use that to calculate a constraint set that describes when two callable types are assignable. If the callable types contain typevars, that constraint set will describe their valid specializations. We can then walk through all of the ways the constraint set can be satisfied, and record a type mapping in the old solver for each one.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 20, 2025

Diagnostic diff on typing conformance tests

Changes were detected when running ty on typing conformance tests
--- old-output.txt	2025-12-16 16:22:20.701518109 +0000
+++ new-output.txt	2025-12-16 16:22:24.208547350 +0000
@@ -150,7 +150,7 @@
 callables_protocol.py:169:7: error[invalid-assignment] Object of type `def cb8_bad1(x: int) -> Any` is not assignable to `Proto8`
 callables_protocol.py:186:5: error[invalid-assignment] Object of type `Literal["str"]` is not assignable to attribute `other_attribute` of type `int`
 callables_protocol.py:187:5: error[unresolved-attribute] Unresolved attribute `xxx` on type `Proto9[P@decorator1, R@decorator1]`.
-callables_protocol.py:197:7: error[unresolved-attribute] Object of type `Proto9[(x: int), Unknown]` has no attribute `other_attribute2`
+callables_protocol.py:197:7: error[unresolved-attribute] Object of type `Proto9[(x: int), str]` has no attribute `other_attribute2`
 callables_protocol.py:238:8: error[invalid-assignment] Object of type `def cb11_bad1(x: int, y: str, /) -> Any` is not assignable to `Proto11`
 callables_protocol.py:260:8: error[invalid-assignment] Object of type `def cb12_bad1(*args: Any, *, kwarg0: Any) -> None` is not assignable to `Proto12`
 callables_protocol.py:284:27: error[invalid-assignment] Object of type `def cb13_no_default(path: str) -> str` is not assignable to `Proto13_Default`
@@ -236,37 +236,38 @@
 constructors_call_type.py:59:9: error[too-many-positional-arguments] Too many positional arguments to bound method `__init__`: expected 1, got 2
 constructors_call_type.py:81:5: error[missing-argument] No argument provided for required parameter `y` of function `__new__`
 constructors_call_type.py:82:12: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `str`, found `Literal[2]`
-constructors_callable.py:36:13: info[revealed-type] Revealed type: `(x: int) -> Unknown`
-constructors_callable.py:37:1: error[type-assertion-failure] Type `Class1` does not match asserted type `Unknown`
+constructors_callable.py:36:13: info[revealed-type] Revealed type: `(x: int) -> Class1`
 constructors_callable.py:38:1: error[missing-argument] No argument provided for required parameter `x`
 constructors_callable.py:39:1: error[missing-argument] No argument provided for required parameter `x`
 constructors_callable.py:39:4: error[unknown-argument] Argument `y` does not match any known parameter
-constructors_callable.py:49:13: info[revealed-type] Revealed type: `() -> Unknown`
-constructors_callable.py:50:1: error[type-assertion-failure] Type `Class2` does not match asserted type `Unknown`
+constructors_callable.py:49:13: info[revealed-type] Revealed type: `() -> Class2`
 constructors_callable.py:51:4: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
 constructors_callable.py:57:42: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Self@__new__`
-constructors_callable.py:63:13: info[revealed-type] Revealed type: `(...) -> Unknown`
-constructors_callable.py:64:1: error[type-assertion-failure] Type `Class3` does not match asserted type `Unknown`
+constructors_callable.py:63:13: info[revealed-type] Revealed type: `(...) -> Class3`
 constructors_callable.py:73:33: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int`
-constructors_callable.py:77:13: info[revealed-type] Revealed type: `(x: int) -> Unknown`
-constructors_callable.py:78:1: error[type-assertion-failure] Type `int` does not match asserted type `Unknown`
+constructors_callable.py:77:13: info[revealed-type] Revealed type: `(x: int) -> int`
 constructors_callable.py:79:1: error[missing-argument] No argument provided for required parameter `x`
 constructors_callable.py:80:1: error[missing-argument] No argument provided for required parameter `x`
 constructors_callable.py:80:4: error[unknown-argument] Argument `y` does not match any known parameter
 constructors_callable.py:97:13: info[revealed-type] Revealed type: `(...) -> Unknown`
 constructors_callable.py:100:5: error[type-assertion-failure] Type `Never` does not match asserted type `Unknown`
 constructors_callable.py:105:5: error[type-assertion-failure] Type `Never` does not match asserted type `Unknown`
-constructors_callable.py:125:13: info[revealed-type] Revealed type: `() -> Unknown`
-constructors_callable.py:126:1: error[type-assertion-failure] Type `Class6Proxy` does not match asserted type `Unknown`
+constructors_callable.py:125:13: info[revealed-type] Revealed type: `() -> Class6Proxy`
 constructors_callable.py:127:4: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
-constructors_callable.py:142:13: info[revealed-type] Revealed type: `(...) -> Unknown`
-constructors_callable.py:162:5: info[revealed-type] Revealed type: `Overload[(x: int) -> Unknown, (x: str) -> Unknown]`
-constructors_callable.py:164:1: error[type-assertion-failure] Type `Class7[int]` does not match asserted type `Unknown`
-constructors_callable.py:165:1: error[type-assertion-failure] Type `Class7[str]` does not match asserted type `Unknown`
-constructors_callable.py:182:13: info[revealed-type] Revealed type: `(x: list[Unknown], y: list[Unknown]) -> Unknown`
-constructors_callable.py:183:1: error[type-assertion-failure] Type `Class8[str]` does not match asserted type `Unknown`
-constructors_callable.py:193:13: info[revealed-type] Revealed type: `(x: list[T@__init__], y: list[T@__init__]) -> Unknown`
-constructors_callable.py:194:1: error[type-assertion-failure] Type `Class9` does not match asserted type `Unknown`
+constructors_callable.py:141:27: error[invalid-argument-type] Argument to function `accepts_callable` is incorrect: Expected `() -> Any | Class6Any`, found `<class 'Class6Any'>`
+constructors_callable.py:142:13: info[revealed-type] Revealed type: `() -> Any | Class6Any`
+constructors_callable.py:143:1: error[type-assertion-failure] Type `Any` does not match asserted type `Any | Class6Any`
+constructors_callable.py:144:8: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1
+constructors_callable.py:162:5: info[revealed-type] Revealed type: `Overload[(x: int) -> Class7[int] | Class7[str], (x: str) -> Class7[int] | Class7[str]]`
+constructors_callable.py:164:1: error[type-assertion-failure] Type `Class7[int]` does not match asserted type `Class7[int] | Class7[str]`
+constructors_callable.py:165:1: error[type-assertion-failure] Type `Class7[str]` does not match asserted type `Class7[int] | Class7[str]`
+constructors_callable.py:182:13: info[revealed-type] Revealed type: `(x: list[T@Class8], y: list[T@Class8]) -> Class8[T@Class8]`
+constructors_callable.py:183:1: error[type-assertion-failure] Type `Class8[str]` does not match asserted type `Class8[T@Class8]`
+constructors_callable.py:183:16: error[invalid-argument-type] Argument is incorrect: Expected `list[T@Class8]`, found `list[Unknown | str]`
+constructors_callable.py:183:22: error[invalid-argument-type] Argument is incorrect: Expected `list[T@Class8]`, found `list[Unknown | str]`
+constructors_callable.py:184:4: error[invalid-argument-type] Argument is incorrect: Expected `list[T@Class8]`, found `list[Unknown | int]`
+constructors_callable.py:184:9: error[invalid-argument-type] Argument is incorrect: Expected `list[T@Class8]`, found `list[Unknown | str]`
+constructors_callable.py:193:13: info[revealed-type] Revealed type: `(x: list[T@__init__], y: list[T@__init__]) -> Class9`
 constructors_callable.py:194:16: error[invalid-argument-type] Argument is incorrect: Expected `list[T@__init__]`, found `list[Unknown | str]`
 constructors_callable.py:194:22: error[invalid-argument-type] Argument is incorrect: Expected `list[T@__init__]`, found `list[Unknown | str]`
 constructors_callable.py:195:4: error[invalid-argument-type] Argument is incorrect: Expected `list[T@__init__]`, found `list[Unknown | int]`
@@ -304,6 +305,10 @@
 dataclasses_transform_converter.py:25:6: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `T@model_field`
 dataclasses_transform_converter.py:48:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter1() -> int`
 dataclasses_transform_converter.py:49:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter2(*, x: int) -> int`
+dataclasses_transform_converter.py:102:42: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | bytes, /) -> ConverterClass`, found `<class 'ConverterClass'>`
+dataclasses_transform_converter.py:103:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | list[str], /) -> int`, found `Overload[(s: str) -> int, (s: list[str]) -> int]`
+dataclasses_transform_converter.py:104:30: error[invalid-assignment] Object of type `dict[str, str] | dict[bytes, bytes]` is not assignable to `dict[str, str]`
+dataclasses_transform_converter.py:104:42: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Iterable[list[str]] | Iterable[list[bytes]], /) -> dict[str, str] | dict[bytes, bytes]`, found `<class 'dict'>`
 dataclasses_transform_converter.py:107:8: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f1"]`
 dataclasses_transform_converter.py:107:14: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["f2"]`
 dataclasses_transform_converter.py:107:20: error[invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal[b"f3"]`
@@ -333,7 +338,8 @@
 dataclasses_transform_converter.py:121:29: error[invalid-argument-type] Argument is incorrect: Expected `ConverterClass`, found `Literal["f6"]`
 dataclasses_transform_converter.py:121:35: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]`
 dataclasses_transform_converter.py:121:40: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, str]`, found `tuple[tuple[Literal["a"], Literal["1"]], tuple[Literal["b"], Literal["2"]]]`
-dataclasses_transform_converter.py:130:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Literal[1], /) -> Unknown`, found `def converter_simple(s: str) -> int`
+dataclasses_transform_converter.py:130:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | Literal[1], /) -> int`, found `def converter_simple(s: str) -> int`
+dataclasses_transform_converter.py:133:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | int, /) -> int`, found `def converter_simple(s: str) -> int`
 dataclasses_transform_field.py:49:43: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `(type[T@create_model], /) -> type[T@create_model]`
 dataclasses_transform_field.py:64:16: error[unknown-argument] Argument `id` does not match any known parameter
 dataclasses_transform_field.py:75:1: error[missing-argument] No argument provided for required parameter `name`
@@ -357,6 +363,7 @@
 dataclasses_usage.py:51:28: error[invalid-argument-type] Argument is incorrect: Expected `int | float`, found `Literal["price"]`
 dataclasses_usage.py:52:36: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4
 dataclasses_usage.py:83:13: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2
+dataclasses_usage.py:88:14: error[invalid-assignment] Object of type `dataclasses.Field[str | int]` is not assignable to `int`
 dataclasses_usage.py:127:8: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2
 dataclasses_usage.py:130:1: error[missing-argument] No argument provided for required parameter `y` of bound method `__init__`
 dataclasses_usage.py:179:6: error[too-many-positional-arguments] Too many positional arguments to bound method `__init__`: expected 1, got 2
@@ -407,7 +414,7 @@
 enums_member_values.py:96:1: error[type-assertion-failure] Type `int` does not match asserted type `Unknown`
 enums_members.py:82:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `Unknown | ((x) -> Unknown)`
 enums_members.py:82:37: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member
-enums_members.py:83:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `Unknown | ((x: int) -> Unknown)`
+enums_members.py:83:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `Unknown | ((x: int) -> int)`
 enums_members.py:83:37: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member
 enums_members.py:84:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `property`
 enums_members.py:84:35: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member
@@ -1026,4 +1033,4 @@
 typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
 typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie`: Unknown key "title"
 typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions
-Found 1028 diagnostics
+Found 1035 diagnostics

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 20, 2025

mypy_primer results

Changes were detected when running on open source projects
mypy_primer (https://github.com/hauntsaninja/mypy_primer)
+ mypy_primer/model.py:189:40: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `Generator[Path, None, None]`
- Found 4 diagnostics
+ Found 5 diagnostics

more-itertools (https://github.com/more-itertools/more-itertools)
+ more_itertools/more.py:1057:33: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Never]`, found `repeat[tuple[Unknown, ...]]`
+ more_itertools/recipes.py:826:25: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Never]`, found `repeat[list[Unknown]]`
+ more_itertools/recipes.py:1106:18: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `(int | float | complex | _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], int | float | complex | _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], /) -> int | float | complex | ... omitted 3 union elements`, found `Overload[(base: int, exp: int, mod: int) -> int, (base: int, exp: Literal[0], mod: None = None) -> Literal[1], (base: int, exp: Literal[1, 2, 3, 4, 5, ... omitted 20 literals], mod: None = None) -> int, (base: int, exp: Literal[-1, -2, -3, -4, -5, ... omitted 15 literals], mod: None = None) -> int | float, (base: int, exp: int, mod: None = None) -> Any, (base: Literal[1, 2, 3, 4, 5, ... omitted 20 literals], exp: int | float, mod: None = None) -> int | float, (base: Literal[-1, -2, -3, -4, -5, ... omitted 15 literals], exp: int | float, mod: None = None) -> int | float | complex, (base: int | float, exp: int, mod: None = None) -> int | float, (base: int | float, exp: int | float | complex | _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], mod: None = None) -> Any, (base: int | float | complex, exp: int | float | complex | _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], mod: None = None) -> int | float | complex, (base: _SupportsPow2[_E_contra@pow, _T_co@pow], exp: _E_contra@pow, mod: None = None) -> _T_co@pow, (base: _SupportsPow3NoneOnly[_E_contra@pow, _T_co@pow], exp: _E_contra@pow, mod: None = None) -> _T_co@pow, (base: _SupportsPow3[_E_contra@pow, _M_contra@pow, _T_co@pow], exp: _E_contra@pow, mod: _M_contra@pow) -> _T_co@pow, (base: _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], exp: int | float, mod: None = None) -> Any, (base: _SupportsPow2[Any, Any] | _SupportsPow3[Any, Any, Any], exp: int | float | complex, mod: None = None) -> int | float | complex]`
- Found 32 diagnostics
+ Found 35 diagnostics

attrs (https://github.com/python-attrs/attrs)
- tests/test_converters.py:113:24: error[unresolved-attribute] Object of type `Converter[Unknown, Unknown]` has no attribute `__call__`
+ tests/test_converters.py:113:24: error[unresolved-attribute] Object of type `Converter[str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc, int]` has no attribute `__call__`
+ tests/test_validators.py:185:68: error[invalid-argument-type] Argument to function `matches_re` is incorrect: Expected `((str | bytes, str | bytes, int, /) -> Match[str | bytes] | None) | None`, found `Overload[(pattern: str | Pattern[str], string: str, flags: int = Literal[0]) -> Match[str] | None, (pattern: bytes | Pattern[bytes], string: Buffer, flags: int = Literal[0]) -> Match[bytes] | None]`
+ tests/test_validators.py:219:56: error[invalid-argument-type] Argument to function `matches_re` is incorrect: Expected `((str | bytes, str | bytes, int, /) -> Match[str | bytes] | None) | None`, found `Overload[(pattern: str | Pattern[str], string: str, flags: int = Literal[0]) -> Match[str] | None, (pattern: bytes | Pattern[bytes], string: Buffer, flags: int = Literal[0]) -> Match[bytes] | None]`
+ tests/test_validators.py:297:18: error[no-matching-overload] No overload of function `attrib` matches arguments
+ tests/test_validators.py:544:24: error[invalid-argument-type] Argument is incorrect: Expected `tuple[Unknown, ...]`, found `list[Unknown | int]`
+ tests/test_validators.py:555:24: error[invalid-argument-type] Argument is incorrect: Expected `tuple[Unknown, ...]`, found `list[Unknown | int | str]`
+ tests/test_validators.py:700:24: error[invalid-argument-type] Argument is incorrect: Expected `dict[Unknown, Unknown]`, found `None`
+ tests/test_validators.py:787:21: error[invalid-argument-type] Argument to function `and_` is incorrect: Expected `(Any, Attribute[int], int, /) -> Any`, found `(Any, Attribute[Literal[10]], Literal[10], /) -> Any`
- tests/test_validators.py:1143:64: error[unresolved-attribute] Object of type `(Any, Attribute[Unknown], Unknown, /) -> Any` has no attribute `exc_types`
+ tests/test_validators.py:1143:64: error[unresolved-attribute] Object of type `(Any, Attribute[Unknown | int], Unknown | int, /) -> Any` has no attribute `exc_types`
+ tests/test_validators.py:1257:20: error[invalid-argument-type] Argument is incorrect: Expected `int | float`, found `Literal["spam"]`
+ typing-examples/mypy.py:250:64: error[invalid-argument-type] Argument to function `matches_re` is incorrect: Expected `((str | bytes, str | bytes, int, /) -> Match[str | bytes] | None) | None`, found `Overload[(pattern: str | Pattern[str], string: str, flags: int = Literal[0]) -> Match[str] | None, (pattern: bytes | Pattern[bytes], string: Buffer, flags: int = Literal[0]) -> Match[bytes] | None]`
- Found 608 diagnostics
+ Found 617 diagnostics

anyio (https://github.com/agronholm/anyio)
+ src/anyio/_backends/_asyncio.py:1554:40: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `StreamProtocol`, found `Unknown | BaseProtocol`
+ src/anyio/_backends/_asyncio.py:2564:50: error[invalid-assignment] Object of type `Future[T_Retval@run_async_from_thread] | Future[_T@run_coroutine_threadsafe]` is not assignable to `Future[T_Retval@run_async_from_thread]`
+ src/anyio/_backends/_asyncio.py:2702:12: error[unresolved-attribute] Object of type `BaseProtocol` has no attribute `exception`
+ src/anyio/_backends/_asyncio.py:2704:19: error[unresolved-attribute] Object of type `BaseProtocol` has no attribute `exception`
+ src/anyio/_backends/_asyncio.py:2707:41: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `DatagramProtocol`, found `BaseProtocol`
+ src/anyio/_backends/_asyncio.py:2709:50: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `DatagramProtocol`, found `BaseProtocol`
+ src/anyio/_backends/_asyncio.py:2928:40: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `StreamProtocol`, found `BaseProtocol`
+ src/anyio/_backends/_asyncio.py:2939:37: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `DatagramProtocol`, found `BaseProtocol`
+ src/anyio/_backends/_asyncio.py:2946:46: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `DatagramProtocol`, found `BaseProtocol`
+ src/anyio/_core/_fileio.py:190:22: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `IO[str]`, found `TextIOWrapper[_WrappedBuffer] | BinaryIO | IO[Any]`
+ src/anyio/_core/_fileio.py:627:26: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `IO[str]`, found `TextIOWrapper[_WrappedBuffer] | BinaryIO | IO[Any]`
- Found 90 diagnostics
+ Found 101 diagnostics

pytest-robotframework (https://github.com/detachhead/pytest-robotframework)
+ pytest_robotframework/_internal/robot/utils.py:248:12: error[invalid-return-type] Return type does not match returned value: expected `dict[str, object]`, found `Mapping[str, object]`
- Found 173 diagnostics
+ Found 174 diagnostics

spack (https://github.com/spack/spack)
+ lib/spack/spack/binary_distribution.py:2044:38: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `str | PathLike[str]`, found `Sized`
+ lib/spack/spack/detection/common.py:363:25: error[no-matching-overload] No overload of function `__new__` matches arguments
+ lib/spack/spack/detection/common.py:377:20: error[no-matching-overload] No overload of function `__new__` matches arguments
+ lib/spack/spack/llnl/util/filesystem.py:1668:35: error[invalid-argument-type] Argument to function `exists` is incorrect: Expected `int | str | bytes | PathLike[str] | PathLike[bytes]`, found `Unknown | Sized`
+ lib/spack/spack/llnl/util/filesystem.py:1672:24: error[no-matching-overload] No overload of function `basename` matches arguments
+ lib/spack/spack/llnl/util/filesystem.py:1674:25: error[invalid-argument-type] Argument to function `move` is incorrect: Expected `str | PathLike[str]`, found `Unknown | Sized`
+ lib/spack/spack/relocate.py:61:46: error[invalid-argument-type] Argument to function `escape` is incorrect: Argument type `Sized` does not satisfy constraints (`str`, `bytes`) of type variable `AnyStr`
+ lib/spack/spack/relocate.py:71:59: error[invalid-argument-type] Argument to function `escape` is incorrect: Argument type `Sized` does not satisfy constraints (`str`, `bytes`) of type variable `AnyStr`
+ lib/spack/spack/relocate.py:78:56: error[invalid-argument-type] Argument to function `escape` is incorrect: Argument type `Sized` does not satisfy constraints (`str`, `bytes`) of type variable `AnyStr`
+ lib/spack/spack/spec_parser.py:501:21: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Iterable[Spec]`, found `Iterator[Spec | None]`
+ lib/spack/spack/vendor/jinja2/environment.py:1494:20: error[invalid-return-type] Return type does not match returned value: expected `list[tuple[int, int]]`, found `list[tuple[int, ...] | Unknown]`
+ lib/spack/spack/verify_libraries.py:155:29: error[no-matching-overload] No overload of function `join` matches arguments
+ lib/spack/spack/verify_libraries.py:164:46: error[invalid-argument-type] Argument to function `candidate_matches` is incorrect: Expected `bytes`, found `bytes | Unknown | str | PathLike[str] | PathLike[bytes]`
+ lib/spack/spack/verify_libraries.py:165:17: error[invalid-assignment] Invalid subscript assignment with key of type `bytes | Unknown | str | PathLike[str] | PathLike[bytes]` and value of type `bytes | Unknown | str | PathLike[str] | PathLike[bytes]` on object of type `dict[bytes, bytes]`
+ lib/spack/spack/verify_libraries.py:170:57: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `list[bytes]`, found `list[bytes | Unknown | str | PathLike[str] | PathLike[bytes]]`
+ lib/spack/spack/verify_libraries.py:170:69: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `list[bytes]`, found `list[bytes | Unknown | str | PathLike[str] | PathLike[bytes]]`
- Found 4281 diagnostics
+ Found 4297 diagnostics

asynq (https://github.com/quora/asynq)
- asynq/tests/test_debug.py:179:15: error[unresolved-attribute] Object of type `AsyncDecorator[Any, ()]` has no attribute `__name__`
+ asynq/tests/test_debug.py:179:15: error[unresolved-attribute] Object of type `AsyncDecorator[Any, (...)]` has no attribute `__name__`
- asynq/tests/test_debug.py:181:9: error[unresolved-attribute] Object of type `AsyncDecorator[Any, ()]` has no attribute `__name__`
+ asynq/tests/test_debug.py:181:9: error[unresolved-attribute] Object of type `AsyncDecorator[Any, (...)]` has no attribute `__name__`
- asynq/tests/test_mock.py:271:13: error[unresolved-attribute] Unresolved attribute `cant_set_attribute` on type `bound method AsyncDecorator[Any, ()].asynq(...) -> AsyncTask[Any]`.
+ asynq/tests/test_mock.py:271:13: error[unresolved-attribute] Unresolved attribute `cant_set_attribute` on type `bound method AsyncDecorator[Any, (...)].asynq(...) -> AsyncTask[Any]`.
- asynq/tests/test_tools.py:440:5: error[unresolved-attribute] Object of type `AsyncDecorator[Any, ()]` has no attribute `dirty`
+ asynq/tests/test_tools.py:440:5: error[unresolved-attribute] Object of type `AsyncDecorator[Any, (...)]` has no attribute `dirty`
- asynq/tests/test_tools.py:450:5: error[unresolved-attribute] Object of type `AsyncDecorator[Any, ()]` has no attribute `dirty`
+ asynq/tests/test_tools.py:450:5: error[unresolved-attribute] Object of type `AsyncDecorator[Any, (...)]` has no attribute `dirty`
- asynq/tests/test_typing.py:16:9: error[type-assertion-failure] Type `FutureBase[str]` does not match asserted type `FutureBase[Unknown]`
- asynq/tests/typing_example/param_spec.py:15:21: error[invalid-argument-type] Argument to bound method `asyncio` is incorrect: Expected `int`, found `Literal["1"]`
- asynq/tests/typing_example/param_spec.py:15:31: error[invalid-argument-type] Argument to bound method `asyncio` is incorrect: Expected `str`, found `Literal[2]`
- Found 218 diagnostics
+ Found 215 diagnostics

websockets (https://github.com/aaugustin/websockets)
+ src/websockets/asyncio/client.py:471:16: error[invalid-return-type] Return type does not match returned value: expected `ClientConnection`, found `BaseProtocol`
+ src/websockets/asyncio/client.py:799:15: error[unresolved-attribute] Object of type `BaseProtocol` has no attribute `response`
+ src/websockets/cli.py:119:43: error[unresolved-attribute] Object of type `BaseProtocol` has no attribute `messages`
- Found 41 diagnostics
+ Found 44 diagnostics

pip (https://github.com/pypa/pip)
+ src/pip/_internal/operations/check.py:99:37: error[no-matching-overload] No overload of function `sorted` matches arguments
+ src/pip/_internal/operations/check.py:101:41: error[no-matching-overload] No overload of function `sorted` matches arguments
+ src/pip/_internal/req/req_uninstall.py:101:13: error[unresolved-attribute] Object of type `Sized` has no attribute `startswith`
+ src/pip/_internal/req/req_uninstall.py:102:17: error[non-subscriptable] Cannot subscript object of type `Sized` with no `__getitem__` method
+ src/pip/_internal/req/req_uninstall.py:106:29: error[invalid-argument-type] Argument to bound method `add` is incorrect: Expected `str`, found `Sized`
+ src/pip/_internal/req/req_uninstall.py:125:16: error[no-matching-overload] No overload of function `normcase` matches arguments
+ src/pip/_internal/req/req_uninstall.py:132:42: error[invalid-argument-type] Argument to function `norm_join` is incorrect: Expected `str`, found `Sized | Unknown`
+ src/pip/_internal/req/req_uninstall.py:133:40: error[invalid-argument-type] Argument to function `norm_join` is incorrect: Expected `str`, found `Sized | Unknown`
+ src/pip/_internal/req/req_uninstall.py:139:27: error[unsupported-operator] Operator `+` is not supported between objects of type `Sized | Unknown` and `LiteralString`
+ src/pip/_vendor/rich/text.py:1285:16: error[invalid-return-type] Return type does not match returned value: expected `int`, found `(SupportsIndex & ~AlwaysFalsy) | (Unknown & ~AlwaysFalsy)`
- Found 603 diagnostics
+ Found 613 diagnostics

jinja (https://github.com/pallets/jinja)
+ src/jinja2/environment.py:1496:20: error[invalid-return-type] Return type does not match returned value: expected `list[tuple[int, int]]`, found `list[tuple[int, ...] | Unknown]`
- Found 181 diagnostics
+ Found 182 diagnostics

isort (https://github.com/pycqa/isort)
+ isort/api.py:636:50: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `Iterator[str | Path]`
- Found 32 diagnostics
+ Found 33 diagnostics

werkzeug (https://github.com/pallets/werkzeug)
+ src/werkzeug/_reloader.py:149:22: error[not-iterable] Object of type `Sized` is not iterable
+ src/werkzeug/routing/rules.py:510:43: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `Mapping[str, Any] & ~AlwaysFalsy`
- tests/test_datastructures.py:582:41: error[invalid-argument-type] Argument to bound method `get` is incorrect: Expected `(Unknown, /) -> Literal[-1]`, found `<class 'int'>`
- tests/test_datastructures.py:583:41: error[invalid-argument-type] Argument to bound method `get` is incorrect: Expected `(Unknown, /) -> Literal[-1]`, found `<class 'int'>`
+ tests/test_wrappers.py:729:5: error[invalid-assignment] Invalid assignment to data descriptor attribute `content_length` on type `Response` with custom `__set__` method
- Found 385 diagnostics
+ Found 386 diagnostics

paasta (https://github.com/yelp/paasta)
- paasta_tools/setup_prometheus_adapter_config.py:946:12: error[invalid-return-type] Return type does not match returned value: expected `PrometheusAdapterConfig`, found `dict[Unknown | str, Unknown | list[PrometheusAdapterRule]]`
+ paasta_tools/setup_prometheus_adapter_config.py:946:12: error[invalid-return-type] Return type does not match returned value: expected `PrometheusAdapterConfig`, found `dict[Unknown | str, Unknown | list[PrometheusAdapterRule | Unknown]]`
+ paasta_tools/utils.py:2926:20: error[unresolved-attribute] Object of type `str` has no attribute `decode`
- Found 1105 diagnostics
+ Found 1106 diagnostics

pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/_py/path.py:318:67: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
+ src/_pytest/_py/path.py:759:17: error[invalid-argument-type] Argument to bound method `checked_call` is incorrect: Expected `Overload[(file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["r+", "+r", "rt+", "r+t", "+rt", ... omitted 48 literals] = Literal["r"], buffering: int = Literal[-1], encoding: str | None = None, errors: str | None = None, newline: str | None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 33 literals], buffering: Literal[0], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 19 literals], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["wb", "bw", "ab", "ba", "xb", "bx"], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb", "br", "rbU", "rUb", "Urb", ... omitted 3 literals], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 33 literals], buffering: int = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: str, buffering: int = Literal[-1], encoding: str | None = None, errors: str | None = None, newline: str | None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown]`, found `Unknown | str`
+ src/_pytest/_py/path.py:763:41: error[invalid-argument-type] Argument to bound method `checked_call` is incorrect: Expected `Overload[(file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["r+", "+r", "rt+", "r+t", "+rt", ... omitted 48 literals] = Literal["r"], buffering: int = Literal[-1], encoding: str | None = None, errors: str | None = None, newline: str | None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 33 literals], buffering: Literal[0], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 19 literals], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["wb", "bw", "ab", "ba", "xb", "bx"], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb", "br", "rbU", "rUb", "Urb", ... omitted 3 literals], buffering: Literal[-1, 1] = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: Literal["rb+", "r+b", "+rb", "br+", "b+r", ... omitted 33 literals], buffering: int = Literal[-1], encoding: None = None, errors: None = None, newline: None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown, (file: int | str | bytes | PathLike[str] | PathLike[bytes], mode: str, buffering: int = Literal[-1], encoding: str | None = None, errors: str | None = None, newline: str | None = None, closefd: bool = Literal[True], opener: ((str, int, /) -> int) | None = None) -> Unknown]`, found `Unknown | str`
+ src/_pytest/_py/path.py:808:52: error[invalid-argument-type] Argument to bound method `checked_call` is incorrect: Expected `Overload[(path: str | PathLike[str] | None = None) -> Unknown, (path: bytes | PathLike[bytes]) -> Unknown, (path: int) -> Unknown]`, found `Unknown | str`
+ src/_pytest/_py/path.py:817:48: error[invalid-argument-type] Argument to bound method `checked_call` is incorrect: Expected `Overload[(path: str | PathLike[str] | None = None) -> Unknown, (path: bytes | PathLike[bytes]) -> Unknown, (path: int) -> Unknown]`, found `Unknown | str`
- Found 440 diagnostics
+ Found 443 diagnostics

pylint (https://github.com/pycqa/pylint)
+ pylint/checkers/base/name_checker/checker.py:370:59: error[invalid-argument-type] Argument to bound method `_raise_name_warning` is incorrect: Expected `str`, found `Unknown | str | Confidence`
+ pylint/checkers/base/name_checker/checker.py:370:59: error[invalid-argument-type] Argument to bound method `_raise_name_warning` is incorrect: Expected `str`, found `Unknown | str | Confidence`
+ pylint/checkers/base/name_checker/checker.py:370:59: error[invalid-argument-type] Argument to bound method `_raise_name_warning` is incorrect: Expected `Confidence`, found `Unknown | str | Confidence`
+ pylint/checkers/base/name_checker/checker.py:370:59: error[invalid-argument-type] Argument to bound method `_raise_name_warning` is incorrect: Expected `str`, found `Unknown | str | Confidence`
- Found 212 diagnostics
+ Found 216 diagnostics

starlette (https://github.com/encode/starlette)
+ starlette/endpoints.py:42:15: error[call-non-callable] Object of type `CoroutineType[Any, Any, Response]` is not callable
- Found 209 diagnostics
+ Found 210 diagnostics

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/utilities/lexicographic_sort_schema.py:115:32: error[invalid-argument-type] Argument to function `replace_named_type` is incorrect: Expected `GraphQLNamedType`, found `GraphQLNamedType | GraphQLDirective | DirectiveLocation`
+ src/graphql/utilities/lexicographic_sort_schema.py:170:37: error[invalid-argument-type] Argument to function `sort_named_type` is incorrect: Expected `GraphQLNamedType`, found `GraphQLNamedType | GraphQLDirective | DirectiveLocation`
+ src/graphql/utilities/lexicographic_sort_schema.py:177:28: error[invalid-argument-type] Argument to function `sort_directive` is incorrect: Expected `GraphQLDirective`, found `GraphQLDirective | GraphQLNamedType | DirectiveLocation`
+ src/graphql/validation/rules/possible_type_extensions.py:48:29: error[invalid-assignment] Object of type `str | bytes` is not assignable to `str | None`
- tests/pyutils/test_async_reduce.py:50:22: error[invalid-await] `Awaitable[Literal["foo"]] | Literal["foo"]` is not awaitable
+ tests/pyutils/test_async_reduce.py:50:22: error[invalid-await] `Awaitable[Unknown | Literal["foo"]] | Unknown | Literal["foo"]` is not awaitable
- tests/validation/test_no_deprecated.py:23:8: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- Found 427 diagnostics
+ Found 430 diagnostics

sockeye (https://github.com/awslabs/sockeye)
- sockeye/test_utils.py:147:1: error[invalid-argument-type] Argument to function `contextmanager` is incorrect: Expected `(prefix: str, train_line_count: int, train_line_count_empty: int, train_max_length: int, dev_line_count: int, dev_max_length: int, test_line_count: int, test_line_count_empty: int, test_max_length: int, sort_target: bool = Literal[False], seed_train: int = Literal[13], seed_dev: int = Literal[13], source_text_prefix_token: str = Literal[""], with_n_source_factors: int = Literal[0], with_n_target_factors: int = Literal[0]) -> Iterator[Unknown]`, found `def tmp_digits_dataset(prefix: str, train_line_count: int, train_line_count_empty: int, train_max_length: int, dev_line_count: int, dev_max_length: int, test_line_count: int, test_line_count_empty: int, test_max_length: int, sort_target: bool = Literal[False], seed_train: int = Literal[13], seed_dev: int = Literal[13], source_text_prefix_token: str = Literal[""], with_n_source_factors: int = Literal[0], with_n_target_factors: int = Literal[0]) -> dict[str, Any]`
+ sockeye/test_utils.py:147:1: error[invalid-argument-type] Argument to function `contextmanager` is incorrect: Expected `(...) -> Iterator[Unknown]`, found `def tmp_digits_dataset(prefix: str, train_line_count: int, train_line_count_empty: int, train_max_length: int, dev_line_count: int, dev_max_length: int, test_line_count: int, test_line_count_empty: int, test_max_length: int, sort_target: bool = Literal[False], seed_train: int = Literal[13], seed_dev: int = Literal[13], source_text_prefix_token: str = Literal[""], with_n_source_factors: int = Literal[0], with_n_target_factors: int = Literal[0]) -> dict[str, Any]`

porcupine (https://github.com/Akuli/porcupine)
+ porcupine/pluginmanager.py:132:49: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Never]`, found `Unknown | str`
- Found 17 diagnostics
+ Found 18 diagnostics

scrapy (https://github.com/scrapy/scrapy)
+ tests/test_downloadermiddleware_httpcompression.py:419:16: error[unresolved-attribute] Object of type `Response` has no attribute `encoding`
+ tests/test_downloadermiddleware_retry.py:37:16: warning[possibly-missing-attribute] Attribute `priority` may be missing on object of type `Unknown | Request | Response`
- Found 1792 diagnostics
+ Found 1794 diagnostics

rich (https://github.com/Textualize/rich)
- examples/table_movie.py:62:1: error[invalid-argument-type] Argument to function `contextmanager` is incorrect: Expected `(length: int = Literal[1]) -> Iterator[Unknown]`, found `def beat(length: int = Literal[1]) -> None`
+ examples/table_movie.py:62:1: error[invalid-argument-type] Argument to function `contextmanager` is incorrect: Expected `(...) -> Iterator[Unknown]`, found `def beat(length: int = Literal[1]) -> None`
+ rich/text.py:1285:16: error[invalid-return-type] Return type does not match returned value: expected `int`, found `(SupportsIndex & ~AlwaysFalsy) | (Unknown & ~AlwaysFalsy)`
- Found 348 diagnostics
+ Found 349 diagnostics

dulwich (https://github.com/dulwich/dulwich)
- dulwich/diff_tree.py:421:26: error[invalid-argument-type] Argument to function `_all_eq` is incorrect: Expected `(Unknown, /) -> Literal["delete"]`, found `def change_type(c: TreeChange) -> str`
+ dulwich/porcelain/__init__.py:643:16: error[invalid-return-type] Return type does not match returned value: expected `AbstractContextManager[T@open_repo | Repo, bool | None]`, found `_GeneratorContextManager[T@_noop_context_manager, None, None]`
+ dulwich/porcelain/__init__.py:697:16: error[invalid-return-type] Return type does not match returned value: expected `AbstractContextManager[T@open_repo_closing | Repo, bool | None]`, found `_GeneratorContextManager[T@_noop_context_manager, None, None]`
+ dulwich/walk.py:474:35: error[invalid-argument-type] Argument to bound method `_reorder` is incorrect: Expected `Iterator[WalkEntry]`, found `Iterator[WalkEntry | None]`
- Found 226 diagnostics
+ Found 228 diagnostics

ppb-vector (https://github.com/ppb/ppb-vector)
+ ppb_vector/__init__.py:468:13: error[invalid-argument-type] Argument to function `max` is incorrect: Argument type `Unknown | Vector | Sequence[SupportsFloat]` does not satisfy upper bound `SupportsDunderLT[Any] | SupportsDunderGT[Any]` of type variable `SupportsRichComparisonT`
+ ppb_vector/__init__.py:468:13: error[invalid-argument-type] Argument to function `max` is incorrect: Expected `int | float`, found `Unknown | Vector | Sequence[SupportsFloat]`
- Found 21 diagnostics
+ Found 23 diagnostics

poetry (https://github.com/python-poetry/poetry)
- src/poetry/inspection/info.py:466:9: error[invalid-assignment] Object of type `Literal["directory"]` is not assignable to attribute `_source_type` on type `PackageInfo | None | Unknown`
+ src/poetry/inspection/info.py:466:9: error[invalid-assignment] Object of type `Literal["directory"]` is not assignable to attribute `_source_type` on type `PackageInfo | None`
- src/poetry/inspection/info.py:467:9: error[invalid-assignment] Object of type `str` is not assignable to attribute `_source_url` on type `PackageInfo | None | Unknown`
+ src/poetry/inspection/info.py:467:9: error[invalid-assignment] Object of type `str` is not assignable to attribute `_source_url` on type `PackageInfo | None`
- src/poetry/inspection/info.py:469:16: error[invalid-return-type] Return type does not match returned value: expected `PackageInfo`, found `PackageInfo | None | Unknown`
+ src/poetry/inspection/info.py:469:16: error[invalid-return-type] Return type does not match returned value: expected `PackageInfo`, found `PackageInfo | None`
- tests/utils/env/test_env.py:518:34: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
+ tests/utils/test_password_manager.py:325:5: error[unresolved-attribute] Object of type `bound method PoetryKeyring.get_password(name: str, username: str) -> str | None` has no attribute `assert_not_called`
+ tests/utils/test_password_manager.py:343:5: error[unresolved-attribute] Object of type `bound method PoetryKeyring.get_password(name: str, username: str) -> str | None` has no attribute `assert_not_called`
- Found 969 diagnostics
+ Found 970 diagnostics

cki-lib (https://gitlab.com/cki-project/cki-lib)
- tests/kcidb/test_kcidb_file.py:83:14: error[missing-argument] No argument provided for required parameter `kcidb_data`
+ tests/test_gitlab.py:231:35: warning[possibly-missing-attribute] Attribute `attributes` may be missing on object of type `Unknown | None`
+ tests/test_gitlab.py:236:35: warning[possibly-missing-attribute] Attribute `attributes` may be missing on object of type `Unknown | None`
+ tests/test_gitlab.py:241:38: warning[possibly-missing-attribute] Attribute `attributes` may be missing on object of type `Unknown | None`
- Found 259 diagnostics
+ Found 261 diagnostics

schemathesis (https://github.com/schemathesis/schemathesis)
+ src/schemathesis/openapi/checks.py:118:34: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `Unknown | list[str | int]`
+ src/schemathesis/specs/openapi/checks.py:491:42: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `set[Unknown | int] | set[int]`
- Found 270 diagnostics
+ Found 272 diagnostics

tornado (https://github.com/tornadoweb/tornado)
+ tornado/gen.py:239:45: error[invalid-argument-type] Argument to bound method `run` is incorrect: Expected `Overload[(i: SupportsNext[_T@next], /) -> Unknown, (i: SupportsNext[_T@next], default: _VT@next, /) -> Unknown]`, found `(Generator[Any, Any, _T@coroutine] & Generator[object, None, None]) | (_T@coroutine & Generator[object, None, None])`
+ tornado/gen.py:255:46: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Generator[None | Awaitable[Unknown] | list[Awaitable[Unknown]] | dict[Any, Awaitable[Unknown]] | Future[Unknown], Any, Unknown]`, found `(Generator[Any, Any, _T@coroutine] & Generator[object, None, None]) | (_T@coroutine & Generator[object, None, None])`
+ tornado/gen.py:255:62: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `None | Awaitable[Unknown] | list[Awaitable[Unknown]] | dict[Any, Awaitable[Unknown]] | Future[Unknown]`, found `_T@next | _VT@next | _T@next`
+ tornado/gen.py:532:47: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[None | Awaitable[Unknown] | list[Awaitable[Unknown]] | dict[Any, Awaitable[Unknown]] | Future[Unknown]]`, found `dict_values[object, object] | (Sequence[None | Awaitable[Unknown] | list[Awaitable[Unknown]] | dict[Any, Awaitable[Unknown]] | Future[Unknown]] & ~Top[dict[Unknown, Unknown]]) | (Mapping[Any, None | Awaitable[Unknown] | list[Awaitable[Unknown]] | dict[Any, Awaitable[Unknown]] | Future[Unknown]] & ~Top[dict[Unknown, Unknown]])`
+ tornado/test/simple_httpclient_test.py:536:27: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `(str | bytes | None | Unknown, /) -> str | bytes | None | Unknown`, found `Overload[(value: str) -> str, (value: bytes) -> str, (value: None) -> None]`
+ tornado/test/simple_httpclient_test.py:557:27: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `(str | bytes | None | Unknown, /) -> str | bytes | None | Unknown`, found `Overload[(value: str) -> str, (value: bytes) -> str, (value: None) -> None]`
- tornado/wsgi.py:180:39: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- Found 323 diagnostics
+ Found 328 diagnostics

mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/addons/save.py:101:45: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `BinaryIO`, found `IO[Any]`
+ mitmproxy/dns.py:303:17: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `chain[Question | ResourceRecord]`
+ mitmproxy/proxy/mode_specs.py:217:17: error[invalid-assignment] Property `address` defined in `Self@__post_init__` is read-only
+ mitmproxy/proxy/mode_specs.py:235:9: error[invalid-assignment] Property `scheme` defined in `Self@__post_init__` is read-only
+ mitmproxy/proxy/mode_specs.py:235:22: error[invalid-assignment] Property `address` defined in `Self@__post_init__` is read-only
- test/mitmproxy/addons/test_clientplayback.py:18:1: error[invalid-argument-type] Argument to function `asynccontextmanager` is incorrect: Expected `(handle_conn, **server_args) -> AsyncIterator[Unknown]`, found `def tcp_server(handle_conn, **server_args) -> tuple[str, int]`
+ test/mitmproxy/addons/test_clientplayback.py:18:1: error[invalid-argument-type] Argument to function `asynccontextmanager` is incorrect: Expected `(...) -> AsyncIterator[Unknown]`, found `def tcp_server(handle_conn, **server_args) -> tuple[str, int]`
- test/mitmproxy/addons/test_next_layer.py:492:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[Unknown]]`
+ test/mitmproxy/addons/test_next_layer.py:492:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[HttpLayer] | partial[HttpStream]]`
- test/mitmproxy/addons/test_next_layer.py:524:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[Unknown]]`
+ test/mitmproxy/addons/test_next_layer.py:524:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[HttpLayer] | partial[HttpStream]]`
- test/mitmproxy/addons/test_next_layer.py:536:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[Unknown]]`
+ test/mitmproxy/addons/test_next_layer.py:536:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[HttpLayer] | partial[HttpStream]]`
- test/mitmproxy/addons/test_next_layer.py:554:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[Unknown]]`
+ test/mitmproxy/addons/test_next_layer.py:554:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[HttpLayer] | partial[HttpStream]]`
- test/mitmproxy/addons/test_next_layer.py:575:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[Unknown]]`
+ test/mitmproxy/addons/test_next_layer.py:575:13: error[invalid-argument-type] Argument is incorrect: Expected `list[type[Layer]]`, found `list[type[Layer] | partial[HttpLayer] | partial[HttpStream]]`
- test/mitmproxy/addons/test_proxyserver.py:337:1: error[invalid-argument-type] Argument to function `asynccontextmanager` is incorrect: Expected `(handle_datagram: (DatagramTransport, bytes, tuple[str, int], /) -> None) -> AsyncIterator[Unknown]`, found `def udp_server(handle_datagram: (DatagramTransport, bytes, tuple[str, int], /) -> None) -> tuple[str, int]`
+ test/mitmproxy/addons/test_proxyserver.py:337:1: error[invalid-argument-type] Argument to function `asynccontextmanager` is incorrect: Expected `(...) -> AsyncIterator[Unknown]`, found `def udp_server(handle_datagram: (DatagramTransport, bytes, tuple[str, int], /) -> None) -> tuple[str, int]`
+ test/mitmproxy/addons/test_proxyserver.py:491:9: error[unresolved-attribute] Object of type `BaseProtocol` has no attribute `close`
+ test/mitmproxy/tools/console/test_quickhelp.py:63:19: error[invalid-argument-type] Argument to bound method `unbind` is incorrect: Expected `Binding`, found `Binding | None`
- Found 2135 diagnostics
+ Found 2142 diagnostics

nox (https://github.com/wntrblm/nox)
+ nox/_option_set.py:63:66: error[invalid-assignment] Object of type `dataclasses.Field[Unknown | str | None]` is not assignable to `None | Literal["auto", "never", "always"]`
+ nox/registry.py:91:16: error[invalid-return-type] Return type does not match returned value: expected `Func | ((((...) -> Any) | Func, /) -> Func)`, found `partial[Func | ((((...) -> Any) | Func, /) -> Func)]`
- Found 23 diagnostics
+ Found 25 diagnostics

Expression (https://github.com/cognitedata/Expression)
+ README.py:637:11: error[invalid-assignment] Object of type `Unknown | range | int` is not assignable to `int`
+ expression/collections/array.py:376:16: error[invalid-return-type] Return type does not match returned value: expected `int`, found `Unknown | Self@sum_by | int`
+ expression/collections/array.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `TypedArray[_TSource@TypedArray]`, found `TypedArray[_TSource@TypedArray] | Unknown | _TState@unfold`
+ expression/collections/array.py:655:23: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Iterable[_TSource@unfold] | None`, found `Unknown | _TState@unfold`
+ expression/collections/block.py:274:16: error[invalid-return-type] Return type does not match returned value: expected `_TResult@sum_by`, found `Unknown | Block[_TSourceSum@sum_by]`
+ expression/collections/block.py:486:16: error[invalid-return-type] Return type does not match returned value: expected `Block[_TSource@Block]`, found `Block[_TSource@Block] | Unknown | _TState@unfold`
- expression/collections/block.py:940:30: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
+ expression/collections/block.py:595:12: error[invalid-return-type] Return type does not match returned value: expected `Block[_TSource@concat]`, found `Block[_TSource@concat] | Unknown | Iterable[Block[_TSource@concat]]`
+ expression/collections/block.py:1014:18: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Iterable[_TSource@unfold]`, found `Unknown | _TState@unfold`
+ expression/collections/map.py:252:24: error[no-matching-overload] No overload of bound method `join` matches arguments
+ expression/collections/seq.py:299:16: error[invalid-return-type] Return type does not match returned value: expected `_TSupportsSum@sum_by`, found `Unknown | Self@sum_by`
+ expression/collections/seq.py:353:16: error[invalid-return-type] Return type does not match returned value: expected `Iterable[_TSource@Seq]`, found `Unknown | _TState@unfold | Iterable[_TSource@Seq]`
+ expression/collections/seq.py:446:12: error[invalid-return-type] Return type does not match returned value: expected `Iterable[_TResult@choose]`, found `Unknown | Iterable[_TSource@choose] | Iterable[_TResult@choose]`
+ expression/core/result.py:121:24: error[invalid-return-type] Return type does not match returned value: expected `Result[_TResult@map2, _TErrorOut@Result]`, found `Result[_TResult@map2 | Unknown | _TOther@map2, _TErrorOut@Result]`
- expression/extra/option/pipeline.py:91:19: error[invalid-argument-type] Argument to function `reduce` is incorrect: Expected `(def Some[_T1](value: _T1@Some) -> Option[_T1@Some], (Any, /) -> Option[Any], /) -> def Some[_T1](value: _T1@Some) -> Option[_T1@Some]`, found `def reducer(acc: (Any, /) -> Option[Any], fn: (Any, /) -> Option[Any]) -> (Any, /) -> Option[Any]`
+ expression/effect/result.py:33:16: error[invalid-return-type] Return type does not match returned value: expected `Result[_TResult@bind, _TError@ResultBuilder]`, found `Result[_TResult@bind, _TError@ResultBuilder] | Unknown | Result[_TSource@ResultBuilder, _TError@ResultBuilder]`
+ expression/extra/parser.py:413:13: error[invalid-argument-type] Argument to function `pipe` is incorrect: Expected `(Unknown | Block[str] | str | ... omitted 4 union elements, /) -> int`, found `<class 'int'>`
+ expression/extra/parser.py:441:13: error[invalid-argument-type] Argument to function `pipe` is incorrect: Expected `(Unknown | Block[str] | str | ... omitted 3 union elements, /) -> float`, found `<class 'float'>`
- expression/extra/result/pipeline.py:96:19: error[invalid-argument-type] Argument to function `reduce` is incorrect: Expected `(def Ok[_TSource](value: _TSource@Ok) -> Result[_TSource@Ok, Any], (Any, /) -> Result[Any, Any], /) -> def Ok[_TSource](value: _TSource@Ok) -> Result[_TSource@Ok, Any]`, found `def reducer(acc: (Any, /) -> Result[Any, Any], fn: (Any, /) -> Result[Any, Any]) -> (Any, /) -> Result[Any, Any]`
+ tests/test_array.py:47:27: error[invalid-assignment] Object of type `TypedArray[str] | Unknown | TypedArray[Unknown | int]` is not assignable to `TypedArray[str]`
+ tests/test_array.py:58:35: error[invalid-assignment] Object of type `TypedArray[uint8] | Unknown | TypedArray[Unknown | str]` is not assignable to `TypedArray[uint8]`
+ tests/test_array.py:69:36: error[invalid-assignment] Object of type `TypedArray[uint16] | Unknown | TypedArray[Unknown | str]` is not assignable to `TypedArray[uint16]`
+ tests/test_array.py:80:36: error[invalid-assignment] Object of type `TypedArray[uint32] | Unknown | TypedArray[Unknown | str]` is not assignable to `TypedArray[uint32]`
+ tests/test_array.py:91:37: error[invalid-assignment] Object of type `TypedArray[float32] | Unknown | TypedArray[Unknown | str]` is not assignable to `TypedArray[float32]`
+ tests/test_array.py:99:35: error[invalid-assignment] Object of type `TypedArray[int16] | Unknown | TypedArray[Unknown | int]` is not assignable to `TypedArray[int16]`
- tests/test_array.py:307:38: error[invalid-argument-type] Argument to function `reduce` is incorrect: Expected `(Literal[0], int, /) -> Literal[0]`, found `def folder(x: int, y: int) -> int`
- tests/test_array.py:322:49: error[invalid-argument-type] Argument to function `unfold` is incorrect: Expected `(Literal[0], /) -> Option[tuple[Unknown, Literal[0]]]`, found `def unfolder(state: int) -> Option[tuple[int, int]]`
- tests/test_block.py:274:38: error[invalid-argument-type] Argument to function `reduce` is incorrect: Expected `(Literal[0], int, /) -> Literal[0]`, found `def folder(x: int, y: int) -> int`
- tests/test_block.py:289:39: error[invalid-argument-type] Argument to function `unfold` is incorrect: Expected `(Literal[0], /) -> Option[tuple[Unknown, Literal[0]]]`, found `def unfolder(state: int) -> Option[tuple[int, int]]`
+ tests/test_seq.py:154:17: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Iterable[Unknown]`, found `Unknown | Literal[0]`
- Found 210 diagnostics
+ Found 226 diagnostics

optuna (https://github.com/optuna/optuna)
+ optuna/storages/_rdb/alembic/versions/v2.4.0.a.py:181:42: error[invalid-argument-type] Argument to bound method `drop_constraint` is incorrect: Expected `str`, found `str | None`
+ tests/samplers_tests/tpe_tests/test_multi_objective_sampler.py:44:9: error[unresolved-attribute] Unresolved attribute `return_value` on type `CallableMixin`.
+ tests/samplers_tests/tpe_tests/test_multi_objective_sampler.py:45:9: error[unresolved-attribute] Unresolved attribute `return_value` on type `CallableMixin`.
+ tests/samplers_tests/tpe_tests/test_multi_objective_sampler.py:113:13: error[unresolved-attribute] Unresolved attribute `return_value` on type `CallableMixin`.
+ tests/samplers_tests/tpe_tests/test_multi_objective_sampler.py:114:13: error[unresolved-attribute] Unresolved attribute `return_value` on type `CallableMixin`.
- Found 547 diagnostics
+ Found 552 diagnostics

artigraph (https://github.com/artigraph/artigraph)
+ src/arti/graphs/__init__.py:358:36: error[invalid-argument-type] Argument to bound method `write_artifact_and_graph_partitions` is incorrect: Expected `Artifact`, found `Artifact | TypedBox[Artifact]`
+ tests/arti/graphs/test_graph.py:450:20: error[unresolved-attribute] Object of type `CallableMixin` has no attribute `called`
+ tests/arti/graphs/test_graph.py:452:16: error[unresolved-attribute] Object of type `CallableMixin` has no attribute `called`
- Found 148 diagnostics
+ Found 151 diagnostics

vision (https://github.com/pytorch/vision)
+ references/depth/stereo/transforms.py:124:16: error[invalid-return-type] Return type does not match returned value: expected `tuple[tuple[Unknown, Unknown], tuple[Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None, Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None], tuple[Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None, Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None]]`, found `tuple[tuple[Unknown, ...], tuple[Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None, ...], tuple[Unknown | ndarray[tuple[Any, ...], dtype[Any]] | None, ...]]`
+ test/test_datasets.py:873:17: error[invalid-argument-type] Method `__getitem__` of type `bound method VisionDataset.__getitem__(index: int) -> Any` cannot be called with key of type `slice[None, Literal[2], None]` on object of type `VisionDataset`
+ torchvision/utils.py:763:14: error[invalid-assignment] Object of type `list[tuple[int, int, int] | tuple[int, int, int, int] | Unknown]` is not assignable to `None | str | tuple[int, int, int] | list[str | tuple[int, int, int]]`
- Found 1409 diagnostics
+ Found 1412 diagnostics

antidote (https://github.com/Finistere/antidote)
- tests/core/test_thread_safety.py:277:61: error[invalid-argument-type] Argument to bound method `set_value` is incorrect: Expected `() -> Literal["a"]`, found `def callback() -> str`
- Found 273 diagnostics
+ Found 272 diagnostics

sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/ext/apidoc/_generate.py:43:21: error[unsupported-operator] Operator `+` is not supported between objects of type `Literal["__init__"]` and `Sized | @Todo(StarredExpression)`
+ sphinx/util/_serialise.py:49:16: error[no-matching-overload] No overload of function `sorted` matches arguments
+ sphinx/util/matching.py:112:17: error[no-matching-overload] No overload of function `__new__` matches arguments
- Found 337 diagnostics
+ Found 340 diagnostics

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/_typeinfo.py:200:48: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `(Unknown & ~AlwaysFalsy) | (tuple[int, ...] & ~AlwaysFalsy)`
+ tests/typing_example.py:171:21: error[invalid-argument-type] Argument to bound method `connect` is incorrect: Expected `RowFactory[tuple[Any, ...]] | None`, found `BaseRowFactory[int | float]`
+ tests/typing_example.py:174:21: error[invalid-argument-type] Argument to bound method `connect` is incorrect: Expected `RowFactory[tuple[Any, ...]] | None`, found `BaseRowFactory[int]`
+ tests/utils.py:114:47: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `(Unknown & ~AlwaysFalsy) | (tuple[int, ...] & ~AlwaysFalsy)`
+ tests/utils.py:123:47: error[invalid-argument-type] Argument to function `__new__` is incorrect: Expected `Iterable[Buffer]`, found `(Unknown & ~AlwaysFalsy) | (tuple[int, ...] & ~AlwaysFalsy)`
- Found 682 diagnostics


... (truncated 1735 lines) ...
Memory usage changes were detected when running on open source projects
trio (https://github.com/python-trio/trio)
- TOTAL MEMORY USAGE: ~159MB
+ TOTAL MEMORY USAGE: ~167MB
-     struct metadata = ~10MB
+     struct metadata = ~11MB

sphinx (https://github.com/sphinx-doc/sphinx)
- TOTAL MEMORY USAGE: ~287MB
+ TOTAL MEMORY USAGE: ~301MB

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Nov 21, 2025
@dcreager dcreager force-pushed the dcreager/callable-return branch from 965a9f8 to dbecc68 Compare November 26, 2025 21:22
@dcreager dcreager changed the title [ty] Infer typevar specializations for Callable return types [ty] Infer typevar specializations for Callable types Nov 26, 2025
@dcreager dcreager force-pushed the dcreager/callable-return branch from 7995e43 to f89ec1a Compare November 26, 2025 23:14
Comment on lines 390 to 391
# TODO: this should be `Unknown | int`
reveal_type(invoke(head, [1, 2, 3])) # revealed: Unknown
Copy link
Member Author

Choose a reason for hiding this comment

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

This TODO is not also removed because we end up inferring this constraint set when comparing head to Callable[[A], B]:

(B@invoke ≤ T@head) ∧ (list[T@head] ≤ A@invoke)

We then try to remove T@head from the constraint set by calculating

∃T@head ⋅ (B@invoke ≤ T@head) ∧ (list[T@head] ≤ A@invoke)

We should be able to pick T@head = B@invoke and simplify that to

(B@invoke = *) ∧ (list[B@invoke] ≤ A@invoke)

which I think would then be enough to propagate through the return type to discharge this TODO. I think this would require adding more derived facts to the sequent map.


x12: Y[Y[Literal[1]]] = [[1]]
reveal_type(x12) # revealed: list[Y[Literal[1]]]
reveal_type(x12) # revealed: list[list[Literal[1]]]
Copy link
Member Author

Choose a reason for hiding this comment

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

This is because we're now using specialize_recursive instead of specialize_partial.

Comment on lines 1680 to 1718
let when = formal_signature.when_constraint_set_assignable_to(
self.db,
actual_signature,
self.inferable,
);
when.for_each_path(self.db, |path| {
for constraint in path.positive_constraints() {
let typevar = constraint.typevar(self.db);
let lower = constraint.lower(self.db);
let upper = constraint.upper(self.db);
if !upper.is_object() {
self.add_type_mapping(typevar, upper, polarity, &mut f);
}
if let Type::TypeVar(lower_bound_typevar) = lower {
self.add_type_mapping(
lower_bound_typevar,
Type::TypeVar(typevar),
polarity,
&mut f,
);
}
}
});
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the meat of the change. We use the new ConstraintAssignability type relation here to calculate a constraint set describing when the two callables are assignable. That will recurse into any typevars in the callables, and find whatever constraint set allows them to unify.

We then use this new for_each_path method to find each way that constraint set can be satisfied, and add new typevar bindings to the old solver for whatever we find.

Comment on lines +968 to +1020
result.intersect(
db,
ConstraintSet::from(
relation.is_assignability() || relation.is_constraint_set_assignability(),
),
);
return result;
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to combine this with result to hang on to any typevar mapping our constraint set has recorded for the return type comparison above. That's need to support something like

Callable[[int], int] ≤ Callable[..., T]

and have it return int ≤ T.

Comment on lines +1751 to +1973
// A typevar satisfies a relation when...it satisfies the relation. Yes that's a
// tautology! We're moving the caller's subtyping/assignability requirement into a
// constraint set. If the typevar has an upper bound or constraints, then the relation
// only has to hold when the typevar has a valid specialization (i.e., one that
// satisfies the upper bound/constraints).
Copy link
Member Author

Choose a reason for hiding this comment

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

This is what #20093 is trying to add to replace assignability across the board. In the meantime, I've added a new TypeRelation that lets us opt into the new behavior only in certain places.

@dcreager dcreager force-pushed the dcreager/callable-return branch from 3868294 to 2c62674 Compare November 26, 2025 23:37
@dcreager dcreager marked this pull request as ready for review November 26, 2025 23:38
@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 26, 2025

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 262 110 143
type-assertion-failure 4 56 51
invalid-return-type 88 0 16
invalid-assignment 75 0 28
invalid-await 101 0 1
possibly-missing-attribute 63 6 27
unresolved-attribute 62 2 28
unsupported-operator 56 0 19
missing-argument 0 59 0
no-matching-overload 36 8 0
unused-ignore-comment 0 27 0
not-iterable 21 0 0
non-subscriptable 7 0 13
unknown-argument 14 1 0
too-many-positional-arguments 11 1 0
call-non-callable 9 0 0
invalid-context-manager 1 0 2
invalid-raise 0 0 1
redundant-cast 1 0 0
Total 811 270 329

Full report with detailed diff (timing results)

* origin/main: (67 commits)
  Move `Token`, `TokenKind` and `Tokens` to `ruff-python-ast` (#21760)
  [ty] Don't confuse multiple occurrences of `typing.Self` when binding bound methods (#21754)
  Use our org-wide Renovate preset (#21759)
  Delete `my-script.py` (#21751)
  [ty] Move `all_members`, and related types/routines, out of `ide_support.rs` (#21695)
  [ty] Fix find-references for import aliases (#21736)
  [ty] add tests for workspaces (#21741)
  [ty] Stop testing the (brittle) constraint set display implementation (#21743)
  [ty] Use generator over list comprehension to avoid cast (#21748)
  [ty] Add a diagnostic for prohibited `NamedTuple` attribute overrides (#21717)
  [ty] Fix subtyping with `type[T]` and unions (#21740)
  Use `npm ci --ignore-scripts` everywhere (#21742)
  [`flake8-simplify`] Fix truthiness assumption for non-iterable arguments in tuple/list/set calls (`SIM222`, `SIM223`) (#21479)
  [`flake8-use-pathlib`] Mark fixes unsafe for return type changes (`PTH104`, `PTH105`, `PTH109`, `PTH115`) (#21440)
  [ty] Fix auto-import code action to handle pre-existing import
  Enable PEP 740 attestations when publishing to PyPI (#21735)
  [ty] Fix find references for type defined in stub (#21732)
  Use OIDC instead of codspeed token (#21719)
  [ty] Exclude `typing_extensions` from completions unless it's really available
  [ty] Fix false positives for `class F(Generic[*Ts]): ...` (#21723)
  ...
@carljm
Copy link
Contributor

carljm commented Dec 2, 2025

Took a look at the conformance suite. Everything looks good except that it seems like we've lost our ability to recognize the deprecated decorator? See the conformance suite changes on directives_deprecated.py.

@dcreager
Copy link
Member Author

dcreager commented Dec 2, 2025

Took a look at the conformance suite. Everything looks good except that it seems like we've lost our ability to recognize the deprecated decorator? See the conformance suite changes on directives_deprecated.py.

Yeah this showed up in the deprecated mdtest too. I'm looking at whether it's affected by the same cycle-merging issue as functools.cache.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Yeah, I was wrong, homeassistant does look like a bug. Other type checkers are fine with it. Minimal repro:

from typing import Generic, TypeVar, Mapping, Any, Callable, Never

T = TypeVar("T", bound=Mapping[str, Any], default=Mapping[str, Any])

class Event(Generic[T]):
    pass

class EventType(Generic[T]):
    pass

STOP: EventType[Mapping[str, Never]] = EventType()

def listen(et: EventType[T], cb: Callable[[Event[T]], None]):
    pass

def mycb(event: Event) -> None:
    pass

def _():
    listen(STOP, mycb)

We have a false positive on the last line, we solve T to Mapping[str, object] instead of Mapping[str, Any], and then fail to assign STOP (which is EventType[Mapping[str, Never]] to EventType[T].

Do we still take top materializations of gradual upper bounds? That seems like it might be the problem here.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

That one is chunky enough that I think it does deserve a separate PR.

Are you thinking to get that PR landed first and then rebase this and revisit its ecosystem report? Or would you prefer to just land this with its remaining issues and tackle them all as follow-ups?

@dcreager
Copy link
Member Author

Are you thinking to get that PR landed first and then rebase this and revisit its ecosystem report? Or would you prefer to just land this with its remaining issues and tackle them all as follow-ups?

Given the time, another option would be to combine this PR and the generic protocol one and try to land it all together. I'm down to one last mdtest file with test regressions to fix.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

We can try that. Do you have a lot of other fixes in the protocol PR that might help this PR, too? If so, combining might be most expedient.

The downside might be if the combined PR is harder to review ecosystem impact and land, because its so big?

If you want, I could also pull out that commit into its own PR and add tests for it, while you keep working on the protocol PR?

@dcreager
Copy link
Member Author

If you want, I could also pull out that commit into its own PR and add tests for it, while you keep working on the protocol PR?

Sure, that works! I've got it started on the dcreager/explicit-constraints branch

@dcreager
Copy link
Member Author

We have a false positive on the last line, we solve T to Mapping[str, object] instead of Mapping[str, Any], and then fail to assign STOP (which is EventType[Mapping[str, Never]] to EventType[T].

Do we still take top materializations of gradual upper bounds? That seems like it might be the problem here.

For this homeassistant error, I think it's the same as this TODO: https://github.com/astral-sh/ruff/pull/21551/changes#diff-d08dc0c815ce12a46b39702c611395d769e53bc49746c3a0c8533ed8af014863R814-R815

(That same comment appears as a TODO in some other mdtests, too.)

The issue is that we're creating a constraint set of the form Never ≤ T, where we mean that we actually want to map T = Never, but we're confusing that constraint set with "T has no lower bound". The new explicit markings should help take care of this, but I think we can do that as a follow-on.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Hmm, not sure about that, because the issue repros even without Never. If I change the Never to int, we still have the assignability error where we infer Mapping[str, object] and try to assign Mapping[str, int].

But removing the default and changing the upper bound Mapping[str, Any] to Mapping[str, object] doesn't solve the problem, either, so it's not related to typevar defaults or to gradual upper bounds. Here's the even-more-simplified example (removing Never and Any and typevar defaults):

from typing import Generic, TypeVar, Mapping, Any, Callable, Never

T = TypeVar("T", bound=Mapping[str, object])

class Event(Generic[T]):
    pass

class EventType(Generic[T]):
    pass

STOP: EventType[Mapping[str, int]] = EventType()

def listen(et: EventType[T], cb: Callable[[Event[T]], None]):
    pass

def mycb(event: Event) -> None:
    pass

def _():
    listen(STOP, mycb)

Other typecheckers are still happy with this; we emit an error on the bottom line yet.

Maybe this is a variant of the same problem as the Expression one, and will also be fixed by the explicit-constraints change?

If not I agree we can look into it as follow-on.

@dcreager
Copy link
Member Author

Maybe this is a variant of the same problem as the Expression one, and will also be fixed by the explicit-constraints change?

Ah that's a good catch. If we're inferring the bounds, then yes I think the explicit-constraints change will help.

* main:
  [ty] Document `TY_CONFIG_FILE` (#22001)
  [ty] Cache `KnownClass::to_class_literal` (#22000)
  [ty] Fix benchmark assertion (#22003)
  Add uv and ty to the Ruff README (#21996)
  [ty] Infer precise types for `isinstance(…)` calls involving typevars (#21999)
  [ty] Use `FxHashMap` in `Signature::has_relation_to` (#21997)
  [ty] Avoid enforcing standalone expression for tests in f-strings (#21967)
  [ty] Use `title` for configuration code fences in ty reference documentation (#21992)
@dcreager
Copy link
Member Author

Any idea where a 101 exit code comes from? Does that happen when we panic? When I run ty locally on spack (both dev and profiling profiles), I get a successful type check.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Just in terms of numbers, ecosystem diff is definitely improved here! We were adding almost 1200 new diagnostics before, now its under 800.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Where do you see the 101 return code? Primer and ecosystem analyzer jobs both completed successfully...

EDIT: oh never mind, I see it once I link to the full ecosystem analyzer output... but spack checked fine on the mypy-primer job. Running ecosystem analyzer again to see if it persists.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Spack error is gone on the second run; I suspect an infra issue. (I guess I didn't check the logs for the ecosystem analyzer run to see if there was anything useful there.)

Spot-checked the first Expression failure -- looks like a combo of generic protocol inference, plus us using narrower inferred types. Not an issue for this PR. I'll look at a couple more quick?

@dcreager
Copy link
Member Author

Spot-checked the first Expression failure -- looks like a combo of generic protocol inference, plus us using narrower inferred types. Not an issue for this PR. I'll look at a couple more quick?

Sounds good, thanks!

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

Second one also looks like generic protocols.

@carljm
Copy link
Contributor

carljm commented Dec 16, 2025

In bokeh I see an example that boils down to this:

from typing import Callable, TypeVar

T = TypeVar("T")

def infer(func: Callable[[T | str], T]) -> T:
    return func("foo")

def convert_str_seq(value: list[str] | str) -> list[str]:
    if isinstance(value, str):
        return [value]
    return value

reveal_type(infer(convert_str_seq))  # expected `list[str]` but we say `list[str] | str`

Basically we don't unpack the union and match it item-by-item, so the T | str in the formal parameter doesn't eliminate | str from the union in the actual argument.

But pyrefly does the same as we do here -- I think this one is fine to look at as follow-up.

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Ship it!

@carljm carljm merged commit c02bd11 into main Dec 16, 2025
44 checks passed
@carljm carljm deleted the dcreager/callable-return branch December 16, 2025 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecosystem-analyzer ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants