-
Notifications
You must be signed in to change notification settings - Fork 135
Description
Description
When using mutmut 3.4.0 with async functions (async def), the generated trampoline wrapper is synchronous (def), which causes the coroutine to not be awaited.
Environment
- mutmut version: 3.4.0
- Python version: 3.12.3
- OS: Linux (WSL2)
Steps to Reproduce
- Have a codebase with async functions like:
async def get_current_user(
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> User:
"""Get current authenticated user from JWT token."""
# ... async code
user = await service.get_user_by_id(uuid.UUID(user_id))
return user-
Run
mutmut run -
Observe the generated trampoline in
mutants/directory
Expected Behavior
The trampoline for async functions should be:
async def get_current_user(*args, **kwargs):
result = await _mutmut_trampoline(x_get_current_user__mutmut_orig, x_get_current_user__mutmut_mutants, args, kwargs)
return resultActual Behavior
The generated trampoline is synchronous:
def get_current_user(*args, **kwargs):
result = _mutmut_trampoline(x_get_current_user__mutmut_orig, x_get_current_user__mutmut_mutants, args, kwargs)
return result This causes the error:
RuntimeWarning: coroutine 'x_get_current_user__mutmut_orig' was never awaited
And tests fail because the coroutine is returned instead of being awaited.
Root Cause
In trampoline_templates.py, the build_trampoline function always generates a synchronous def:
def {orig_name}({'self, ' if class_name is not None else ''}*args, **kwargs):
result = {trampoline_name}(...)
return result It should detect if the original function is async and generate:
async def {orig_name}({'self, ' if class_name is not None else ''}*args, **kwargs):
result = await {trampoline_name}(...)
return result Related
- problems with mutations to async generators #406 - Fixed async generators, but not regular async functions
- Support async generators #407 - PR that added async generator support
Workaround
Currently excluding files with async functions using do_not_mutate in pyproject.toml:
[tool.mutmut]
do_not_mutate = [
"**/dependencies.py",
"**/routes.py",
"**/service.py",
"**/database.py",
]This significantly limits mutation testing coverage for async Python codebases (FastAPI, asyncio, etc.).