inspect.markcoroutinefunction() é uma função do módulo inspect usada para marcar uma função síncrona como compatível com a detecção de funções de corrotina. Ela é especialmente útil quando um wrapper comum retorna um objeto awaitable, mas ferramentas, frameworks ou bibliotecas precisam reconhecê-lo como uma função assíncrona antes de chamá-lo. Neste guia, você entenderá quando usar essa marcação, como ela se relaciona com async def, quais problemas resolve e quais cuidados evitam APIs confusas.
O problema que a função resolve
Normalmente, uma função criada com async def é detectada por inspect.iscoroutinefunction(). Porém, alguns decorators e adaptadores são implementados com def e retornam uma corrotina produzida por outra função. Para quem executa o wrapper, o resultado ainda precisa ser aguardado com await, mas a inspeção estática do callable pode dizer que ele não é assíncrono.
import inspect
async def buscar():
return 42
def wrapper():
return buscar()
print(inspect.iscoroutinefunction(buscar))
print(inspect.iscoroutinefunction(wrapper))O segundo resultado tende a ser falso porque wrapper foi declarado com def. Ainda assim, chamá-lo produz uma corrotina. Esse desencontro pode afetar roteadores web, sistemas de plugins, runners de testes, injeção de dependências e decorators que escolhem fluxos diferentes para funções síncronas e assíncronas.
Como usar markcoroutinefunction
A solução é aplicar inspect.markcoroutinefunction() ao wrapper. A marca informa aos mecanismos de inspeção que aquele callable deve ser tratado como função de corrotina.
import inspect
async def buscar():
return 42
@inspect.markcoroutinefunction
def wrapper():
return buscar()
print(inspect.iscoroutinefunction(wrapper))A marcação não transforma automaticamente o corpo em assíncrono, não adiciona um event loop e não executa await por você. Ela apenas altera a forma como o callable é reconhecido. Portanto, o wrapper precisa continuar retornando um objeto awaitable válido.
Diferença entre função de corrotina e objeto corrotina
Essa distinção é fundamental. Uma função de corrotina é o callable, normalmente declarado com async def. Um objeto corrotina é o valor retornado ao chamar esse callable. inspect.iscoroutinefunction() examina a função; inspect.iscoroutine() examina o objeto produzido.
import inspect
async def tarefa():
return "ok"
objeto = tarefa()
print(inspect.iscoroutinefunction(tarefa))
print(inspect.iscoroutine(objeto))
objeto.close()Fechar a corrotina no exemplo evita aviso de corrotina nunca aguardada. Em aplicações reais, use await tarefa() dentro de um contexto assíncrono.
Quando um wrapper síncrono faz sentido
O caminho mais claro costuma ser criar o decorator com async def. Entretanto, wrappers síncronos podem ser necessários para preservar assinaturas, interagir com APIs de terceiros, construir callables dinamicamente ou retornar diferentes classes de awaitables. Também aparecem em adaptadores que apenas coletam argumentos e delegam a execução para uma função assíncrona.
import inspect
async def processar(valor):
return valor * 2
def criar_adaptador(funcao):
@inspect.markcoroutinefunction
def adaptador(*args, **kwargs):
return funcao(*args, **kwargs)
return adaptador
adaptado = criar_adaptador(processar)O contrato deve ser explícito: se o callable é marcado como função de corrotina, toda chamada válida precisa produzir algo aguardável. Retornar ocasionalmente um valor comum cria um comportamento difícil de testar e pode quebrar frameworks.
Uso em decorators
Decorators frequentemente ocultam a natureza da função original. Ao escrever um decorator compatível com código assíncrono, prefira preservar metadados com functools.wraps e escolher uma implementação coerente.
from functools import wraps
import inspect
def registrar(funcao):
@wraps(funcao)
@inspect.markcoroutinefunction
def wrapper(*args, **kwargs):
print(f"Chamando {funcao.__name__}")
return funcao(*args, **kwargs)
return wrapper
@registrar
async def carregar_usuario(id_usuario):
return {"id": id_usuario}A ordem dos decorators pode influenciar quais atributos ficam no wrapper. Teste a detecção depois da decoração e valide o resultado com uma chamada real aguardada.
Compatibilidade com frameworks
Muitos frameworks decidem se devem executar diretamente, aguardar ou mover uma função para uma thread com base em inspeção. Um callable que retorna corrotina, mas não é reconhecido como assíncrono, pode ser tratado pelo caminho errado. A marcação ajuda quando o framework usa inspect.iscoroutinefunction() ou uma lógica compatível.
Não suponha que todos os frameworks respeitam a marca. Alguns verificam o tipo do retorno, atributos próprios ou funções internas. Leia a documentação da biblioteca e crie um teste de integração. Para aprofundar a base assíncrona, consulte asyncio no Python, contextlib.chdir no Python, loop_factory em testes asyncio e TaskGroup eager_start no Python.
Preservando a assinatura
Ferramentas de documentação, validadores e injetores de dependência podem usar inspect.signature(). functools.wraps ajuda a preservar nome, documentação e referência à função original, mas wrappers complexos talvez precisem definir __signature__. Evite fazer isso sem necessidade, pois uma assinatura artificial diferente do comportamento real prejudica usuários e ferramentas.
Tratamento de exceções
Como o wrapper apenas retorna a corrotina, exceções levantadas durante a execução assíncrona surgem quando o resultado é aguardado. Um bloco try em torno da simples criação da corrotina pode não capturar falhas posteriores.
@inspect.markcoroutinefunction
def wrapper(*args, **kwargs):
return operacao_assincrona(*args, **kwargs)Se você precisa registrar tempo, capturar erros ou garantir limpeza durante a execução, um wrapper com async def normalmente é melhor, pois permite usar try, except, finally e await no ponto correto.
Testando corretamente
Teste pelo menos três aspectos: a detecção do callable, o tipo do resultado e o valor final. Também verifique exceções e cancelamento.
import inspect
import pytest
def test_marcacao():
assert inspect.iscoroutinefunction(wrapper)
@pytest.mark.asyncio
async def test_resultado():
resultado = await wrapper()
assert resultado == 42Testes apenas de inspeção são insuficientes. Uma função pode estar marcada e ainda retornar um valor não aguardável. O teste de execução protege o contrato real.
Erros comuns
O primeiro erro é usar a marcação para esconder uma implementação incoerente. O segundo é acreditar que ela converte qualquer retorno em corrotina. O terceiro é marcar uma função que às vezes retorna awaitable e às vezes retorna valor comum. O quarto é esquecer que o chamador deve usar await. O quinto é depender de comportamento específico de versão sem declarar a versão mínima do Python.
Alternativa com async def
Quando possível, prefira um wrapper assíncrono explícito.
from functools import wraps
def registrar(funcao):
@wraps(funcao)
async def wrapper(*args, **kwargs):
print("início")
try:
return await funcao(*args, **kwargs)
finally:
print("fim")
return wrapperEsse formato comunica melhor a intenção, permite controlar a execução e é reconhecido naturalmente. Use markcoroutinefunction quando existe uma razão arquitetural real para manter um wrapper síncrono.
Boas práticas
Documente que a função retorna um awaitable, use type hints adequados, preserve metadados, teste com o framework real e mantenha comportamento uniforme. Não exponha detalhes de event loop dentro do wrapper sem necessidade. Evite chamar asyncio.run() em bibliotecas, pois isso conflita com loops já ativos. Não crie uma tarefa automaticamente se o contrato promete apenas uma corrotina, porque tarefas têm semântica de agendamento e cancelamento diferente.
Type hints
Uma anotação como Callable[..., Awaitable[T]] descreve melhor wrappers desse tipo. Em decorators genéricos, ParamSpec e TypeVar ajudam a preservar argumentos e retorno. A marcação em tempo de execução não substitui a tipagem estática; as duas camadas resolvem problemas diferentes.
Segurança e previsibilidade
Inspeção dinâmica pode influenciar rotas, permissões e pipelines de execução. Não marque callables desconhecidos apenas para fazê-los passar por uma validação. Confirme a origem do wrapper e limite os tipos aceitos. Em sistemas de plugins, valide que o retorno implementa o protocolo awaitable antes de executá-lo.
Compatibilidade entre versões
Verifique a documentação da versão mínima do projeto. Recursos de inspeção evoluem, e frameworks podem ter suas próprias camadas de compatibilidade. Consulte a documentação oficial de inspect e a documentação oficial de tarefas asyncio para comportamento atualizado.
Conclusão
inspect.markcoroutinefunction() resolve um caso específico: um callable síncrono que, por contrato, retorna um objeto aguardável e precisa ser reconhecido como função de corrotina. Ela não substitui async def, não executa o await e não corrige um wrapper inconsistente. Usada com parcimônia, tipagem, testes e documentação, a função melhora a integração entre decorators, frameworks e ferramentas de introspecção sem esconder a natureza assíncrona da operação.







