Skip to content

Commit 0e5d196

Browse files
Add test case to check whether GenericAlias objects are not considered to be class, update docstring of typing._GenericAlias
1 parent c273f59 commit 0e5d196

File tree

2 files changed

+72
-53
lines changed

2 files changed

+72
-53
lines changed

Lib/test/test_typing.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5638,6 +5638,21 @@ def foo(x: T):
56385638

56395639
foo(42)
56405640

5641+
def test_genericalias_isclass(self):
5642+
T = TypeVar('T')
5643+
5644+
class Node(Generic[T]):
5645+
def __init__(self, label: T,
5646+
left: 'Node[T] | None' = None,
5647+
right: 'Node[T] | None' = None):
5648+
self.label = label
5649+
self.left = left
5650+
self.right = right
5651+
5652+
self.assertTrue(inspect.isclass(Node))
5653+
self.assertFalse(inspect.isclass(Node[int]))
5654+
self.assertFalse(inspect.isclass(Node[str]))
5655+
56415656
def test_implicit_any(self):
56425657
T = TypeVar('T')
56435658

Lib/typing.py

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,31 +1283,34 @@ def __dir__(self):
12831283

12841284

12851285
class _GenericAlias(_BaseGenericAlias, _root=True):
1286-
# The type of parameterized generics.
1287-
#
1288-
# That is, for example, `type(List[int])` is `_GenericAlias`.
1289-
#
1290-
# Objects which are instances of this class include:
1291-
# * Parameterized container types, e.g. `Tuple[int]`, `List[int]`.
1292-
# * Note that native container types, e.g. `tuple`, `list`, use
1293-
# `types.GenericAlias` instead.
1294-
# * Parameterized classes:
1295-
# class C[T]: pass
1296-
# # C[int] is a _GenericAlias
1297-
# * `Callable` aliases, generic `Callable` aliases, and
1298-
# parameterized `Callable` aliases:
1299-
# T = TypeVar('T')
1300-
# # _CallableGenericAlias inherits from _GenericAlias.
1301-
# A = Callable[[], None] # _CallableGenericAlias
1302-
# B = Callable[[T], None] # _CallableGenericAlias
1303-
# C = B[int] # _CallableGenericAlias
1304-
# * Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`:
1305-
# # All _GenericAlias
1306-
# Final[int]
1307-
# ClassVar[float]
1308-
# TypeGuard[bool]
1309-
# TypeIs[range]
1310-
1286+
"""The type of parameterized generics.
1287+
1288+
That is, for example, `type(List[int])` is `_GenericAlias`.
1289+
1290+
Objects which are instances of this class include:
1291+
* Parameterized container types, e.g. `Tuple[int]`, `List[int]`.
1292+
* Note that native container types, e.g. `tuple`, `list`, use
1293+
`types.GenericAlias` instead.
1294+
* Parameterized classes:
1295+
class C[T]: pass
1296+
# C[int] is a _GenericAlias
1297+
* `Callable` aliases, generic `Callable` aliases, and
1298+
parameterized `Callable` aliases:
1299+
T = TypeVar('T')
1300+
# _CallableGenericAlias inherits from _GenericAlias.
1301+
A = Callable[[], None] # _CallableGenericAlias
1302+
B = Callable[[T], None] # _CallableGenericAlias
1303+
C = B[int] # _CallableGenericAlias
1304+
* Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`:
1305+
# All _GenericAlias
1306+
Final[int]
1307+
ClassVar[float]
1308+
TypeGuard[bool]
1309+
TypeIs[range]
1310+
1311+
Note that objects of this class is not considered to be a class (e.g by `inspect.isclass`),
1312+
even though they behave like them.
1313+
"""
13111314
def __init__(self, origin, args, *, inst=True, name=None):
13121315
super().__init__(origin, inst=inst, name=name)
13131316
if not isinstance(args, tuple):
@@ -1339,20 +1342,21 @@ def __ror__(self, left):
13391342

13401343
@_tp_cache
13411344
def __getitem__(self, args):
1342-
# Parameterizes an already-parameterized object.
1343-
#
1344-
# For example, we arrive here doing something like:
1345-
# T1 = TypeVar('T1')
1346-
# T2 = TypeVar('T2')
1347-
# T3 = TypeVar('T3')
1348-
# class A(Generic[T1]): pass
1349-
# B = A[T2] # B is a _GenericAlias
1350-
# C = B[T3] # Invokes _GenericAlias.__getitem__
1351-
#
1352-
# We also arrive here when parameterizing a generic `Callable` alias:
1353-
# T = TypeVar('T')
1354-
# C = Callable[[T], None]
1355-
# C[int] # Invokes _GenericAlias.__getitem__
1345+
"""Parameterizes an already-parameterized object.
1346+
1347+
For example, we arrive here doing something like:
1348+
T1 = TypeVar('T1')
1349+
T2 = TypeVar('T2')
1350+
T3 = TypeVar('T3')
1351+
class A(Generic[T1]): pass
1352+
B = A[T2] # B is a _GenericAlias
1353+
C = B[T3] # Invokes _GenericAlias.__getitem__
1354+
1355+
We also arrive here when parameterizing a generic `Callable` alias:
1356+
T = TypeVar('T')
1357+
C = Callable[[T], None]
1358+
C[int] # Invokes _GenericAlias.__getitem__
1359+
"""
13561360

13571361
if self.__origin__ in (Generic, Protocol):
13581362
# Can't subscript Generic[...] or Protocol[...].
@@ -1369,20 +1373,20 @@ def __getitem__(self, args):
13691373
return r
13701374

13711375
def _determine_new_args(self, args):
1372-
# Determines new __args__ for __getitem__.
1373-
#
1374-
# For example, suppose we had:
1375-
# T1 = TypeVar('T1')
1376-
# T2 = TypeVar('T2')
1377-
# class A(Generic[T1, T2]): pass
1378-
# T3 = TypeVar('T3')
1379-
# B = A[int, T3]
1380-
# C = B[str]
1381-
# `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`.
1382-
# Unfortunately, this is harder than it looks, because if `T3` is
1383-
# anything more exotic than a plain `TypeVar`, we need to consider
1384-
# edge cases.
1385-
1376+
"""Determines new __args__ for __getitem__.
1377+
1378+
For example, suppose we had:
1379+
T1 = TypeVar('T1')
1380+
T2 = TypeVar('T2')
1381+
class A(Generic[T1, T2]): pass
1382+
T3 = TypeVar('T3')
1383+
B = A[int, T3]
1384+
C = B[str]
1385+
`B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`.
1386+
Unfortunately, this is harder than it looks, because if `T3` is
1387+
anything more exotic than a plain `TypeVar`, we need to consider
1388+
edge cases.
1389+
"""
13861390
params = self.__parameters__
13871391
# In the example above, this would be {T3: str}
13881392
for param in params:

0 commit comments

Comments
 (0)