Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions conformance/results/mypy/namedtuples_define_class.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ namedtuples_define_class.py:46: error: Argument 2 to "Point" has incompatible ty
namedtuples_define_class.py:47: error: Argument "units" to "Point" has incompatible type "int"; expected "str" [arg-type]
namedtuples_define_class.py:48: error: Too many arguments for "Point" [call-arg]
namedtuples_define_class.py:49: error: Unexpected keyword argument "other" for "Point" [call-arg]
namedtuples_define_class.py:59: error: Non-default NamedTuple fields cannot follow default fields [misc]
namedtuples_define_class.py:98: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type]
namedtuples_define_class.py:105: error: NamedTuple should be a single base [misc]
namedtuples_define_class.py:59: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]
namedtuples_define_class.py:65: error: Missing positional argument "units" in call to "Point2" [call-arg]
namedtuples_define_class.py:67: error: Too many values to unpack (2 expected, 3 provided) [misc]
namedtuples_define_class.py:76: error: NamedTuple field name cannot start with an underscore: _y [misc]
namedtuples_define_class.py:86: error: Non-default NamedTuple fields cannot follow default fields [misc]
namedtuples_define_class.py:125: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type]
namedtuples_define_class.py:132: error: NamedTuple should be a single base [misc]
"""
conformance_automated = "Fail"
errors_diff = """
Line 79: Expected 1 errors
Line 69: Expected 1 errors
Line 106: Expected 1 errors
Line 59: Unexpected errors ['namedtuples_define_class.py:59: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]']
Line 65: Unexpected errors ['namedtuples_define_class.py:65: error: Missing positional argument "units" in call to "Point2" [call-arg]']
Line 67: Unexpected errors ['namedtuples_define_class.py:67: error: Too many values to unpack (2 expected, 3 provided) [misc]']
"""
3 changes: 2 additions & 1 deletion conformance/results/mypy/namedtuples_define_functional.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namedtuples_define_functional.py:43: error: Argument "x" to "Point6" has incompa
namedtuples_define_functional.py:52: error: "namedtuple()" has duplicate field name "a" [misc]
namedtuples_define_functional.py:53: error: "namedtuple()" field name "def" is a keyword [misc]
namedtuples_define_functional.py:54: error: "namedtuple()" field name "def" is a keyword [misc]
namedtuples_define_functional.py:66: error: Missing positional argument "a" in call to "NT5" [call-arg]
namedtuples_define_functional.py:55: error: "namedtuple()" field name "_d" starts with an underscore [misc]
namedtuples_define_functional.py:69: error: Missing positional argument "a" in call to "NT7" [call-arg]
"""
conformance_automated = "Pass"
errors_diff = """
Expand Down
17 changes: 11 additions & 6 deletions conformance/results/pyrefly/namedtuples_define_class.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
conformant = "Pass"
conformance_automated = "Pass"
conformant = "Partial"
notes = """
Doesn't catch illegal field name starting with underscore
"""
conformance_automated = "Fail"
errors_diff = """
Line 76: Expected 1 errors
"""
output = """
ERROR namedtuples_define_class.py:32:7-12: Index 3 out of range for tuple with 3 elements [index-error]
Expand All @@ -11,8 +15,9 @@ ERROR namedtuples_define_class.py:46:15-17: Argument `Literal['']` is not assign
ERROR namedtuples_define_class.py:47:24-25: Argument `Literal[3]` is not assignable to parameter `units` with type `str` in function `Point.__new__` [bad-argument-type]
ERROR namedtuples_define_class.py:48:22-24: Expected 3 positional arguments, got 4 in function `Point.__new__` [bad-argument-count]
ERROR namedtuples_define_class.py:49:23-28: Unexpected keyword argument `other` in function `Point.__new__` [unexpected-keyword]
ERROR namedtuples_define_class.py:59:5-13: NamedTuple field 'latitude' without a default may not follow NamedTuple field with a default [bad-class-definition]
ERROR namedtuples_define_class.py:79:5-6: Cannot override named tuple element `x` [bad-override]
ERROR namedtuples_define_class.py:98:19-22: Argument `float` is not assignable to parameter `value` with type `str` in function `Property.__new__` [bad-argument-type]
ERROR namedtuples_define_class.py:105:7-11: Named tuples do not support multiple inheritance [invalid-inheritance]
ERROR namedtuples_define_class.py:69:20-22: Expected 2 positional arguments, got 3 in function `Point2.__new__` [bad-argument-count]
ERROR namedtuples_define_class.py:86:5-13: NamedTuple field 'latitude' without a default may not follow NamedTuple field with a default [bad-class-definition]
ERROR namedtuples_define_class.py:106:5-6: Cannot override named tuple element `x` [bad-override]
ERROR namedtuples_define_class.py:125:19-22: Argument `float` is not assignable to parameter `value` with type `str` in function `Property.__new__` [bad-argument-type]
ERROR namedtuples_define_class.py:132:7-11: Named tuples do not support multiple inheritance [invalid-inheritance]
"""
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ ERROR namedtuples_define_functional.py:43:17-20: Argument `float` is not assigna
ERROR namedtuples_define_functional.py:52:31-34: Duplicate field `a` [bad-class-definition]
ERROR namedtuples_define_functional.py:53:33-38: `def` is not a valid identifier [bad-class-definition]
ERROR namedtuples_define_functional.py:54:33-38: `def` is not a valid identifier [bad-class-definition]
ERROR namedtuples_define_functional.py:66:4-6: Missing argument `a` in function `NT5.__new__` [missing-argument]
ERROR namedtuples_define_functional.py:55:33-37: NamedTuple field name may not start with an underscore: `_d` [bad-class-definition]
ERROR namedtuples_define_functional.py:69:4-6: Missing argument `a` in function `NT7.__new__` [missing-argument]
"""
10 changes: 6 additions & 4 deletions conformance/results/pyright/namedtuples_define_class.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ namedtuples_define_class.py:47:24 - error: Argument of type "Literal[3]" cannot
  "Literal[3]" is not assignable to "str" (reportArgumentType)
