.. versionadded:: 3.9
+.. data:: TypeForm
+
+ A special form representing the value that results from evaluating a
+ type expression.
+
+ This value encodes the information supplied in the type expression, and
+ it represents the type described by that type expression.
+
+ When used in a type expression, ``TypeForm`` describes a set of type form
+ objects. It accepts a single type argument, which must be a valid type
+ expression. ``TypeForm[T]`` describes the set of all type form objects that
+ represent the type ``T`` or types assignable to ``T``.
+
+ ``TypeForm(obj)`` simply returns ``obj`` unchanged. This is useful for
+ explicitly marking a value as a type form for static type checkers.
+
+ Example::
+
+ from typing import Any, TypeForm
+
+ def cast[T](typ: TypeForm[T], value: Any) -> T: ...
+
+ reveal_type(cast(int, "x")) # Revealed type is "int"
+
+ See :pep:`747` for details.
+
+ .. versionadded:: 3.15
+
+
.. data:: TypeIs
Special typing construct for marking user-defined type predicate functions.
typing
------
+* :pep:`747`: Add :data:`~typing.TypeForm`, a new special form for annotating
+ values that are themselves type expressions.
+ ``TypeForm[T]`` means "a type form object describing ``T`` (or a type
+ assignable to ``T``)". At runtime, ``TypeForm(x)`` simply returns ``x``,
+ which allows explicit annotation of type-form values without changing
+ behavior.
+
+ This helps libraries that accept user-provided type expressions
+ (for example ``int``, ``str | None``, :class:`~typing.TypedDict`
+ classes, or ``list[int]``) expose precise signatures:
+
+ .. code-block:: python
+
+ from typing import Any, TypeForm
+
+ def cast[T](typ: TypeForm[T], value: Any) -> T: ...
+
+ (Contributed by Jelle Zijlstra in :gh:`145033`.)
+
* The undocumented keyword argument syntax for creating
:class:`~typing.NamedTuple` classes (for example,
``Point = NamedTuple("Point", x=int, y=int)``) is no longer supported.
from typing import Self, LiteralString
from typing import TypeAlias
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
-from typing import TypeGuard, TypeIs, NoDefault
+from typing import TypeForm, TypeGuard, TypeIs, NoDefault
import abc
import textwrap
import typing
Final[int],
Literal[1, 2],
Concatenate[int, ParamSpec("P")],
+ TypeForm[int],
TypeGuard[int],
TypeIs[range],
):
self.assertEqual(get_args(Required[int]), (int,))
self.assertEqual(get_args(NotRequired[int]), (int,))
self.assertEqual(get_args(TypeAlias), ())
+ self.assertEqual(get_args(TypeForm[int]), (int,))
self.assertEqual(get_args(TypeGuard[int]), (int,))
self.assertEqual(get_args(TypeIs[range]), (range,))
Ts = TypeVarTuple('Ts')
issubclass(int, TypeIs)
+class TypeFormTests(BaseTestCase):
+ def test_basics(self):
+ TypeForm[int] # OK
+ self.assertEqual(TypeForm[int], TypeForm[int])
+
+ def foo(arg) -> TypeForm[int]: ...
+ self.assertEqual(gth(foo), {'return': TypeForm[int]})
+
+ with self.assertRaises(TypeError):
+ TypeForm[int, str]
+
+ def test_repr(self):
+ self.assertEqual(repr(TypeForm), 'typing.TypeForm')
+ cv = TypeForm[int]
+ self.assertEqual(repr(cv), 'typing.TypeForm[int]')
+ cv = TypeForm[Employee]
+ self.assertEqual(repr(cv), 'typing.TypeForm[%s.Employee]' % __name__)
+ cv = TypeForm[tuple[int]]
+ self.assertEqual(repr(cv), 'typing.TypeForm[tuple[int]]')
+
+ def test_cannot_subclass(self):
+ with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
+ class C(type(TypeForm)):
+ pass
+ with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE):
+ class D(type(TypeForm[int])):
+ pass
+ with self.assertRaisesRegex(TypeError,
+ r'Cannot subclass typing\.TypeForm'):
+ class E(TypeForm):
+ pass
+ with self.assertRaisesRegex(TypeError,
+ r'Cannot subclass typing\.TypeForm\[int\]'):
+ class F(TypeForm[int]):
+ pass
+
+ def test_call(self):
+ objs = [
+ 1,
+ "int",
+ int,
+ tuple[int, str],
+ Tuple[int, str],
+ ]
+ for obj in objs:
+ with self.subTest(obj=obj):
+ self.assertIs(TypeForm(obj), obj)
+
+ with self.assertRaises(TypeError):
+ TypeForm()
+ with self.assertRaises(TypeError):
+ TypeForm("too", "many")
+
+ def test_cannot_init_type(self):
+ with self.assertRaises(TypeError):
+ type(TypeForm)()
+ with self.assertRaises(TypeError):
+ type(TypeForm[Optional[int]])()
+
+ def test_no_isinstance(self):
+ with self.assertRaises(TypeError):
+ isinstance(1, TypeForm[int])
+ with self.assertRaises(TypeError):
+ issubclass(int, TypeForm)
+
+
SpecialAttrsP = typing.ParamSpec('SpecialAttrsP')
SpecialAttrsT = typing.TypeVar('SpecialAttrsT', int, float, complex)
typing.Never: 'Never',
typing.Optional: 'Optional',
typing.TypeAlias: 'TypeAlias',
+ typing.TypeForm: 'TypeForm',
typing.TypeGuard: 'TypeGuard',
typing.TypeIs: 'TypeIs',
typing.TypeVar: 'TypeVar',
typing.Literal[1, 2]: 'Literal',
typing.Literal[True, 2]: 'Literal',
typing.Optional[Any]: 'Union',
+ typing.TypeForm[Any]: 'TypeForm',
typing.TypeGuard[Any]: 'TypeGuard',
typing.TypeIs[Any]: 'TypeIs',
typing.Union[Any]: 'Any',
'Text',
'TYPE_CHECKING',
'TypeAlias',
+ 'TypeForm',
'TypeGuard',
'TypeIs',
'TypeAliasType',
return self._getitem(self, *parameters)
+class _TypeFormForm(_SpecialForm, _root=True):
+ # TypeForm(X) is equivalent to X but indicates to the type checker
+ # that the object is a TypeForm.
+ def __call__(self, obj, /):
+ return obj
+
+
class _AnyMeta(type):
def __instancecheck__(self, obj):
if self is Any:
return _GenericAlias(self, (item,))
+@_TypeFormForm
+def TypeForm(self, parameters):
+ """A special form representing the value that results from the evaluation
+ of a type expression.
+
+ This value encodes the information supplied in the type expression, and it
+ represents the type described by that type expression.
+
+ When used in a type expression, TypeForm describes a set of type form
+ objects. It accepts a single type argument, which must be a valid type
+ expression. ``TypeForm[T]`` describes the set of all type form objects that
+ represent the type T or types that are assignable to T.
+
+ Usage::
+
+ def cast[T](typ: TypeForm[T], value: Any) -> T: ...
+
+ reveal_type(cast(int, "x")) # int
+
+ See PEP 747 for more information.
+ """
+ item = _type_check(parameters, f'{self} accepts only single type.')
+ return _GenericAlias(self, (item,))
+
+
@_SpecialForm
def TypeIs(self, parameters):
"""Special typing construct for marking user-defined type predicate functions.
# A = Callable[[], None] # _CallableGenericAlias
# B = Callable[[T], None] # _CallableGenericAlias
# C = B[int] # _CallableGenericAlias
- # * Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`:
+ # * Parameterized `Final`, `ClassVar`, `TypeForm`, `TypeGuard`, and `TypeIs`:
# # All _GenericAlias
# Final[int]
# ClassVar[float]
+ # TypeForm[bytes]
# TypeGuard[bool]
# TypeIs[range]