Skip to content

[ty] Preserve qualifiers in functional TypedDicts#24176

Merged
charliermarsh merged 1 commit intocharlie/typed-dict-1from
charlie/typed-dict-3
Mar 27, 2026
Merged

[ty] Preserve qualifiers in functional TypedDicts#24176
charliermarsh merged 1 commit intocharlie/typed-dict-1from
charlie/typed-dict-3

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

Summary

Part of: astral-sh/ty#3095.

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

astral-sh-bot bot commented Mar 25, 2026

Typing conformance results improved 🎉

The percentage of diagnostics emitted that were expected errors increased from 86.50% to 86.60%. The percentage of expected errors that received a diagnostic increased from 81.37% to 81.47%. The number of fully passing files improved from 67/132 to 69/132.

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 865 866 +1 ⏫ (✅)
False Positives 135 134 -1 ⏬ (✅)
False Negatives 198 197 -1 ⏬ (✅)
Total Diagnostics 1063 1063 +0
Precision 86.50% 86.60% +0.10% ⏫ (✅)
Recall 81.37% 81.47% +0.09% ⏫ (✅)
Passing Files 67/132 69/132 +2 ⏫ (✅)

Test file breakdown

2 files altered
File True Positives False Positives False Negatives Status
typeddicts_readonly.py 6 (+1) ✅ 0 0 (-1) ✅ ✅ Newly Passing 🎉
typeddicts_required.py 4 0 (-1) ✅ 0 ✅ Newly Passing 🎉
Total (all files) 866 (+1) ✅ 134 (-1) ✅ 197 (-1) ✅ 69/132

True positives added (1)

1 diagnostic
Test case Diff

typeddicts_readonly.py:36

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

False positives removed (1)

1 diagnostic
Test case Diff

typeddicts_required.py:74

