From 0e5d19615e56cf31e2052596ca24208219fbd6c8 Mon Sep 17 00:00:00 2001 From: Abduaziz Ziyodov Date: Tue, 6 May 2025 10:59:40 +0500 Subject: [PATCH 1/4] Add test case to check whether GenericAlias objects are not considered to be class, update docstring of typing._GenericAlias --- Lib/test/test_typing.py | 15 ++++++ Lib/typing.py | 110 +++++++++++++++++++++------------------- 2 files changed, 72 insertions(+), 53 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 8c55ba4623e719..17a347566d89ee 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -5638,6 +5638,21 @@ def foo(x: T): foo(42) + def test_genericalias_isclass(self): + T = TypeVar('T') + + class Node(Generic[T]): + def __init__(self, label: T, + left: 'Node[T] | None' = None, + right: 'Node[T] | None' = None): + self.label = label + self.left = left + self.right = right + + self.assertTrue(inspect.isclass(Node)) + self.assertFalse(inspect.isclass(Node[int])) + self.assertFalse(inspect.isclass(Node[str])) + def test_implicit_any(self): T = TypeVar('T') diff --git a/Lib/typing.py b/Lib/typing.py index e019c5975800a9..09c431a1bfe8cb 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1283,31 +1283,34 @@ def __dir__(self): class _GenericAlias(_BaseGenericAlias, _root=True): - # The type of parameterized generics. - # - # That is, for example, `type(List[int])` is `_GenericAlias`. - # - # Objects which are instances of this class include: - # * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. - # * Note that native container types, e.g. `tuple`, `list`, use - # `types.GenericAlias` instead. - # * Parameterized classes: - # class C[T]: pass - # # C[int] is a _GenericAlias - # * `Callable` aliases, generic `Callable` aliases, and - # parameterized `Callable` aliases: - # T = TypeVar('T') - # # _CallableGenericAlias inherits from _GenericAlias. - # A = Callable[[], None] # _CallableGenericAlias - # B = Callable[[T], None] # _CallableGenericAlias - # C = B[int] # _CallableGenericAlias - # * Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`: - # # All _GenericAlias - # Final[int] - # ClassVar[float] - # TypeGuard[bool] - # TypeIs[range] - + """The type of parameterized generics. + + That is, for example, `type(List[int])` is `_GenericAlias`. + + Objects which are instances of this class include: + * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. + * Note that native container types, e.g. `tuple`, `list`, use + `types.GenericAlias` instead. + * Parameterized classes: + class C[T]: pass + # C[int] is a _GenericAlias + * `Callable` aliases, generic `Callable` aliases, and + parameterized `Callable` aliases: + T = TypeVar('T') + # _CallableGenericAlias inherits from _GenericAlias. + A = Callable[[], None] # _CallableGenericAlias + B = Callable[[T], None] # _CallableGenericAlias + C = B[int] # _CallableGenericAlias + * Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`: + # All _GenericAlias + Final[int] + ClassVar[float] + TypeGuard[bool] + TypeIs[range] + + Note that objects of this class is not considered to be a class (e.g by `inspect.isclass`), + even though they behave like them. + """ def __init__(self, origin, args, *, inst=True, name=None): super().__init__(origin, inst=inst, name=name) if not isinstance(args, tuple): @@ -1339,20 +1342,21 @@ def __ror__(self, left): @_tp_cache def __getitem__(self, args): - # Parameterizes an already-parameterized object. - # - # For example, we arrive here doing something like: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # T3 = TypeVar('T3') - # class A(Generic[T1]): pass - # B = A[T2] # B is a _GenericAlias - # C = B[T3] # Invokes _GenericAlias.__getitem__ - # - # We also arrive here when parameterizing a generic `Callable` alias: - # T = TypeVar('T') - # C = Callable[[T], None] - # C[int] # Invokes _GenericAlias.__getitem__ + """Parameterizes an already-parameterized object. + + For example, we arrive here doing something like: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + class A(Generic[T1]): pass + B = A[T2] # B is a _GenericAlias + C = B[T3] # Invokes _GenericAlias.__getitem__ + + We also arrive here when parameterizing a generic `Callable` alias: + T = TypeVar('T') + C = Callable[[T], None] + C[int] # Invokes _GenericAlias.__getitem__ + """ if self.__origin__ in (Generic, Protocol): # Can't subscript Generic[...] or Protocol[...]. @@ -1369,20 +1373,20 @@ def __getitem__(self, args): return r def _determine_new_args(self, args): - # Determines new __args__ for __getitem__. - # - # For example, suppose we had: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # class A(Generic[T1, T2]): pass - # T3 = TypeVar('T3') - # B = A[int, T3] - # C = B[str] - # `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. - # Unfortunately, this is harder than it looks, because if `T3` is - # anything more exotic than a plain `TypeVar`, we need to consider - # edge cases. - + """Determines new __args__ for __getitem__. + + For example, suppose we had: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + class A(Generic[T1, T2]): pass + T3 = TypeVar('T3') + B = A[int, T3] + C = B[str] + `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. + Unfortunately, this is harder than it looks, because if `T3` is + anything more exotic than a plain `TypeVar`, we need to consider + edge cases. + """ params = self.__parameters__ # In the example above, this would be {T3: str} for param in params: From fbaaa4e87348fa8b0b38c8eebc6b5a7b36a59480 Mon Sep 17 00:00:00 2001 From: Abduaziz Ziyodov Date: Tue, 6 May 2025 21:58:20 +0500 Subject: [PATCH 2/4] Update online docs, explain that GenericAlias instances are not classes at runtime --- Doc/library/stdtypes.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 39aaa5da0786f8..b1251d1222cd22 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5346,6 +5346,15 @@ creation:: >>> type(l) + +Additionally, instances of ``GenericAlias`` are not considered to be classes at runtime, even though they behave like classes (e.g. they can be instantiated):: + + >>> import inspect + >>> inspect.isclass(list[int]) + False + +This is true for :ref:`user-defined generics ` also. + Calling :func:`repr` or :func:`str` on a generic shows the parameterized type:: >>> repr(list[int]) From 817a313a16d407f79396681e73fd4543367a4d74 Mon Sep 17 00:00:00 2001 From: Abduaziz Ziyodov Date: Tue, 6 May 2025 22:35:50 +0500 Subject: [PATCH 3/4] Add tests against standard generic classes, rename test case --- Lib/test/test_typing.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 17a347566d89ee..14fe9921c44388 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -5638,7 +5638,8 @@ def foo(x: T): foo(42) - def test_genericalias_isclass(self): + def test_genericalias_instance_isclass(self): + # test against user-defined generic classes T = TypeVar('T') class Node(Generic[T]): @@ -5653,6 +5654,11 @@ def __init__(self, label: T, self.assertFalse(inspect.isclass(Node[int])) self.assertFalse(inspect.isclass(Node[str])) + # test against standard generic classes + self.assertFalse(inspect.isclass(set[int])) + self.assertFalse(inspect.isclass(list[bytes])) + self.assertFalse(inspect.isclass(dict[str, str])) + def test_implicit_any(self): T = TypeVar('T') From ee0ed36e605291c805fe3a6834cdeb22c59702c8 Mon Sep 17 00:00:00 2001 From: Abduaziz Date: Wed, 7 May 2025 09:15:27 +0500 Subject: [PATCH 4/4] Update Doc/library/stdtypes.rst Co-authored-by: Jelle Zijlstra --- Doc/library/stdtypes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index b1251d1222cd22..37c78aff8d3779 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5347,7 +5347,7 @@ creation:: -Additionally, instances of ``GenericAlias`` are not considered to be classes at runtime, even though they behave like classes (e.g. they can be instantiated):: +Additionally, instances of ``GenericAlias`` are not classes at runtime, even though they behave like classes (e.g. they can be instantiated):: >>> import inspect >>> inspect.isclass(list[int]) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy