Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion mypy/semanal_namedtuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,10 @@ def add_field(
var._fullname = f"{info.fullname}.{var.name}"
info.names[var.name] = SymbolTableNode(MDEF, var)

fields = [Var(item, typ) for item, typ in zip(items, types)]
fields = [
Var(item, self.api.named_type("collections._tuplegetter", [typ]))
for item, typ in zip(items, types)
]
for var in fields:
add_field(var, is_property=True)
# We can't share Vars between fields and method arguments, since they
Expand Down
4 changes: 2 additions & 2 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2097,7 +2097,7 @@ class Y(TypedDict):
@collect_cases
def test_named_tuple(self) -> Iterator[Case]:
yield Case(
stub="from typing import NamedTuple",
stub="from typing import NamedTuple; import collections",
Copy link
Member

@brianschubert brianschubert Jul 21, 2025

Choose a reason for hiding this comment

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

Instead, let's add import collections to stubtest_typing_stub

runtime="from typing import NamedTuple",
error=None,
)
Expand Down Expand Up @@ -2133,7 +2133,7 @@ class X2(NamedTuple):
@collect_cases
def test_named_tuple_typing_and_collections(self) -> Iterator[Case]:
yield Case(
stub="from typing import NamedTuple",
stub="from typing import NamedTuple; import collections",
runtime="from collections import namedtuple",
error=None,
)
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ def typeddict() -> Sequence[D]:

a = (a.A(), A())
a.x # E: "tuple[a.A, b.A]" has no attribute "x"
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
Copy link
Member

Choose a reason for hiding this comment

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

Instead of replacing these fixtures and expanding fixtures/tuple.pyi, I'd suggest leaving them as-is and adding import collections to the fixtures that are used in named tuple tests. That should help reduce the diff

[typing fixtures/typing-full.pyi]

[case testReturnAnyFromFunctionDeclaredToReturnObject]
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-callable.test
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def g(o: Thing) -> None:
i + s # E: Unsupported operand types for + ("str" and "int")
o(1,2,3)

[builtins fixtures/callable.pyi]
[builtins fixtures/tuple.pyi]

[case testCallableNoArgs]

Expand Down
80 changes: 65 additions & 15 deletions test-data/unit/check-class-namedtuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ i, i = l[0] # E: Need more than 1 value to unpack (2 expected)
l = [A(1)]
a = (1,) # E: Incompatible types in assignment (expression has type "tuple[int]", \
variable has type "A")
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleMissingClassAttribute]
from typing import NamedTuple
Expand Down Expand Up @@ -259,7 +259,7 @@ class C(B): pass
B(1).b
C(2).b

[builtins fixtures/property.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleAsDict]
from typing import NamedTuple, Any
Expand All @@ -271,7 +271,7 @@ class X(NamedTuple):
x: X
reveal_type(x._asdict()) # N: Revealed type is "builtins.dict[builtins.str, Any]"

[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleReplaceTyped]
from typing import NamedTuple
Expand Down Expand Up @@ -301,7 +301,7 @@ reveal_type(X._field_defaults) # N: Revealed type is "builtins.dict[builtins.st
# but it's inferred as `Mapping[str, object]` here due to the fixture we're using
reveal_type(X.__annotations__) # N: Revealed type is "typing.Mapping[builtins.str, builtins.object]"

[builtins fixtures/dict-full.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleUnit]
from typing import NamedTuple
Expand All @@ -326,7 +326,7 @@ class Y(NamedTuple):

