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  Before Python 3.10, accessing ``__annotations__`` on a class that
61  defines no annotations but that has a parent class with
62  annotations would return the parent's ``__annotations__``.
63  In Python 3.10 and newer, the child class's annotations
64  will be an empty dict instead.
65
66
67Accessing The Annotations Dict Of An Object In Python 3.9 And Older
68===================================================================
69
70  In Python 3.9 and older, accessing the annotations dict
71  of an object is much more complicated than in newer versions.
72  The problem is a design flaw in these older versions of Python,
73  specifically to do with class annotations.
74
75  Best practice for accessing the annotations dict of other
76  objects--functions, other callables, and modules--is the same
77  as best practice for 3.10, assuming you aren't calling
78  :func:`inspect.get_annotations`: you should use three-argument
79  :func:`getattr` to access the object's ``__annotations__``
80  attribute.
81
82  Unfortunately, this isn't best practice for classes.  The problem
83  is that, since ``__annotations__`` is optional on classes, and
84  because classes can inherit attributes from their base classes,
85  accessing the ``__annotations__`` attribute of a class may
86  inadvertently return the annotations dict of a *base class.*
87  As an example::
88
89      class Base:
90          a: int = 3
91          b: str = 'abc'
92
93      class Derived(Base):
94          pass
95
96      print(Derived.__annotations__)
97
98  This will print the annotations dict from ``Base``, not
99  ``Derived``.
100
101  Your code will have to have a separate code path if the object
102  you're examining is a class (``isinstance(o, type)``).
103  In that case, best practice relies on an implementation detail
104  of Python 3.9 and before: if a class has annotations defined,
105  they are stored in the class's ``__dict__`` dictionary.  Since
106  the class may or may not have annotations defined, best practice
107  is to call the ``get`` method on the class dict.
108
109  To put it all together, here is some sample code that safely
110  accesses the ``__annotations__`` attribute on an arbitrary
111  object in Python 3.9 and before::
112
113      if isinstance(o, type):
114          ann = o.__dict__.get('__annotations__', None)
115      else:
116          ann = getattr(o, '__annotations__', None)
117
118  After running this code, ``ann`` should be either a
119  dictionary or ``None``.  You're encouraged to double-check
120  the type of ``ann`` using :func:`isinstance` before further
121  examination.
122
123  Note that some exotic or malformed type objects may not have
124  a ``__dict__`` attribute, so for extra safety you may also wish
125  to use :func:`getattr` to access ``__dict__``.
126
127
128Manually Un-Stringizing Stringized Annotations
129==============================================
130
131  In situations where some annotations may be "stringized",
132  and you wish to evaluate those strings to produce the
133  Python values they represent, it really is best to
134  call :func:`inspect.get_annotations` to do this work
135  for you.
136
137  If you're using Python 3.9 or older, or if for some reason
138  you can't use :func:`inspect.get_annotations`, you'll need
139  to duplicate its logic.  You're encouraged to examine the
140  implementation of :func:`inspect.get_annotations` in the
141  current Python version and follow a similar approach.
142
143  In a nutshell, if you wish to evaluate a stringized annotation
144  on an arbitrary object ``o``:
145
146  * If ``o`` is a module, use ``o.__dict__`` as the
147    ``globals`` when calling :func:`eval`.
148  * If ``o`` is a class, use ``sys.modules[o.__module__].__dict__``
149    as the ``globals``, and ``dict(vars(o))`` as the ``locals``,
150    when calling :func:`eval`.
151  * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`,
152    :func:`functools.wraps`, or :func:`functools.partial`, iteratively
153    unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as
154    appropriate, until you have found the root unwrapped function.
155  * If ``o`` is a callable (but not a class), use
156    ``o.__globals__`` as the globals when calling :func:`eval`.
157
158  However, not all string values used as annotations can
159  be successfully turned into Python values by :func:`eval`.
160  String values could theoretically contain any valid string,
161  and in practice there are valid use cases for type hints that
162  require annotating with string values that specifically
163  *can't* be evaluated.  For example:
164
165  * :pep:`604` union types using ``|``, before support for this
166    was added to Python 3.10.
167  * Definitions that aren't needed at runtime, only imported
168    when :const:`typing.TYPE_CHECKING` is true.
169
170  If :func:`eval` attempts to evaluate such values, it will
171  fail and raise an exception.  So, when designing a library
172  API that works with annotations, it's recommended to only
173  attempt to evaluate string values when explicitly requested
174  to by the caller.
175
176
177Best Practices For ``__annotations__`` In Any Python Version
178============================================================
179
180  * You should avoid assigning to the ``__annotations__`` member
181    of objects directly.  Let Python manage setting ``__annotations__``.
182
183  * If you do assign directly to the ``__annotations__`` member
184    of an object, you should always set it to a ``dict`` object.
185
186  * If you directly access the ``__annotations__`` member
187    of an object, you should ensure that it's a
188    dictionary before attempting to examine its contents.
189
190  * You should avoid modifying ``__annotations__`` dicts.
191
192  * You should avoid deleting the ``__annotations__`` attribute
193    of an object.
194
195
196``__annotations__`` Quirks
197==========================
198
199  In all versions of Python 3, function
200  objects lazy-create an annotations dict if no annotations
201  are defined on that object.  You can delete the ``__annotations__``
202  attribute using ``del fn.__annotations__``, but if you then
203  access ``fn.__annotations__`` the object will create a new empty dict
204  that it will store and return as its annotations.  Deleting the
205  annotations on a function before it has lazily created its annotations
206  dict will throw an ``AttributeError``; using ``del fn.__annotations__``
207  twice in a row is guaranteed to always throw an ``AttributeError``.
208
209  Everything in the above paragraph also applies to class and module
210  objects in Python 3.10 and newer.
211
212  In all versions of Python 3, you can set ``__annotations__``
213  on a function object to ``None``.  However, subsequently
214  accessing the annotations on that object using ``fn.__annotations__``
215  will lazy-create an empty dictionary as per the first paragraph of
216  this section.  This is *not* true of modules and classes, in any Python
217  version; those objects permit setting ``__annotations__`` to any
218  Python value, and will retain whatever value is set.
219
220  If Python stringizes your annotations for you
221  (using ``from __future__ import annotations``), and you
222  specify a string as an annotation, the string will
223  itself be quoted.  In effect the annotation is quoted
224  *twice.*  For example::
225
226       from __future__ import annotations
227       def foo(a: "str"): pass
228
229       print(foo.__annotations__)
230
231  This prints ``{'a': "'str'"}``.  This shouldn't really be considered
232  a "quirk"; it's mentioned here simply because it might be surprising.
233