# `contextvars` — Variáveis de contexto

Este módulo fornece APIs para gerenciar, armazenar e acessar o estado local do contexto. A classe `ContextVar` é usada para declarar e trabalhar com Variáveis de Contexto. A função `copy_context()` e a classe `Context` devem ser usadas para gerenciar o contexto atual em frameworks assíncronos.

Context managers that have state should use Context Variables instead of `threading.local()` to prevent their state from bleeding to other code unexpectedly, when used in concurrent code.

Veja também a PEP 567 para detalhes adicionais.

Adicionado na versão 3.7.

## Variáveis de contexto

class contextvars.ContextVar(name[, *, default])

  Esta classe é usada para declarar uma nova variável de contexto, como, por exemplo:

      var: ContextVar[int] = ContextVar('var', default=42)

  O parâmetro obrigatório name é usado para fins de introspecção e depuração.

  O parâmetro somente-nomeado opcional default é retornado por `ContextVar.get()` quando nenhum valor para a variável é encontrado no contexto atual.

  Importante: Variáveis de Contexto devem ser criadas no nível do módulo superior e nunca em fechamentos. Os objetos `Context` contêm referências fortes a variáveis de contexto que evitam que as variáveis de contexto sejam coletadas como lixo corretamente.

  name

    O nome da variável. Esta é uma propriedade somente leitura.

    Adicionado na versão 3.7.1.

  get([default])

    Retorna um valor para a variável de contexto para o contexto atual.

    Se não houver valor para a variável no contexto atual, o método vai:

    - retornar o valor do argumento default do método, se fornecido; ou

    - retornar o valor padrão para a variável de contexto, se ela foi criada com uma; ou

    - levantar uma `LookupError`.

  set(value)

    Chame para definir um novo valor para a variável de contexto no contexto atual.

    O argumento value obrigatório é o novo valor para a variável de contexto.

    Retorna um objeto `Token` que pode ser usado para restaurar a variável ao seu valor anterior através do método `ContextVar.reset()`.

  reset(token)

    Redefine a variável de contexto para o valor que tinha antes de `ContextVar.set()`. que criou o token, ser usado.

    Por exemplo:

        var = ContextVar('var')

        token = var.set('new value')
        # code that uses 'var'; var.get() returns 'new value'.
        var.reset(token)

        # After the reset call the var has no value again, so
        # var.get() would raise a LookupError.

class contextvars.Token

  Objetos token são retornados pelo método `ContextVar.set()`. Eles podem ser passados para o método `ContextVar.reset()` para reverter o valor da variável para o que era antes do set correspondente.

  var

    Uma propriedade somente leitura. Aponta para o objeto `ContextVar` que criou o token.

  old_value

    Uma propriedade somente leitura. Defina como o valor que a variável tinha antes da chamada do método `ContextVar.set()` que criou o token. Aponta para `Token.MISSING` se a variável não foi definida antes da chamada.

  MISSING

    Um objeto marcador usado por `Token.old_value`.

## Gerenciamento de contexto manual

contextvars.copy_context()

  Retorna uma cópia do objeto `Context` atual.

  O trecho a seguir obtém uma cópia do contexto atual e imprime todas as variáveis e seus valores que são definidos nele:

      ctx: Context = copy_context()
      print(list(ctx.items()))

  A função tem uma complexidade O(1) , ou seja, funciona igualmente rápida para contextos com algumas variáveis de contexto e para contextos que têm muitas delas.