reveal_type([X(3, 'b'), Y(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"

[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleJoinTuple]
from typing import NamedTuple
Expand All @@ -338,7 +338,7 @@ class X(NamedTuple):
reveal_type([(3, 'b'), X(1, 'a')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"
reveal_type([X(1, 'a'), (3, 'b')]) # N: Revealed type is "builtins.list[tuple[builtins.int, builtins.str]]"

[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleWithTooManyArguments]
from typing import NamedTuple
Expand All @@ -358,7 +358,7 @@ class X(typing.NamedTuple):
x.x: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]"
z: str = 'z'
aa: int # E: Non-default NamedTuple fields cannot follow default fields
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleWithoutTypesSpecified]
from typing import NamedTuple
Expand All @@ -377,7 +377,7 @@ class N(NamedTuple):

def f(a: Type[N]):
a() # E: Missing positional arguments "x", "y" in call to "N"
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleWithDefaults]
from typing import List, NamedTuple, Optional
Expand Down Expand Up @@ -416,7 +416,7 @@ reveal_type(UserDefined()) # N: Revealed type is "tuple[__main__.Default, fallb
reveal_type(UserDefined(Default())) # N: Revealed type is "tuple[__main__.Default, fallback=__main__.UserDefined]"
UserDefined(1) # E: Argument 1 to "UserDefined" has incompatible type "int"; expected "Default"

[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleWithDefaultsStrictOptional]
from typing import List, NamedTuple, Optional
Expand All @@ -434,7 +434,7 @@ class CannotBeNone(NamedTuple):
x: int
y: int = None # E: Incompatible types in assignment (expression has type "None", variable has type "int")

[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleWrongType]
from typing import NamedTuple
Expand All @@ -448,7 +448,8 @@ class X(NamedTuple):
from typing import NamedTuple

class X(NamedTuple):
x: int = 1 + '1' # E: Unsupported left operand type for + ("int")
x: int = 1 + '1' # E: Incompatible types in assignment (expression has type "str", variable has type "int") \
# E: Unsupported operand types for + ("int" and "str")
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleInheritance]
Expand Down Expand Up @@ -607,7 +608,7 @@ class ReuseCallableNamed(NamedTuple):
def z(self) -> int: # E: Name "z" already defined on line 31
return 0

[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleDocString]
from typing import NamedTuple
Expand Down Expand Up @@ -639,7 +640,7 @@ class HasClassMethod(NamedTuple):
reveal_type(HasClassMethod) # N: Revealed type is "def (x: builtins.str) -> tuple[builtins.str, fallback=__main__.HasClassMethod]"
return cls(x=f)

[builtins fixtures/classmethod.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleStaticMethod]
from typing import NamedTuple
Expand All @@ -651,7 +652,7 @@ class HasStaticMethod(NamedTuple):
def new(f: str) -> 'HasStaticMethod':
return HasStaticMethod(x=f)

[builtins fixtures/classmethod.pyi]
[builtins fixtures/tuple.pyi]

[case testNewNamedTupleProperty]
from typing import NamedTuple
Expand All @@ -664,7 +665,7 @@ class HasStaticMethod(NamedTuple):
reveal_type(self) # N: Revealed type is "tuple[builtins.str, fallback=__main__.HasStaticMethod]"
return 4

[builtins fixtures/property.pyi]
[builtins fixtures/tuple.pyi]

[case testTypingExtensionsNamedTuple]
from typing_extensions import NamedTuple
Expand All @@ -683,3 +684,52 @@ reveal_type(y) # N: Revealed type is "builtins.int"
point.y = 6 # E: Property "y" defined in "Point" is read-only

[builtins fixtures/tuple.pyi]

[case testNamedTupleClassAttributeAccess]
from typing import NamedTuple

class A(NamedTuple):
x: str

reveal_type(A.x) # N: Revealed type is "collections._tuplegetter[builtins.str]"
A.x + 3 # E: Unsupported left operand type for + ("_tuplegetter[str]")
A.y # E: "type[A]" has no attribute "y"

[builtins fixtures/tuple.pyi]

[case testNamedTupleTupleGetterIsCompatibleWithObject]
from typing import NamedTuple

class A(NamedTuple):
x: str = "x"

a: object = A.x
b: int = A.x # E: Incompatible types in assignment (expression has type "_tuplegetter[str]", variable has type "int")
[builtins fixtures/tuple.pyi]

[case testNamedTupleTupleGetterNested]
from typing import NamedTuple

class Inner(NamedTuple):
x: int

class Outer(NamedTuple):
y: Inner

reveal_type(Inner.x) # N: Revealed type is "collections._tuplegetter[builtins.int]"
reveal_type(Outer.y) # N: Revealed type is "collections._tuplegetter[tuple[builtins.int, fallback=__main__.Inner]]"
[builtins fixtures/tuple.pyi]

[case testNamedTupleTupleGetterGeneric]
from typing import Generic
from typing import NamedTuple
from typing import TypeVar

T = TypeVar("T")

class GenericTuple(NamedTuple, Generic[T]):
x: T

reveal_type(GenericTuple.x) # E: Access to generic instance variables via class is ambiguous \
# N: Revealed type is "collections._tuplegetter[Any]"
[builtins fixtures/tuple.pyi]
20 changes: 10 additions & 10 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -3868,7 +3868,7 @@ from typing import Type, NamedTuple
N = NamedTuple('N', [('x', int), ('y', int)])
def f(a: Type[N]):
a()
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]
[out]
main:4: error: Missing positional arguments "x", "y" in call to "N"

Expand Down Expand Up @@ -5346,7 +5346,7 @@ class M(NamedTuple):
n: N
m: M
lst = [n, m]
[builtins fixtures/isinstancelist.pyi]
[builtins fixtures/tuple.pyi]

[case testCorrectJoinOfSelfRecursiveTypedDicts]
from typing import TypedDict
Expand Down Expand Up @@ -5377,7 +5377,7 @@ def parse_ast(name_dict: NameDict) -> None:
if isinstance(name_dict[''], int):
pass
reveal_type(name_dict['test']) # N: Revealed type is "tuple[builtins.bool, fallback=__main__.NameInfo]"
[builtins fixtures/isinstancelist.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-medium.pyi]

