"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.

Os gerenciadores de contexto que possuem estado devem usar Variáveis
de Contexto ao invés de "threading.local()" para evitar que seu estado
vaze para outro código inesperadamente, quando usado em código
concorrente.

Veja também a **PEP 567** para detalhes adicionais.

Novo 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.

      Novo 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. Definida com 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 estão definidos nele:

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

   A função tem uma complexidade O(1), ou seja, funciona igualmente
   rápido 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()".

   Cada thread terá um objeto "Context" de nível superior diferente.
   Isso significa que um objeto "ContextVar" se comporta de maneira
   semelhante a "threading.local()" quando valores são atribuídos em
   diferentes threads.

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

   run(callable, *args, **kwargs)

      Executa o código "callable(*args, **kwargs)" no objeto contexto
      em que o método *run* é chamado. Retorna o resultado da execução
      ou propaga uma exceção, se ocorrer.

      Quaisquer mudanças em quaisquer variáveis de contexto que
      *callable* faça estarão contidas no objeto de contexto:

         var = ContextVar('var')
         var.set('spam')

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

             var.set('ham')

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

         ctx = copy_context()

         # 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:
         # ctx[var] == 'ham'

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

      O método levanta uma "RuntimeError" quando chamado no mesmo
      objeto de contexto de mais de uma thread do sistema operacional,
      ou quando chamado recursivamente.

   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}\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(line)

       writer.write(render_goodbye())
       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:
   #     telnet 127.0.0.1 8081
