From 0a3814a34193ce4b97986fe9236beb2e9bb8a41e Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 27 Sep 2024 19:36:03 -0700 Subject: [PATCH 1/3] Rename TypeExpr to TypeForm No backwards compatibility required because we never released TypeExpr. Also took the opportunity to expand the docstring. --- CHANGELOG.md | 2 +- doc/index.rst | 2 +- src/test_typing_extensions.py | 46 +++++++++++++++++------------------ src/typing_extensions.py | 40 ++++++++++++++++++------------ 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eafc6b6..3f92982f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Unreleased -- Add `typing_extensions.TypeExpr` from PEP 747. Patch by +- Add `typing_extensions.TypeForm` from PEP 747. Patch by Jelle Zijlstra. - Add `typing_extensions.get_annotations`, a backport of `inspect.get_annotations` that adds features specified diff --git a/doc/index.rst b/doc/index.rst index 23a531c4..a5eca12c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -367,7 +367,7 @@ Special typing primitives .. versionadded:: 4.6.0 -.. data:: TypeExpr +.. data:: TypeForm See :pep:`747`. A type hint representing a type expression. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 474c02cc..bf945b1e 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -69,7 +69,7 @@ TypeAlias, TypeAliasType, TypedDict, - TypeExpr, + TypeForm, TypeGuard, TypeIs, TypeVar, @@ -5507,33 +5507,33 @@ def test_no_isinstance(self): issubclass(int, TypeIs) -class TypeExprTests(BaseTestCase): +class TypeFormTests(BaseTestCase): def test_basics(self): - TypeExpr[int] # OK - self.assertEqual(TypeExpr[int], TypeExpr[int]) + TypeForm[int] # OK + self.assertEqual(TypeForm[int], TypeForm[int]) - def foo(arg) -> TypeExpr[int]: ... - self.assertEqual(gth(foo), {'return': TypeExpr[int]}) + def foo(arg) -> TypeForm[int]: ... + self.assertEqual(gth(foo), {'return': TypeForm[int]}) def test_repr(self): - if hasattr(typing, 'TypeExpr'): + if hasattr(typing, 'TypeForm'): mod_name = 'typing' else: mod_name = 'typing_extensions' - self.assertEqual(repr(TypeExpr), f'{mod_name}.TypeExpr') - cv = TypeExpr[int] - self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[int]') - cv = TypeExpr[Employee] - self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[{__name__}.Employee]') - cv = TypeExpr[Tuple[int]] - self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[typing.Tuple[int]]') + self.assertEqual(repr(TypeForm), f'{mod_name}.TypeForm') + cv = TypeForm[int] + self.assertEqual(repr(cv), f'{mod_name}.TypeForm[int]') + cv = TypeForm[Employee] + self.assertEqual(repr(cv), f'{mod_name}.TypeForm[{__name__}.Employee]') + cv = TypeForm[Tuple[int]] + self.assertEqual(repr(cv), f'{mod_name}.TypeForm[typing.Tuple[int]]') def test_cannot_subclass(self): with self.assertRaises(TypeError): - class C(type(TypeExpr)): + class C(type(TypeForm)): pass with self.assertRaises(TypeError): - class D(type(TypeExpr[int])): + class D(type(TypeForm[int])): pass def test_call(self): @@ -5545,24 +5545,24 @@ def test_call(self): ] for obj in objs: with self.subTest(obj=obj): - self.assertIs(TypeExpr(obj), obj) + self.assertIs(TypeForm(obj), obj) with self.assertRaises(TypeError): - TypeExpr() + TypeForm() with self.assertRaises(TypeError): - TypeExpr("too", "many") + TypeForm("too", "many") def test_cannot_init_type(self): with self.assertRaises(TypeError): - type(TypeExpr)() + type(TypeForm)() with self.assertRaises(TypeError): - type(TypeExpr[Optional[int]])() + type(TypeForm[Optional[int]])() def test_no_isinstance(self): with self.assertRaises(TypeError): - isinstance(1, TypeExpr[int]) + isinstance(1, TypeForm[int]) with self.assertRaises(TypeError): - issubclass(int, TypeExpr) + issubclass(int, TypeForm) class LiteralStringTests(BaseTestCase): diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 1adc5823..774eb956 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -86,7 +86,7 @@ 'Text', 'TypeAlias', 'TypeAliasType', - 'TypeExpr', + 'TypeForm', 'TypeGuard', 'TypeIs', 'TYPE_CHECKING', @@ -2047,23 +2047,28 @@ def f(val: Union[int, Awaitable[int]]) -> int: """) # 3.14+? -if hasattr(typing, 'TypeExpr'): - TypeExpr = typing.TypeExpr +if hasattr(typing, 'TypeForm'): + TypeForm = typing.TypeForm # 3.9 elif sys.version_info[:2] >= (3, 9): - class _TypeExprForm(_ExtensionsSpecialForm, _root=True): - # TypeExpr(X) is equivalent to X but indicates to the type checker - # that the object is a TypeExpr. + class _TypeFormForm(_ExtensionsSpecialForm, _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 - @_TypeExprForm - def TypeExpr(self, parameters): - """Special typing form used to represent a type expression. + @_TypeFormForm + def TypeForm(self, parameters): + """A special typing construct to represent forms that are valid as type expressions. + + Expressions are assignable to TypeForm if they represent valid type + expressions. For example, ``int`` is assignable to ``TypeForm`` (because + ``int``, as a type, is a valid type expression), but ``1`` is not (because + integers are not valid type expressions). Usage: - def cast[T](typ: TypeExpr[T], value: Any) -> T: ... + def cast[T](typ: TypeForm[T], value: Any) -> T: ... reveal_type(cast(int, "x")) # int @@ -2073,7 +2078,7 @@ def cast[T](typ: TypeExpr[T], value: Any) -> T: ... return typing._GenericAlias(self, (item,)) # 3.8 else: - class _TypeExprForm(_ExtensionsSpecialForm, _root=True): + class _TypeFormForm(_ExtensionsSpecialForm, _root=True): def __getitem__(self, parameters): item = typing._type_check(parameters, f'{self._name} accepts only a single type') @@ -2082,13 +2087,18 @@ def __getitem__(self, parameters): def __call__(self, obj, /): return obj - TypeExpr = _TypeExprForm( - 'TypeExpr', - doc="""Special typing form used to represent a type expression. + TypeForm = _TypeFormForm( + 'TypeForm', + doc="""A special typing construct to represent forms that are valid as type expressions. + + Expressions are assignable to TypeForm if they represent valid type + expressions. For example, ``int`` is assignable to ``TypeForm`` (because + ``int``, as a type, is a valid type expression), but ``1`` is not (because + integers are not valid type expressions). Usage: - def cast[T](typ: TypeExpr[T], value: Any) -> T: ... + def cast[T](typ: TypeForm[T], value: Any) -> T: ... reveal_type(cast(int, "x")) # int From 4935e810b04905590e81ef6d9519f6dc067bb814 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 28 Sep 2024 06:13:18 -0700 Subject: [PATCH 2/3] shorten --- src/typing_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 1510e7da..ab820bf3 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2059,7 +2059,7 @@ def __call__(self, obj, /): @_TypeFormForm def TypeForm(self, parameters): - """A special typing construct to represent forms that are valid as type expressions. + """A special typing construct to represent valid type expressions. Expressions are assignable to TypeForm if they represent valid type expressions. For example, ``int`` is assignable to ``TypeForm`` (because @@ -2089,7 +2089,7 @@ def __call__(self, obj, /): TypeForm = _TypeFormForm( 'TypeForm', - doc="""A special typing construct to represent forms that are valid as type expressions. + doc="""A special typing construct to represent valid type expressions. Expressions are assignable to TypeForm if they represent valid type expressions. For example, ``int`` is assignable to ``TypeForm`` (because From 1f717242ff72861be4826b2632814e018490b1ce Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 28 Sep 2024 12:52:17 -0700 Subject: [PATCH 3/3] rewrite docstring --- doc/index.rst | 2 +- src/typing_extensions.py | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index a5eca12c..91740aa7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -369,7 +369,7 @@ Special typing primitives .. data:: TypeForm - See :pep:`747`. A type hint representing a type expression. + See :pep:`747`. A special form representing the value of a type expression. .. versionadded:: 4.13.0 diff --git a/src/typing_extensions.py b/src/typing_extensions.py index ab820bf3..5bf4f2dc 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2059,12 +2059,14 @@ def __call__(self, obj, /): @_TypeFormForm def TypeForm(self, parameters): - """A special typing construct to represent valid type expressions. + """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. - Expressions are assignable to TypeForm if they represent valid type - expressions. For example, ``int`` is assignable to ``TypeForm`` (because - ``int``, as a type, is a valid type expression), but ``1`` is not (because - integers are not valid type expressions). + 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: @@ -2089,12 +2091,14 @@ def __call__(self, obj, /): TypeForm = _TypeFormForm( 'TypeForm', - doc="""A special typing construct to represent valid type expressions. - - Expressions are assignable to TypeForm if they represent valid type - expressions. For example, ``int`` is assignable to ``TypeForm`` (because - ``int``, as a type, is a valid type expression), but ``1`` is not (because - integers are not valid type expressions). + doc="""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: