diff --git a/conformance/results/mypy/namedtuples_define_class.toml b/conformance/results/mypy/namedtuples_define_class.toml index adf590b4..575c5c0c 100644 --- a/conformance/results/mypy/namedtuples_define_class.toml +++ b/conformance/results/mypy/namedtuples_define_class.toml @@ -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]'] """ diff --git a/conformance/results/mypy/namedtuples_define_functional.toml b/conformance/results/mypy/namedtuples_define_functional.toml index 151e6588..49ea3797 100644 --- a/conformance/results/mypy/namedtuples_define_functional.toml +++ b/conformance/results/mypy/namedtuples_define_functional.toml @@ -1,4 +1,4 @@ -conformant = "Pass" +conformant = "Partial" output = """ namedtuples_define_functional.py:16: error: Missing positional argument "y" in call to "Point1" [call-arg] namedtuples_define_functional.py:21: error: Missing positional arguments "x", "y" in call to "Point2" [call-arg] @@ -11,8 +11,15 @@ 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:66: error: Name "NT5" already defined on line 57 [no-redef] +namedtuples_define_functional.py:67: error: Missing positional argument "_1" in call to "NT5" [call-arg] +namedtuples_define_functional.py:68: error: Too many arguments for "NT5" [call-arg] +namedtuples_define_functional.py:69: error: Missing positional arguments "abc", "_1" in call to "NT5" [call-arg] """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 66: Unexpected errors ['namedtuples_define_functional.py:66: error: Name "NT5" already defined on line 57 [no-redef]'] +Line 67: Unexpected errors ['namedtuples_define_functional.py:67: error: Missing positional argument "_1" in call to "NT5" [call-arg]'] +Line 68: Unexpected errors ['namedtuples_define_functional.py:68: error: Too many arguments for "NT5" [call-arg]'] """ diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml index d3740879..8be86843 100644 --- a/conformance/results/mypy/version.toml +++ b/conformance/results/mypy/version.toml @@ -1,2 +1,2 @@ version = "mypy 1.16.1" -test_duration = 2.2 +test_duration = 2.0 diff --git a/conformance/results/pyre/generics_paramspec_specialization.toml b/conformance/results/pyre/generics_paramspec_specialization.toml index 34a215ee..d4da441a 100644 --- a/conformance/results/pyre/generics_paramspec_specialization.toml +++ b/conformance/results/pyre/generics_paramspec_specialization.toml @@ -3,8 +3,6 @@ notes = """ Reports error for legitimate use of `...` to specialize ParamSpec """ output = """ -generics_paramspec_specialization.py:32:14 Invalid type parameters [24]: Callable parameters expected for parameter specification `P2`, but a single type `...` was given for generic type ClassB. -generics_paramspec_specialization.py:36:14 Invalid type parameters [24]: Callable parameters expected for parameter specification `P1`, but a single type `...` was given for generic type ClassA. generics_paramspec_specialization.py:44:14 Invalid type parameters [24]: Callable parameters expected for parameter specification `P1`, but a single type `int` was given for generic type ClassA. generics_paramspec_specialization.py:53:4 Too many arguments [19]: PositionalOnly call expects 2 positional arguments, 3 were provided. generics_paramspec_specialization.py:54:4 Too many arguments [19]: PositionalOnly call expects 2 positional arguments, 3 were provided. @@ -15,8 +13,6 @@ generics_paramspec_specialization.py:61:4 Too many arguments [19]: PositionalOnl """ conformance_automated = "Fail" errors_diff = """ -Line 32: Unexpected errors ['generics_paramspec_specialization.py:32:14 Invalid type parameters [24]: Callable parameters expected for parameter specification `P2`, but a single type `...` was given for generic type ClassB.'] -Line 36: Unexpected errors ['generics_paramspec_specialization.py:36:14 Invalid type parameters [24]: Callable parameters expected for parameter specification `P1`, but a single type `...` was given for generic type ClassA.'] Line 53: Unexpected errors ['generics_paramspec_specialization.py:53:4 Too many arguments [19]: PositionalOnly call expects 2 positional arguments, 3 were provided.'] Line 59: Unexpected errors ['generics_paramspec_specialization.py:59:4 Too many arguments [19]: PositionalOnly call expects 2 positional arguments, 3 were provided.'] """ diff --git a/conformance/results/pyre/namedtuples_define_class.toml b/conformance/results/pyre/namedtuples_define_class.toml index 3ce86df7..d60d70ca 100644 --- a/conformance/results/pyre/namedtuples_define_class.toml +++ b/conformance/results/pyre/namedtuples_define_class.toml @@ -16,11 +16,15 @@ namedtuples_define_class.py:46:14 Incompatible parameter type [6]: In call `Poin namedtuples_define_class.py:47:17 Incompatible parameter type [6]: In call `Point.__init__`, for argument `units`, expected `str` but got `int`. namedtuples_define_class.py:48:5 Too many arguments [19]: Call `Point.__init__` expects 3 positional arguments, 4 were provided. namedtuples_define_class.py:49:6 Unexpected keyword [28]: Unexpected keyword argument `other` to call `Point.__init__`. -namedtuples_define_class.py:59:4 Missing named tuple default [74]: Named tuple field without default value may not be preceded by a field with default value. -namedtuples_define_class.py:79:4 Invalid assignment [41]: Cannot reassign final attribute `x`. -namedtuples_define_class.py:98:18 Incompatible parameter type [6]: In call `Property.__init__`, for 2nd positional argument, expected `str` but got `float`. -namedtuples_define_class.py:105:23 Invalid inheritance [39]: If NamedTuple is included as a base class, the class may not extend anything else besides Generic. +namedtuples_define_class.py:67:0 Unable to unpack [23]: Unable to unpack 3 values, 2 were expected. +namedtuples_define_class.py:86:4 Missing named tuple default [74]: Named tuple field without default value may not be preceded by a field with default value. +namedtuples_define_class.py:106:4 Invalid assignment [41]: Cannot reassign final attribute `x`. +namedtuples_define_class.py:125:18 Incompatible parameter type [6]: In call `Property.__init__`, for 2nd positional argument, expected `str` but got `float`. +namedtuples_define_class.py:132:23 Invalid inheritance [39]: If NamedTuple is included as a base class, the class may not extend anything else besides Generic. """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 69: Expected 1 errors +Line 76: Expected 1 errors +Line 67: Unexpected errors ['namedtuples_define_class.py:67:0 Unable to unpack [23]: Unable to unpack 3 values, 2 were expected.'] """ diff --git a/conformance/results/pyre/namedtuples_define_functional.toml b/conformance/results/pyre/namedtuples_define_functional.toml index dc132443..825bb5fc 100644 --- a/conformance/results/pyre/namedtuples_define_functional.toml +++ b/conformance/results/pyre/namedtuples_define_functional.toml @@ -1,4 +1,4 @@ -conformant = "Pass" +conformant = "Partial" notes = """ """ output = """ @@ -12,8 +12,12 @@ namedtuples_define_functional.py:42:17 Incompatible parameter type [6]: In call namedtuples_define_functional.py:43:14 Incompatible parameter type [6]: In call `Point6.__init__`, for argument `x`, expected `int` but got `float`. namedtuples_define_functional.py:52:0 Duplicate parameter [65]: Duplicate parameter name `a`. namedtuples_define_functional.py:52:0 Duplicate parameter [65]: Duplicate parameter name `a`. -namedtuples_define_functional.py:66:0 Missing argument [20]: Call `NT5.__init__` expects argument `a`. +namedtuples_define_functional.py:58:0 Unexpected keyword [28]: Unexpected keyword argument `abc` to call `NT5.__init__`. +namedtuples_define_functional.py:60:0 Unexpected keyword [28]: Unexpected keyword argument `_1` to call `NT6.__init__`. +namedtuples_define_functional.py:69:0 Missing argument [20]: Call `NT5.__init__` expects argument `a`. """ -conformance_automated = "Pass" +conformance_automated = "Fail" errors_diff = """ +Line 58: Unexpected errors ['namedtuples_define_functional.py:58:0 Unexpected keyword [28]: Unexpected keyword argument `abc` to call `NT5.__init__`.'] +Line 60: Unexpected errors ['namedtuples_define_functional.py:60:0 Unexpected keyword [28]: Unexpected keyword argument `_1` to call `NT6.__init__`.'] """ diff --git a/conformance/results/pyre/version.toml b/conformance/results/pyre/version.toml index c759e78d..b589c739 100644 --- a/conformance/results/pyre/version.toml +++ b/conformance/results/pyre/version.toml @@ -1,2 +1,2 @@ -version = "pyre 0.9.23" -test_duration = 10.7 +version = "pyre 0.9.25" +test_duration = 7.6 diff --git a/conformance/results/pyright/namedtuples_define_class.toml b/conformance/results/pyright/namedtuples_define_class.toml index 19f9aadc..e04b26fc 100644 --- a/conformance/results/pyright/namedtuples_define_class.toml +++ b/conformance/results/pyright/namedtuples_define_class.toml @@ -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 = """ diff --git a/conformance/results/pyright/namedtuples_define_functional.toml b/conformance/results/pyright/namedtuples_define_functional.toml index 07c43204..266f4083 100644 --- a/conformance/results/pyright/namedtuples_define_functional.toml +++ b/conformance/results/pyright/namedtuples_define_functional.toml @@ -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) @@ -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)'] """ diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml index ca4eb79c..79f18fab 100644 --- a/conformance/results/pyright/version.toml +++ b/conformance/results/pyright/version.toml @@ -1,2 +1,2 @@ -version = "pyright 1.1.402" -test_duration = 1.8 +version = "pyright 1.1.403" +test_duration = 1.4 diff --git a/conformance/results/results.html b/conformance/results/results.html index 4cc4ea00..e5f07714 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -159,13 +159,13 @@

Python Type System Conformance Test Results

- - - + @@ -423,7 +423,7 @@

Python Type System Conformance Test Results

- + diff --git a/conformance/tests/namedtuples_define_class.py b/conformance/tests/namedtuples_define_class.py index e345a197..5e6e9c4c 100644 --- a/conformance/tests/namedtuples_define_class.py +++ b/conformance/tests/namedtuples_define_class.py @@ -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:: diff --git a/conformance/tests/namedtuples_define_functional.py b/conformance/tests/namedtuples_define_functional.py index 432dff73..51506e3d 100644 --- a/conformance/tests/namedtuples_define_functional.py +++ b/conformance/tests/namedtuples_define_functional.py @@ -43,18 +43,21 @@ 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 diff --git a/docs/spec/namedtuples.rst b/docs/spec/namedtuples.rst index 1ac14136..936d2fa7 100644 --- a/docs/spec/namedtuples.rst +++ b/docs/spec/namedtuples.rst @@ -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 @@ -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::
 
mypy 1.16.1
-
2.2sec
+
2.0sec
pyright 1.1.402
-
1.8sec
+
pyright 1.1.403
+
1.4sec
pyre 0.9.23
-
10.7sec
+
pyre 0.9.25
+
7.6sec
@@ -324,7 +324,7 @@

Python Type System Conformance Test Results

     generics_type_erasure
Partial

Infers Node[Never] instead of Node[Any] when argument is not provided.

False negative on instance attribute access on type(node).

Partial

Missing error regarding `type(instance).generic_attribute`.

Pass
Partial

Does not erase unspecified type variables to `Any` prior to `assert_type` handling.

False negatives on instance attribute access on the type.

Does not infer type of `DefaultDict` with explicit type parameters on constructor.

False negatives on assert_type uses.

     generics_typevartuple_args
     aliases_newtype
Partial

`NewType`s are considered classes, not functions.

Partial

`NewType`s are considered classes, not functions.

Pass
Partial

Does not reject use of NewType in `isinstance` call.

Does not reject use of NewType in class definition statement.

Does not report inconsistency between name of NewType and assigned identifier name.

Does not reject use of NewType with generic class with TypeVar.

Does not reject use of NewType with protocol class.

Does not reject use of NewType with TypedDict class.

Does not reject use of NewType with Any.

     aliases_recursive