Skip to content

[ty] Support Unpack[TypedDict]#24653

Open
charliermarsh wants to merge 5 commits intomainfrom
charlie/unpack
Open

[ty] Support Unpack[TypedDict]#24653
charliermarsh wants to merge 5 commits intomainfrom
charlie/unpack

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 15, 2026

Summary

We now support Unpack[TypedDict] as an annotation on **kwargs, as in the following example:

from typing_extensions import TypedDict, Unpack

class MovieKwargs(TypedDict):
    title: str
    year: int

def show_movie(**kwargs: Unpack[MovieKwargs]) -> None:
    ...

show_movie(title="Alien", year=1979)  # OK
show_movie(title="Alien")             # missing required key
show_movie(name="Alien", year=1979)   # unknown keyword

@astral-sh-bot astral-sh-bot bot added the ty Multi-file analysis & type inference label Apr 15, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 15, 2026

Typing conformance results improved 🎉

The percentage of diagnostics emitted that were expected errors increased from 87.94% to 88.34%. The percentage of expected errors that received a diagnostic increased from 83.36% to 84.42%. The number of fully passing files improved from 79/133 to 81/133.

Summary

How are test cases classified?

Each test case represents one expected error annotation or a group of annotations sharing a tag. Counts are per test case, not per diagnostic — multiple diagnostics on the same line count as one. Required annotations (E) are true positives when ty flags the expected location and false negatives when it does not. Optional annotations (E?) are true positives when flagged but true negatives (not false negatives) when not. Tagged annotations (E[tag]) require ty to flag exactly one of the tagged lines; tagged multi-annotations (E[tag+]) allow any number up to the tag count. Flagging unexpected locations counts as a false positive.

Metric Old New Diff Outcome
True Positives 882 894 +12 ⏫ (✅)
False Positives 121 118 -3 ⏬ (✅)
False Negatives 176 165 -11 ⏬ (✅)
Total Diagnostics 1053 1065 +12
Precision 87.94% 88.34% +0.40% ⏫ (✅)
Recall 83.36% 84.42% +1.05% ⏫ (✅)
Passing Files 79/133 81/133 +2 ⏫ (✅)

Test file breakdown

3 files altered
File True Positives False Positives False Negatives Status
callables_kwargs.py 14 (+10) ✅ 0 (-4) ✅ 0 (-9) ✅ ✅ Newly Passing 🎉
typeddicts_extra_items.py 12 (+1) ✅ 23 (+1) ❌ 16 (-1) ✅ ➡️ Neutral
typeddicts_readonly_kwargs.py 1 (+1) ✅ 0 0 (-1) ✅ ✅ Newly Passing 🎉
Total (all files) 894 (+12) ✅ 118 (-3) ✅ 165 (-11) ✅ 81/133

True positives added (11)

11 diagnostics
Test case Diff

callables_kwargs.py:101

+error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: TD2) -> None` is not assignable to `TDProtocol3`

callables_kwargs.py:102

+error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: TD2) -> None` is not assignable to `TDProtocol4`

callables_kwargs.py:111

+error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation

callables_kwargs.py:122

+error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6`

callables_kwargs.py:46

+error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1`

callables_kwargs.py:51

+error[unknown-argument] Argument `v4` does not match any known parameter of function `func1`

callables_kwargs.py:58

+error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1`
+error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `TD2`, found `dict[str, str]`

callables_kwargs.py:63

+error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1`

callables_kwargs.py:65

+error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2`

typeddicts_extra_items.py:143

+error[unknown-argument] Argument `year` does not match any known parameter of function `unpack_no_extra`

typeddicts_readonly_kwargs.py:33

+error[invalid-assignment] Cannot assign to key "key1" on TypedDict `ReadOnlyArgs`: key is marked read-only

False positives removed (4)

4 diagnostics
Test case Diff

callables_kwargs.py:24

-error[type-assertion-failure] Type `@Todo(`Unpack[]` special form)` does not match asserted type `int`

callables_kwargs.py:32

-error[type-assertion-failure] Type `@Todo(`Unpack[]` special form)` does not match asserted type `str`

callables_kwargs.py:35

-error[type-assertion-failure] Type `@Todo(`Unpack[]` special form)` does not match asserted type `str`

callables_kwargs.py:41

-error[type-assertion-failure] Type `dict[str, @Todo(`Unpack[]` special form)]` does not match asserted type `TD1`

True positives changed (2)

2 diagnostics
Test case Diff

callables_kwargs.py:103

-error[invalid-assignment] Object of type `def func1(**kwargs: @Todo(`Unpack[]` special form)) -> None` is not assignable to `TDProtocol5`
+error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: TD2) -> None` is not assignable to `TDProtocol5`

