markcoroutinefunction: detecta wrappers async

Publicado el: 10/10/2026
Tempo de leitura: 5 minutos
Código Python asíncrono en un portátil para inspect.markcoroutinefunction

inspect.markcoroutinefunction() permite marcar una función normal para que los mecanismos de inspección la reconozcan como función de corrutina. Es útil cuando un wrapper está escrito con def, pero devuelve de forma constante un objeto awaitable creado por otra función asíncrona. Frameworks web, sistemas de plugins, inyectores de dependencias y runners de pruebas suelen inspeccionar el callable antes de ejecutarlo, por lo que una clasificación incorrecta puede enviar la función por el flujo equivocado.

El problema que resuelve

Una función declarada con async def normalmente es reconocida por inspect.iscoroutinefunction(). Un wrapper síncrono que solo devuelve una corrutina es distinto: al llamarlo produce un objeto que debe esperarse con await, pero la inspección del wrapper puede indicar que no es asíncrono.

import inspect

async def obtener_valor():
    return 42

def wrapper():
    return obtener_valor()

print(inspect.iscoroutinefunction(obtener_valor))
print(inspect.iscoroutinefunction(wrapper))

Esta diferencia puede provocar corrutinas no esperadas, respuestas incorrectas, ejecución en threads innecesarios o fallos difíciles de diagnosticar.

Uso básico

Aplica la marca al wrapper y sigue devolviendo un awaitable real.

import inspect

async def obtener_valor():
    return 42

@inspect.markcoroutinefunction
def wrapper():
    return obtener_valor()

print(inspect.iscoroutinefunction(wrapper))

La marca no convierte el cuerpo, no inicia un event loop y no añade await. Solo cambia cómo el callable es clasificado por código compatible. La implementación debe garantizar que todas las llamadas válidas devuelvan un objeto esperable.

Función de corrutina y objeto corrutina

Una función de corrutina es el callable, normalmente creado con async def. Un objeto corrutina es el valor producido al invocarlo. inspect.iscoroutinefunction() analiza la función; inspect.iscoroutine() analiza el resultado.

import inspect

async def tarea():
    return "ok"

resultado = tarea()
print(inspect.iscoroutinefunction(tarea))
print(inspect.iscoroutine(resultado))
resultado.close()

El cierre explícito solo evita un aviso en este ejemplo. En una aplicación real, lo habitual es usar await tarea().

Por qué mantener un wrapper síncrono

Un wrapper con async def suele ser más claro, pero a veces una API externa, un descriptor, la generación dinámica de callables o un adaptador de firmas obliga a conservar def. En esos casos, la marca comunica la naturaleza asíncrona a las herramientas de introspección.

import inspect

async def procesar(valor):
    return valor * 2

def crear_adaptador(funcion):
    @inspect.markcoroutinefunction
    def adaptador(*args, **kwargs):
        return funcion(*args, **kwargs)
    return adaptador

adaptado = crear_adaptador(procesar)

El contrato debe ser estable. Una función marcada que a veces devuelve un valor común y otras veces un awaitable resulta impredecible.

Decorators y metadatos

Los decorators pueden ocultar la naturaleza de la función original. Usa functools.wraps para conservar nombre, documentación, anotaciones y referencia al callable envuelto.

from functools import wraps
import inspect


def registrar(funcion):
    @wraps(funcion)
    @inspect.markcoroutinefunction
    def wrapper(*args, **kwargs):
        print(f"Llamando {funcion.__name__}")
        return funcion(*args, **kwargs)
    return wrapper

@registrar
async def cargar_usuario(id_usuario):
    return {"id": id_usuario}

El orden de los decorators puede influir en los atributos finales. Prueba el callable ya decorado y no solo la función original.

Integración con frameworks

Muchos frameworks deciden si deben hacer await, ejecutar directamente o mover el trabajo a un thread a partir de la inspección. Un wrapper no reconocido puede seguir el flujo síncrono y devolver una corrutina sin esperar. La marca ayuda cuando el framework usa inspect.iscoroutinefunction() o una lógica equivalente.

No todos los frameworks respetan la misma señal. Algunos inspeccionan el valor de retorno o usan atributos propios. Consulta la documentación y crea una prueba de integración. También puedes revisar asyncio en Python, contextlib.chdir en Python, loop_factory en pruebas asyncio y TaskGroup eager_start en Python.

Conservar la firma

