Skip to content

[ty] Validate unpacked TypedDict **kwargs arguments#24710

Open
charliermarsh wants to merge 2 commits intocharlie/unpackfrom
charlie/unpack-validate
Open

[ty] Validate unpacked TypedDict **kwargs arguments#24710
charliermarsh wants to merge 2 commits intocharlie/unpackfrom
charlie/unpack-validate

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 19, 2026

Summary

This PR improves validation of **kwargs unpacking against Unpack[TypedDict] at call sites.

In particular, we now distinguish definitely-present keys from maybe-present keys, so required parameters are no
longer treated as satisfied by optional TypedDict entries, duplicate explicit keywords are diagnosed correctly, etc.

Comparing our behavior before and after:

from typing import TypedDict, Unpack

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

class HasX(TypedDict):
    x: int

def takes_required_x(**kwargs: Unpack[HasX]) -> None: ...
def takes_x(*, x: int) -> None: ...

maybe_x: MaybeX = {}

# Before: accepted
# After: error[missing-argument]
takes_required_x(**maybe_x)
                 
# Before: accepted
# After: error[parameter-already-assigned]
takes_x(x=1, **maybe_x)

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

astral-sh-bot Bot commented Apr 19, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 88.34%. The percentage of expected errors that received a diagnostic held steady at 84.42%. The number of fully passing files held steady at 81/133.

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 19, 2026

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented Apr 19, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
missing-argument 33 0 0
invalid-argument-type 0 28 0
unknown-argument 11 0 0
Total 44 28 0
Raw diff (72 changes)
bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `attr` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `cols` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `data` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `kind` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `model` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `msg_data` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `msg_type` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `new` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `patches` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `rollover` does not match any known parameter of `DocumentChangedEvent.__init__`
+ src/bokeh/document/events.py:244:56 error[unknown-argument] Argument `title` does not match any known parameter of `DocumentChangedEvent.__init__`

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

discord.py (https://github.com/Rapptz/discord.py)
- discord/ext/commands/help.py:1087:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `_CommandKwargs`, found `str`
- discord/ext/commands/help.py:1087:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `bool | None`, found `str`
- discord/ext/commands/help.py:1087:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `bool`, found `_CommandKwargs`
- discord/ext/commands/help.py:1378:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `_CommandKwargs`, found `str`
- discord/ext/commands/help.py:1378:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `bool | None`, found `str`
- discord/ext/commands/help.py:1378:26 error[invalid-argument-type] Argument to `HelpCommand.__init__` is incorrect: Expected `bool`, found `_CommandKwargs`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `MaxConcurrency`, found `str | None`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `bool`, found `MaxConcurrency`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `bool`, found `list[int]`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `bool`, found `str`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `dict[Any, Any]`, found `bool`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `str | None`, found `dict[Any, Any]`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `str`, found `bool`
- discord/ext/commands/hybrid.py:528:32 error[invalid-argument-type] Argument to `Command.__init__` is incorrect: Expected `str`, found `bool`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `MaxConcurrency`, found `str | None`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `bool`, found `MaxConcurrency`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `bool`, found `list[int]`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `bool`, found `str`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `dict[Any, Any]`, found `bool`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `str | None`, found `dict[Any, Any]`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `str`, found `bool`
- discord/ext/commands/hybrid.py:651:33 error[invalid-argument-type] Argument to `Group.__init__` is incorrect: Expected `str`, found `bool`
- discord/shard.py:380:50 error[invalid-argument-type] Argument to `Client.__init__` is incorrect: Expected `Status | None`, found `int | None`
- discord/shard.py:380:50 error[invalid-argument-type] Argument to `Client.__init__` is incorrect: Expected `int | None`, found `int | float | None`

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 `GraphQLDirective.__init__`
+ src/graphql/utilities/extend_schema.py:295:16 error[missing-argument] No arguments provided for required parameters `name`, `locations` of `GraphQLDirective.__init__`
+ src/graphql/utilities/extend_schema.py:333:23 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLInputField.__init__`
+ src/graphql/utilities/extend_schema.py:352:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:367:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:384:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:418:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:457:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:482:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/extend_schema.py:492:16 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLField.__init__`
+ src/graphql/utilities/extend_schema.py:502:16 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLArgument.__init__`
+ src/graphql/type/definition.py:291:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:443:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:547:16 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLField.__init__`
+ src/graphql/type/definition.py:693:16 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLArgument.__init__`
+ src/graphql/type/definition.py:773:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:877:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:980:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:1115:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:1347:16 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/type/definition.py:1444:16 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLInputField.__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:67:16 error[missing-argument] No arguments provided for required parameters `name`, `locations` of `GraphQLDirective.__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:78:26 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLArgument.__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:89:28 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLField.__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:102:19 error[missing-argument] No argument provided for required parameter `type_` of `GraphQLInputField.__init__`
+ src/graphql/utilities/lexicographic_sort_schema.py:123:20 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:131:20 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:139:20 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:143:20 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ src/graphql/utilities/lexicographic_sort_schema.py:159:20 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`
+ tests/type/test_definition.py:1280:17 error[missing-argument] No argument provided for required parameter `name` of constructor `GraphQLNamedType.__new__`

prefect (https://github.com/PrefectHQ/prefect)
+ src/prefect/cli/deployment.py:753:20 error[missing-argument] No argument provided for required parameter `interval` of `IntervalSchedule.__init__`

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

rotki (https://github.com/rotki/rotki)
- rotkehlchen/history/events/structures/solana_swap.py:111:17 error[invalid-argument-type] Argument to `SolanaSwapEvent.__init__` is incorrect: Expected `TimestampMS`, found `str | None`
- rotkehlchen/history/events/structures/solana_swap.py:111:17 error[invalid-argument-type] Argument to `SolanaSwapEvent.__init__` is incorrect: Expected `str | None`, found `Location`

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/unpack-validate branch from 532079f to 9bd701b Compare April 22, 2026 20:33
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