-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Implement default values on NamedTuple #2719
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
Changes from all commits
40c32d3
def572f
8ce550a
6ba17b3
df36213
44f381f
c8715b8
6ad489b
291c059
88b63fb
1fe9792
51c3dc4
91acfc7
3df509a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: --python-version 3.6 | ||
|
@@ -349,9 +353,17 @@ import typing | |
|
||
class X(typing.NamedTuple): | ||
x: int | ||
y: str = 'y' # E: Right hand side values are not supported in NamedTuple | ||
z = None # type: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type" | ||
x[0]: int # E: Invalid statement in NamedTuple definition; expected "field_name: field_type" | ||
y = 1 | ||
x.x: int | ||
z: str = 'z' | ||
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] | ||
|
||
|
@@ -376,3 +388,97 @@ def f(a: Type[N]): | |
[builtins fixtures/list.pyi] | ||
[out] | ||
main:8: error: Unsupported type Type["N"] | ||
|
||
[case testNewNamedTupleWithDefaults] | ||
# flags: --fast-parser --python-version 3.6 | ||
from typing import List, NamedTuple, Optional | ||
|
||
class X(NamedTuple): | ||
x: int | ||
y: int = 2 | ||
|
||
reveal_type(X(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.X]' | ||
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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add few more test cases. For example, the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for these suggestions! The |
||
X(1, z=3) # E: Unexpected keyword argument "z" for "X" | ||
|
||
class HasNone(NamedTuple): | ||
x: int | ||
y: Optional[int] = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
reveal_type(HasNone(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.HasNone]' | ||
|
||
class Parameterized(NamedTuple): | ||
x: int | ||
y: List[int] = [1] + [2] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test case where the default value if |
||
z: List[int] = [] | ||
|
||
reveal_type(Parameterized(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.list[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]' | ||
UserDefined(1) # E: Argument 1 to "UserDefined" has incompatible type "int"; expected "Default" | ||
|
||
[builtins fixtures/list.pyi] | ||
|
||
[case testNewNamedTupleWithDefaultsStrictOptional] | ||
# flags: --fast-parser --strict-optional --python-version 3.6 | ||
from typing import List, NamedTuple, Optional | ||
|
||
class HasNone(NamedTuple): | ||
x: int | ||
y: Optional[int] = None | ||
|
||
reveal_type(HasNone(1)) # E: Revealed type is 'Tuple[builtins.int, Union[builtins.int, builtins.None], fallback=__main__.HasNone]' | ||
HasNone(None) # E: Argument 1 to "HasNone" has incompatible type None; expected "int" | ||
HasNone(1, y=None) | ||
HasNone(1, y=2) | ||
|
||
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] | ||
|
||
[case testNewNamedTupleWrongType] | ||
# flags: --fast-parser --python-version 3.6 | ||
from typing import NamedTuple | ||
|
||
class X(NamedTuple): | ||
x: int | ||
y: int = 'not an int' # E: Incompatible types in assignment (expression has type "str", variable has type "int") | ||
|
||
[case testNewNamedTupleErrorInDefault] | ||
# flags: --fast-parser --python-version 3.6 | ||
from typing import NamedTuple | ||
|
||
class X(NamedTuple): | ||
x: int = 1 + '1' # E: Unsupported operand types for + ("int" and "str") | ||
|
||
[case testNewNamedTupleInheritance] | ||
# flags: --fast-parser --python-version 3.6 | ||
from typing import NamedTuple | ||
|
||
class X(NamedTuple): | ||
x: str | ||
y: int = 3 | ||
|
||
class Y(X): | ||
def method(self) -> str: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test calling base class |
||
self.y | ||
return self.x | ||
|
||
reveal_type(Y('a')) # E: Revealed type is 'Tuple[builtins.str, builtins.int, fallback=__main__.Y]' | ||
Y(y=1, x='1').method() | ||
|
||
class CallsBaseInit(X): | ||
def __init__(self, x: str) -> None: | ||
super().__init__(x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default value could be itself
None
. So that I would usesentinel = object()
and thendefault_items.get(var.name(), sentinel)
and belowif default is sentinel
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Python default value could be
None
, but as I understand it mypy would use a value of typemypy.types.NoneTyp
internally. The previous code also passesNone
to indicate that theArgument
had no default, so I think this is fine.