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

    Arquivadores organizados representando módulos importados diretamente de arquivos ZIP com zipimport no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipimport no Python: importe de ZIPs

    Aprenda zipimport no Python para importar módulos e pacotes de arquivos ZIP, trabalhar com loaders e evitar riscos de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    14/08/2026
    Diagrama de diretórios representando caminhos site-packages e configuração do módulo site no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    site no Python: entenda os caminhos

    Aprenda o módulo site no Python para entender site-packages, user site, arquivos .pth, sitecustomize, usercustomize e opções de inicialização.

    Ler mais

    Tempo de leitura: 8 minutos
    14/08/2026
    Ícone de instalador representando o bootstrap offline do pip com ensurepip no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ensurepip no Python: reinstale o pip

    Aprenda ensurepip no Python para instalar ou restaurar o pip offline, escolher ambiente, scripts, upgrade e evitar conflitos com o

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026
    Caixa de software representando metadados de pacotes consultados com importlib.metadata no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    importlib.metadata no Python: pacotes

    Aprenda importlib.metadata no Python para consultar versões, dependências, arquivos, metadados e entry points de pacotes instalados.

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026
    Código em execução representando módulos e caminhos executados com runpy no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    runpy no Python: execute módulos

    Aprenda runpy no Python para executar módulos, scripts, diretórios e arquivos ZIP, controlar namespaces e evitar problemas de segurança e

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026
    Pacote de software representando descoberta de módulos com pkgutil no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pkgutil no Python: descubra pacotes

    Aprenda pkgutil no Python para descobrir módulos, percorrer pacotes, resolver objetos, estender caminhos e acessar recursos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    13/08/2026