Skip to content

[ty] Emit "arguments after ** must be a mapping" before overload resolution#22921

Merged
dhruvmanila merged 2 commits intoastral-sh:mainfrom
denyszhak:ty/kwargs-mapping-error
Jan 30, 2026
Merged

[ty] Emit "arguments after ** must be a mapping" before overload resolution#22921
dhruvmanila merged 2 commits intoastral-sh:mainfrom
denyszhak:ty/kwargs-mapping-error

Conversation

@denyszhak
Copy link
Contributor

@denyszhak denyszhak commented Jan 28, 2026

Summary

Closes astral-sh/ty#2653

Move the KeywordsNotAMapping check from bind.rs to builder.rs, matching the existing pattern for starred argument iterability. This ensures users see "must be a mapping type" instead of "no matching overload" when passing a non-mapping **kwargs to an overloaded function.

Test Plan

Added test case for **kwargs with non-mapping types on overloaded functions in function.md.

@denyszhak denyszhak force-pushed the ty/kwargs-mapping-error branch from 54c1d56 to 1f91ae1 Compare January 28, 2026 17:53
@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 28, 2026

Typing conformance results

No changes detected ✅

@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 28, 2026

mypy_primer results

Changes were detected when running on open source projects
scrapy (https://github.com/scrapy/scrapy)
- docs/_ext/scrapydocs.py:101:55: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ docs/_ext/scrapydocs.py:101:57: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`

antidote (https://github.com/Finistere/antidote)
- src/antidote/core/_inject.py:348:82: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
+ src/antidote/core/_inject.py:348:84: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`

mkdocs (https://github.com/mkdocs/mkdocs)
- mkdocs/tests/structure/page_tests.py:670:39: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | dict[Unknown | str, Unknown | str] | str | None | dict[Unknown, Unknown]`
+ mkdocs/tests/structure/page_tests.py:670:41: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | dict[Unknown | str, Unknown | str] | str | None | dict[Unknown, Unknown]`
- mkdocs/tests/structure/page_tests.py:693:39: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | dict[Unknown | str, Unknown | str] | str`
+ mkdocs/tests/structure/page_tests.py:693:41: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | dict[Unknown | str, Unknown | str] | str`

openlibrary (https://github.com/internetarchive/openlibrary)
- openlibrary/data/db.py:89:23: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `None`
+ openlibrary/data/db.py:89:25: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `None`

apprise (https://github.com/caronc/apprise)
- tests/test_plugin_discord.py:1120:25: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
+ tests/test_plugin_discord.py:1120:27: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
- tests/test_plugin_discord.py:1149:25: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
+ tests/test_plugin_discord.py:1149:27: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
- tests/test_plugin_discord.py:1170:25: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`
+ tests/test_plugin_discord.py:1170:27: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `dict[str, Any] | None`

cloud-init (https://github.com/canonical/cloud-init)
- tests/unittests/sources/test_smartos.py:823:13: error[no-matching-overload] No overload of bound method `format` matches arguments
+ tests/unittests/sources/test_smartos.py:826:19: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `property`

setuptools (https://github.com/pypa/setuptools)
- setuptools/tests/test_warnings.py:72:39: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | tuple[str, str] | dict[Unknown | str, Unknown | int | str] | ... omitted 3 union elements`
+ setuptools/tests/test_warnings.py:72:41: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | tuple[str, str] | dict[Unknown | str, Unknown | int | str] | ... omitted 3 union elements`
- setuptools/tests/test_wheel.py:677:13: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `str | dict[Unknown | str, Unknown] | dict[str, list[Unknown | str]] | Unknown`
+ setuptools/tests/test_wheel.py:677:15: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `str | dict[Unknown | str, Unknown] | dict[str, list[Unknown | str]] | Unknown`
- setuptools/tests/test_wheel.py:677:13: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `str | dict[Unknown | str, Unknown] | dict[str, list[Unknown | str]] | Unknown`
- Found 1144 diagnostics
+ Found 1143 diagnostics

scikit-learn (https://github.com/scikit-learn/scikit-learn)
- sklearn/gaussian_process/kernels.py:2360:13: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None | dict[Unknown, Unknown]`
+ sklearn/gaussian_process/kernels.py:2360:15: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None | dict[Unknown, Unknown]`
- sklearn/gaussian_process/kernels.py:2374:25: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None | dict[Unknown, Unknown]`
+ sklearn/gaussian_process/kernels.py:2374:27: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None | dict[Unknown, Unknown]`
- sklearn/impute/_iterative.py:427:45: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ sklearn/impute/_iterative.py:427:47: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`

strawberry (https://github.com/strawberry-graphql/strawberry)
- strawberry/federation/schema.py:209:48: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `FederationAny`
+ strawberry/federation/schema.py:209:50: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `FederationAny`

prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:94:28: error[invalid-assignment] Object of type `dict[str, Any] | int | dict[Any, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:99:28: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:86:21: error[invalid-assignment] Object of type `dict[str, Any] | int | dict[Any, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:87:21: error[invalid-assignment] Object of type `int | dict[Any, Any] | float | ... omitted 3 union elements` is not assignable to `dict[str, Any]`
- src/prefect/deployments/steps/core.py:137:38: error[invalid-argument-type] Argument is incorrect: Argument type `dict[str, Any] | int | dict[Any, Any] | ... omitted 4 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/input/run_input.py:254:28: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `(Any & ~AlwaysFalsy) | (Coroutine[Any, Any, Any] & ~AlwaysFalsy)`
+ src/prefect/input/run_input.py:254:30: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `(Any & ~AlwaysFalsy) | (Coroutine[Any, Any, Any] & ~AlwaysFalsy)`
- src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `dict[str, Any] | int | Unknown | ... omitted 4 union elements` on object of type `dict[str, Any]`
+ src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `Unknown | dict[str, Any]` on object of type `dict[str, Any]`
- src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | dict[str, Any] | int | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | dict[str, Any]]`
- src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:437:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `dict[object, Unknown]`
- src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown | int | float | ... omitted 4 union elements]`
+ src/prefect/utilities/templating.py:442:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_variables`, found `list[Unknown]`
+ src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `str | dict[str, Any]` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Argument type `dict[str, Any] | int | str | ... omitted 3 union elements` does not satisfy constraints (`str`, `int`, `int | float`, `bool`, `dict[Any, Any]`, `list[Any]`, `None`) of type variable `T`
- src/prefect/workers/base.py:234:20: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `int | Unknown | float | ... omitted 4 union elements`
- Found 5374 diagnostics
+ Found 5368 diagnostics

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ddtrace/internal/remoteconfig/client.py:124:48: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Signature`
+ ddtrace/internal/remoteconfig/client.py:124:50: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Signature`
- ddtrace/internal/remoteconfig/client.py:164:48: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Signature`
+ ddtrace/internal/remoteconfig/client.py:164:50: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Signature`
- ddtrace/internal/remoteconfig/client.py:191:51: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `TargetFile`
+ ddtrace/internal/remoteconfig/client.py:191:53: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `TargetFile`

ibis (https://github.com/ibis-project/ibis)
- ibis/backends/impala/__init__.py:1304:69: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `set[Unknown | str | dict[Unknown, Unknown]]`
+ ibis/backends/impala/__init__.py:1304:71: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `set[Unknown | str | dict[Unknown, Unknown]]`

pandas (https://github.com/pandas-dev/pandas)
- pandas/core/apply.py:199:50: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ pandas/core/apply.py:199:52: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/chain/decoding/tools.py:96:44: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- rotkehlchen/chain/decoding/tools.py:97:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress`, found `A@BaseDecoderTools`
+ rotkehlchen/chain/decoding/tools.py:99:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `Sequence[A@BaseDecoderTools]`, found `Unknown | tuple[BTCAddress, ...] | tuple[ChecksumAddress, ...] | tuple[SubstrateAddress, ...] | tuple[SolanaAddress, ...]`
- rotkehlchen/chain/decoding/tools.py:98:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress | None`, found `A@BaseDecoderTools | None`
+ rotkehlchen/chain/decoding/tools.py:100:62: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- Found 2051 diagnostics
+ Found 2052 diagnostics

core (https://github.com/home-assistant/core)
- homeassistant/util/variance.py:47:12: error[invalid-return-type] Return type does not match returned value: expected `(**_P@ignore_variance) -> _R@ignore_variance`, found `_Wrapped[_P@ignore_variance, int | _R@ignore_variance | float | datetime, _P@ignore_variance, _R@ignore_variance | int | float | datetime]`
- Found 14506 diagnostics
+ Found 14505 diagnostics

scipy (https://github.com/scipy/scipy)
- scipy/optimize/_differentiable_functions.py:45:17: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/optimize/_differentiable_functions.py:45:19: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
- scipy/optimize/_differentiable_functions.py:106:34: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/optimize/_differentiable_functions.py:106:36: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
- scipy/optimize/_differentiable_functions.py:458:17: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/optimize/_differentiable_functions.py:458:19: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
- scipy/optimize/_differentiable_functions.py:507:31: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/optimize/_differentiable_functions.py:507:33: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
- scipy/stats/_distribution_infrastructure.py:2088:30: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/stats/_distribution_infrastructure.py:2088:32: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
- scipy/stats/_distribution_infrastructure.py:2119:36: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`
+ scipy/stats/_distribution_infrastructure.py:2119:38: error[invalid-argument-type] Argument expression after ** must be a mapping type: Found `Unknown | None`

No memory usage changes detected ✅

for keyword in arguments.keywords.iter().filter(|k| k.arg.is_none()) {
let mapping_type = self.expression_type(&keyword.value);

let is_paramspec = matches!(
Copy link
Contributor Author

@denyszhak denyszhak Jan 28, 2026

Choose a reason for hiding this comment

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

Does it make sense to skip the mapping check for ParamSpec and Union[ParamSpec, Unknown] types here?

The test under pep695/paramspec.md would fail otherwise also

Flagging it here would be a premature false positive

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we'd need to skip those in here. It's a bit unfortunate that this would lead to duplicate code. If you can find a way to de-duplicate it, that would be great. I think one way would be to have a as_paramspec_typevar method which returns Option<Type> if it's P or P | Unknown.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use common method across all cases where this duplicated logic was used

@ntBre ntBre added the ty Multi-file analysis & type inference label Jan 28, 2026
@denyszhak
Copy link
Contributor Author

The failing CI is from pre-current version of the commit I believe

@denyszhak
Copy link
Contributor Author

This new check now points to the actual value (expression after **) not the whole **expr which seems more precise to be and okay?

For some cases like cloud-init it's now invalid-argument-type and not no-matching-overload

@dhruvmanila
Copy link
Member

This new check now points to the actual value (expression after **) not the whole **expr which seems more precise to be and okay?

Yeah, this seems good to me.

@denyszhak denyszhak requested a review from dhruvmanila January 29, 2026 12:29
@dhruvmanila dhruvmanila changed the title [ty] report **kwargs mapping error before overload resolution [ty] Emit "arguments after ** must be a mapping" before overload resolution Jan 30, 2026
@dhruvmanila dhruvmanila added the diagnostics Related to reporting of diagnostics. label Jan 30, 2026
Copy link
Member

@dhruvmanila dhruvmanila left a comment

Choose a reason for hiding this comment

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

ty for following through with this additional PR :)

@dhruvmanila dhruvmanila merged commit bc0427e into astral-sh:main Jan 30, 2026
80 of 81 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

diagnostics Related to reporting of diagnostics. ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ty] Better error message for **kwargs with non-mapping type in overloaded functions

3 participants