blob: 3e61103e99c9a625dc7e831030657864a39c1875 [file] [log] [blame]
larryhastings49b26fa2021-05-01 21:19:24 -07001.. _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.