namedtuples_define_class.py:48:22 - error: Expected 3 positional arguments (reportCallIssue)
namedtuples_define_class.py:49:23 - error: No parameter named "other" (reportCallIssue)
namedtuples_define_class.py:59:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues)
namedtuples_define_class.py:79:5 - error: Cannot override "x" because parent class "Point" is a named tuple (reportIncompatibleVariableOverride)
namedtuples_define_class.py:98:19 - error: Argument of type "float" cannot be assigned to parameter "value" of type "str" in function "__new__"
namedtuples_define_class.py:69:20 - error: Expected 2 positional arguments (reportCallIssue)
namedtuples_define_class.py:76:5 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues)
namedtuples_define_class.py:86:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues)
namedtuples_define_class.py:106:5 - error: Cannot override "x" because parent class "Point" is a named tuple (reportIncompatibleVariableOverride)
namedtuples_define_class.py:125:19 - error: Argument of type "float" cannot be assigned to parameter "value" of type "str" in function "__new__"
  "float" is not assignable to "str" (reportArgumentType)
namedtuples_define_class.py:105:7 - error: Multiple inheritance with NamedTuple is not supported (reportGeneralTypeIssues)
namedtuples_define_class.py:132:7 - error: Multiple inheritance with NamedTuple is not supported (reportGeneralTypeIssues)
"""
conformance_automated = "Pass"
errors_diff = """
Expand Down
11 changes: 8 additions & 3 deletions conformance/results/pyright/namedtuples_define_functional.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
conformant = "Pass"
conformant = "Partial"
output = """
namedtuples_define_functional.py:16:8 - error: Argument missing for parameter "y" (reportCallIssue)
namedtuples_define_functional.py:21:8 - error: Arguments missing for parameters "x", "y" (reportCallIssue)
Expand All @@ -15,8 +15,13 @@ namedtuples_define_functional.py:43:17 - error: Argument of type "float" cannot
namedtuples_define_functional.py:52:31 - error: Names within a named tuple must be unique (reportGeneralTypeIssues)
namedtuples_define_functional.py:53:33 - error: Field names cannot be a keyword (reportGeneralTypeIssues)
namedtuples_define_functional.py:54:33 - error: Field names cannot be a keyword (reportGeneralTypeIssues)
namedtuples_define_functional.py:66:1 - error: Argument missing for parameter "a" (reportCallIssue)
namedtuples_define_functional.py:55:33 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues)
namedtuples_define_functional.py:59:33 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues)
namedtuples_define_functional.py:60:13 - error: No parameter named "_1" (reportCallIssue)
namedtuples_define_functional.py:69:1 - error: Argument missing for parameter "a" (reportCallIssue)
"""
conformance_automated = "Pass"
conformance_automated = "Fail"
errors_diff = """
Line 59: Unexpected errors ['namedtuples_define_functional.py:59:33 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues)']
Line 60: Unexpected errors ['namedtuples_define_functional.py:60:13 - error: No parameter named "_1" (reportCallIssue)']
"""
4 changes: 2 additions & 2 deletions conformance/results/results.html
Original file line number Diff line number Diff line change
Expand Up @@ -915,11 +915,11 @@ <h3>Python Type System Conformance Test Results</h3>
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not reject override of named tuple attribute in child class.</p></span></div></th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not reject override of named tuple attribute in child class.</p></span></div></th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Doesn't catch illegal field name starting with underscore</p></span></div></th>
</tr>
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;namedtuples_define_functional</th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 partially-conformant">Partial</th>
<th class="column col2 conformant">Pass</th>
<th class="column col2 conformant">Pass</th>
</tr>
Expand Down
12 changes: 8 additions & 4 deletions conformance/results/zuban/namedtuples_define_class.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Does not reject override of named tuple attribute in child class.
"""
conformance_automated = "Fail"
errors_diff = """
Line 79: Expected 1 errors
Line 106: Expected 1 errors
Line 59: Unexpected errors ['namedtuples_define_class.py:59: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]']
"""
output = """
namedtuples_define_class.py:32: error: Tuple index out of range [misc]
Expand All @@ -15,7 +16,10 @@ namedtuples_define_class.py:46: error: Argument 2 to "Point" has incompatible ty
namedtuples_define_class.py:47: error: Argument "units" to "Point" has incompatible type "int"; expected "str" [arg-type]
namedtuples_define_class.py:48: error: Too many arguments for "Point" [call-arg]
namedtuples_define_class.py:49: error: Unexpected keyword argument "other" for "Point" [call-arg]
namedtuples_define_class.py:59: error: Non-default NamedTuple fields cannot follow default fields [misc]
namedtuples_define_class.py:98: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type]
namedtuples_define_class.py:105: error: NamedTuple should be a single base [misc]
namedtuples_define_class.py:59: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]
namedtuples_define_class.py:69: error: Too many arguments for "Point2" [call-arg]
namedtuples_define_class.py:76: error: NamedTuple field name cannot start with an underscore: _y [misc]
namedtuples_define_class.py:86: error: Non-default NamedTuple fields cannot follow default fields [misc]
namedtuples_define_class.py:125: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type]
namedtuples_define_class.py:132: error: NamedTuple should be a single base [misc]
"""
3 changes: 2 additions & 1 deletion conformance/results/zuban/namedtuples_define_functional.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ namedtuples_define_functional.py:43: error: Argument "x" to "Point6" has incompa
namedtuples_define_functional.py:52: error: "namedtuple()" has duplicate field name "a" [misc]
namedtuples_define_functional.py:53: error: "namedtuple()" field name "def" is a keyword [misc]
namedtuples_define_functional.py:54: error: "namedtuple()" field name "def" is a keyword [misc]
namedtuples_define_functional.py:66: error: Missing positional argument "a" in call to "NT5" [call-arg]
namedtuples_define_functional.py:55: error: "namedtuple()" field name "_d" starts with an underscore [misc]
namedtuples_define_functional.py:69: error: Missing positional argument "a" in call to "NT7" [call-arg]
"""
27 changes: 27 additions & 0 deletions conformance/tests/namedtuples_define_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,33 @@ class Point(NamedTuple):
p10 = Point(1, 2, "", other="") # E