callables_kwargs.py:52

-error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3
+error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1`
+error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3

False positives added (1)

1 diagnostic
Test case Diff

typeddicts_extra_items.py:144

+error[unknown-argument] Argument `year` does not match any known parameter of function `unpack_extra`

Optional Diagnostics Added (1)

1 diagnostic
Test case Diff

callables_kwargs.py:61

+error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1`
+error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `TD2`, found `dict[str, int | str] & <Protocol with members '__getitem__'>`

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 15, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 709.06MB 709.49MB +0.06% (440.57kB)
sphinx 262.60MB 262.65MB +0.02% (45.35kB)
trio 117.49MB 117.51MB +0.02% (21.20kB)
flake8 47.89MB 47.90MB +0.02% (10.90kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
FunctionType<'db>::last_definition_raw_signature_ 3.41MB 3.50MB +2.83% (98.72kB)
UnionType<'db>::from_two_elements_ 4.49MB 4.57MB +1.85% (85.20kB)
CallableType 2.12MB 2.19MB +3.40% (73.69kB)
FunctionType<'db>::signature_ 4.02MB 4.08MB +1.40% (57.52kB)
FunctionType<'db>::last_definition_signature_ 797.91kB 818.98kB +2.64% (21.07kB)
Type<'db>::apply_specialization_ 3.65MB 3.67MB +0.44% (16.36kB)
UnionType 2.99MB 3.00MB +0.46% (14.00kB)
is_redundant_with_impl::interned_arguments 4.35MB 4.36MB +0.27% (11.86kB)
is_redundant_with_impl 4.26MB 4.27MB +0.23% (10.03kB)
Type<'db>::apply_specialization_::interned_arguments 2.95MB 2.95MB +0.26% (7.81kB)
Type<'db>::class_member_with_policy_ 17.40MB 17.41MB +0.04% (7.04kB)
FunctionType 8.78MB 8.78MB +0.08% (6.83kB)
Type<'db>::try_call_dunder_get_ 10.48MB 10.48MB +0.05% (5.45kB)
UnionType<'db>::from_two_elements_::interned_arguments 2.09MB 2.09MB +0.20% (4.21kB)
TupleType 733.22kB 737.39kB +0.57% (4.17kB)
... 44 more

sphinx

Name Old New Diff Outcome
FunctionType<'db>::last_definition_raw_signature_ 2.04MB 2.06MB +1.09% (22.71kB)
FunctionType<'db>::signature_ 2.27MB 2.29MB +0.87% (20.18kB)
FunctionType<'db>::last_definition_signature_ 221.68kB 224.14kB +1.11% (2.46kB)

trio

Name Old New Diff Outcome
FunctionType<'db>::signature_ 1.06MB 1.07MB +0.92% (10.07kB)
FunctionType<'db>::last_definition_raw_signature_ 601.46kB 604.90kB +0.57% (3.45kB)
FunctionType<'db>::last_definition_signature_ 241.98kB 244.38kB +0.99% (2.39kB)
infer_definition_types 7.61MB 7.61MB +0.02% (1.23kB)
StaticClassLiteral<'db>::own_fields_ 7.35kB 8.46kB +15.15% (1.11kB)
StaticClassLiteral<'db>::fields_inner_ 12.88kB 13.92kB +8.10% (1.04kB)
class_based_items 3.62kB 3.91kB +8.21% (304.00B)
place_by_id 550.21kB 550.45kB +0.04% (248.00B)
Type<'db>::apply_specialization_::interned_arguments 645.16kB 645.39kB +0.04% (240.00B)
StaticClassLiteral<'db>::try_metaclass_ 139.43kB 139.66kB +0.16% (232.00B)
StaticClassLiteral<'db>::try_mro_ 820.49kB 820.70kB +0.03% (212.00B)
Type<'db>::apply_specialization_ 720.46kB 720.62kB +0.02% (168.00B)
place_by_id::interned_arguments 409.50kB 409.64kB +0.03% (144.00B)
code_generator_of_static_class 211.96kB 212.10kB +0.06% (140.00B)
Specialization 465.44kB 465.31kB -0.03% (128.00B)
... 8 more

flake8

Name Old New Diff Outcome
FunctionType<'db>::signature_ 362.10kB 370.32kB +2.27% (8.23kB)
FunctionType<'db>::last_definition_raw_signature_ 80.73kB 82.98kB +2.79% (2.25kB)
FunctionType<'db>::last_definition_signature_ 59.69kB 60.11kB +0.71% (432.00B)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 15, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 53 5 5
unused-type-ignore-comment 0 36 0
unknown-argument 32 1 0
missing-argument 32 0 0
invalid-type-form 11 0 0
no-matching-overload 8 0 0
invalid-method-override 3 0 0
not-subscriptable 0 3 0
invalid-return-type 1 1 0
unresolved-attribute 0 2 0
invalid-assignment 0 1 0
type-assertion-failure 0 1 0
Total 140 50 5

Large timing changes:

Project Old Time New Time Change
pandas-stubs 7.70s 11.72s +52%

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

Raw diff (195 changes)
aiohttp-devtools (https://github.com/aio-libs/aiohttp-devtools)
+ aiohttp_devtools/runserver/watch.py:120:55 error[invalid-argument-type] Argument to bound method `get` is incorrect: Expected `SSLContext | bool | Fingerprint`, found `None | SSLContext`
+ aiohttp_devtools/runserver/watch.py:155:57 error[invalid-argument-type] Argument to bound method `get` is incorrect: Expected `SSLContext | bool | Fingerprint`, found `None | SSLContext`

altair (https://github.com/vega/altair)
- altair/datasets/_constraints.py:54:24 error[invalid-return-type] Return type does not match returned value: expected `Metadata`, found `dict[str, @Todo]`
- altair/datasets/_constraints.py:108:33 error[invalid-argument-type] Argument to bound method `from_metadata` is incorrect: Expected `Metadata`, found `dict[str, @Todo]`
- altair/utils/core.py:680:18 error[unresolved-attribute] Attribute `schema` is not defined on `NativeDataFrame`, `DataFrameLike`, `None` in union `NativeDataFrame | DataFrameLike | Unknown | None`
- altair/utils/core.py:682:22 error[not-subscriptable] Cannot subscript object of type `DataFrameLike` with no `__getitem__` method
- altair/utils/core.py:682:22 error[not-subscriptable] Cannot subscript object of type `NativeDataFrame` with no `__getitem__` method
- altair/utils/core.py:682:22 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
- altair/utils/core.py:686:39 error[unresolved-attribute] Attribute `to_native` is not defined on `NativeDataFrame`, `DataFrameLike`, `None` in union `NativeDataFrame | DataFrameLike | Unknown | None`

bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/plotting/_graph.py:131:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `Glyph | None` does not satisfy upper bound `Glyph` of type variable `GlyphType`
+ src/bokeh/plotting/_graph.py:131:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Glyph`, found `Glyph | None`
+ src/bokeh/plotting/_graph.py:149:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `Glyph | None` does not satisfy upper bound `Glyph` of type variable `GlyphType`
+ src/bokeh/plotting/_graph.py:149:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Glyph`, found `Glyph | None`
+ src/bokeh/transform.py:155:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[str | tuple[int, int, int] | tuple[int, int, int, int | float]]`, found `Sequence[str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]]`
+ src/bokeh/transform.py:158:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float]`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]`
+ src/bokeh/transform.py:159:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/transform.py:160:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/transform.py:200:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[str | tuple[int, int, int] | tuple[int, int, int, int | float]]`, found `Sequence[str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]]`
+ src/bokeh/transform.py:202:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int`, found `int | float`
+ src/bokeh/transform.py:203:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | None`, found `int | float | None`
+ src/bokeh/transform.py:204:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float]`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]`
+ src/bokeh/transform.py:242:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[Property[Literal["blank", "dot", "ring", "horizontal_line", "vertical_line", ... omitted 29 literals]]]`, found `Sequence[str]`
+ src/bokeh/transform.py:244:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int`, found `int | float`
+ src/bokeh/transform.py:245:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | None`, found `int | float | None`
+ src/bokeh/transform.py:285:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[Literal["asterisk", "circle", "circle_cross", "circle_dot", "circle_x", ... omitted 23 literals]]`, found `Sequence[str]`
+ src/bokeh/transform.py:287:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int`, found `int | float`
+ src/bokeh/transform.py:288:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | None`, found `int | float | None`
+ src/bokeh/transform.py:368:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[str | tuple[int, int, int] | tuple[int, int, int, int | float]]`, found `Sequence[str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]]`
+ src/bokeh/transform.py:371:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float]`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]`
+ src/bokeh/transform.py:372:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/transform.py:373:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/transform.py:415:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[str | tuple[int, int, int] | tuple[int, int, int, int | float]]`, found `Sequence[str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]]`
+ src/bokeh/transform.py:418:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float]`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float]`
+ src/bokeh/transform.py:419:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/transform.py:420:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | tuple[int, int, int] | tuple[int, int, int, int | float] | None`, found `str | Color | tuple[int, int, int] | tuple[int, int, int, int | float] | None`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `attr` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `cols` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `data` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `kind` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `model` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `msg_data` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `msg_type` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `new` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `patches` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `rollover` does not match any known parameter of bound method `__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `title` does not match any known parameter of bound method `__init__`
+ src/bokeh/layouts.py:384:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["normal", "grey"] | None`, found `Literal["normal", "grey"] | None | UndefinedType`
+ src/bokeh/layouts.py:385:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `bool`, found `bool | UndefinedType`
+ src/bokeh/layouts.py:386:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["auto"] | Drag | ToolProxy | None`, found `ToolProxy | Tool | UndefinedType`
+ src/bokeh/layouts.py:387:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["auto"] | InspectTool | ToolProxy | Sequence[InspectTool] | None`, found `ToolProxy | Tool | UndefinedType`
+ src/bokeh/layouts.py:388:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["auto"] | Scroll | ToolProxy | None`, found `ToolProxy | Tool | UndefinedType`
+ src/bokeh/layouts.py:389:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["auto"] | Tap | ToolProxy | None`, found `ToolProxy | Tool | UndefinedType`
+ src/bokeh/layouts.py:390:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Literal["auto"] | GestureTool | ToolProxy | None`, found `ToolProxy | Tool | UndefinedType`
+ src/bokeh/layouts.py:394:9 error[unknown-argument] Argument `children` does not match any known parameter of bound method `__init__`
+ src/bokeh/models/renderers/contour_renderer.py:112:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Glyph]`, found `Instance[GlyphRenderer[Unknown]]`
+ src/bokeh/models/renderers/contour_renderer.py:113:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Glyph]`, found `Instance[GlyphRenderer[Unknown]]`
+ src/bokeh/models/renderers/contour_renderer.py:114:13 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[int | float]`, found `Seq[T@Seq]`
+ src/bokeh/models/renderers/contour_renderer.py:115:32 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Sequence[int | float]`, found `Seq[T@Seq]`
- src/bokeh/models/renderers/graph_renderer.py:84:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `() -> GlyphRenderer[Unknown]` does not satisfy upper bound `Serializable` of type variable `S`
+ src/bokeh/models/renderers/graph_renderer.py:84:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `() -> GlyphRenderer[Scatter]` does not satisfy upper bound `Serializable` of type variable `S`
- src/bokeh/models/renderers/graph_renderer.py:84:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Unknown] | UndefinedType | IntrinsicType`, found `() -> GlyphRenderer[Unknown]`
+ src/bokeh/models/renderers/graph_renderer.py:84:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Unknown] | UndefinedType | IntrinsicType`, found `() -> GlyphRenderer[Scatter]`
- src/bokeh/models/renderers/graph_renderer.py:89:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `() -> GlyphRenderer[Unknown]` does not satisfy upper bound `Serializable` of type variable `S`
+ src/bokeh/models/renderers/graph_renderer.py:89:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `() -> GlyphRenderer[MultiLine]` does not satisfy upper bound `Serializable` of type variable `S`
- src/bokeh/models/renderers/graph_renderer.py:89:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Unknown] | UndefinedType | IntrinsicType`, found `() -> GlyphRenderer[Unknown]`
+ src/bokeh/models/renderers/graph_renderer.py:89:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `GlyphRenderer[Unknown] | UndefinedType | IntrinsicType`, found `() -> GlyphRenderer[MultiLine]`
+ src/bokeh/plotting/_figure.py:243:41 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Range`, found `Range | None`
+ src/bokeh/plotting/_figure.py:243:60 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Range`, found `Range | None`
+ src/bokeh/plotting/_geo_feature.pyi:24:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:31:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:38:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:45:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:52:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:59:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:66:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:73:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:79:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:86:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_geo_feature.pyi:93:24 error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `Unknown`
+ src/bokeh/plotting/_plot.py:98:32 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | float | datetime | timedelta`, found `int | float | (Unknown & ~None) | str | IntrinsicType`
+ src/bokeh/plotting/_plot.py:98:45 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int | float | datetime | timedelta`, found `int | float | (Unknown & ~None) | str | IntrinsicType`
+ src/bokeh/plotting/_renderer.py:127:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Argument type `Glyph | None` does not satisfy upper bound `Glyph` of type variable `GlyphType`
+ src/bokeh/plotting/_renderer.py:127:9 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Glyph`, found `Glyph | None`
+ src/bokeh/plotting/_renderer.py:139:56 error[invalid-argument-type] Argument to function `update_legend` is incorrect: Expected `GlyphRenderer[Glyph]`, found `GlyphRenderer[GlyphType@GlyphRenderer]`
+ src/bokeh/plotting/_renderer.py:141:12 error[invalid-return-type] Return type does not match returned value: expected `GlyphRenderer[Glyph]`, found `GlyphRenderer[GlyphType@GlyphRenderer]`

