Removing `not` from a parenthesized multi-line expression drops the parentheses (InvalidGeneratedSyntaxException, 3.8.0)
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 72/100
Línea de trabajo
El error está en operator_remove_unary_ops en src/mutmut/mutation/mutators.py, que devuelve el operando de una expresión not o ~ y descarta los lpar/rpar del nodo UnaryOperation. Reproduce el problema con el fragmento corto de mutate_file_contents del issue y comprueba que ast.parse falla en el mutante. Está terminado cuando el operando conserva los paréntesis externos, el mutante se puede parsear y el ejemplo del issue produce mutantes válidos.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
🤖 Written by Claude (AI assistant). The reproduction below was run on a clean environment.
Summary
operator_remove_unary_ops replaces a not / ~ expression with its operand. In libcst, the parentheses that wrap the whole unary expression belong to the UnaryOperation node (lpar / rpar), so they are dropped together with the operator. When the expression spans several lines (which is how black and ruff format a long boolean return), the mutant is invalid Python and mutmut run aborts with InvalidGeneratedSyntaxException, so no mutant of the run gets tested.
Reproduction (mutmut 3.8.0, Python 3.12)
mylib/core.py:
def is_not_before(a, b):
return (
not a
< b
)
tests/test_core.py:
from mylib.core import is_not_before
def test_is_not_before():
assert is_not_before(2, 1) is True
assert is_not_before(1, 2) is False
pyproject.toml:
[tool.mutmut]
source_paths = ["mylib/"]
pytest_add_cli_args_test_selection = ["tests/"]
mutmut run:
mutmut.__main__.InvalidGeneratedSyntaxException: Mutmut generated invalid python syntax for Mutmut generated invalid python syntax for mutants/mylib/core.py. If the original file has valid python syntax, please file an issue with a minimal reproducible example file.. If the original file has valid python syntax, please file an issue with a minimal reproducible example file.
It also reproduces without running any test:
import ast
from mutmut.mutation.file_mutation import mutate_file_contents
mf = mutate_file_contents("mylib/core.py", open("mylib/core.py").read())
ast.parse(mf.code) # SyntaxError: unexpected indent
The generated mutant:
def x_is_not_before__mutmut_1(a, b):
return a
< b
Cause
src/mutmut/mutation/mutators.py (same on main):
def operator_remove_unary_ops(node: cst.UnaryOperation) -> Iterable[cst.BaseExpression]:
if isinstance(node.operator, cst.Not | cst.BitInvert):
yield node.expression
In (not a\n < b), the parentheses are node.lpar / node.rpar, and node.expression is the Comparison without them.
Suggested fix
Carry the parentheses over to the operand:
def operator_remove_unary_ops(node: cst.UnaryOperation) -> Iterable[cst.BaseExpression]:
if isinstance(node.operator, cst.Not | cst.BitInvert):
yield node.expression.with_changes(
lpar=[*node.lpar, *node.expression.lpar],
rpar=[*node.expression.rpar, *node.rpar],
)
With this change, the example above generates 2 valid mutants (1 killed, 1 survived), and mutmut show mylib.core.x_is_not_before__mutmut_1 gives:
def is_not_before(a, b):
return (
- not a
+ a
< b
)
Side note: the exception message is printed twice ("Mutmut generated invalid python syntax for Mutmut generated invalid python syntax for ...").
- Lenguaje dominante
- Python
- Estrellas
- 1.5k
- Forks
- 181
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de boxed/mutmut
-
`run` with a `name__mutmut_*` pattern runs the whole test suite in the clean-test checkPosiblemente ocupada @Ishaan400 la tomó hace 6 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
-
Invalid unary plus generated in match statementPosiblemente ocupada @GhostCoder6969 la tomó hace 15 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
What do the emoji's mean?Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
Todos los issues de boxed/mutmut
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
aws-samples/sample-per-model-bedrock#243 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
FuRongJun-1999/dsh-memory#404 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
bug frontend
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
PennyLaneAI/catalyst#3337 ·
Los mantenedores suelen responder en 1 día
-
Workflow.schedule stops when a cron deadline passes before sleepPosiblemente ocupada @HuaTNA la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día