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 result
Actual 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
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.).
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
Steps to Reproduce
Run
mutmut runObserve the generated trampoline in
mutants/directoryExpected Behavior
The trampoline for async functions should be:
Actual Behavior
The generated trampoline is synchronous:
This causes the error:
And tests fail because the coroutine is returned instead of being awaited.
Root Cause
In
trampoline_templates.py, thebuild_trampolinefunction always generates a synchronousdef:It should detect if the original function is async and generate:
Related
Workaround
Currently excluding files with async functions using
do_not_mutateinpyproject.toml:This significantly limits mutation testing coverage for async Python codebases (FastAPI, asyncio, etc.).