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:: contextvars.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` is 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 Context implements the :class:`collections.abc.Mapping` interface. 148 149 .. method:: run(callable, \*args, \*\*kwargs) 150 151 Execute ``callable(*args, **kwargs)`` code in the context object 152 the *run* method is called on. Return the result of the execution 153 or propagate an exception if one occurred. 154 155 Any changes to any context variables that *callable* makes will 156 be contained in the context object:: 157 158 var = ContextVar('var') 159 var.set('spam') 160 161 def main(): 162 # 'var' was set to 'spam' before 163 # calling 'copy_context()' and 'ctx.run(main)', so: 164 # var.get() == ctx[var] == 'spam' 165 166 var.set('ham') 167 168 # Now, after setting 'var' to 'ham': 169 # var.get() == ctx[var] == 'ham' 170 171 ctx = copy_context() 172 173 # Any changes that the 'main' function makes to 'var' 174 # will be contained in 'ctx'. 175 ctx.run(main) 176 177 # The 'main()' function was run in the 'ctx' context, 178 # so changes to 'var' are contained in it: 179 # ctx[var] == 'ham' 180 181 # However, outside of 'ctx', 'var' is still set to 'spam': 182 # var.get() == 'spam' 183 184 The method raises a :exc:`RuntimeError` when called on the same 185 context object from more than one OS thread, or when called 186 recursively. 187 188 .. method:: copy() 189 190 Return a shallow copy of the context object. 191 192 .. describe:: var in context 193 194 Return ``True`` if the *context* has a value for *var* set; 195 return ``False`` otherwise. 196 197 .. describe:: context[var] 198 199 Return the value of the *var* :class:`ContextVar` variable. 200 If the variable is not set in the context object, a 201 :exc:`KeyError` is raised. 202 203 .. method:: get(var, [default]) 204 205 Return the value for *var* if *var* has the value in the context 206 object. Return *default* otherwise. If *default* is not given, 207 return ``None``. 208 209 .. describe:: iter(context) 210 211 Return an iterator over the variables stored in the context 212 object. 213 214 .. describe:: len(proxy) 215 216 Return the number of variables set in the context object. 217 218 .. method:: keys() 219 220 Return a list of all variables in the context object. 221 222 .. method:: values() 223 224 Return a list of all variables' values in the context object. 225 226 227 .. method:: items() 228 229 Return a list of 2-tuples containing all variables and their 230 values in the context object. 231 232 233asyncio support 234--------------- 235 236Context variables are natively supported in :mod:`asyncio` and are 237ready to be used without any extra configuration. For example, here 238is a simple echo server, that uses a context variable to make the 239address of a remote client available in the Task that handles that 240client:: 241 242 import asyncio 243 import contextvars 244 245 client_addr_var = contextvars.ContextVar('client_addr') 246 247 def render_goodbye(): 248 # The address of the currently handled client can be accessed 249 # without passing it explicitly to this function. 250 251 client_addr = client_addr_var.get() 252 return f'Good bye, client @ {client_addr}\n'.encode() 253 254 async def handle_request(reader, writer): 255 addr = writer.transport.get_extra_info('socket').getpeername() 256 client_addr_var.set(addr) 257 258 # In any code that we call is now possible to get 259 # client's address by calling 'client_addr_var.get()'. 260 261 while True: 262 line = await reader.readline() 263 print(line) 264 if not line.strip(): 265 break 266 writer.write(line) 267 268 writer.write(render_goodbye()) 269 writer.close() 270 271 async def main(): 272 srv = await asyncio.start_server( 273 handle_request, '127.0.0.1', 8081) 274 275 async with srv: 276 await srv.serve_forever() 277 278 asyncio.run(main()) 279 280 # To test it you can use telnet: 281 # telnet 127.0.0.1 8081 282