Skip to content

[ty] Preserve qualifiers in functional TypedDicts#24226

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

[ty] Preserve qualifiers in functional TypedDicts#24226
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 27, 2026
@charliermarsh
Copy link
Copy Markdown
Member Author

This is a revival of #24176 which was accidentally merged.

@charliermarsh charliermarsh marked this pull request as ready for review March 27, 2026 00:56
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 27, 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

Optional Diagnostics Changed (2)

2 diagnostics
Test case Diff

typeddicts_alt_syntax.py:44

-error[invalid-key] Unknown key "name" for TypedDict `Movie2`
-error[invalid-key] Unknown key "year" for TypedDict `Movie2`
+error[invalid-argument-type] Invalid argument to key "name" with declared type `<class 'str'>` on TypedDict `Movie2`: value of type `Literal["Blade Runner"]`
+error[invalid-argument-type] Invalid argument to key "year" with declared type `<class 'int'>` on TypedDict `Movie2`: value of type `Literal[1982]`

typeddicts_alt_syntax.py:45

-error[invalid-key] Unknown key "name" for TypedDict `Movie2`
-error[invalid-key] Unknown key "year" for TypedDict `Movie2`
+error[invalid-argument-type] Invalid argument to key "name" with declared type `<class 'str'>` on TypedDict `Movie2`: value of type `Literal["Blade Runner"]`
+error[invalid-argument-type] Invalid argument to key "year" with declared type `<class 'int'>` on TypedDict `Movie2`: value of type `Literal[""]`

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 27, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 715.40MB 716.45MB +0.15% (1.05MB)
sphinx 264.89MB 265.27MB +0.14% (385.16kB)
trio 117.73MB 117.96MB +0.20% (236.12kB)
flake8 48.04MB 48.09MB +0.10% (47.22kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
infer_definition_types 88.67MB 89.68MB +1.14% (1.01MB)
infer_deferred_types 14.68MB 14.72MB +0.28% (41.62kB)

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.50MB 7.72MB +3.06% (235.22kB)
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 27, 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)

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 27, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks
⏩ 30 skipped benchmarks1


Comparing charlie/typed-dict-3 (0792b00) with charlie/typed-dict-1 (3a532d7)2

Open in CodSpeed

Footnotes

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

  2. No successful run was found on charlie/typed-dict-1 (9f5784a) during the generation of this report, so 5dd40cd was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@carljm carljm removed their request for review March 27, 2026 03:11
Base automatically changed from charlie/typed-dict-2 to charlie/typed-dict-1 March 27, 2026 20:48
@charliermarsh charliermarsh merged commit c3fdbbc into charlie/typed-dict-1 Mar 27, 2026
29 checks passed
@charliermarsh charliermarsh deleted the charlie/typed-dict-3 branch March 27, 2026 20:49
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