Skip to content

Commit

Permalink
Rename TypeExpr to TypeForm (#475)
Browse files Browse the repository at this point in the history
No backwards compatibility required because we never released
TypeExpr.

Also took the opportunity to expand the docstring.
  • Loading branch information
JelleZijlstra authored Sep 28, 2024
1 parent 7632716 commit 08d866b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 41 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ Special typing primitives

.. versionadded:: 4.6.0

.. data:: TypeExpr
.. 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

Expand Down
46 changes: 23 additions & 23 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
TypeAlias,
TypeAliasType,
TypedDict,
TypeExpr,
TypeForm,
TypeGuard,
TypeIs,
TypeVar,
Expand Down Expand Up @@ -5508,33 +5508,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):
Expand All @@ -5546,24 +5546,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):
Expand Down
44 changes: 29 additions & 15 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
'Text',
'TypeAlias',
'TypeAliasType',
'TypeExpr',
'TypeForm',
'TypeGuard',
'TypeIs',
'TYPE_CHECKING',
Expand Down Expand Up @@ -2047,23 +2047,30 @@ 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 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: TypeExpr[T], value: Any) -> T: ...
def cast[T](typ: TypeForm[T], value: Any) -> T: ...
reveal_type(cast(int, "x")) # int
Expand All @@ -2073,7 +2080,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')
Expand All @@ -2082,13 +2089,20 @@ 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 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: TypeExpr[T], value: Any) -> T: ...
def cast[T](typ: TypeForm[T], value: Any) -> T: ...
reveal_type(cast(int, "x")) # int
Expand Down

0 comments on commit 08d866b

Please sign in to comment.