From f450733cb551bdd6e331bad1be97226d87a56a35 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 20 Aug 2025 16:32:17 +0100 Subject: [PATCH] Fixup language around `NewType` in the spec --- conformance/results/mypy/aliases_newtype.toml | 2 +- conformance/results/results.html | 2 +- conformance/tests/aliases_newtype.py | 12 ++++++------ docs/spec/aliases.rst | 15 +++++++++------ docs/spec/protocol.rst | 1 + 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/conformance/results/mypy/aliases_newtype.toml b/conformance/results/mypy/aliases_newtype.toml index e1c8c64a7..f4c4bc856 100644 --- a/conformance/results/mypy/aliases_newtype.toml +++ b/conformance/results/mypy/aliases_newtype.toml @@ -1,6 +1,6 @@ conformant = "Partial" notes = """ -`NewType`s are considered classes, not functions. +`NewType`s are incorrectly considered to be classes. """ output = """ aliases_newtype.py:11: error: Argument 1 to "UserId" has incompatible type "str"; expected "int" [arg-type] diff --git a/conformance/results/results.html b/conformance/results/results.html index 151069d7a..6cbe8f005 100644 --- a/conformance/results/results.html +++ b/conformance/results/results.html @@ -468,7 +468,7 @@

Python Type System Conformance Test Results

Pass      aliases_newtype -
Partial

`NewType`s are considered classes, not functions.

+
Partial

`NewType`s are incorrectly considered to be classes.

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.

Pass diff --git a/conformance/tests/aliases_newtype.py b/conformance/tests/aliases_newtype.py index 713b0bf88..58baeb8af 100644 --- a/conformance/tests/aliases_newtype.py +++ b/conformance/tests/aliases_newtype.py @@ -1,5 +1,5 @@ """ -Tests the `typing.NewType` function. +Tests the `typing.NewType` type constructor. """ # Specification: https://typing.readthedocs.io/en/latest/spec/aliases.html#newtype @@ -14,12 +14,12 @@ assert_type(UserId(5) + 1, int) -# > NewType('Derived', Base) returns a dummy function -_: type = UserId # E: functions are not instances of `type` +# > NewType('Derived', Base) returns a dummy object +_: type = UserId # E: `NewType()` does not return an instance of `type` -# > Both isinstance and issubclass, as well as subclassing will fail for -# > NewType('Derived', Base) since function objects don’t support these -# > operations. +# > Both ``isinstance`` and ``issubclass``, as well as subclassing will fail +# > for ``NewType('Derived', Base)``, since the object returned by a call to +# > ``NewType`` is not a class. isinstance(u2, UserId) # E: not allowed in isinstance call diff --git a/docs/spec/aliases.rst b/docs/spec/aliases.rst index adf303440..a965eca41 100644 --- a/docs/spec/aliases.rst +++ b/docs/spec/aliases.rst @@ -156,9 +156,9 @@ to a definition:: def __init__(self, _x: Base) -> None: ... -While at runtime, ``NewType('Derived', Base)`` returns a dummy function -that simply returns its argument. Type checkers require explicit casts -from ``int`` where ``UserId`` is expected, while implicitly casting +While at runtime, ``NewType('Derived', Base)`` returns a dummy object +that simply returns its argument when called. Type checkers require explicit +casts from ``int`` where ``UserId`` is expected, while implicitly casting from ``UserId`` where ``int`` is expected. Examples:: UserId = NewType('UserId', int) @@ -176,7 +176,7 @@ from ``UserId`` where ``int`` is expected. Examples:: ``NewType`` accepts exactly two arguments: a name for the new unique type, and a base class. The latter should be a proper class (i.e., not a type construct like ``Union``, etc.), or another unique type created -by calling ``NewType``. The function returned by ``NewType`` +by calling ``NewType``. The callable returned by ``NewType`` accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above). Example:: @@ -193,5 +193,8 @@ constructor accepting an instance of the base class (see above). Example:: tcp_packet = TcpPacketId(127, 0) # Fails in type checker and at runtime Both ``isinstance`` and ``issubclass``, as well as subclassing will fail -for ``NewType('Derived', Base)`` since function objects don't support -these operations. +for ``NewType('Derived', Base)``, since the object returned by a call to +``NewType`` is not a class. + +See also :ref:`protocol-newtype-aliases` for a discussion of how +``NewType`` interacts with protocol definitions. diff --git a/docs/spec/protocol.rst b/docs/spec/protocol.rst index 50b64f372..39e7a82a6 100644 --- a/docs/spec/protocol.rst +++ b/docs/spec/protocol.rst @@ -514,6 +514,7 @@ For example:: a: ProtoA = C # Type check error, signatures don't match! b: ProtoB = C # OK +.. _`protocol-newtype-aliases`: ``NewType()`` and type aliases ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^