Skip to content

[ty] Infer dict(**TypedDict) in TypedDict context#24709

Merged
charliermarsh merged 3 commits intomainfrom
charlie/unpack-dict
Apr 28, 2026
Merged

[ty] Infer dict(**TypedDict) in TypedDict context#24709
charliermarsh merged 3 commits intomainfrom
charlie/unpack-dict

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 19, 2026

Summary

Given, e.g., def f() -> TD: return dict(**src), we now infer dict(**src) as matching TD if src is TD, for example. So the following are accepted, whereas on main they all produce diagnostics:

from typing import TypedDict

class TD(TypedDict):
    x: int
    y: str

src: TD = {
    "x": 1,
    "y": "foo",
}

x: TD = dict(**src)

def f() -> TD: return dict(**src)

@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.70%. The percentage of expected errors that received a diagnostic held steady at 84.63%. The number of fully passing files held steady at 84/134.

@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

No diagnostic changes detected ✅

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

Full report with detailed diff (timing results)

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 19, 2026

Merging this PR will not alter performance

✅ 53 untouched benchmarks
⏩ 60 skipped benchmarks1


Comparing charlie/unpack-dict (bb3fc91) with main (5a74b37)

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.

Base automatically changed from charlie/unpack-literal to main April 22, 2026 15:11
@charliermarsh charliermarsh force-pushed the charlie/unpack-dict branch 2 times, most recently from c15a2b6 to e52290a Compare April 22, 2026 20:33
@charliermarsh charliermarsh marked this pull request as ready for review April 26, 2026 23:38
@carljm carljm removed their request for review April 27, 2026 00:25
@sharkdp
Copy link
Copy Markdown
Contributor

sharkdp commented Apr 27, 2026

The upstream issue says "This showed up in hydpy in the ecosystem report on #24653 …". Should we expect any hydpy ecosystem changes on this PR?

Also, the PR descriptions says "So the following are accepted […]", but I still get two errors for that snippet on this branch? (I guess it only works if src is annotated as TD?

Comment thread crates/ty_python_semantic/resources/mdtest/generics/pep695/aliases.md Outdated
@charliermarsh

This comment was marked as resolved.

@charliermarsh

This comment was marked as resolved.

@carljm

This comment was marked as resolved.

@charliermarsh charliermarsh marked this pull request as draft April 27, 2026 19:46
@charliermarsh charliermarsh marked this pull request as ready for review April 27, 2026 19:49
@astral-sh-bot astral-sh-bot Bot requested a review from sharkdp April 27, 2026 19:49
Copy link
Copy Markdown
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

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

Thank you.

Comment on lines +42 to +43
keyword_ty.is_dynamic()
|| keyword_ty.is_never()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you explain these two checks here? No tests fail when I remove them.

There is one test that looks like it's testing something similar (maybe_td), but it does not fail when I remove these. Neither does any other test.

Is the intent to cover something like this?

def _(unknown) -> None:
    td_unknown: TD = dict(**unknown)

If so, isn't there a more principled check that we could perform here instead? Like

Suggested change
keyword_ty.is_dynamic()
|| keyword_ty.is_never()
keyword_ty
.is_assignable_to(speculative_builder.db(), Type::TypedDict(typed_dict))

@charliermarsh charliermarsh merged commit 9742dcb into main Apr 28, 2026
57 checks passed
@charliermarsh charliermarsh deleted the charlie/unpack-dict branch April 28, 2026 13:55
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