class contextvars.Context

  Um mapeamento de `ContextVars` para seus valores.

  `Context()` cria um contexto vazio sem valores nele. Para obter uma cópia do contexto atual, use a função `copy_context()`.

  Each thread has its own effective stack of `Context` objects. The current context is the `Context` object at the top of the current thread’s stack. All `Context` objects in the stacks are considered to be entered.

  Entering a context, which can be done by calling its `run()` method, makes the context the current context by pushing it onto the top of the current thread’s context stack.

  Exiting from the current context, which can be done by returning from the callback passed to the `run()` method, restores the current context to what it was before the context was entered by popping the context off the top of the context stack.

  Since each thread has its own context stack, `ContextVar` objects behave in a similar fashion to `threading.local()` when values are assigned in different threads.

  Attempting to enter an already entered context, including contexts entered in other threads, raises a `RuntimeError`.

  After exiting a context, it can later be re-entered (from any thread).

  Any changes to `ContextVar` values via the `ContextVar.set()` method are recorded in the current context. The `ContextVar.get()` method returns the value associated with the current context. Exiting a context effectively reverts any changes made to context variables while the context was entered (if needed, the values can be restored by re-entering the context).

  Context implementa a interface `collections.abc.Mapping`.

  run(callable, *args, **kwargs)

    Enters the Context, executes `callable(*args, **kwargs)`, then exits the Context. Returns callable’s return value, or propagates an exception if one occurred.

    Example:

        import contextvars

        var = contextvars.ContextVar('var')
        var.set('spam')
        print(var.get())  # 'spam'

        ctx = contextvars.copy_context()

        def main():
            # 'var' was set to 'spam' before
            # calling 'copy_context()' and 'ctx.run(main)', so:
            print(var.get())  # 'spam'
            print(ctx[var])  # 'spam'

            var.set('ham')

            # Now, after setting 'var' to 'ham':
            print(var.get())  # 'ham'
            print(ctx[var])  # 'ham'

        # Any changes that the 'main' function makes to 'var'
        # will be contained in 'ctx'.
        ctx.run(main)

        # The 'main()' function was run in the 'ctx' context,
        # so changes to 'var' are contained in it:
        print(ctx[var])  # 'ham'

        # However, outside of 'ctx', 'var' is still set to 'spam':
        print(var.get())  # 'spam'

  copy()

    Retorna uma cópia rasa do objeto contexto.

  var in context

    Retorna `True` se context tem uma variável para var definida; do contrário, retorna `False`.

  context[var]

    Retorna o valor da variável `ContextVar` var. Se a variável não for definida no objeto contexto, uma `KeyError` é levantada.

  get(var[, default])

    Retorna o valor para var se var tiver o valor no objeto contexto. Caso contrário, retorna default. Se default não for fornecido, retorna `None`.

  iter(context)

    Retorna um iterador sobre as variáveis armazenadas no objeto contexto.

  len(proxy)

    Retorna o número das variáveis definidas no objeto contexto.

  keys()

    Retorna uma lista de todas as variáveis no objeto contexto.

  values()

    Retorna uma lista dos valores de todas as variáveis no objeto contexto.

  items()

    Retorna uma lista de tuplas de 2 elementos contendo todas as variáveis e seus valores no objeto contexto.

## Suporte a asyncio

Variáveis de contexto encontram suporte nativo em `asyncio` e estão prontas para serem usadas sem qualquer configuração extra. Por exemplo, aqui está um servidor simples de eco, que usa uma variável de contexto para disponibilizar o endereço de um cliente remoto na Task que lida com esse cliente:

    import asyncio
    import contextvars

    client_addr_var = contextvars.ContextVar('client_addr')

    def render_goodbye():
        # The address of the currently handled client can be accessed
        # without passing it explicitly to this function.

        client_addr = client_addr_var.get()
        return f'Good bye, client @ {client_addr}\r\n'.encode()

    async def handle_request(reader, writer):
        addr = writer.transport.get_extra_info('socket').getpeername()
        client_addr_var.set(addr)

        # In any code that we call is now possible to get
        # client's address by calling 'client_addr_var.get()'.

        while True:
            line = await reader.readline()
            print(line)
            if not line.strip():
                break

        writer.write(b'HTTP/1.1 200 OK\r\n')  # status line
        writer.write(b'\r\n')  # headers
        writer.write(render_goodbye())  # body
        writer.close()

    async def main():
        srv = await asyncio.start_server(
            handle_request, '127.0.0.1', 8081)

        async with srv:
            await srv.serve_forever()

    asyncio.run(main())

    # To test it you can use telnet or curl:
    #     telnet 127.0.0.1 8081
    #     curl 127.0.0.1:8081