core (https://github.com/home-assistant/core)
- homeassistant/components/aprilaire/coordinator.py:106:21 error[invalid-argument-type] Argument to bound method `async_update_device` is incorrect: Expected `DeviceEntryType | None | UndefinedType`, found `set[tuple[str, str]]`
- homeassistant/components/aprilaire/coordinator.py:106:21 error[invalid-argument-type] Argument to bound method `async_update_device` is incorrect: Expected `str | None | UndefinedType`, found `set[tuple[str, str]]`

discord.py (https://github.com/Rapptz/discord.py)
- discord/permissions.py:216:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/permissions.py:488:46 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ discord/shard.py:380:50 error[unknown-argument] Argument `shard_connect_timeout` does not match any known parameter of bound method `__init__`
+ discord/shard.py:380:50 error[unknown-argument] Argument `shard_ids` does not match any known parameter of bound method `__init__`
- discord/ext/commands/bot.py:294:59 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/bot.py:306:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/bot.py:307:107 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/bot.py:318:57 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/bot.py:330:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/bot.py:331:105 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/core.py:1551:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/core.py:1608:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ discord/ext/commands/core.py:602:38 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `_CommandKwargs`, found `dict[str, Any | list[str] | tuple[str, ...] | ... omitted 7 union elements]`
+ discord/ext/commands/core.py:1859:12 error[no-matching-overload] No overload of function `command` matches arguments
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `arguments_heading` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `commands_heading` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `default_argument_description` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `dm_help_threshold` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `dm_help` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `indent` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `no_category` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `paginator` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `show_parameter_descriptions` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `sort_commands` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1087:26 error[unknown-argument] Argument `width` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `aliases_heading` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `commands_heading` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `dm_help_threshold` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `dm_help` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `no_category` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `paginator` does not match any known parameter of bound method `__init__`
+ discord/ext/commands/help.py:1378:26 error[unknown-argument] Argument `sort_commands` does not match any known parameter of bound method `__init__`
- discord/ext/commands/hybrid.py:517:50 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:641:47 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:849:59 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:861:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:862:107 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:873:57 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:885:48 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:886:105 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:897:54 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:940:92 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:949:52 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- discord/ext/commands/hybrid.py:973:90 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ discord/ext/commands/hybrid.py:844:9 error[invalid-method-override] Invalid override of method `command`: Definition is incompatible with `GroupMixin.command`
+ discord/ext/commands/hybrid.py:868:9 error[invalid-method-override] Invalid override of method `group`: Definition is incompatible with `GroupMixin.group`

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/type/directives.py:133:16 error[missing-argument] No arguments provided for required parameters `name`, `locations` of bound method `__init__`
+ src/graphql/utilities/extend_schema.py:295:16 error[missing-argument] No arguments provided for required parameters `name`, `locations` of bound method `__init__`
+ src/graphql/utilities/extend_schema.py:333:23 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/utilities/extend_schema.py:352:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:367:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:384:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:418:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:457:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:482:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/extend_schema.py:492:16 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/utilities/extend_schema.py:502:16 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/type/definition.py:291:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:443:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:547:16 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/type/definition.py:693:16 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/type/definition.py:773:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:877:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:980:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:1115:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:1347:16 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/type/definition.py:1444:16 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:67:16 error[missing-argument] No arguments provided for required parameters `name`, `locations` of bound method `__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:78:26 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:89:28 error[missing-argument] No argument provided for required parameter `type_` of bound method `__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:124:20 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:132:20 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:140:20 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:144:20 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:160:20 error[missing-argument] No argument provided for required parameter `name` of function `__new__`
+ tests/type/test_definition.py:1280:17 error[missing-argument] No argument provided for required parameter `name` of function `__new__`

pandas (https://github.com/pandas-dev/pandas)
- pandas/io/parsers/readers.py:1604:41 error[invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `(@Todo & ~Literal[False] & ~_NoDefault) | None`
+ pandas/io/parsers/readers.py:1604:41 error[invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `Hashable & ~Literal[False] & ~_NoDefault`
+ pandas/tests/io/parser/test_read_fwf.py:986:26 error[invalid-argument-type] Argument to function `read_fwf` is incorrect: Expected `Literal["pyarrow", "numpy_nullable"] | _NoDefault`, found `Literal["numpy"]`
+ pandas/tests/io/parser/test_unsupported.py:43:17 error[no-matching-overload] No overload of function `read_csv` matches arguments
+ pandas/tests/io/test_common.py:442:13 error[no-matching-overload] No overload of function `read_csv` matches arguments

prefect (https://github.com/PrefectHQ/prefect)
+ src/integrations/prefect-dbt/prefect_dbt/core/_orchestrator.py:1833:25 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `list[Asset | str] | None`, found `(list[Asset] & ~AlwaysFalsy) | None`
- src/integrations/prefect-dbt/tests/cli/configs/test_snowflake.py:100:9 error[unknown-argument] Argument `schema` does not match any known parameter
+ src/integrations/prefect-gitlab/prefect_gitlab/repositories.py:90:32 error[no-matching-overload] No overload of function `Field` matches arguments
+ src/prefect/cli/deployment.py:753:20 error[missing-argument] No argument provided for required parameter `interval` of bound method `__init__`
+ src/prefect/tasks.py:2218:9 error[invalid-method-override] Invalid override of method `with_options`: Definition is incompatible with `Task.with_options`

pycryptodome (https://github.com/Legrandin/pycryptodome)
+ lib/Crypto/Protocol/HPKE.py:181:39 error[invalid-argument-type] Argument to function `key_agreement` is incorrect: Expected `RequestParams[T@key_agreement]`, found `dict[Unknown, Unknown] | dict[str, EccKey]`
+ lib/Crypto/Protocol/HPKE.py:221:39 error[invalid-argument-type] Argument to function `key_agreement` is incorrect: Expected `RequestParams[T@key_agreement]`, found `dict[Unknown, Unknown] | dict[str, EccKey]`

pydantic (https://github.com/pydantic/pydantic)
- pydantic/fields.py:239:95 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:278:57 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1278:39 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1282:47 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1292:47 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1302:53 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1312:57 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1322:39 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1335:40 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- pydantic/fields.py:1350:43 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ pydantic/fields.py:669:16 error[no-matching-overload] No overload of function `Field` matches arguments

pyinstrument (https://github.com/joerick/pyinstrument)
- pyinstrument/context_manager.py:41:9 error[invalid-assignment] Object of type `dict[str, @Todo]` is not assignable to attribute `options` of type `ProfileContextOptions`

pylint (https://github.com/pycqa/pylint)
+ pylint/checkers/base_checker.py:207:16 error[missing-argument] No argument provided for required parameter `scope` of bound method `__init__`

pywin32 (https://github.com/mhammond/pywin32)
+ com/win32comext/shell/demos/servers/folder_view.py:855:9 error[invalid-argument-type] Argument to function `UseCommandLine` is incorrect: Expected `bool`, found `Literal[0]`
+ com/win32comext/shell/demos/servers/shell_view.py:968:9 error[invalid-argument-type] Argument to function `UseCommandLine` is incorrect: Expected `bool`, found `Literal[0]`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/db/dbhandler.py:910:59 error[no-matching-overload] No overload of bound method `get_db_key` matches arguments
- rotkehlchen/history/events/structures/solana_swap.py:111:17 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `TimestampMS`, found `str | None`
- rotkehlchen/history/events/structures/solana_swap.py:111:17 error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `str | None`, found `Location`
+ rotkehlchen/tests/db/test_db.py:407:20 error[no-matching-overload] No overload of bound method `get_dynamic_cache` matches arguments
+ rotkehlchen/tests/db/test_db.py:447:20 error[no-matching-overload] No overload of bound method `get_dynamic_cache` matches arguments

scipy-stubs (https://github.com/scipy/scipy-stubs)
- tests/stats/test_new_distributions.pyi:15:1 error[type-assertion-failure] Type `Normal[tuple[()], float64]` does not match asserted type `Normal[tuple[int], floating]`

trio (https://github.com/python-trio/trio)
- src/trio/_tests/test_subprocess.py:497:55 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- src/trio/_tests/type_tests/subprocesses.py:15:70 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- src/trio/_tests/type_tests/subprocesses.py:22:60 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- src/trio/_tests/type_tests/subprocesses.py:23:70 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/unpack branch 2 times, most recently from 93d7cb9 to 1b9a9bd Compare April 15, 2026 14:09
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 15, 2026

Merging this PR will not alter performance

✅ 47 untouched benchmarks
⏩ 60 skipped benchmarks1


Comparing charlie/unpack (9387793) with main (ee9088e)

Open in CodSpeed

Footnotes

  1. 60 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.

has_starred_annotation: bool,

/// Whether this parameter was declared as `**kwargs: Unpack[TypedDict]`.
has_unpacked_kwargs_annotation: bool,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This lets us distinguish **kwargs: Unpack[TD] from **kwargs: TD. The latter means that every kwarg value is itself of type TD.

Comment on lines +1018 to +1019
/// Per [PEP 692](https://peps.python.org/pep-0692/#typeddict-unions), unions (for example) are not
/// allowed in such annotations.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Slightly annoying because we already have some logic for extracting these keys in the more general case (see, e.g., extract_unpacked_typed_dict_keys_from_value_type), but PEP 692 explicitly forbids unions.

/// The argument definitely binds this parameter.
Definitive,
/// The argument may bind this parameter at runtime, but does not guarantee its presence.
Provisional,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is necessary for cases like...

class MaybeX(TypedDict, total=False):
    x: str

def takes_x(*, x: int) -> None: ...

takes_x(**maybe_x)

Where we need to report both missing-argument and invalid-argument-type.

@charliermarsh
Copy link
Copy Markdown
Member Author

For the ecosystem report...

  • I believe the graphql-core diagnostics are true positives? They're unpacking with a TypedDict(total=False), although according to Codex other type checkers don't raise these errors, i.e., they don't require that the key is definitely present. I prefer our behavior but it is stricter.
  • For bokeh, there are generally a lot of TypedDict usages in that codebase that type checkers can't model (e.g., calls on an event type that then need to narrow the return type, etc.), so I'm not overly concerned.

Based on Codex's analysis, the rest are either true positives or things that are sufficiently dynamic that we shouldn't really expect to model them. But I'll go through a few more on my own.

@charliermarsh
Copy link
Copy Markdown
Member Author

Also, note that the newly-added false positive on the conformance tests is related to extra_items, which we don't yet support.


## `Unpack[TypedDict]` in `**kwargs`

Using `Unpack[TypedDict]` on a `**kwargs` parameter should expose the `TypedDict` shape both inside
Copy link
Copy Markdown
Member

@MichaReiser MichaReiser Apr 15, 2026

Choose a reason for hiding this comment

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

I haven't read the mdtest but could we split this test into multiple smaller snippets with some prose between snippets explaining what's being tested and what the expected behavior is?

This is one of my main learnings from maintaining Ruff. The long fixture files are a pain to maintain over time because they lack context of why something has been tested in the first place and if it's even asserting something intentionally or if they just tried to be exhaustive.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes sir!

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.

3 participants