glom.glom() silently mutates the spec argument
Environment
- glom current release and
main
- Python 3.11.15
Reproduction
import glom
from glom import T
target = {'a': [{'k': 'v3'}, {'missing': 'x'}, {'k': None}], 'k': 'v0'}
spec = T
print(repr(spec)) # before
result = glom.glom(target, spec, default='nada', skip_exc=(Exception,), scope={'UP': None})
print(repr(spec)) # after — may be silently modified
Expected behavior
glom.glom() should be a read-only operation on its spec argument. The speclet object passed by the caller should be identical before and after the call, regardless of whether the glom operation succeeds or raises.
Cause
The internal spec-processing pipeline appears to modify the spec object in place — possibly by normalizing path components, resolving T-expressions, or attaching internal bookkeeping state. The metamorphic test detected that the spec parameter value changed after the call to glom.glom(), violating the non-mutation invariant.
Suggested fix
Process specs on a copy or ensure that internal transformations are non-destructive to the caller's object. Since specs can be complex nested structures (including T, Path, and other spec types), the cloning strategy should handle all speclet types.
A regression test should assert that spec is unchanged after a call to glom.glom() for common spec types (T, Path, dict specs, and combined specs).
How this was found
This was found by metamorphic testing of the non-mutation relation: the test asserts that calling an API should not silently modify its arguments. glom.mutation.glom violated this invariant by mutating the spec parameter, as detected by pre- and post-call comparison.
Thanks for maintaining glom and for reviewing this issue. I appreciate the time and effort put into improving the project.
glom.glom()silently mutates thespecargumentEnvironment
mainReproduction
Expected behavior
glom.glom()should be a read-only operation on itsspecargument. The speclet object passed by the caller should be identical before and after the call, regardless of whether the glom operation succeeds or raises.Cause
The internal spec-processing pipeline appears to modify the spec object in place — possibly by normalizing path components, resolving
T-expressions, or attaching internal bookkeeping state. The metamorphic test detected that thespecparameter value changed after the call toglom.glom(), violating the non-mutation invariant.Suggested fix
Process specs on a copy or ensure that internal transformations are non-destructive to the caller's object. Since specs can be complex nested structures (including
T,Path, and other spec types), the cloning strategy should handle all speclet types.A regression test should assert that
specis unchanged after a call toglom.glom()for common spec types (T,Path, dict specs, and combined specs).How this was found
This was found by metamorphic testing of the non-mutation relation: the test asserts that calling an API should not silently modify its arguments.
glom.mutation.glomviolated this invariant by mutating thespecparameter, as detected by pre- and post-call comparison.Thanks for maintaining glom and for reviewing this issue. I appreciate the time and effort put into improving the project.