1:mod:`contextvars` --- Context Variables
2========================================
3
4.. module:: contextvars
5   :synopsis: Context Variables
6
7.. sectionauthor:: Yury Selivanov <yury@magic.io>
8
9--------------
10
11This module provides APIs to manage, store, and access context-local
12state.  The :class:`~contextvars.ContextVar` class is used to declare
13and work with *Context Variables*.  The :func:`~contextvars.copy_context`
14function and the :class:`~contextvars.Context` class should be used to
15manage the current context in asynchronous frameworks.
16
17Context managers that have state should use Context Variables
18instead of :func:`threading.local()` to prevent their state from
19bleeding to other code unexpectedly, when used in concurrent code.
20
21See also :pep:`567` for additional details.
22
23.. versionadded:: 3.7
24
25
26Context Variables
27-----------------
28
29.. class:: ContextVar(name, [*, default])
30
31   This class is used to declare a new Context Variable, e.g.::
32
33       var: ContextVar[int] = ContextVar('var', default=42)
34
35   The required *name* parameter is used for introspection and debug
36   purposes.
37
38   The optional keyword-only *default* parameter is returned by
39   :meth:`ContextVar.get` when no value for the variable is found
40   in the current context.
41
42   **Important:** Context Variables should be created at the top module
43   level and never in closures.  :class:`Context` objects hold strong
44   references to context variables which prevents context variables
45   from being properly garbage collected.
46
47   .. attribute:: ContextVar.name
48
49      The name of the variable.  This is a read-only property.
50
51      .. versionadded:: 3.7.1
52
53   .. method:: get([default])
54
55      Return a value for the context variable for the current context.
56
57      If there is no value for the variable in the current context,
58      the method will:
59
60      * return the value of the *default* argument of the method,
61        if provided; or
62
63      * return the default value for the context variable,
64        if it was created with one; or
65
66      * raise a :exc:`LookupError`.
67
68   .. method:: set(value)
69
70      Call to set a new value for the context variable in the current
71      context.
72
73      The required *value* argument is the new value for the context
74      variable.
75
76      Returns a :class:`~contextvars.Token` object that can be used
77      to restore the variable to its previous value via the
78      :meth:`ContextVar.reset` method.
79
80   .. method:: reset(token)
81
82      Reset the context variable to the value it had before the
83      :meth:`ContextVar.set` that created the *token* was used.
84
85      For example::
86
87          var = ContextVar('var')
88
89          token = var.set('new value')
90          # code that uses 'var'; var.get() returns 'new value'.
91          var.reset(token)
92
93          # After the reset call the var has no value again, so
94          # var.get() would raise a LookupError.
95
96
97.. class:: Token
98
99   *Token* objects are returned by the :meth:`ContextVar.set` method.
100   They can be passed to the :meth:`ContextVar.reset` method to revert
101   the value of the variable to what it was before the corresponding
102   *set*.
103
104   .. attribute:: Token.var
105
106      A read-only property.  Points to the :class:`ContextVar` object
107      that created the token.
108
109   .. attribute:: Token.old_value
110
111      A read-only property.  Set to the value the variable had before
112      the :meth:`ContextVar.set` method call that created the token.
113      It points to :attr:`Token.MISSING` if the variable was not set
114      before the call.
115
116   .. attribute:: Token.MISSING
117
118      A marker object used by :attr:`Token.old_value`.
119
120
121Manual Context Management
122-------------------------
123
124.. function:: copy_context()
125
126   Returns a copy of the current :class:`~contextvars.Context` object.
127
128   The following snippet gets a copy of the current context and prints
129   all variables and their values that are set in it::
130
131      ctx: Context = copy_context()
132      print(list(ctx.items()))
133
134   The function has an O(1) complexity, i.e. works equally fast for
135   contexts with a few context variables and for contexts that have
136   a lot of them.
137
138
139.. class:: Context()
140
141   A mapping of :class:`ContextVars <ContextVar>` to their values.
142
143   ``Context()`` creates an empty context with no values in it.
144   To get a copy of the current context use the
145   :func:`~contextvars.copy_context` function.
146
147   Every thread will have a different top-level :class:`~contextvars.Context`
148   object. This means that a :class:`ContextVar` object behaves in a similar
149   fashion to :func:`threading.local()` when values are assigned in different
150   threads.
151
152   Context implements the :class:`collections.abc.Mapping` interface.
153
154   .. method:: run(callable, *args, **kwargs)
155
156      Execute ``callable(*args, **kwargs)`` code in the context object
157      the *run* method is called on.  Return the result of the execution
158      or propagate an exception if one occurred.
159
160      Any changes to any context variables that *callable* makes will
161      be contained in the context object::
162
163        var = ContextVar('var')
164        var.set('spam')
165
166        def main():
167            # 'var' was set to 'spam' before
168            # calling 'copy_context()' and 'ctx.run(main)', so:
169            # var.get() == ctx[var] == 'spam'
170
171            var.set('ham')
172
173            # Now, after setting 'var' to 'ham':
174            # var.get() == ctx[var] == 'ham'
175
176        ctx = copy_context()
177
178        # Any changes that the 'main' function makes to 'var'
179        # will be contained in 'ctx'.
180        ctx.run(main)
181
182        # The 'main()' function was run in the 'ctx' context,
183        # so changes to 'var' are contained in it:
184        # ctx[var] == 'ham'
185
186        # However, outside of 'ctx', 'var' is still set to 'spam':
187        # var.get() == 'spam'
188
189      The method raises a :exc:`RuntimeError` when called on the same
190      context object from more than one OS thread, or when called
191      recursively.
192
193   .. method:: copy()
194
195      Return a shallow copy of the context object.
196
197   .. describe:: var in context
198
199      Return ``True`` if the *context* has a value for *var* set;
200      return ``False`` otherwise.
201
202   .. describe:: context[var]
203
204      Return the value of the *var* :class:`ContextVar` variable.
205      If the variable is not set in the context object, a
206      :exc:`KeyError` is raised.
207
208   .. method:: get(var, [default])
209
210      Return the value for *var* if *var* has the value in the context
211      object.  Return *default* otherwise.  If *default* is not given,
212      return ``None``.
213
214   .. describe:: iter(context)
215
216      Return an iterator over the variables stored in the context
217      object.
218
219   .. describe:: len(proxy)
220
221      Return the number of variables set in the context object.
222
223   .. method:: keys()
224
225      Return a list of all variables in the context object.
226
227   .. method:: values()
228
229      Return a list of all variables' values in the context object.
230
231
232   .. method:: items()
233
234      Return a list of 2-tuples containing all variables and their
235      values in the context object.
236
237
238asyncio support
239---------------
240
241Context variables are natively supported in :mod:`asyncio` and are
242ready to be used without any extra configuration.  For example, here
243is a simple echo server, that uses a context variable to make the
244address of a remote client available in the Task that handles that
245client::
246
247    import asyncio
248    import contextvars
249
250    client_addr_var = contextvars.ContextVar('client_addr')
251
252    def render_goodbye():
253        # The address of the currently handled client can be accessed
254        # without passing it explicitly to this function.
255
256        client_addr = client_addr_var.get()
257        return f'Good bye, client @ {client_addr}\n'.encode()
258
259    async def handle_request(reader, writer):
260        addr = writer.transport.get_extra_info('socket').getpeername()
261        client_addr_var.set(addr)
262
263        # In any code that we call is now possible to get
264        # client's address by calling 'client_addr_var.get()'.
265
266        while True:
267            line = await reader.readline()
268            print(line)
269            if not line.strip():
270                break
271            writer.write(line)
272
273        writer.write(render_goodbye())
274        writer.close()
275
276    async def main():
277        srv = await asyncio.start_server(
278            handle_request, '127.0.0.1', 8081)
279
280        async with srv:
281            await srv.serve_forever()
282
283    asyncio.run(main())
284
285    # To test it you can use telnet:
286    #     telnet 127.0.0.1 8081
287