annotationlib no Python: leia anotações

Publicado em: 14/08/2026
Tempo de leitura: 7 minutos
Código com anotações de tipos representando introspecção com annotationlib no Python

O módulo annotationlib no Python, adicionado no Python 3.14, oferece ferramentas de baixo nível para recuperar e avaliar anotações de funções, classes e módulos. Ele foi criado para lidar com o novo modelo de avaliação adiada, referências futuras e casos em que bibliotecas precisam inspecionar tipos sem forçar todos os nomes a existir imediatamente.

Apesar do nome, o módulo não é apenas um leitor de dicionários. Muitas operações podem executar expressões presentes nas anotações. Por isso, introspecção de anotações deve ser tratada como execução de código, principalmente em frameworks, plugins, geradores de documentação e sistemas que recebem dados externos.

Por que o modelo mudou

Até o Python 3.13, anotações eram normalmente avaliadas quando a função ou classe era definida. Isso causava problemas com nomes declarados depois e aumentava o custo de importação. A opção from __future__ import annotations armazenava textos, mas dificultava a introspecção de runtime.

No Python 3.14, o modelo padrão usa avaliação adiada. As expressões são avaliadas quando alguém acessa as anotações. O annotationlib fornece uma interface consistente para esse cenário.

A função get_annotations

A principal entrada é get_annotations(). Ela aceita função, classe, módulo ou outro objeto compatível e sempre devolve um novo dicionário.

from annotationlib import get_annotations

def calcular(valor: int, escala: float) -> float:
    return valor * escala

print(get_annotations(calcular))
# {'valor': <class 'int'>, 'escala': <class 'float'>,
#  'return': <class 'float'>}

Usar essa função é preferível a acessar __annotations__ diretamente porque ela lida com funções de anotação, classes, wrappers e detalhes do modelo adiado.

Três formatos principais

A enumeração Format define como os resultados são produzidos:

  • VALUE avalia e devolve objetos reais.
  • FORWARDREF devolve objetos reais quando possível e proxies para nomes não resolvidos.
  • STRING produz representações textuais aproximadas.
from annotationlib import Format, get_annotations

def processar(item: TipoDeclaradoDepois) -> list[str]:
    ...

print(get_annotations(processar, format=Format.FORWARDREF))
print(get_annotations(processar, format=Format.STRING))

VALUE é simples para validação de runtime, mas pode gerar NameError. FORWARDREF é útil em frameworks e STRING costuma servir melhor para documentação.

Formato VALUE

Quando todos os nomes existem, VALUE devolve classes, aliases e objetos de typing já avaliados.

from annotationlib import Format, get_annotations

class Usuario:
    pass

def salvar(usuario: Usuario) -> None:
    pass

anotacoes = get_annotations(salvar, format=Format.VALUE)
assert anotacoes['usuario'] is Usuario

A avaliação pode chamar código arbitrário contido na expressão. Não use o formato em anotações provenientes de fonte não confiável.

Formato FORWARDREF

Quando um nome não pode ser resolvido, o módulo cria um ForwardRef.

from annotationlib import Format, ForwardRef, get_annotations

def carregar(valor: ModeloFuturo) -> ModeloFuturo:
    pass

resultado = get_annotations(
    carregar,
    format=Format.FORWARDREF,
)
print(resultado)

O proxy mantém o código associado e, quando obtido por get_annotations(), pode guardar informações de escopo.

Avalie uma referência futura

ForwardRef.evaluate() tenta resolver o nome. Você pode fornecer o objeto proprietário ou namespaces explícitos.

referencia = resultado['valor']

class ModeloFuturo:
    pass

valor = referencia.evaluate(
    globals=globals(),
    locals=locals(),
)
assert valor is ModeloFuturo

Com format=Format.FORWARDREF, uma parte ainda não resolvida pode continuar como proxy. Com STRING, o método devolve a representação textual.

Formato STRING

STRING tenta aproximar a expressão escrita na fonte.

texto = get_annotations(
    salvar,
    format=Format.STRING,
)
print(texto)
# {'usuario': 'Usuario', 'return': 'None'}

O resultado não é uma cópia exata. Espaços, comentários, parênteses e representações de constantes podem mudar. Algumas expressões não são suportadas ou produzem resultado aproximado.

STRING também pode executar código

É um erro pensar que o formato textual é seguro. A implementação pode executar operações da anotação para reconstruir a representação. Expressões construídas com atributos, chamadas ou constantes podem produzir efeitos colaterais.

Não aceite uma string de usuário, coloque-a em __annotations__ e passe ao módulo. Para documentação de código externo, analise a fonte com AST em um processo isolado.

eval_str e anotações antigas

Quando código usa from __future__ import annotations, valores podem estar armazenados como strings. eval_str=True pede que get_annotations() execute eval() nesses valores.

anotacoes = get_annotations(
    objeto,
    eval_str=True,
    globals=namespace_global,
    locals=namespace_local,
)

Essa opção só é compatível com Format.VALUE e possui os riscos normais de eval(). Use apenas com código confiável.

annotations_to_string

annotations_to_string() converte um dicionário de valores de runtime em strings adequadas para exibição.

from annotationlib import annotations_to_string

resultado = annotations_to_string({
    'item': list[int],
    'return': type(None),
})
print(resultado)