-error[missing-typed-dict-key] Missing required key 'predecessor' in TypedDict `RecursiveMovie` constructor

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 25, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 712.12MB 713.17MB +0.15% (1.05MB)
sphinx 264.36MB 264.74MB +0.14% (385.16kB)
trio 117.42MB 117.65MB +0.20% (236.06kB)
flake8 47.88MB 47.93MB +0.10% (47.22kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_definition_types 88.42MB 89.42MB +1.14% (1.01MB)
infer_deferred_types 14.63MB 14.67MB +0.28% (41.50kB)

sphinx

Name Old New Diff Outcome
infer_definition_types 23.60MB 23.97MB +1.59% (384.16kB)
infer_deferred_types 5.61MB 5.61MB +0.02% (1.00kB)

trio

Name Old New Diff Outcome
infer_definition_types 7.49MB 7.72MB +3.06% (235.16kB)
infer_deferred_types 2.38MB 2.38MB +0.04% (928.00B)

flake8

Name Old New Diff Outcome
infer_definition_types 1.82MB 1.87MB +2.53% (47.22kB)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 25, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-await 0 40 0
missing-typed-dict-key 2 10 0
invalid-argument-type 2 2 0
invalid-return-type 0 2 0
Total 4 54 0

Changes in flaky projects detected. Raw diff output excludes flaky projects; see the HTML report for details.

Raw diff (17 changes)
archinstall (https://github.com/archlinux/archinstall)
- archinstall/lib/models/users.py:176:10 error[missing-typed-dict-key] Missing required key '!password' in TypedDict `UserSerialization` constructor
- archinstall/lib/models/users.py:176:10 error[invalid-return-type] Return type does not match returned value: expected `UserSerialization`, found `dict[str, str | Unknown | None | bool | list[str]]`

meson (https://github.com/mesonbuild/meson)
- mesonbuild/scripts/depscan.py:170:26 error[missing-typed-dict-key] Missing required key 'outputs' in TypedDict `Rule` constructor
- mesonbuild/scripts/depscan.py:182:45 error[invalid-argument-type] Argument to bound method `append` is incorrect: Expected `Require`, found `dict[str, str]`
- mesonbuild/scripts/depscan.py:182:45 error[missing-typed-dict-key] Missing required key 'compiled-module-path' in TypedDict `Require` constructor
- mesonbuild/scripts/depscan.py:182:45 error[missing-typed-dict-key] Missing required key 'lookup-method' in TypedDict `Require` constructor
- mesonbuild/scripts/depscan.py:182:45 error[missing-typed-dict-key] Missing required key 'source-path' in TypedDict `Require` constructor
- mesonbuild/scripts/depscan.py:182:45 error[missing-typed-dict-key] Missing required key 'unique-on-source-path' in TypedDict `Require` constructor
- mesonbuild/scripts/depscan.py:192:41 error[invalid-argument-type] Argument to bound method `append` is incorrect: Expected `Provide`, found `dict[str, str | Unknown]`
- mesonbuild/scripts/depscan.py:192:41 error[missing-typed-dict-key] Missing required key 'is-interface' in TypedDict `Provide` constructor
- mesonbuild/scripts/depscan.py:192:41 error[missing-typed-dict-key] Missing required key 'unique-on-source-path' in TypedDict `Provide` constructor
+ unittests/cargotests.py:333:33 error[invalid-argument-type] Argument to bound method `from_raw` is incorrect: Expected `Manifest`, found `dict[str, dict[str, str] | FromWorkspace]`
+ unittests/cargotests.py:333:45 error[missing-typed-dict-key] Missing required key 'version' in TypedDict `Package` constructor
+ unittests/cargotests.py:340:33 error[invalid-argument-type] Argument to bound method `from_raw` is incorrect: Expected `Manifest`, found `dict[str, dict[str, str]]`
+ unittests/cargotests.py:340:45 error[missing-typed-dict-key] Missing required key 'version' in TypedDict `Package` constructor

operator (https://github.com/canonical/operator)
- ops/pebble.py:2102:32 error[missing-typed-dict-key] Missing required key 'basic' in TypedDict `IdentityDict` constructor
- ops/pebble.py:2102:32 error[missing-typed-dict-key] Missing required key 'local' in TypedDict `IdentityDict` constructor

Full report with detailed diff (timing results)

@charliermarsh charliermarsh marked this pull request as ready for review March 25, 2026 16:28
@carljm carljm removed their request for review March 25, 2026 18:20
@sharkdp sharkdp removed their assignment Mar 25, 2026
@AlexWaygood
Copy link
Copy Markdown
Member

(looks like there's a fair bit of failing CI on this one rn)

@charliermarsh charliermarsh marked this pull request as draft March 25, 2026 20:56
@charliermarsh charliermarsh marked this pull request as ready for review March 25, 2026 21:12
@charliermarsh charliermarsh force-pushed the charlie/typed-dict-3 branch 2 times, most recently from a3e5ce7 to 3fab758 Compare March 26, 2026 01:05
Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Nice, this LGTM! One inline comment -- and also, as we discussed on Discord, it looks like this new field should probably go on DefinitionInferenceExtra rather than DefinitionInference

@charliermarsh charliermarsh force-pushed the charlie/typed-dict-2 branch 2 times, most recently from 86a9862 to 6369ebb Compare March 26, 2026 22:20
@charliermarsh charliermarsh force-pushed the charlie/typed-dict-3 branch 2 times, most recently from c6fd2aa to 4b4d514 Compare March 26, 2026 22:26
Base automatically changed from charlie/typed-dict-2 to charlie/typed-dict-1 March 27, 2026 00:49
@charliermarsh charliermarsh merged commit b1e747d into charlie/typed-dict-1 Mar 27, 2026
49 checks passed
@charliermarsh charliermarsh deleted the charlie/typed-dict-3 branch March 27, 2026 00:49
@charliermarsh
Copy link
Copy Markdown
Member Author

(Sorry, this accidentally got merged because I pushed a downstream local change to the upstream branch. I will re-open.)

charliermarsh added a commit that referenced this pull request Mar 30, 2026
## Summary

This PR adds basic support for functional TypedDict construction,
including recursive TypedDicts. The intent is to follow the patterns
we've established for functional NamedTuple and `type(...)` calls as
closely as we can.

There are two follow-up PRs that were carved out to make them easier to
review:

- #24226
- #24227

(My intent is to merge the stack once all three are approved. E.g., the
new false positive in the ecosystem test is fixed in
#24176.)

Part of: astral-sh/ty#3095.

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
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