• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _annotations-howto:
2
3**************************
4Annotations Best Practices
5**************************
6
7:author: Larry Hastings
8
9.. topic:: Abstract
10
11  This document is designed to encapsulate the best practices
12  for working with annotations dicts.  If you write Python code
13  that examines ``__annotations__`` on Python objects, we
14  encourage you to follow the guidelines described below.
15
16  The document is organized into four sections:
17  best practices for accessing the annotations of an object
18  in Python versions 3.10 and newer,
19  best practices for accessing the annotations of an object
20  in Python versions 3.9 and older,
21  other best practices
22  for ``__annotations__`` that apply to any Python version,
23  and
24  quirks of ``__annotations__``.
25
26  Note that this document is specifically about working with
27  ``__annotations__``, not uses *for* annotations.
28  If you're looking for information on how to use "type hints"
29  in your code, please see the :mod:`typing` module.
30
31
32Accessing The Annotations Dict Of An Object In Python 3.10 And Newer
33====================================================================
34
35  Python 3.10 adds a new function to the standard library:
36  :func:`inspect.get_annotations`.  In Python versions 3.10
37  and newer, calling this function is the best practice for
38  accessing the annotations dict of any object that supports
39  annotations.  This function can also "un-stringize"
40  stringized annotations for you.
41
42  If for some reason :func:`inspect.get_annotations` isn't
43  viable for your use case, you may access the
44  ``__annotations__`` data member manually.  Best practice
45  for this changed in Python 3.10 as well: as of Python 3.10,
46  ``o.__annotations__`` is guaranteed to *always* work
47  on Python functions, classes, and modules.  If you're
48  certain the object you're examining is one of these three
49  *specific* objects, you may simply use ``o.__annotations__``
50  to get at the object's annotations dict.
51
52  However, other types of callables--for example,
53  callables created by :func:`functools.partial`--may
54  not have an ``__annotations__`` attribute defined.  When
55  accessing the ``__annotations__`` of a possibly unknown
56  object,  best practice in Python versions 3.10 and
57  newer is to call :func:`getattr` with three arguments,
58  for example ``getattr(o, '__annotations__', None)``.
59
60
61Accessing The Annotations Dict Of An Object In Python 3.9 And Older
62===================================================================
63
64  In Python 3.9 and older, accessing the annotations dict
65  of an object is much more complicated than in newer versions.
66  The problem is a design flaw in these older versions of Python,
67  specifically to do with class annotations.
68
69  Best practice for accessing the annotations dict of other
70  objects--functions, other callables, and modules--is the same
71  as best practice for 3.10, assuming you aren't calling
72  :func:`inspect.get_annotations`: you should use three-argument
73  :func:`getattr` to access the object's ``__annotations__``
74  attribute.
75
76  Unfortunately, this isn't best practice for classes.  The problem
77  is that, since ``__annotations__`` is optional on classes, and
78  because classes can inherit attributes from their base classes,
79  accessing the ``__annotations__`` attribute of a class may
80  inadvertently return the annotations dict of a *base class.*
81  As an example::
82
83      class Base:
84          a: int = 3
85          b: str = 'abc'
86
87      class Derived(Base):
88          pass
89
90      print(Derived.__annotations__)
91
92  This will print the annotations dict from ``Base``, not
93  ``Derived``.
94
95  Your code will have to have a separate code path if the object
96  you're examining is a class (``isinstance(o, type)``).
97  In that case, best practice relies on an implementation detail
98  of Python 3.9 and before: if a class has annotations defined,
99  they are stored in the class's ``__dict__`` dictionary.  Since
100  the class may or may not have annotations defined, best practice
101  is to call the ``get`` method on the class dict.
102
103  To put it all together, here is some sample code that safely
104  accesses the ``__annotations__`` attribute on an arbitrary
105  object in Python 3.9 and before::
106
107      if isinstance(o, type):
108          ann = o.__dict__.get('__annotations__', None)
109      else:
110          ann = getattr(o, '__annotations__', None)
111
112  After running this code, ``ann`` should be either a
113  dictionary or ``None``.  You're encouraged to double-check
114  the type of ``ann`` using :func:`isinstance` before further
115  examination.
116
117  Note that some exotic or malformed type objects may not have
118  a ``__dict__`` attribute, so for extra safety you may also wish
119  to use :func:`getattr` to access ``__dict__``.
120
121
122Manually Un-Stringizing Stringized Annotations
123==============================================
124
125  In situations where some annotations may be "stringized",
126  and you wish to evaluate those strings to produce the
127  Python values they represent, it really is best to
128  call :func:`inspect.get_annotations` to do this work
129  for you.
130
131  If you're using Python 3.9 or older, or if for some reason
132  you can't use :func:`inspect.get_annotations`, you'll need
133  to duplicate its logic.  You're encouraged to examine the
134  implementation of :func:`inspect.get_annotations` in the
135  current Python version and follow a similar approach.
136
137  In a nutshell, if you wish to evaluate a stringized annotation
138  on an arbitrary object ``o``:
139
140  * If ``o`` is a module, use ``o.__dict__`` as the
141    ``globals`` when calling :func:`eval`.
142  * If ``o`` is a class, use ``sys.modules[o.__module__].__dict__``
143    as the ``globals``, and ``dict(vars(o))`` as the ``locals``,
144    when calling :func:`eval`.
145  * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`,
146    :func:`functools.wraps`, or :func:`functools.partial`, iteratively
147    unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as
148    appropriate, until you have found the root unwrapped function.
149  * If ``o`` is a callable (but not a class), use
150    ``o.__globals__`` as the globals when calling :func:`eval`.
151
152  However, not all string values used as annotations can
153  be successfully turned into Python values by :func:`eval`.
154  String values could theoretically contain any valid string,
155  and in practice there are valid use cases for type hints that
156  require annotating with string values that specifically
157  *can't* be evaluated.  For example:
158
159  * :pep:`604` union types using ``|``, before support for this
160    was added to Python 3.10.
161  * Definitions that aren't needed at runtime, only imported
162    when :const:`typing.TYPE_CHECKING` is true.
163
164  If :func:`eval` attempts to evaluate such values, it will
165  fail and raise an exception.  So, when designing a library
166  API that works with annotations, it's recommended to only
167  attempt to evaluate string values when explicitly requested
168  to by the caller.
169
170
171Best Practices For ``__annotations__`` In Any Python Version
172============================================================
173
174  * You should avoid assigning to the ``__annotations__`` member
175    of objects directly.  Let Python manage setting ``__annotations__``.
176
177  * If you do assign directly to the ``__annotations__`` member
178    of an object, you should always set it to a ``dict`` object.
179
180  * If you directly access the ``__annotations__`` member
181    of an object, you should ensure that it's a
182    dictionary before attempting to examine its contents.
183
184  * You should avoid modifying ``__annotations__`` dicts.
185
186  * You should avoid deleting the ``__annotations__`` attribute
187    of an object.
188
189
190``__annotations__`` Quirks
191==========================
192
193  In all versions of Python 3, function
194  objects lazy-create an annotations dict if no annotations
195  are defined on that object.  You can delete the ``__annotations__``
196  attribute using ``del fn.__annotations__``, but if you then
197  access ``fn.__annotations__`` the object will create a new empty dict
198  that it will store and return as its annotations.  Deleting the
199  annotations on a function before it has lazily created its annotations
200  dict will throw an ``AttributeError``; using ``del fn.__annotations__``
201  twice in a row is guaranteed to always throw an ``AttributeError``.
202
203  Everything in the above paragraph also applies to class and module
204  objects in Python 3.10 and newer.
205
206  In all versions of Python 3, you can set ``__annotations__``
207  on a function object to ``None``.  However, subsequently
208  accessing the annotations on that object using ``fn.__annotations__``
209  will lazy-create an empty dictionary as per the first paragraph of
210  this section.  This is *not* true of modules and classes, in any Python
211  version; those objects permit setting ``__annotations__`` to any
212  Python value, and will retain whatever value is set.
213
214  If Python stringizes your annotations for you
215  (using ``from __future__ import annotations``), and you
216  specify a string as an annotation, the string will
217  itself be quoted.  In effect the annotation is quoted
218  *twice.*  For example::
219
220       from __future__ import annotations
221       def foo(a: "str"): pass
222
223       print(foo.__annotations__)
224
225  This prints ``{'a': "'str'"}``.  This shouldn't really be considered
226  a "quirk"; it's mentioned here simply because it might be surprising.
227