# > Fields must be annotated attributes - methods and un-annotated attributes are not
# > considered fields.


class Point2(NamedTuple):
x: int
y: int
units = "meters" # Not a field

def is_origin(self) -> int: # Not a field
return self.x == 0 and self.y == 0


p11 = Point2(1, 2)
assert_type(p11, Point2)
x, y = p11

p12 = Point2(1, 2, "") # E


# > Field names may not start with an underscore.

class Point3(NamedTuple):
x: int
_y: int # E: illegal field name


# > The runtime implementation of ``NamedTuple`` enforces that fields with default
# > values must come after fields without default values. Type checkers should
# > likewise enforce this restriction::
Expand Down
25 changes: 14 additions & 11 deletions conformance/tests/namedtuples_define_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,27 @@
p6_4 = Point6(x=1.1, y=2) # E


# > At runtime, the ``namedtuple`` function disallows field names that are
# > illegal Python identifiers and either raises an exception or replaces these
# > fields with a parameter name of the form ``_N``. The behavior depends on
# > the value of the ``rename`` argument. Type checkers may replicate this
# > behavior statically.
# > At runtime, the ``namedtuple`` function disallows field names that begin with
# > an underscore or are illegal Python identifiers, and either raises an exception
# > or replaces these fields with a parameter name of the form ``_N``. The behavior
# > depends on the value of the ``rename`` argument. Type checkers may replicate
# > this behavior statically.

