17db96d56Sopenharmony_ci:mod:`contextvars` --- Context Variables 27db96d56Sopenharmony_ci======================================== 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci.. module:: contextvars 57db96d56Sopenharmony_ci :synopsis: Context Variables 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci.. sectionauthor:: Yury Selivanov <yury@magic.io> 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ci-------------- 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciThis module provides APIs to manage, store, and access context-local 127db96d56Sopenharmony_cistate. The :class:`~contextvars.ContextVar` class is used to declare 137db96d56Sopenharmony_ciand work with *Context Variables*. The :func:`~contextvars.copy_context` 147db96d56Sopenharmony_cifunction and the :class:`~contextvars.Context` class should be used to 157db96d56Sopenharmony_cimanage the current context in asynchronous frameworks. 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ciContext managers that have state should use Context Variables 187db96d56Sopenharmony_ciinstead of :func:`threading.local()` to prevent their state from 197db96d56Sopenharmony_cibleeding to other code unexpectedly, when used in concurrent code. 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ciSee also :pep:`567` for additional details. 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci.. versionadded:: 3.7 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci 267db96d56Sopenharmony_ciContext Variables 277db96d56Sopenharmony_ci----------------- 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci.. class:: ContextVar(name, [*, default]) 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci This class is used to declare a new Context Variable, e.g.:: 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci var: ContextVar[int] = ContextVar('var', default=42) 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci The required *name* parameter is used for introspection and debug 367db96d56Sopenharmony_ci purposes. 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ci The optional keyword-only *default* parameter is returned by 397db96d56Sopenharmony_ci :meth:`ContextVar.get` when no value for the variable is found 407db96d56Sopenharmony_ci in the current context. 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_ci **Important:** Context Variables should be created at the top module 437db96d56Sopenharmony_ci level and never in closures. :class:`Context` objects hold strong 447db96d56Sopenharmony_ci references to context variables which prevents context variables 457db96d56Sopenharmony_ci from being properly garbage collected. 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_ci .. attribute:: ContextVar.name 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci The name of the variable. This is a read-only property. 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ci .. versionadded:: 3.7.1 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ci .. method:: get([default]) 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci Return a value for the context variable for the current context. 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci If there is no value for the variable in the current context, 587db96d56Sopenharmony_ci the method will: 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci * return the value of the *default* argument of the method, 617db96d56Sopenharmony_ci if provided; or 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ci * return the default value for the context variable, 647db96d56Sopenharmony_ci if it was created with one; or 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_ci * raise a :exc:`LookupError`. 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci .. method:: set(value) 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci Call to set a new value for the context variable in the current 717db96d56Sopenharmony_ci context. 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_ci The required *value* argument is the new value for the context 747db96d56Sopenharmony_ci variable. 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci Returns a :class:`~contextvars.Token` object that can be used 777db96d56Sopenharmony_ci to restore the variable to its previous value via the 787db96d56Sopenharmony_ci :meth:`ContextVar.reset` method. 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci .. method:: reset(token) 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci Reset the context variable to the value it had before the 837db96d56Sopenharmony_ci :meth:`ContextVar.set` that created the *token* was used. 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci For example:: 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci var = ContextVar('var') 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci token = var.set('new value') 907db96d56Sopenharmony_ci # code that uses 'var'; var.get() returns 'new value'. 917db96d56Sopenharmony_ci var.reset(token) 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci # After the reset call the var has no value again, so 947db96d56Sopenharmony_ci # var.get() would raise a LookupError. 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci 977db96d56Sopenharmony_ci.. class:: Token 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_ci *Token* objects are returned by the :meth:`ContextVar.set` method. 1007db96d56Sopenharmony_ci They can be passed to the :meth:`ContextVar.reset` method to revert 1017db96d56Sopenharmony_ci the value of the variable to what it was before the corresponding 1027db96d56Sopenharmony_ci *set*. 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci .. attribute:: Token.var 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci A read-only property. Points to the :class:`ContextVar` object 1077db96d56Sopenharmony_ci that created the token. 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci .. attribute:: Token.old_value 1107db96d56Sopenharmony_ci 1117db96d56Sopenharmony_ci A read-only property. Set to the value the variable had before 1127db96d56Sopenharmony_ci the :meth:`ContextVar.set` method call that created the token. 1137db96d56Sopenharmony_ci It points to :attr:`Token.MISSING` if the variable was not set 1147db96d56Sopenharmony_ci before the call. 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci .. attribute:: Token.MISSING 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci A marker object used by :attr:`Token.old_value`. 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci 1217db96d56Sopenharmony_ciManual Context Management 1227db96d56Sopenharmony_ci------------------------- 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci.. function:: copy_context() 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci Returns a copy of the current :class:`~contextvars.Context` object. 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci The following snippet gets a copy of the current context and prints 1297db96d56Sopenharmony_ci all variables and their values that are set in it:: 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci ctx: Context = copy_context() 1327db96d56Sopenharmony_ci print(list(ctx.items())) 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci The function has an O(1) complexity, i.e. works equally fast for 1357db96d56Sopenharmony_ci contexts with a few context variables and for contexts that have 1367db96d56Sopenharmony_ci a lot of them. 1377db96d56Sopenharmony_ci 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci.. class:: Context() 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci A mapping of :class:`ContextVars <ContextVar>` to their values. 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci ``Context()`` creates an empty context with no values in it. 1447db96d56Sopenharmony_ci To get a copy of the current context use the 1457db96d56Sopenharmony_ci :func:`~contextvars.copy_context` function. 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ci Every thread will have a different top-level :class:`~contextvars.Context` 1487db96d56Sopenharmony_ci object. This means that a :class:`ContextVar` object behaves in a similar 1497db96d56Sopenharmony_ci fashion to :func:`threading.local()` when values are assigned in different 1507db96d56Sopenharmony_ci threads. 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci Context implements the :class:`collections.abc.Mapping` interface. 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci .. method:: run(callable, *args, **kwargs) 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci Execute ``callable(*args, **kwargs)`` code in the context object 1577db96d56Sopenharmony_ci the *run* method is called on. Return the result of the execution 1587db96d56Sopenharmony_ci or propagate an exception if one occurred. 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci Any changes to any context variables that *callable* makes will 1617db96d56Sopenharmony_ci be contained in the context object:: 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci var = ContextVar('var') 1647db96d56Sopenharmony_ci var.set('spam') 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci def main(): 1677db96d56Sopenharmony_ci # 'var' was set to 'spam' before 1687db96d56Sopenharmony_ci # calling 'copy_context()' and 'ctx.run(main)', so: 1697db96d56Sopenharmony_ci # var.get() == ctx[var] == 'spam' 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci var.set('ham') 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci # Now, after setting 'var' to 'ham': 1747db96d56Sopenharmony_ci # var.get() == ctx[var] == 'ham' 1757db96d56Sopenharmony_ci 1767db96d56Sopenharmony_ci ctx = copy_context() 1777db96d56Sopenharmony_ci 1787db96d56Sopenharmony_ci # Any changes that the 'main' function makes to 'var' 1797db96d56Sopenharmony_ci # will be contained in 'ctx'. 1807db96d56Sopenharmony_ci ctx.run(main) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci # The 'main()' function was run in the 'ctx' context, 1837db96d56Sopenharmony_ci # so changes to 'var' are contained in it: 1847db96d56Sopenharmony_ci # ctx[var] == 'ham' 1857db96d56Sopenharmony_ci 1867db96d56Sopenharmony_ci # However, outside of 'ctx', 'var' is still set to 'spam': 1877db96d56Sopenharmony_ci # var.get() == 'spam' 1887db96d56Sopenharmony_ci 1897db96d56Sopenharmony_ci The method raises a :exc:`RuntimeError` when called on the same 1907db96d56Sopenharmony_ci context object from more than one OS thread, or when called 1917db96d56Sopenharmony_ci recursively. 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci .. method:: copy() 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci Return a shallow copy of the context object. 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ci .. describe:: var in context 1987db96d56Sopenharmony_ci 1997db96d56Sopenharmony_ci Return ``True`` if the *context* has a value for *var* set; 2007db96d56Sopenharmony_ci return ``False`` otherwise. 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci .. describe:: context[var] 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci Return the value of the *var* :class:`ContextVar` variable. 2057db96d56Sopenharmony_ci If the variable is not set in the context object, a 2067db96d56Sopenharmony_ci :exc:`KeyError` is raised. 2077db96d56Sopenharmony_ci 2087db96d56Sopenharmony_ci .. method:: get(var, [default]) 2097db96d56Sopenharmony_ci 2107db96d56Sopenharmony_ci Return the value for *var* if *var* has the value in the context 2117db96d56Sopenharmony_ci object. Return *default* otherwise. If *default* is not given, 2127db96d56Sopenharmony_ci return ``None``. 2137db96d56Sopenharmony_ci 2147db96d56Sopenharmony_ci .. describe:: iter(context) 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci Return an iterator over the variables stored in the context 2177db96d56Sopenharmony_ci object. 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci .. describe:: len(proxy) 2207db96d56Sopenharmony_ci 2217db96d56Sopenharmony_ci Return the number of variables set in the context object. 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci .. method:: keys() 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ci Return a list of all variables in the context object. 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci .. method:: values() 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ci Return a list of all variables' values in the context object. 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_ci .. method:: items() 2337db96d56Sopenharmony_ci 2347db96d56Sopenharmony_ci Return a list of 2-tuples containing all variables and their 2357db96d56Sopenharmony_ci values in the context object. 2367db96d56Sopenharmony_ci 2377db96d56Sopenharmony_ci 2387db96d56Sopenharmony_ciasyncio support 2397db96d56Sopenharmony_ci--------------- 2407db96d56Sopenharmony_ci 2417db96d56Sopenharmony_ciContext variables are natively supported in :mod:`asyncio` and are 2427db96d56Sopenharmony_ciready to be used without any extra configuration. For example, here 2437db96d56Sopenharmony_ciis a simple echo server, that uses a context variable to make the 2447db96d56Sopenharmony_ciaddress of a remote client available in the Task that handles that 2457db96d56Sopenharmony_ciclient:: 2467db96d56Sopenharmony_ci 2477db96d56Sopenharmony_ci import asyncio 2487db96d56Sopenharmony_ci import contextvars 2497db96d56Sopenharmony_ci 2507db96d56Sopenharmony_ci client_addr_var = contextvars.ContextVar('client_addr') 2517db96d56Sopenharmony_ci 2527db96d56Sopenharmony_ci def render_goodbye(): 2537db96d56Sopenharmony_ci # The address of the currently handled client can be accessed 2547db96d56Sopenharmony_ci # without passing it explicitly to this function. 2557db96d56Sopenharmony_ci 2567db96d56Sopenharmony_ci client_addr = client_addr_var.get() 2577db96d56Sopenharmony_ci return f'Good bye, client @ {client_addr}\n'.encode() 2587db96d56Sopenharmony_ci 2597db96d56Sopenharmony_ci async def handle_request(reader, writer): 2607db96d56Sopenharmony_ci addr = writer.transport.get_extra_info('socket').getpeername() 2617db96d56Sopenharmony_ci client_addr_var.set(addr) 2627db96d56Sopenharmony_ci 2637db96d56Sopenharmony_ci # In any code that we call is now possible to get 2647db96d56Sopenharmony_ci # client's address by calling 'client_addr_var.get()'. 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci while True: 2677db96d56Sopenharmony_ci line = await reader.readline() 2687db96d56Sopenharmony_ci print(line) 2697db96d56Sopenharmony_ci if not line.strip(): 2707db96d56Sopenharmony_ci break 2717db96d56Sopenharmony_ci writer.write(line) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci writer.write(render_goodbye()) 2747db96d56Sopenharmony_ci writer.close() 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ci async def main(): 2777db96d56Sopenharmony_ci srv = await asyncio.start_server( 2787db96d56Sopenharmony_ci handle_request, '127.0.0.1', 8081) 2797db96d56Sopenharmony_ci 2807db96d56Sopenharmony_ci async with srv: 2817db96d56Sopenharmony_ci await srv.serve_forever() 2827db96d56Sopenharmony_ci 2837db96d56Sopenharmony_ci asyncio.run(main()) 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_ci # To test it you can use telnet: 2867db96d56Sopenharmony_ci # telnet 127.0.0.1 8081 287