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