Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
add more tests, add _field_defaults
  • Loading branch information
JelleZijlstra committed Jan 23, 2017
commit df36213e546e0aefff8ba53c7ff826f90c8652dc
1 change: 1 addition & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,7 @@ def add_field(var: Var, is_initialized_in_class: bool = False,
tuple_of_strings = TupleType([strtype for _ in items], basetuple_type)
add_field(Var('_fields', tuple_of_strings), is_initialized_in_class=True)
add_field(Var('_field_types', dictype), is_initialized_in_class=True)
add_field(Var('_field_defaults', dictype), is_initialized_in_class=True)
add_field(Var('_source', strtype), is_initialized_in_class=True)

tvd = TypeVarDef('NT', 1, [], info.tuple_type)
Expand Down
55 changes: 46 additions & 9 deletions test-data/unit/check-class-namedtuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ class X(NamedTuple):
y: str

reveal_type(X._fields) # E: Revealed type is 'Tuple[builtins.str, builtins.str]'
reveal_type(X._field_types) # E: Revealed type is 'builtins.dict[builtins.str, Any]'
reveal_type(X._field_defaults) # E: Revealed type is 'builtins.dict[builtins.str, Any]'

[builtins fixtures/dict.pyi]

[case testNewNamedTupleUnit]
# flags: --fast-parser --python-version 3.6
Expand Down Expand Up @@ -349,10 +353,17 @@ import typing

class X(typing.NamedTuple):
x: int
y = None # type: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type"
x.__init__: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type"
y = 1
x.x: int
z: str = 'z'
aa: int # E: Non-default NamedTuple fields cannot follow default fields
aa: int

[out]
main:6: error: Invalid statement in NamedTuple definition; expected "field_name: field_type"
main:7: error: Invalid statement in NamedTuple definition; expected "field_name: field_type"
main:7: error: Type cannot be declared in assignment to non-self attribute
main:7: error: "int" has no attribute "x"
main:9: error: Non-default NamedTuple fields cannot follow default fields

[builtins fixtures/list.pyi]

Expand Down Expand Up @@ -380,7 +391,7 @@ main:8: error: Unsupported type Type["N"]

[case testNewNamedTupleWithDefaults]
# flags: --fast-parser --python-version 3.6
from typing import NamedTuple
from typing import List, NamedTuple, Optional

class X(NamedTuple):
x: int
Expand All @@ -390,6 +401,31 @@ reveal_type(X(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fall
reveal_type(X(1, 2)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.X]'

X(1, 'a') # E: Argument 2 to "X" has incompatible type "str"; expected "int"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would add few more test cases. For example, the None default value mentioned above, user defined classes as field types, wrong types like x: str = 5, etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it makes sense to add tests with classes inheriting form a named tuple with default values.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for these suggestions! The x: str = 5 case actually wasn't checked, and I had some trouble coming up with a way to make that throw an error, because the body of the NamedTuple is currently almost completely ignored. I managed to fix it by typechecking the body like a normal class. This has the side effect that some errors (stuff like x[0]: int) produce multiple errors on the same line, but that doesn't seem like a big deal.

X(1, z=3) # E: Unexpected keyword argument "z" for "X"

class HasNone(NamedTuple):
x: int
y: Optional[int] = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for writing more tests. I will be even more happy if you add few more tests for these (also with --strict-optional)


reveal_type(HasNone(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.HasNone]'

class Parameterized(NamedTuple):
x: int
y: List[int] = []

reveal_type(Parameterized(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.list[builtins.int], fallback=__main__.Parameterized]'
Parameterized(1, ['not an int']) # E: List item 0 has incompatible type "str"

class Default:
pass

class UserDefined(NamedTuple):
x: Default = Default()

reveal_type(UserDefined()) # E: Revealed type is 'Tuple[__main__.Default, fallback=__main__.UserDefined]'
reveal_type(UserDefined(Default())) # E: Revealed type is 'Tuple[__main__.Default, fallback=__main__.UserDefined]'

[builtins fixtures/list.pyi]

[case testNewNamedTupleWrongType]
# flags: --fast-parser --python-version 3.6
Expand All @@ -401,14 +437,15 @@ class X(NamedTuple):

[case testNewNamedTupleInheritance]
# flags: --fast-parser --python-version 3.6
from typing import NamedTuple
from typing import cast, NamedTuple

class X(NamedTuple):
x: int
x: str
y: int = 3

class Y(X):
def method(self) -> int:
return self.x + self.y
def method(self) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Test calling base class __init__ in a subclass of a named tuple.

return self.x + cast(str, self.y)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To be honest, I don't like casts of unsafe expressions in tests, but formally this test works as it should.
Maybe you could modify it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll just change it to access self.y without using it. I did str(self.y) first but that doesn't work because the fixture doesn't have it.


reveal_type(Y(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.Y]'
reveal_type(Y('a')) # E: Revealed type is 'Tuple[builtins.str, builtins.int, fallback=__main__.Y]'
Y(y=1, x='1').method()