Herramientas de documentación, validadores e inyectores pueden usar inspect.signature(). functools.wraps es un buen punto de partida. Solo define __signature__ manualmente si el wrapper realmente expone esa firma, porque una firma artificial puede engañar a usuarios y herramientas.

Momento de las excepciones

El wrapper síncrono suele crear y devolver la corrutina. Los errores ocurridos dentro de la ejecución asíncrona aparecen cuando el llamador espera el resultado, no necesariamente cuando el wrapper retorna.

@inspect.markcoroutinefunction
def wrapper(*args, **kwargs):
    return operacion_asincrona(*args, **kwargs)

Si necesitas medir toda la ejecución, capturar excepciones asíncronas o garantizar limpieza, un wrapper con async def es más adecuado.

Pruebas correctas

Prueba la clasificación, el objeto devuelto, el valor final, las excepciones y la cancelación.

import inspect
import pytest


def test_clasificacion():
    assert inspect.iscoroutinefunction(wrapper)

@pytest.mark.asyncio
async def test_resultado():
    valor = await wrapper()
    assert valor == 42

Una prueba que solo verifica la marca no garantiza que el callable devuelva realmente un awaitable.

Errores comunes

El primer error es pensar que la marca convierte resultados normales en corrutinas. El segundo es usarla en una función inconsistente. El tercero es olvidar que el llamador todavía necesita await. El cuarto es asumir que todos los frameworks usan la inspección estándar. El quinto es ignorar la versión mínima de Python del proyecto.

Alternativa con async def

Cuando sea posible, usa un wrapper asíncrono explícito.

from functools import wraps


def registrar(funcion):
    @wraps(funcion)
    async def wrapper(*args, **kwargs):
        print("inicio")
        try:
            return await funcion(*args, **kwargs)
        finally:
            print("fin")
    return wrapper

Este formato expresa mejor la intención, se detecta de forma natural y permite controlar toda la ejecución.

Type hints

Un tipo como Callable[..., Awaitable[T]] describe bien el contrato. En decorators genéricos, ParamSpec y TypeVar ayudan a conservar parámetros y retorno. La marca en runtime y el tipado estático resuelven problemas diferentes.

Límites del event loop

No uses asyncio.run() dentro de un wrapper reutilizable para fingir una API síncrona. Fallará si ya existe un loop activo y cambia la semántica de cancelación. Tampoco crees una tarea silenciosamente si el contrato promete una corrutina.

Cancelación y contexto

Devolver la corrutina original suele preservar cancelación y contextvars. Crear tasks, cambiar de thread o envolver en futuros puede alterar esas reglas. Un adaptador fino suele ser más seguro.

Seguridad en sistemas de plugins

La inspección puede decidir rutas, permisos y entornos de ejecución. No marques callables desconocidos solo para superar una validación. Verifica el origen, confirma que el retorno sea awaitable y limita el protocolo aceptado.

Compatibilidad entre versiones

Declara la versión mínima de Python y prueba en todos los runtimes compatibles. El comportamiento de introspección y los frameworks puede evolucionar. Consulta la documentación oficial de inspect y la documentación oficial de asyncio.

Lista práctica

Usa la marca solo cuando el wrapper deba seguir siendo síncrono, garantiza un awaitable en cada llamada, conserva metadatos, documenta el uso de await, prueba con el framework real, valida excepciones y cancelación, y evita gestionar el event loop de forma oculta.

Conclusión

inspect.markcoroutinefunction() resuelve un caso concreto: un callable regular que devuelve siempre un awaitable y debe ser reconocido como función de corrutina. No sustituye a async def ni corrige un wrapper inconsistente. Utilizada con tipado, documentación y pruebas, mejora la interoperabilidad entre decorators, frameworks, plugins y herramientas de introspección.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código Python para recorrer carpetas y archivos con Path.walk
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: recorre directorios con seguridad

    Aprende Path.walk en Python para recorrer directorios, filtrar archivos, tratar errores y controlar la travesía con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Depuración de un proceso Python en terminal con código
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depura procesos Python en ejecución

    Aprende a conectar pdb a un proceso Python en ejecución, inspeccionar la pila y diagnosticar bloqueos de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python para representar fracciones exactas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: convierte números en fracciones

    Aprende fractions.from_number en Python para convertir números en fracciones exactas, controlar precisión, validar entradas y evitar redondeos inesperados.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026