[case testCrashInForwardRefToTypedDictWithIsinstance]
Expand Down Expand Up @@ -5600,7 +5600,7 @@ class B(object):
return self.a.x

reveal_type(x.x) # N: Revealed type is "builtins.int"
[builtins fixtures/property.pyi]
[builtins fixtures/tuple.pyi]
[out]

[case testCorrectIsinstanceWithForwardUnion]
Expand All @@ -5613,7 +5613,7 @@ def f(x: ForwardUnion) -> None:
reveal_type(x) # N: Revealed type is "Union[tuple[builtins.int, fallback=__main__.TP], builtins.int]"
if isinstance(x, TP):
reveal_type(x) # N: Revealed type is "tuple[builtins.int, fallback=__main__.TP]"
[builtins fixtures/isinstance.pyi]
[builtins fixtures/tuple.pyi]
[out]

[case testCrashInvalidArgsSyntheticClassSyntax]
Expand All @@ -5626,7 +5626,7 @@ class NM(NamedTuple):
# These two should never crash, reveals are in the next test
TD({'x': []})
NM(x=[])
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]
[out]

Expand All @@ -5645,7 +5645,7 @@ reveal_type(x) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.li
reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})"
reveal_type(y) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]"
reveal_type(y1) # N: Revealed type is "tuple[builtins.list[Any], fallback=__main__.NM]"
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]
[out]

Expand All @@ -5659,7 +5659,7 @@ NT = NewType('NT', List[int, str]) # E: "list" expects 1 type argument, but 2 gi
TD({'x': []})
NM(x=[])
NT([])
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]
[out]

Expand All @@ -5677,7 +5677,7 @@ x: A1
y: A2
reveal_type(x.b) # N: Revealed type is "__main__.B"
reveal_type(y['b']) # N: Revealed type is "__main__.B"
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]
[out]

Expand All @@ -5691,7 +5691,7 @@ x: A1
y: A2
reveal_type(x.b) # N: Revealed type is "__main__.B"
reveal_type(y['b']) # N: Revealed type is "__main__.B"
[builtins fixtures/dict.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]
[out]

Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-custom-plugin.test
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ reveal_type(FullyQualifiedTestNamedTuple('')._asdict()) # N: Revealed type is "b
[file mypy.ini]
\[mypy]
plugins=<ROOT>/test-data/unit/plugins/fully_qualified_test_hook.py
[builtins fixtures/classmethod.pyi]
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-typeddict.pyi]

[case testDynamicClassPlugin]
Expand Down
1 change: 0 additions & 1 deletion test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -2452,7 +2452,6 @@ class Foo(Enum):
# E: Incompatible types in assignment (expression has type "<typing special form>", variable has type "Foo")
Baz: Any = Callable[[Dict[str, "Missing"]], None] # E: Enum members must be left unannotated \
# N: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members \
# E: Type application targets a non-generic function or class \
# E: Name "Missing" is not defined

reveal_type(Foo.Bar) # N: Revealed type is "Literal[__main__.Foo.Bar]?"
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,7 @@ class Custom(Base):
Base(int()) == int() # E: Non-overlapping equality check (left operand type: "Base", right operand type: "int")
Base(int()) == tuple()
Custom(int()) == int()
[builtins fixtures/bool.pyi]
[builtins fixtures/tuple.pyi]

[case testCustomEqCheckStrictEqualityMeta]
# flags: --strict-equality
Expand Down Expand Up @@ -2459,7 +2459,7 @@ def f() -> int: # E: Missing return statement
from typing import TypeVar
T = TypeVar("T")
x: int
x + T # E: Unsupported left operand type for + ("int")
x + T # E: Unsupported operand types for + ("int" and "TypeVar")
T() # E: "TypeVar" not callable
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]
6 changes: 3 additions & 3 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ from missing import Unchecked

Point = NamedTuple('Point', [('x', List[Unchecked]),
('y', Unchecked)])
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]
[out]
main:5: error: NamedTuple type becomes "tuple[list[Any], Any]" due to an unfollowed import

Expand Down Expand Up @@ -1315,7 +1315,7 @@ Point = NamedTuple('Point', [('x', int), ('y', int)]) # no error

def origin() -> Point:
return Point(x=0, y=0)
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testDisallowAnyExprNewType]
# flags: --disallow-any-expr
Expand Down Expand Up @@ -1908,7 +1908,7 @@ z = cast(List[Any], x) # E: Explicit "Any" is not allowed [explicit-any]
from typing import Any, List, NamedTuple

Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) # E: Explicit "Any" is not allowed [explicit-any]
[builtins fixtures/list.pyi]
[builtins fixtures/tuple.pyi]

[case testDisallowAnyExplicitTypeVarConstraint]
# flags: --disallow-any-explicit --show-error-codes
Expand Down
Loading
Loading