Skip to content

Commit 5b9e4cb

Browse files
authored
Merge branch 'main' into main
2 parents 3e6ec0a + 08d866b commit 5b9e4cb

File tree

4 files changed

+78
-42
lines changed

4 files changed

+78
-42
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Unreleased
22

3-
- Add `typing_extensions.TypeExpr` from PEP 747. Patch by
3+
- Add `typing_extensions.TypeForm` from PEP 747. Patch by
44
Jelle Zijlstra.
55
- Add `typing_extensions.get_annotations`, a backport of
66
`inspect.get_annotations` that adds features specified
@@ -12,6 +12,10 @@
1212
- Fix bug where a subscripted `TypeAliasType` instance did not have all
1313
attributes of the original `TypeAliasType` instance on older Python versions.
1414
Patch by [Daraan](https://github.com/Daraan) and Alex Waygood.
15+
- Fix bug where subscripted `TypeAliasType` instances (and some other
16+
subscripted objects) had wrong parameters if they were directly
17+
subscripted with an `Unpack` object.
18+
Patch by [Daraan](https://github.com/Daraan).
1519
- Extended the Concatenate backport for Python 3.8-3.10 to now accept
1620
ellipsis as an argument. Patch by [Daraan](https://github.com/Daraan).
1721

doc/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,9 @@ Special typing primitives
367367

368368
.. versionadded:: 4.6.0
369369

370-
.. data:: TypeExpr
370+
.. data:: TypeForm
371371

372-
See :pep:`747`. A type hint representing a type expression.
372+
See :pep:`747`. A special form representing the value of a type expression.
373373

374374
.. versionadded:: 4.13.0
375375

src/test_typing_extensions.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
TypeAlias,
7171
TypeAliasType,
7272
TypedDict,
73-
TypeExpr,
73+
TypeForm,
7474
TypeGuard,
7575
TypeIs,
7676
TypeVar,
@@ -5572,33 +5572,33 @@ def test_no_isinstance(self):
55725572
issubclass(int, TypeIs)
55735573

55745574

5575-
class TypeExprTests(BaseTestCase):
5575+
class TypeFormTests(BaseTestCase):
55765576
def test_basics(self):
5577-
TypeExpr[int] # OK
5578-
self.assertEqual(TypeExpr[int], TypeExpr[int])
5577+
TypeForm[int] # OK
5578+
self.assertEqual(TypeForm[int], TypeForm[int])
55795579

5580-
def foo(arg) -> TypeExpr[int]: ...
5581-
self.assertEqual(gth(foo), {'return': TypeExpr[int]})
5580+
def foo(arg) -> TypeForm[int]: ...
5581+
self.assertEqual(gth(foo), {'return': TypeForm[int]})
55825582

55835583
def test_repr(self):
5584-
if hasattr(typing, 'TypeExpr'):
5584+
if hasattr(typing, 'TypeForm'):
55855585
mod_name = 'typing'
55865586
else:
55875587
mod_name = 'typing_extensions'
5588-
self.assertEqual(repr(TypeExpr), f'{mod_name}.TypeExpr')
5589-
cv = TypeExpr[int]
5590-
self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[int]')
5591-
cv = TypeExpr[Employee]
5592-
self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[{__name__}.Employee]')
5593-
cv = TypeExpr[Tuple[int]]
5594-
self.assertEqual(repr(cv), f'{mod_name}.TypeExpr[typing.Tuple[int]]')
5588+
self.assertEqual(repr(TypeForm), f'{mod_name}.TypeForm')
5589+
cv = TypeForm[int]
5590+
self.assertEqual(repr(cv), f'{mod_name}.TypeForm[int]')
5591+
cv = TypeForm[Employee]
5592+
self.assertEqual(repr(cv), f'{mod_name}.TypeForm[{__name__}.Employee]')
5593+
cv = TypeForm[Tuple[int]]
5594+
self.assertEqual(repr(cv), f'{mod_name}.TypeForm[typing.Tuple[int]]')
55955595

55965596
def test_cannot_subclass(self):
55975597
with self.assertRaises(TypeError):
5598-
class C(type(TypeExpr)):
5598+
class C(type(TypeForm)):
55995599
pass
56005600
with self.assertRaises(TypeError):
5601-
class D(type(TypeExpr[int])):
5601+
class D(type(TypeForm[int])):
56025602
pass
56035603

56045604
def test_call(self):
@@ -5610,24 +5610,24 @@ def test_call(self):
56105610
]
56115611
for obj in objs:
56125612
with self.subTest(obj=obj):
5613-
self.assertIs(TypeExpr(obj), obj)
5613+
self.assertIs(TypeForm(obj), obj)
56145614

56155615
with self.assertRaises(TypeError):
5616-
TypeExpr()
5616+
TypeForm()
56175617
with self.assertRaises(TypeError):
5618-
TypeExpr("too", "many")
5618+
TypeForm("too", "many")
56195619

56205620
def test_cannot_init_type(self):
56215621
with self.assertRaises(TypeError):
5622-
type(TypeExpr)()
5622+
type(TypeForm)()
56235623
with self.assertRaises(TypeError):
5624-
type(TypeExpr[Optional[int]])()
5624+
type(TypeForm[Optional[int]])()
56255625

56265626
def test_no_isinstance(self):
56275627
with self.assertRaises(TypeError):
5628-
isinstance(1, TypeExpr[int])
5628+
isinstance(1, TypeForm[int])
56295629
with self.assertRaises(TypeError):
5630-
issubclass(int, TypeExpr)
5630+
issubclass(int, TypeForm)
56315631

56325632

56335633
class LiteralStringTests(BaseTestCase):
@@ -7311,6 +7311,21 @@ def test_getitem(self):
73117311
self.assertEqual(get_args(fully_subscripted), (Iterable[float],))
73127312
self.assertIs(get_origin(fully_subscripted), ListOrSetT)
73137313

7314+
def test_unpack_parameter_collection(self):
7315+
Ts = TypeVarTuple("Ts")
7316+
7317+
class Foo(Generic[Unpack[Ts]]):
7318+
bar: Tuple[Unpack[Ts]]
7319+
7320+
FooAlias = TypeAliasType("FooAlias", Foo[Unpack[Ts]], type_params=(Ts,))
7321+
self.assertEqual(FooAlias[Unpack[Tuple[str]]].__parameters__, ())
7322+
self.assertEqual(FooAlias[Unpack[Tuple[T]]].__parameters__, (T,))
7323+
7324+
P = ParamSpec("P")
7325+
CallableP = TypeAliasType("CallableP", Callable[P, Any], type_params=(P,))
7326+
call_int_T = CallableP[Unpack[Tuple[int, T]]]
7327+
self.assertEqual(call_int_T.__parameters__, (T,))
7328+
73147329
def test_alias_attributes(self):
73157330
T = TypeVar('T')
73167331
T2 = TypeVar('T2')

src/typing_extensions.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
'Text',
8787
'TypeAlias',
8888
'TypeAliasType',
89-
'TypeExpr',
89+
'TypeForm',
9090
'TypeGuard',
9191
'TypeIs',
9292
'TYPE_CHECKING',
@@ -2075,23 +2075,30 @@ def f(val: Union[int, Awaitable[int]]) -> int:
20752075
""")
20762076

20772077
# 3.14+?
2078-
if hasattr(typing, 'TypeExpr'):
2079-
TypeExpr = typing.TypeExpr
2078+
if hasattr(typing, 'TypeForm'):
2079+
TypeForm = typing.TypeForm
20802080
# 3.9
20812081
elif sys.version_info[:2] >= (3, 9):
2082-
class _TypeExprForm(_ExtensionsSpecialForm, _root=True):
2083-
# TypeExpr(X) is equivalent to X but indicates to the type checker
2084-
# that the object is a TypeExpr.
2082+
class _TypeFormForm(_ExtensionsSpecialForm, _root=True):
2083+
# TypeForm(X) is equivalent to X but indicates to the type checker
2084+
# that the object is a TypeForm.
20852085
def __call__(self, obj, /):
20862086
return obj
20872087

2088-
@_TypeExprForm
2089-
def TypeExpr(self, parameters):
2090-
"""Special typing form used to represent a type expression.
2088+
@_TypeFormForm
2089+
def TypeForm(self, parameters):
2090+
"""A special form representing the value that results from the evaluation
2091+
of a type expression. This value encodes the information supplied in the
2092+
type expression, and it represents the type described by that type expression.
2093+
2094+
When used in a type expression, TypeForm describes a set of type form objects.
2095+
It accepts a single type argument, which must be a valid type expression.
2096+
``TypeForm[T]`` describes the set of all type form objects that represent
2097+
the type T or types that are assignable to T.
20912098
20922099
Usage:
20932100
2094-
def cast[T](typ: TypeExpr[T], value: Any) -> T: ...
2101+
def cast[T](typ: TypeForm[T], value: Any) -> T: ...
20952102
20962103
reveal_type(cast(int, "x")) # int
20972104
@@ -2101,7 +2108,7 @@ def cast[T](typ: TypeExpr[T], value: Any) -> T: ...
21012108
return typing._GenericAlias(self, (item,))
21022109
# 3.8
21032110
else:
2104-
class _TypeExprForm(_ExtensionsSpecialForm, _root=True):
2111+
class _TypeFormForm(_ExtensionsSpecialForm, _root=True):
21052112
def __getitem__(self, parameters):
21062113
item = typing._type_check(parameters,
21072114
f'{self._name} accepts only a single type')
@@ -2110,13 +2117,20 @@ def __getitem__(self, parameters):
21102117
def __call__(self, obj, /):
21112118
return obj
21122119

2113-
TypeExpr = _TypeExprForm(
2114-
'TypeExpr',
2115-
doc="""Special typing form used to represent a type expression.
2120+
TypeForm = _TypeFormForm(
2121+
'TypeForm',
2122+
doc="""A special form representing the value that results from the evaluation
2123+
of a type expression. This value encodes the information supplied in the
2124+
type expression, and it represents the type described by that type expression.
2125+
2126+
When used in a type expression, TypeForm describes a set of type form objects.
2127+
It accepts a single type argument, which must be a valid type expression.
2128+
``TypeForm[T]`` describes the set of all type form objects that represent
2129+
the type T or types that are assignable to T.
21162130
21172131
Usage:
21182132
2119-
def cast[T](typ: TypeExpr[T], value: Any) -> T: ...
2133+
def cast[T](typ: TypeForm[T], value: Any) -> T: ...
21202134
21212135
reveal_type(cast(int, "x")) # int
21222136
@@ -3096,7 +3110,10 @@ def _collect_type_vars(types, typevar_types=None):
30963110
for t in types:
30973111
if _is_unpacked_typevartuple(t):
30983112
type_var_tuple_encountered = True
3099-
elif isinstance(t, typevar_types) and t not in tvars:
3113+
elif (
3114+
isinstance(t, typevar_types) and not isinstance(t, _UnpackAlias)
3115+
and t not in tvars
3116+
):
31003117
if enforce_default_ordering:
31013118
has_default = getattr(t, '__default__', NoDefault) is not NoDefault
31023119
if has_default:

0 commit comments

Comments
 (0)