NT1 = namedtuple("NT1", ["a", "a"]) # E?: duplicate field name
NT2 = namedtuple("NT2", ["abc", "def"]) # E?: illegal field name
NT3 = namedtuple("NT3", ["abc", "def"], rename=False) # E?: illegal field name
NT4 = namedtuple("NT4", ["abc", "_d"], rename=False) # E?: illegal field name

NT4 = namedtuple("NT4", ["abc", "def"], rename=True) # OK
NT4(abc="", _1="") # OK
NT5 = namedtuple("NT5", ["abc", "def"], rename=True) # OK
NT5(abc="", _1="") # OK
NT6 = namedtuple("NT6", ["abc", "_d"], rename=True) # OK
NT6(abc="", _1="") # OK


# > The ``namedtuple`` function also supports a ``defaults`` keyword argument that
# > specifies default values for the fields. Type checkers may support this.

NT5 = namedtuple("NT5", "a b c", defaults=(1, 2))
NT5(1) # OK
NT5(1, 2, 3) # OK
NT5() # E: too few arguments
NT7 = namedtuple("NT7", "a b c", defaults=(1, 2))
NT7(1) # OK
NT7(1, 2, 3) # OK
NT7() # E: too few arguments
26 changes: 19 additions & 7 deletions docs/spec/namedtuples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ Type checkers should support the class syntax::
y: int
units: str = "meters"

Fields must be annotated attributes - methods and un-annotated attributes are not
considered fields. Field names may not start with an underscore.

class MyTuple(NamedTuple):
x1 = 1 # Not a field
def x2() -> None: pass # Not a field
_x3: int # Type error: illegal field name

Regardless of whether the class syntax or factory function call is used to define
a named tuple, type checkers should synthesize a ``__new__`` method based on
the named tuple fields. This mirrors the runtime behavior. In the example
Expand Down Expand Up @@ -79,17 +87,21 @@ A type checker may support the factory function call in its various forms::
Point5 = NamedTuple('Point5', [('x', int), ('y', int)])
Point6 = NamedTuple('Point6', (('x', int), ('y', int)))

At runtime, the ``namedtuple`` function disallows field names that are
illegal Python identifiers and either raises an exception or replaces these
fields with a parameter name of the form ``_N``. The behavior depends on
the value of the ``rename`` argument. Type checkers may replicate this
behavior statically::
At runtime, the ``namedtuple`` function disallows field names that begin with
an underscore or are illegal Python identifiers, and either raises an exception
or replaces these fields with a parameter name of the form ``_N``. The behavior
depends on the value of the ``rename`` argument. Type checkers may replicate
this behavior statically::

NT1 = namedtuple("NT1", ["a", "a"]) # Type error (duplicate field name)
NT2 = namedtuple("NT2", ["abc", "def"], rename=False) # Type error (illegal field name)
NT3 = namedtuple("NT3", ["abc", "_d"], rename=False) # Type error (illegal field name)

NT4 = namedtuple("NT4", ["abc", "def"], rename=True) # OK
NT4(abc="", _1="") # OK

NT3 = namedtuple("NT3", ["abc", "def"], rename=True) # OK
NT3(abc="", _1="") # OK
NT5 = namedtuple("NT5", ["abc", "_d"], rename=True) # OK
NT5(abc="", _1="") # OK

The ``namedtuple`` function also supports a ``defaults`` keyword argument that
specifies default values for the fields. Type checkers may support this::
Expand Down