[Python-checkins] gh-107432 Fix incorrect indentation in annotations HOWTO (#107445)

hugovk webhook-mailer at python.org
Sat Aug 5 07:28:31 EDT 2023


https://github.com/python/cpython/commit/5e2746d6e2fb0da29225ead7135f078c5f087b57
commit: 5e2746d6e2fb0da29225ead7135f078c5f087b57
branch: main
author: Daniele Procida <daniele at vurt.org>
committer: hugovk <hugovk at users.noreply.github.com>
date: 2023-08-05T14:28:28+03:00
summary:

gh-107432 Fix incorrect indentation in annotations HOWTO (#107445)

gh-107432 Fix incorrect indentation in annotations document

Body text in https://docs.python.org/3/howto/annotations.html was
indented throughout, and was being rendered in blockquote elements.

files:
M Doc/howto/annotations.rst

diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst
index 472069032d650..1134686c947d6 100644
--- a/Doc/howto/annotations.rst
+++ b/Doc/howto/annotations.rst
@@ -32,201 +32,201 @@ Annotations Best Practices
 Accessing The Annotations Dict Of An Object In Python 3.10 And Newer
 ====================================================================
 
-  Python 3.10 adds a new function to the standard library:
-  :func:`inspect.get_annotations`.  In Python versions 3.10
-  and newer, calling this function is the best practice for
-  accessing the annotations dict of any object that supports
-  annotations.  This function can also "un-stringize"
-  stringized annotations for you.
-
-  If for some reason :func:`inspect.get_annotations` isn't
-  viable for your use case, you may access the
-  ``__annotations__`` data member manually.  Best practice
-  for this changed in Python 3.10 as well: as of Python 3.10,
-  ``o.__annotations__`` is guaranteed to *always* work
-  on Python functions, classes, and modules.  If you're
-  certain the object you're examining is one of these three
-  *specific* objects, you may simply use ``o.__annotations__``
-  to get at the object's annotations dict.
-
-  However, other types of callables--for example,
-  callables created by :func:`functools.partial`--may
-  not have an ``__annotations__`` attribute defined.  When
-  accessing the ``__annotations__`` of a possibly unknown
-  object,  best practice in Python versions 3.10 and
-  newer is to call :func:`getattr` with three arguments,
-  for example ``getattr(o, '__annotations__', None)``.
-
-  Before Python 3.10, accessing ``__annotations__`` on a class that
-  defines no annotations but that has a parent class with
-  annotations would return the parent's ``__annotations__``.
-  In Python 3.10 and newer, the child class's annotations
-  will be an empty dict instead.
+Python 3.10 adds a new function to the standard library:
+:func:`inspect.get_annotations`.  In Python versions 3.10
+and newer, calling this function is the best practice for
+accessing the annotations dict of any object that supports
+annotations.  This function can also "un-stringize"
+stringized annotations for you.
+
+If for some reason :func:`inspect.get_annotations` isn't
+viable for your use case, you may access the
+``__annotations__`` data member manually.  Best practice
+for this changed in Python 3.10 as well: as of Python 3.10,
+``o.__annotations__`` is guaranteed to *always* work
+on Python functions, classes, and modules.  If you're
+certain the object you're examining is one of these three
+*specific* objects, you may simply use ``o.__annotations__``
+to get at the object's annotations dict.
+
+However, other types of callables--for example,
+callables created by :func:`functools.partial`--may
+not have an ``__annotations__`` attribute defined.  When
+accessing the ``__annotations__`` of a possibly unknown
+object,  best practice in Python versions 3.10 and
+newer is to call :func:`getattr` with three arguments,
+for example ``getattr(o, '__annotations__', None)``.
+
+Before Python 3.10, accessing ``__annotations__`` on a class that
+defines no annotations but that has a parent class with
+annotations would return the parent's ``__annotations__``.
+In Python 3.10 and newer, the child class's annotations
+will be an empty dict instead.
 
 
 Accessing The Annotations Dict Of An Object In Python 3.9 And Older
 ===================================================================
 
-  In Python 3.9 and older, accessing the annotations dict
-  of an object is much more complicated than in newer versions.
-  The problem is a design flaw in these older versions of Python,
-  specifically to do with class annotations.
+In Python 3.9 and older, accessing the annotations dict
+of an object is much more complicated than in newer versions.
+The problem is a design flaw in these older versions of Python,
+specifically to do with class annotations.
 
-  Best practice for accessing the annotations dict of other
-  objects--functions, other callables, and modules--is the same
-  as best practice for 3.10, assuming you aren't calling
-  :func:`inspect.get_annotations`: you should use three-argument
-  :func:`getattr` to access the object's ``__annotations__``
-  attribute.
+Best practice for accessing the annotations dict of other
+objects--functions, other callables, and modules--is the same
+as best practice for 3.10, assuming you aren't calling
+:func:`inspect.get_annotations`: you should use three-argument
+:func:`getattr` to access the object's ``__annotations__``
+attribute.
 
-  Unfortunately, this isn't best practice for classes.  The problem
-  is that, since ``__annotations__`` is optional on classes, and
-  because classes can inherit attributes from their base classes,
-  accessing the ``__annotations__`` attribute of a class may
-  inadvertently return the annotations dict of a *base class.*
-  As an example::
+Unfortunately, this isn't best practice for classes.  The problem
+is that, since ``__annotations__`` is optional on classes, and
+because classes can inherit attributes from their base classes,
+accessing the ``__annotations__`` attribute of a class may
+inadvertently return the annotations dict of a *base class.*
+As an example::
 
-      class Base:
-          a: int = 3
-          b: str = 'abc'
+    class Base:
+        a: int = 3
+        b: str = 'abc'
 
-      class Derived(Base):
-          pass
+    class Derived(Base):
+        pass
 
-      print(Derived.__annotations__)
+    print(Derived.__annotations__)
 
-  This will print the annotations dict from ``Base``, not
-  ``Derived``.
+This will print the annotations dict from ``Base``, not
+``Derived``.
 
-  Your code will have to have a separate code path if the object
-  you're examining is a class (``isinstance(o, type)``).
-  In that case, best practice relies on an implementation detail
-  of Python 3.9 and before: if a class has annotations defined,
-  they are stored in the class's ``__dict__`` dictionary.  Since
-  the class may or may not have annotations defined, best practice
-  is to call the ``get`` method on the class dict.
+Your code will have to have a separate code path if the object
+you're examining is a class (``isinstance(o, type)``).
+In that case, best practice relies on an implementation detail
+of Python 3.9 and before: if a class has annotations defined,
+they are stored in the class's ``__dict__`` dictionary.  Since
+the class may or may not have annotations defined, best practice
+is to call the ``get`` method on the class dict.
 
-  To put it all together, here is some sample code that safely
-  accesses the ``__annotations__`` attribute on an arbitrary
-  object in Python 3.9 and before::
+To put it all together, here is some sample code that safely
+accesses the ``__annotations__`` attribute on an arbitrary
+object in Python 3.9 and before::
 
-      if isinstance(o, type):
-          ann = o.__dict__.get('__annotations__', None)
-      else:
-          ann = getattr(o, '__annotations__', None)
+    if isinstance(o, type):
+        ann = o.__dict__.get('__annotations__', None)
+    else:
+        ann = getattr(o, '__annotations__', None)
 
-  After running this code, ``ann`` should be either a
-  dictionary or ``None``.  You're encouraged to double-check
-  the type of ``ann`` using :func:`isinstance` before further
-  examination.
+After running this code, ``ann`` should be either a
+dictionary or ``None``.  You're encouraged to double-check
+the type of ``ann`` using :func:`isinstance` before further
+examination.
 
-  Note that some exotic or malformed type objects may not have
-  a ``__dict__`` attribute, so for extra safety you may also wish
-  to use :func:`getattr` to access ``__dict__``.
+Note that some exotic or malformed type objects may not have
+a ``__dict__`` attribute, so for extra safety you may also wish
+to use :func:`getattr` to access ``__dict__``.
 
 
 Manually Un-Stringizing Stringized Annotations
 ==============================================
 
-  In situations where some annotations may be "stringized",
-  and you wish to evaluate those strings to produce the
-  Python values they represent, it really is best to
-  call :func:`inspect.get_annotations` to do this work
-  for you.
-
-  If you're using Python 3.9 or older, or if for some reason
-  you can't use :func:`inspect.get_annotations`, you'll need
-  to duplicate its logic.  You're encouraged to examine the
-  implementation of :func:`inspect.get_annotations` in the
-  current Python version and follow a similar approach.
-
-  In a nutshell, if you wish to evaluate a stringized annotation
-  on an arbitrary object ``o``:
-
-  * If ``o`` is a module, use ``o.__dict__`` as the
-    ``globals`` when calling :func:`eval`.
-  * If ``o`` is a class, use ``sys.modules[o.__module__].__dict__``
-    as the ``globals``, and ``dict(vars(o))`` as the ``locals``,
-    when calling :func:`eval`.
-  * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`,
-    :func:`functools.wraps`, or :func:`functools.partial`, iteratively
-    unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as
-    appropriate, until you have found the root unwrapped function.
-  * If ``o`` is a callable (but not a class), use
-    ``o.__globals__`` as the globals when calling :func:`eval`.
-
-  However, not all string values used as annotations can
-  be successfully turned into Python values by :func:`eval`.
-  String values could theoretically contain any valid string,
-  and in practice there are valid use cases for type hints that
-  require annotating with string values that specifically
-  *can't* be evaluated.  For example:
-
-  * :pep:`604` union types using ``|``, before support for this
-    was added to Python 3.10.
-  * Definitions that aren't needed at runtime, only imported
-    when :const:`typing.TYPE_CHECKING` is true.
-
-  If :func:`eval` attempts to evaluate such values, it will
-  fail and raise an exception.  So, when designing a library
-  API that works with annotations, it's recommended to only
-  attempt to evaluate string values when explicitly requested
-  to by the caller.
+In situations where some annotations may be "stringized",
+and you wish to evaluate those strings to produce the
+Python values they represent, it really is best to
+call :func:`inspect.get_annotations` to do this work
+for you.
+
+If you're using Python 3.9 or older, or if for some reason
+you can't use :func:`inspect.get_annotations`, you'll need
+to duplicate its logic.  You're encouraged to examine the
+implementation of :func:`inspect.get_annotations` in the
+current Python version and follow a similar approach.
+
+In a nutshell, if you wish to evaluate a stringized annotation
+on an arbitrary object ``o``:
+
+* If ``o`` is a module, use ``o.__dict__`` as the
+  ``globals`` when calling :func:`eval`.
+* If ``o`` is a class, use ``sys.modules[o.__module__].__dict__``
+  as the ``globals``, and ``dict(vars(o))`` as the ``locals``,
+  when calling :func:`eval`.
+* If ``o`` is a wrapped callable using :func:`functools.update_wrapper`,
+  :func:`functools.wraps`, or :func:`functools.partial`, iteratively
+  unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as
+  appropriate, until you have found the root unwrapped function.
+* If ``o`` is a callable (but not a class), use
+  ``o.__globals__`` as the globals when calling :func:`eval`.
+
+However, not all string values used as annotations can
+be successfully turned into Python values by :func:`eval`.
+String values could theoretically contain any valid string,
+and in practice there are valid use cases for type hints that
+require annotating with string values that specifically
+*can't* be evaluated.  For example:
+
+* :pep:`604` union types using ``|``, before support for this
+  was added to Python 3.10.
+* Definitions that aren't needed at runtime, only imported
+  when :const:`typing.TYPE_CHECKING` is true.
+
+If :func:`eval` attempts to evaluate such values, it will
+fail and raise an exception.  So, when designing a library
+API that works with annotations, it's recommended to only
+attempt to evaluate string values when explicitly requested
+to by the caller.
 
 
 Best Practices For ``__annotations__`` In Any Python Version
 ============================================================
 
-  * You should avoid assigning to the ``__annotations__`` member
-    of objects directly.  Let Python manage setting ``__annotations__``.
+* You should avoid assigning to the ``__annotations__`` member
+  of objects directly.  Let Python manage setting ``__annotations__``.
 
-  * If you do assign directly to the ``__annotations__`` member
-    of an object, you should always set it to a ``dict`` object.
+* If you do assign directly to the ``__annotations__`` member
+  of an object, you should always set it to a ``dict`` object.
 
-  * If you directly access the ``__annotations__`` member
-    of an object, you should ensure that it's a
-    dictionary before attempting to examine its contents.
+* If you directly access the ``__annotations__`` member
+  of an object, you should ensure that it's a
+  dictionary before attempting to examine its contents.
 
-  * You should avoid modifying ``__annotations__`` dicts.
+* You should avoid modifying ``__annotations__`` dicts.
 
-  * You should avoid deleting the ``__annotations__`` attribute
-    of an object.
+* You should avoid deleting the ``__annotations__`` attribute
+  of an object.
 
 
 ``__annotations__`` Quirks
 ==========================
 
-  In all versions of Python 3, function
-  objects lazy-create an annotations dict if no annotations
-  are defined on that object.  You can delete the ``__annotations__``
-  attribute using ``del fn.__annotations__``, but if you then
-  access ``fn.__annotations__`` the object will create a new empty dict
-  that it will store and return as its annotations.  Deleting the
-  annotations on a function before it has lazily created its annotations
-  dict will throw an ``AttributeError``; using ``del fn.__annotations__``
-  twice in a row is guaranteed to always throw an ``AttributeError``.
-
-  Everything in the above paragraph also applies to class and module
-  objects in Python 3.10 and newer.
-
-  In all versions of Python 3, you can set ``__annotations__``
-  on a function object to ``None``.  However, subsequently
-  accessing the annotations on that object using ``fn.__annotations__``
-  will lazy-create an empty dictionary as per the first paragraph of
-  this section.  This is *not* true of modules and classes, in any Python
-  version; those objects permit setting ``__annotations__`` to any
-  Python value, and will retain whatever value is set.
-
-  If Python stringizes your annotations for you
-  (using ``from __future__ import annotations``), and you
-  specify a string as an annotation, the string will
-  itself be quoted.  In effect the annotation is quoted
-  *twice.*  For example::
-
-       from __future__ import annotations
-       def foo(a: "str"): pass
-
-       print(foo.__annotations__)
-
-  This prints ``{'a': "'str'"}``.  This shouldn't really be considered
-  a "quirk"; it's mentioned here simply because it might be surprising.
+In all versions of Python 3, function
+objects lazy-create an annotations dict if no annotations
+are defined on that object.  You can delete the ``__annotations__``
+attribute using ``del fn.__annotations__``, but if you then
+access ``fn.__annotations__`` the object will create a new empty dict
+that it will store and return as its annotations.  Deleting the
+annotations on a function before it has lazily created its annotations
+dict will throw an ``AttributeError``; using ``del fn.__annotations__``
+twice in a row is guaranteed to always throw an ``AttributeError``.
+
+Everything in the above paragraph also applies to class and module
+objects in Python 3.10 and newer.
+
+In all versions of Python 3, you can set ``__annotations__``
+on a function object to ``None``.  However, subsequently
+accessing the annotations on that object using ``fn.__annotations__``
+will lazy-create an empty dictionary as per the first paragraph of
+this section.  This is *not* true of modules and classes, in any Python
+version; those objects permit setting ``__annotations__`` to any
+Python value, and will retain whatever value is set.
+
+If Python stringizes your annotations for you
+(using ``from __future__ import annotations``), and you
+specify a string as an annotation, the string will
+itself be quoted.  In effect the annotation is quoted
+*twice.*  For example::
+
+     from __future__ import annotations
+     def foo(a: "str"): pass
+
+     print(foo.__annotations__)
+
+This prints ``{'a': "'str'"}``.  This shouldn't really be considered
+a "quirk"; it's mentioned here simply because it might be surprising.



More information about the Python-checkins mailing list