A função usa type_repr() para tratar classes e objetos comuns. Ela é útil para funções de anotação customizadas que não têm acesso ao código-fonte original.

type_repr

type_repr() cria uma representação voltada a tipos e anotações.

from annotationlib import type_repr

print(type_repr(dict[str, int]))

O resultado é para interface e documentação, não para serialização estável. Não dependa do texto como identificador permanente.

Funções __annotate__

No modelo adiado, compilador pode criar uma função __annotate__ que produz o dicionário quando solicitada. call_annotate_function() chama essa função em um dos formatos suportados.

from annotationlib import call_annotate_function, Format

anotacoes = call_annotate_function(
    MinhaClasse.__annotate__,
    Format.FORWARDREF,
    owner=MinhaClasse,
)

Bibliotecas normais devem preferir get_annotations(). A API direta é destinada a frameworks, metaclasses e implementações de typing.

Anotações durante a criação de classes

Uma metaclasse ainda não possui o objeto final quando recebe o namespace. get_annotate_from_class_namespace() recupera a função de anotação desse dicionário.

import annotationlib

class MinhaMeta(type):
    def __new__(mcls, nome, bases, namespace):
        annotate = annotationlib.get_annotate_from_class_namespace(
            namespace
        )
        if annotate:
            dados = annotationlib.call_annotate_function(
                annotate,
                annotationlib.Format.FORWARDREF,
            )
            validar_campos(dados)
        return super().__new__(mcls, nome, bases, namespace)

Esse recurso ajuda frameworks de modelos, ORMs e validadores. Evite avaliar valores completamente antes de a classe estar pronta.

Funções evaluate

call_evaluate_function() trabalha com funções preguiçosas associadas a aliases e parâmetros de tipo. Ela pode retornar valor, ForwardRef ou string.

valor = annotationlib.call_evaluate_function(
    Alias.evaluate_value,
    annotationlib.Format.FORWARDREF,
    owner=Alias,
)

Essa é uma API avançada, especialmente relevante para bibliotecas que implementam typing e genéricos.

Integração com inspect

O guia de inspect no Python mostra assinaturas e objetos vivos. Em Python 3.14, bibliotecas de introspecção podem combinar inspect.signature() com annotationlib.get_annotations() para controlar o formato e referências futuras.

Escopos e nomes

O módulo escolhe namespaces padrão de acordo com o objeto. Módulos usam seu dicionário; classes usam o módulo e o namespace da classe; funções usam seus globais, inclusive após desembrulhar decorators conhecidos.

Para entender como o compilador classifica nomes locais, globais, livres e parâmetros de tipo, veja symtable no Python.

Wrappers e partial

get_annotations() conhece wrappers criados com functools.update_wrapper() e objetos partial. Isso reduz a chance de consultar o namespace errado em funções decoradas. Decorators customizados ainda devem preservar __wrapped__ corretamente.

Classes não herdam anotações automaticamente

A função ignora anotações herdadas e metaclasses. Se uma classe não possui seu próprio dicionário, o resultado é vazio. Para construir um esquema completo de herança, percorra o MRO deliberadamente e defina regras de sobrescrita.

O guia de types no Python apresenta utilitários relacionados a objetos e tipos de runtime.

Compatibilidade entre versões

annotationlib foi adicionado no Python 3.14. Bibliotecas que suportam versões anteriores precisam usar verificações de versão ou o backport fornecido por typing_extensions, quando apropriado.

Não importe o módulo incondicionalmente em um pacote que declara compatibilidade com 3.13 ou anterior.

Segurança

  • Anotações são expressões Python e podem executar código.
  • ForwardRef.evaluate() pode chamar eval().
  • STRING não é uma sandbox.
  • Não processe anotações construídas com entrada externa.
  • Limite tempo e recursos em ferramentas de documentação.
  • Não exponha globais sensíveis ao avaliar.
  • Execute análise de plugins em processo separado.

Testes recomendados

Teste nomes definidos posteriormente, aliases, genéricos, parâmetros de tipo, classes decoradas, funções embrulhadas, future annotations, nomes inexistentes e anotações com efeitos colaterais. Verifique os três formatos separadamente.

Inclua testes por versão do Python porque semântica e representações podem evoluir. O formato textual não deve ser comparado com espaços e parênteses exatos.

Boas práticas

  • Use get_annotations() como entrada principal.
  • Escolha explicitamente o formato.
  • Prefira FORWARDREF durante construção de classes.
  • Use STRING apenas para apresentação.
  • Não presuma que nenhuma avaliação ocorrerá.
  • Trate exceções de nomes e da própria expressão.
  • Mantenha fallback para versões anteriores.
  • Documente a política de segurança do framework.

Conclusão

O annotationlib no Python organiza a introspecção de anotações no modelo de avaliação adiada do Python 3.14. Ele permite obter valores reais, referências futuras ou textos, além de oferecer APIs avançadas para metaclasses e typing.

Seu poder exige cautela: consultar anotações pode executar código arbitrário. Consulte a documentação oficial do annotationlib, a PEP 649 e a PEP 749.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python em tela representando inspeção de módulos e pacotes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifique pacotes Python

    Aprenda inspect.ispackage no Python para identificar pacotes, explorar módulos e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026