From 020d1681ec4dd43b73d228f401015011a7c4a0cc Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 3 Nov 2021 14:37:35 +0000 Subject: [PATCH 01/11] [doc] bpo-45680: Disambiguate ``__getitem__`` and ``__class_getitem__`` in the data model. The documentation explaining Python's data model does not adequately explain the differences between ``__getitem__`` and ``__class_getitem__``, nor does it explain when each is called. There is an attempt at explaining ``__class_getitem__`` in the documentation for ``GenericAlias`` objects, but this does not give sufficient clarity into how the method works. Moreover, it is the wrong place for that information to be found; the explanation of ``__class_getitem__`` should be in the documentation explaining the data model. This PR has been split off from #29335. --- Doc/reference/datamodel.rst | 79 +++++++++++++++++-- .../2021-11-03-14-30-55.bpo-45680.UBpWzY.rst | 2 + 2 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index a6eee22fa332cd..2100db80b47f2f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2215,23 +2215,80 @@ case the instance is itself a class. Emulating generic types ----------------------- -One can implement the generic class syntax as specified by :pep:`484` -(for example ``List[int]``) by defining a special method: +When using :term:`type annotations`, it is often useful to +*parameterize* a generic type using Python's square-brackets notation. +For example, the annotation ``list[int]`` might be used to signify a +:class:`list` in which all the elements are of type :class:`int`. + +.. seealso:: + + :pep:`484` - Type Hints + Introducing Python's framework for type annotations + + :ref:`Generic Alias Types` + Documentation for objects representing parameterized generic classes + + :class:`typing.Generic` + Inherit from :class:`typing.Generic` to implement generic classes that + can be parameterized at runtime and understood by static type-checkers. + +A class can generally only be parameterized if it defines the special +classmethod ``__class_getitem__()``. .. classmethod:: object.__class_getitem__(cls, key) Return an object representing the specialization of a generic class by type arguments found in *key*. -This method is looked up on the class object itself, and when defined in -the class body, this method is implicitly a class method. Note, this -mechanism is primarily reserved for use with static type hints, other usage -is discouraged. +``__getitem__`` *versus* ``__class_getitem__`` -.. seealso:: + Usually, the :ref:`subscription ` of an object in Python + using the square-brackets notation will call the :meth:`~object.__getitem__` + instance method defined on the object's class. - :pep:`560` - Core support for typing module and generic types + For example, if we have a list ``food`` as follows:: + + food = ['spam', 'eggs', 'bacon'] + + Calling ``food[0]`` will return the same value as calling:: + + type(food).__getitem__(food, 0) + + However, if a class defines the classmethod ``__class_getitem__()``, then + the subscription of that class may call the class's implementation of + ``__class_getitem__()`` rather than :meth:`~object.__getitem__`. + ``__class_getitem__()`` should return a + :ref:`GenericAlias` object if it is properly defined. + For example, because the :class:`list` class defines + ``__class_getitem__()``, calling ``list[str]`` is equivalent to calling:: + + list.__class_getitem__(str) + + rather than:: + + type(list).__getitem__(list, str) + +.. note:: + If :meth:`~object.__getitem__` is defined by a class's :term:`metaclass`, it + will take precedence over a ``__class_getitem__()`` classmethod + defined by the class. See :pep:`560` for more details. + +.. note:: + ``__class_getitem__()`` was introduced to implement runtime parameterization + of standard-library generic classes in order to more easily apply + :term:`type-hints` to these classes. + + To implement custom generic classes that can be parameterized at runtime and + understood by static type-checkers, users should either inherit from a + standard library class that already implements ``__class_getitem__()``, or + inherit from :class:`typing.Generic`, which has its own implementation of + ``__class_getitem__()``. + + Custom implementations of ``__class_getitem__()`` on classes defined outside + of the standard library may not be understood by third-party type-checkers + such as mypy. Using ``__class_getitem__()`` on any class for purposes other + than type-hinting is discouraged. .. _callable-types: @@ -2344,6 +2401,12 @@ through the object's keys; for sequences, it should iterate through the values. :keyword:`for` loops expect that an :exc:`IndexError` will be raised for illegal indexes to allow proper detection of the end of the sequence. + .. note:: + + When :ref:`subscripting` a *class*, the special + classmethod :meth:`~object.__class_getitem__` may be called instead of + ``__getitem__()``. + .. method:: object.__setitem__(self, key, value) diff --git a/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst b/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst new file mode 100644 index 00000000000000..1a1228ce3824ce --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst @@ -0,0 +1,2 @@ +Disambiguate the differences between ``__getitem__`` and __class_getitem__`` +in the documentation for the data model. Patch by Alex Waygood. From fe66943595aba3520c90d4ed26456be1e1f5b2d0 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 3 Nov 2021 14:42:17 +0000 Subject: [PATCH 02/11] Update 2021-11-03-14-30-55.bpo-45680.UBpWzY.rst --- .../next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst b/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst index 1a1228ce3824ce..afb48960c95490 100644 --- a/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst +++ b/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst @@ -1,2 +1,2 @@ -Disambiguate the differences between ``__getitem__`` and __class_getitem__`` +Disambiguate the differences between ``__getitem__`` and ``__class_getitem__`` in the documentation for the data model. Patch by Alex Waygood. From 32d77510c6da46da2f01922a863b35a09d200b21 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 3 Nov 2021 16:07:18 +0000 Subject: [PATCH 03/11] Delete news entry --- .../next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst diff --git a/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst b/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst deleted file mode 100644 index afb48960c95490..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2021-11-03-14-30-55.bpo-45680.UBpWzY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Disambiguate the differences between ``__getitem__`` and ``__class_getitem__`` -in the documentation for the data model. Patch by Alex Waygood. From efa07317441c6be1a9e0cb9bc69d6d0ab3f4e841 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 4 Nov 2021 11:44:48 +0000 Subject: [PATCH 04/11] Address review --- Doc/reference/datamodel.rst | 78 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 2100db80b47f2f..e7ae843d78818c 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2240,39 +2240,6 @@ classmethod ``__class_getitem__()``. Return an object representing the specialization of a generic class by type arguments found in *key*. -``__getitem__`` *versus* ``__class_getitem__`` - - Usually, the :ref:`subscription ` of an object in Python - using the square-brackets notation will call the :meth:`~object.__getitem__` - instance method defined on the object's class. - - For example, if we have a list ``food`` as follows:: - - food = ['spam', 'eggs', 'bacon'] - - Calling ``food[0]`` will return the same value as calling:: - - type(food).__getitem__(food, 0) - - However, if a class defines the classmethod ``__class_getitem__()``, then - the subscription of that class may call the class's implementation of - ``__class_getitem__()`` rather than :meth:`~object.__getitem__`. - ``__class_getitem__()`` should return a - :ref:`GenericAlias` object if it is properly defined. - - For example, because the :class:`list` class defines - ``__class_getitem__()``, calling ``list[str]`` is equivalent to calling:: - - list.__class_getitem__(str) - - rather than:: - - type(list).__getitem__(list, str) - -.. note:: - If :meth:`~object.__getitem__` is defined by a class's :term:`metaclass`, it - will take precedence over a ``__class_getitem__()`` classmethod - defined by the class. See :pep:`560` for more details. .. note:: ``__class_getitem__()`` was introduced to implement runtime parameterization @@ -2290,6 +2257,33 @@ classmethod ``__class_getitem__()``. such as mypy. Using ``__class_getitem__()`` on any class for purposes other than type-hinting is discouraged. + +*__class_getitem__* versus *__getitem__* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Usually, the :ref:`subscription ` of an object in Python +using the square-brackets notation will call the :meth:`~object.__getitem__` +instance method defined on the object's class. However, if a class defines the +classmethod ``__class_getitem__()``, then the subscription of that class may +call the class's implementation of ``__class_getitem__()`` rather than +:meth:`~object.__getitem__`. ``__class_getitem__()`` should return a +:ref:`GenericAlias` object if it is properly defined. + +For example, because the :class:`list` class defines ``__class_getitem__()``, +calling ``list[str]`` is equivalent to calling:: + + list.__class_getitem__(str) + +rather than:: + + type(list).__getitem__(list, str) + +.. note:: + If :meth:`~object.__getitem__` is defined by a class's :term:`metaclass`, it + will take precedence over a ``__class_getitem__()`` classmethod defined by + the class. See :pep:`560` for more details. + + .. _callable-types: Emulating callable objects @@ -2387,14 +2381,16 @@ through the object's keys; for sequences, it should iterate through the values. .. method:: object.__getitem__(self, key) - Called to implement evaluation of ``self[key]``. For sequence types, the - accepted keys should be integers and slice objects. Note that the special - interpretation of negative indexes (if the class wishes to emulate a sequence - type) is up to the :meth:`__getitem__` method. If *key* is of an inappropriate - type, :exc:`TypeError` may be raised; if of a value outside the set of indexes - for the sequence (after any special interpretation of negative values), - :exc:`IndexError` should be raised. For mapping types, if *key* is missing (not - in the container), :exc:`KeyError` should be raised. + Called to implement evaluation of ``self[key]``, which is translated by the + interpreter to ``type(self).__getitem__(self, key)``. For :term:`sequence` + types, the accepted keys should be integers and slice objects. Note that + the special interpretation of negative indexes (if the class wishes to + emulate a :term:`sequence` type) is up to the :meth:`__getitem__` method. If + *key* is of an inappropriate type, :exc:`TypeError` may be raised; if of a + value outside the set of indexes for the sequence (after any special + interpretation of negative values), :exc:`IndexError` should be raised. For + :term:`mapping` types, if *key* is missing (not in the container), + :exc:`KeyError` should be raised. .. note:: From 7ae4fac3de3d68a1a5c2c3f911290b595993e8ab Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 4 Nov 2021 12:26:57 +0000 Subject: [PATCH 05/11] Improve links to ``typing`` docs --- Doc/library/typing.rst | 1 + Doc/reference/datamodel.rst | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index eb95af378d45fd..18b770706ea39b 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -257,6 +257,7 @@ called :class:`TypeVar`. def first(l: Sequence[T]) -> T: # Generic function return l[0] +.. _user-defined-generics: User-defined generic types ========================== diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index e7ae843d78818c..43245d9549b6a8 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2228,9 +2228,9 @@ For example, the annotation ``list[int]`` might be used to signify a :ref:`Generic Alias Types` Documentation for objects representing parameterized generic classes - :class:`typing.Generic` - Inherit from :class:`typing.Generic` to implement generic classes that - can be parameterized at runtime and understood by static type-checkers. + :ref:`Generics`, :ref:`user-defined generics` and :class:`typing.Generic` + Documentation on how to implement generic classes that can be + parameterized at runtime and understood by static type-checkers. A class can generally only be parameterized if it defines the special classmethod ``__class_getitem__()``. From 8bc6b7c78d39333a75c7467daac49e72bfb228ff Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 4 Nov 2021 12:53:28 +0000 Subject: [PATCH 06/11] Add link to glossary definition --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 43245d9549b6a8..ac1a0ac5562c06 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2216,7 +2216,7 @@ Emulating generic types ----------------------- When using :term:`type annotations`, it is often useful to -*parameterize* a generic type using Python's square-brackets notation. +*parameterize* a :term:`generic type` using Python's square-brackets notation. For example, the annotation ``list[int]`` might be used to signify a :class:`list` in which all the elements are of type :class:`int`. From b01b0c9bb9cd03a070843012a6c22b0030a591f7 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 7 Nov 2021 22:17:25 +0000 Subject: [PATCH 07/11] Address second review --- Doc/reference/datamodel.rst | 132 ++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 35 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index ac1a0ac5562c06..bda0c6b6891e4b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2232,56 +2232,118 @@ For example, the annotation ``list[int]`` might be used to signify a Documentation on how to implement generic classes that can be parameterized at runtime and understood by static type-checkers. -A class can generally only be parameterized if it defines the special -classmethod ``__class_getitem__()``. +A class can *generally* only be parameterized if it defines the special +class method ``__class_getitem__()``. .. classmethod:: object.__class_getitem__(cls, key) Return an object representing the specialization of a generic class by type arguments found in *key*. + When defined on a class, ``__class_getitem__()`` is automatically a class + method. As such, there is no need for it to be decorated with + :func:`@classmethod` when it is defined. -.. note:: - ``__class_getitem__()`` was introduced to implement runtime parameterization - of standard-library generic classes in order to more easily apply - :term:`type-hints` to these classes. - To implement custom generic classes that can be parameterized at runtime and - understood by static type-checkers, users should either inherit from a - standard library class that already implements ``__class_getitem__()``, or - inherit from :class:`typing.Generic`, which has its own implementation of - ``__class_getitem__()``. +The purpose of *__class_getitem__* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Custom implementations of ``__class_getitem__()`` on classes defined outside - of the standard library may not be understood by third-party type-checkers - such as mypy. Using ``__class_getitem__()`` on any class for purposes other - than type-hinting is discouraged. +The purpose of :meth:`~object.__class_getitem__` is to allow runtime +parameterization of standard-library generic classes in order to more easily +apply :term:`type hints` to these classes. +To implement custom generic classes that can be parameterized at runtime and +understood by static type-checkers, users should either inherit from a standard +library class that already implements :meth:`~object.__class_getitem__`, or +inherit from :class:`typing.Generic`, which has its own implementation of +``__class_getitem__()``. -*__class_getitem__* versus *__getitem__* -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Custom implementations of :meth:`~object.__class_getitem__` on classes defined +outside of the standard library may not be understood by third-party +type-checkers such as mypy. Using ``__class_getitem__()`` on any class for +purposes other than type hinting is discouraged. -Usually, the :ref:`subscription ` of an object in Python -using the square-brackets notation will call the :meth:`~object.__getitem__` -instance method defined on the object's class. However, if a class defines the -classmethod ``__class_getitem__()``, then the subscription of that class may -call the class's implementation of ``__class_getitem__()`` rather than -:meth:`~object.__getitem__`. ``__class_getitem__()`` should return a -:ref:`GenericAlias` object if it is properly defined. -For example, because the :class:`list` class defines ``__class_getitem__()``, -calling ``list[str]`` is equivalent to calling:: +.. _classgetitem-versus-getitem: - list.__class_getitem__(str) -rather than:: +*__class_getitem__* versus *__getitem__* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Usually, the :ref:`subscription` of an object using square +brackets will call the :meth:`~object.__getitem__` instance method defined on +the object's class. However, if the object being subscribed is itself a class, +the class method :meth:`~object.__class_getitem__` may be called instead. +``__class_getitem__()`` should return a :ref:`GenericAlias` +object if it is properly defined. + +Presented with the :term:`expression` ``obj[x]``, the Python interpreter +follows something like the following process to decide whether +:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` should be +called:: + + def subscribe(obj, x): + class_of_obj = type(obj) + + # If the class of `obj` defines `__getitem__()`, + # call `type(obj).__getitem__()` + if hasattr(class_of_obj, '__getitem__'): + return class_of_obj.__getitem__(obj, x) + + # Else, if `obj` defines `__class_getitem__()`, + # call `obj.__class_getitem__()` + elif hasattr(obj, '__class_getitem__'): + return obj.__class_getitem__(x) + + # Else, raise an exception + else: + raise TypeError( + f"'{class_of_obj.__name__}' object is not subscriptable" + ) + +In Python, all classes are themselves instances of other classes. The class of +a class is known as that class's :term:`metaclass`, and most classes have the +:class:`type` class as their metaclass. :class:`type` does not define +:meth:`~object.__getitem__`, meaning that expressions such as ``list[int]``, +``dict[str, float]`` and ``tuple[str, bytes]`` all result in +:meth:`~object.__class_getitem__` being called:: + + >>> # `list` has `type` as its metaclass, like most classes: + >>> type(list) + + >>> type(dict) == type(list) == type(tuple) == type(str) == type(bytes) + True + >>> # `list[int]` calls `list.__class_getitem__()` + >>> list[int] + list[int] + >>> # `list.__class_getitem__()` returns a `GenericAlias` object: + >>> type(list[int]) + + +However, if a class has a custom metaclass that defines +:meth:`~object.__getitem__`, subscribing the class may result in different +behaviour. An example of this can be found in the :mod:`enum` module:: + + >>> from enum import Enum + >>> class Menu(Enum): + ... """A breakfast menu""" + ... SPAM = 'spam' + ... BACON = 'bacon' + ... + >>> # `Enum` classes have a custom metaclass + >>> type(Menu) + + >>> # `EnumMeta` defines `__getitem__()`, + >>> # so `__class_getitem__()` is not called: + >>> Menu['SPAM'] + - type(list).__getitem__(list, str) -.. note:: - If :meth:`~object.__getitem__` is defined by a class's :term:`metaclass`, it - will take precedence over a ``__class_getitem__()`` classmethod defined by - the class. See :pep:`560` for more details. +.. seealso:: + :pep:`560` - Core Support for typing module and generic types + Introducing :meth:`~object.__class_getitem__`, and outlining when a + :ref:`subscription` results in ``__class_getitem__()`` + being called instead of :meth:`~object.__getitem__` .. _callable-types: @@ -2400,8 +2462,8 @@ through the object's keys; for sequences, it should iterate through the values. .. note:: When :ref:`subscripting` a *class*, the special - classmethod :meth:`~object.__class_getitem__` may be called instead of - ``__getitem__()``. + class method :meth:`~object.__class_getitem__` may be called instead of + ``__getitem__()``. See :ref:`classgetitem-versus-getitem`. .. method:: object.__setitem__(self, key, value) From ca568d5eec128f79647ac3b7f74c238a3ee3b1d4 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 7 Nov 2021 22:35:30 +0000 Subject: [PATCH 08/11] Revise changes to ``__getitem__`` --- Doc/reference/datamodel.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index bda0c6b6891e4b..5bd0ee38be9f7f 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2443,27 +2443,27 @@ through the object's keys; for sequences, it should iterate through the values. .. method:: object.__getitem__(self, key) - Called to implement evaluation of ``self[key]``, which is translated by the - interpreter to ``type(self).__getitem__(self, key)``. For :term:`sequence` - types, the accepted keys should be integers and slice objects. Note that - the special interpretation of negative indexes (if the class wishes to - emulate a :term:`sequence` type) is up to the :meth:`__getitem__` method. If - *key* is of an inappropriate type, :exc:`TypeError` may be raised; if of a - value outside the set of indexes for the sequence (after any special + Called to implement evaluation of ``self[key]``. For :term:`sequence` types, + the accepted keys should be integers and slice objects. Note that the + special interpretation of negative indexes (if the class wishes to emulate a + :term:`sequence` type) is up to the :meth:`__getitem__` method. If *key* is + of an inappropriate type, :exc:`TypeError` may be raised; if of a value + outside the set of indexes for the sequence (after any special interpretation of negative values), :exc:`IndexError` should be raised. For :term:`mapping` types, if *key* is missing (not in the container), :exc:`KeyError` should be raised. .. note:: - :keyword:`for` loops expect that an :exc:`IndexError` will be raised for illegal - indexes to allow proper detection of the end of the sequence. + :keyword:`for` loops expect that an :exc:`IndexError` will be raised for + illegal indexes to allow proper detection of the end of the sequence. .. note:: When :ref:`subscripting` a *class*, the special class method :meth:`~object.__class_getitem__` may be called instead of - ``__getitem__()``. See :ref:`classgetitem-versus-getitem`. + ``__getitem__()``. See :ref:`classgetitem-versus-getitem` for more + details. .. method:: object.__setitem__(self, key, value) From c1955d7faf533388d45718dab6e0b8d3d3922784 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 8 Nov 2021 10:48:22 +0000 Subject: [PATCH 09/11] Improve code snippet --- Doc/reference/datamodel.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 5bd0ee38be9f7f..5b0bbe7b6db6a1 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2282,7 +2282,11 @@ follows something like the following process to decide whether :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` should be called:: + from inspect import isclass + def subscribe(obj, x): + """Return the result of the expression `obj[x]`""" + class_of_obj = type(obj) # If the class of `obj` defines `__getitem__()`, @@ -2290,9 +2294,9 @@ called:: if hasattr(class_of_obj, '__getitem__'): return class_of_obj.__getitem__(obj, x) - # Else, if `obj` defines `__class_getitem__()`, + # Else, if `obj` is a class and defines `__class_getitem__()`, # call `obj.__class_getitem__()` - elif hasattr(obj, '__class_getitem__'): + elif isclass(obj) and hasattr(obj, '__class_getitem__'): return obj.__class_getitem__(x) # Else, raise an exception From ace22fda9a0a81a6786255b4cb955d985d1a6a07 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 8 Nov 2021 22:44:32 +0000 Subject: [PATCH 10/11] Remove trailing whitespace --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 5b0bbe7b6db6a1..0b8c058341326e 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2286,7 +2286,7 @@ called:: def subscribe(obj, x): """Return the result of the expression `obj[x]`""" - + class_of_obj = type(obj) # If the class of `obj` defines `__getitem__()`, From f8ea20a0b51711b1c352c4aa4663cb953e7ebdc8 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 11 Nov 2021 09:27:08 +0000 Subject: [PATCH 11/11] Fix CI failures --- Doc/reference/datamodel.rst | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 0b8c058341326e..1ecfa81e3b3363 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2289,13 +2289,13 @@ called:: class_of_obj = type(obj) - # If the class of `obj` defines `__getitem__()`, - # call `type(obj).__getitem__()` + # If the class of obj defines __getitem__, + # call class_of_obj.__getitem__(obj, x) if hasattr(class_of_obj, '__getitem__'): return class_of_obj.__getitem__(obj, x) - # Else, if `obj` is a class and defines `__class_getitem__()`, - # call `obj.__class_getitem__()` + # Else, if obj is a class and defines __class_getitem__, + # call obj.__class_getitem__(x) elif isclass(obj) and hasattr(obj, '__class_getitem__'): return obj.__class_getitem__(x) @@ -2312,15 +2312,15 @@ a class is known as that class's :term:`metaclass`, and most classes have the ``dict[str, float]`` and ``tuple[str, bytes]`` all result in :meth:`~object.__class_getitem__` being called:: - >>> # `list` has `type` as its metaclass, like most classes: + >>> # list has class "type" as its metaclass, like most classes: >>> type(list) >>> type(dict) == type(list) == type(tuple) == type(str) == type(bytes) True - >>> # `list[int]` calls `list.__class_getitem__()` + >>> # "list[int]" calls "list.__class_getitem__(int)" >>> list[int] list[int] - >>> # `list.__class_getitem__()` returns a `GenericAlias` object: + >>> # list.__class_getitem__ returns a GenericAlias object: >>> type(list[int]) @@ -2334,13 +2334,16 @@ behaviour. An example of this can be found in the :mod:`enum` module:: ... SPAM = 'spam' ... BACON = 'bacon' ... - >>> # `Enum` classes have a custom metaclass + >>> # Enum classes have a custom metaclass: >>> type(Menu) - >>> # `EnumMeta` defines `__getitem__()`, - >>> # so `__class_getitem__()` is not called: + >>> # EnumMeta defines __getitem__, + >>> # so __class_getitem__ is not called, + >>> # and the result is not a GenericAlias object: >>> Menu['SPAM'] + >>> type(Menu['SPAM']) + .. seealso::