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