Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support NamedTuple class syntax in all stubs #7646

Merged
merged 1 commit into from Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion mypy/semanal.py
Expand Up @@ -1109,7 +1109,8 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool:
# in the named tuple class body.
is_named_tuple, info = True, defn.info # type: bool, Optional[TypeInfo]
else:
is_named_tuple, info = self.named_tuple_analyzer.analyze_namedtuple_classdef(defn)
is_named_tuple, info = self.named_tuple_analyzer.analyze_namedtuple_classdef(
defn, self.is_stub_file)
if is_named_tuple:
if info is None:
self.mark_incomplete(defn.name, defn)
Expand Down
13 changes: 8 additions & 5 deletions mypy/semanal_namedtuple.py
Expand Up @@ -41,7 +41,8 @@ def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None:
self.options = options
self.api = api

def analyze_namedtuple_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[TypeInfo]]:
def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool
) -> Tuple[bool, Optional[TypeInfo]]:
"""Analyze if given class definition can be a named tuple definition.

Return a tuple where first item indicates whether this can possibly be a named tuple,
Expand All @@ -52,7 +53,7 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[Ty
if isinstance(base_expr, RefExpr):
self.api.accept(base_expr)
if base_expr.fullname == 'typing.NamedTuple':
result = self.check_namedtuple_classdef(defn)
result = self.check_namedtuple_classdef(defn, is_stub_file)
if result is None:
# This is a valid named tuple, but some types are incomplete.
return True, None
Expand All @@ -68,8 +69,10 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[Ty
# This can't be a valid named tuple.
return False, None

def check_namedtuple_classdef(
self, defn: ClassDef) -> Optional[Tuple[List[str], List[Type], Dict[str, Expression]]]:
def check_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool
) -> Optional[Tuple[List[str],
List[Type],
Dict[str, Expression]]]:
"""Parse and validate fields in named tuple class definition.

Return a three tuple:
Expand All @@ -78,7 +81,7 @@ def check_namedtuple_classdef(
* field default values
or None, if any of the types are not ready.
"""
if self.options.python_version < (3, 6):
if self.options.python_version < (3, 6) and not is_stub_file:
self.fail('NamedTuple class syntax is only supported in Python 3.6', defn)
return [], [], {}
if len(defn.base_type_exprs) > 1:
Expand Down
18 changes: 18 additions & 0 deletions test-data/unit/check-namedtuple.test
Expand Up @@ -44,6 +44,24 @@ x.x
x.y
x.z # E: "X" has no attribute "z"

[case testNamedTupleClassPython35]
# flags: --python-version 3.5
from typing import NamedTuple

class A(NamedTuple):
x = 3 # type: int
[out]
main:4: error: NamedTuple class syntax is only supported in Python 3.6

[case testNamedTupleClassInStubPython35]
# flags: --python-version 3.5
import foo

[file foo.pyi]
from typing import NamedTuple

class A(NamedTuple):
x: int

[case testNamedTupleAttributesAreReadOnly]
from collections import namedtuple
Expand Down