[ty] Add support for functional namedtuple creation#22327
[ty] Add support for functional namedtuple creation#22327charliermarsh merged 12 commits intomainfrom
namedtuple creation#22327Conversation
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2026-01-14 17:03:27.870310683 +0000
+++ new-output.txt 2026-01-14 17:03:28.412310658 +0000
@@ -753,6 +753,16 @@
namedtuples_define_class.py:86:5: error[invalid-named-tuple] NamedTuple field without default value cannot follow field(s) with default value(s): Field `latitude` defined here without a default value
namedtuples_define_class.py:125:19: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `float`
namedtuples_define_class.py:132:24: error[invalid-named-tuple] NamedTuple class `Unit` cannot use multiple inheritance except with `Generic[]`
+namedtuples_define_functional.py:16:8: error[missing-argument] No argument provided for required parameter `y`
+namedtuples_define_functional.py:21:8: error[missing-argument] No arguments provided for required parameters `x`, `y`
+namedtuples_define_functional.py:26:21: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4
+namedtuples_define_functional.py:31:8: error[missing-argument] No argument provided for required parameter `y`
+namedtuples_define_functional.py:31:18: error[unknown-argument] Argument `z` does not match any known parameter
+namedtuples_define_functional.py:36:18: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]`
+namedtuples_define_functional.py:37:21: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4
+namedtuples_define_functional.py:42:18: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]`
+namedtuples_define_functional.py:43:15: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `float`
+namedtuples_define_functional.py:69:1: error[missing-argument] No argument provided for required parameter `a`
namedtuples_type_compat.py:22:23: error[invalid-assignment] Object of type `Point` is not assignable to `tuple[int, int]`
namedtuples_type_compat.py:23:28: error[invalid-assignment] Object of type `Point` is not assignable to `tuple[int, str, str]`
namedtuples_usage.py:34:7: error[index-out-of-bounds] Index 3 is out of bounds for tuple `Point` with length 3
@@ -885,6 +895,10 @@
qualifiers_final_annotation.py:81:1: error[invalid-assignment] Cannot assign to final attribute `DEFAULT_ID` on type `<class 'ClassB'>`
qualifiers_final_annotation.py:118:9: error[invalid-type-form] Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)
qualifiers_final_annotation.py:121:11: error[invalid-type-form] `Final` is not allowed in function parameter annotations
+qualifiers_final_annotation.py:134:1: error[missing-argument] No arguments provided for required parameters `x`, `y`
+qualifiers_final_annotation.py:134:3: error[unknown-argument] Argument `a` does not match any known parameter
+qualifiers_final_annotation.py:135:3: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]`
+qualifiers_final_annotation.py:135:9: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]`
qualifiers_final_annotation.py:141:5: error[invalid-assignment] Reassignment of `Final` symbol `ID1` is not allowed: Reassignment of `Final` symbol
qualifiers_final_annotation.py:145:5: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
qualifiers_final_annotation.py:147:10: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
@@ -1035,4 +1049,4 @@
typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie`: Unknown key "title"
typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions
-Found 1037 diagnostics
+Found 1051 diagnostics
|
|
ed8d9c7 to
4610c5a
Compare
f3b7fca to
6a035c0
Compare
|
(The conformance changes are good, but I'm still working through the ecosystem changes.) |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
missing-argument |
107 | 0 | 0 |
invalid-argument-type |
28 | 2 | 2 |
invalid-assignment |
14 | 1 | 2 |
possibly-missing-attribute |
1 | 0 | 9 |
unused-ignore-comment |
2 | 6 | 0 |
call-non-callable |
7 | 0 | 0 |
invalid-return-type |
1 | 1 | 1 |
not-subscriptable |
3 | 0 | 0 |
no-matching-overload |
1 | 0 | 0 |
too-many-positional-arguments |
1 | 0 | 0 |
unresolved-attribute |
1 | 0 | 0 |
unsupported-operator |
1 | 0 | 0 |
| Total | 167 | 10 | 14 |
6a035c0 to
c04ecea
Compare
|
I believe the So they create |
|
The Specifically, they set deferred defaults here: Query = namedtuple('Query', ['where', 'sort', 'group'])
Query.__new__.__defaults__ = ({}, [('_id', 1)], 'status') # type: ignore |
|
SciPy is similar -- deferred |
|
I think the Spack errors are true positives though? They're essentially creating a mock: https://github.com/spack/spack/blob/59b4e440391d5f58a665041ca365d9feb5caf472/lib/spack/spack/test/directives.py#L117-L119 |
c04ecea to
5220cf7
Compare
fe0f728 to
4aa86a6
Compare
4aa86a6 to
a182de6
Compare
AlexWaygood
left a comment
There was a problem hiding this comment.
I feel like for this:
from collections import namedtuple
NT = namedtuple("NT", "x y", defaults=(0,))
# revealed: `(cls: type, x: Any, y: Any = ...) -> NT`
reveal_type(NT.__new__)We should have enough information to reveal y: Any = 0 rather than y: Any = ... there? But that's minor enough that I'm happy for it to be tackled as a followup 😄
This reverts commit 5a69171.
| }; | ||
| // Convert value types to type expression types (e.g., class literals to instances). | ||
| let resolved_ty = | ||
| match field_type.in_type_expression(db, scope_id, typevar_binding_context) { |
There was a problem hiding this comment.
Ahh... I think (broadly) this is what I was maybe missing?
There was a problem hiding this comment.
(It's a bit sloppy of me to just call .ok() here TBH, it's possible we should be emitting diagnostics here... but I'm increasingly in a "let's just merge this already" mood on this one; the manual parsing for all this is nightmarishly complicated and we can always iterate on it in followups 😄)
There was a problem hiding this comment.
Agreed I'm happy to follow-up on all of these with smaller PRs!
|
(LGTM.) |
## Summary Follow-up from #22327.
Summary
This PR is intended to demonstrate how the pattern established in #22291 generalizes to other class "kinds".
Closes astral-sh/ty#1049.