Bug
pydantic_monty input conversion does not appear to do any cycle detection on the Python -> MontyObject path.
As a result:
- a self-referential dataclass input raises
RecursionError
- a self-referential
list or dict input can segfault the Python process
- mixed cyclic graphs can fail in different ways depending on which Python object types the recursion passes through
This is on commit a8645d8be07eac3a8b53ea6cb04d512d38024c41.
Repros
1. Self-referential dataclass: RecursionError
import dataclasses
import pydantic_monty
@dataclasses.dataclass
class A:
a: object
x = A(1)
x.a = x
print(pydantic_monty.Monty('1', inputs=['x']).run(inputs={'x': x}))
This raises:
RecursionError: Stack overflow (used 16353 kB) in comparison
2. Self-referential list: segfault
import pydantic_monty
x = []
x.append(x)
print(pydantic_monty.Monty('1', inputs=['x']).run(inputs={'x': x}))
Running with python3 -X faulthandler shows the crash recursing in _monty's convert::py_to_monty.
The same happens with a self-referential dict:
import pydantic_monty
d = {}
d['self'] = d
print(pydantic_monty.Monty('1', inputs=['x']).run(inputs={'x': d}))
3. Mixed cyclic graphs change the failure mode
Some larger cyclic graphs do not fail the same way as the simple repros.
For example, this dataclass -> dict -> dataclass cycle aborts harder than the simpler dataclass self-cycle:
import dataclasses
import pydantic_monty
@dataclasses.dataclass
class Box:
value: object
obj = {}
box = Box(obj)
obj['box'] = box
print(pydantic_monty.Monty('1', inputs=['x']).run(inputs={'x': box}))
This produced:
Fatal Python error: _Py_CheckRecursiveCall: Unrecoverable stack overflow (used 16376 kB) in comparison
By contrast, these still produced the softer RecursionError path for me:
- dataclass -> list -> dataclass
- dataclass <-> dataclass
- a shared-object graph that later closes a cycle through a dataclass
So the bug is broader than just “dataclasses recurse” or “lists segfault”: the exact crash mode depends on the mix of Python container/object types traversed by py_to_monty().
Why this looks like the root cause
py_to_monty() recursively walks Python containers and dataclass fields, but I don't see any visited-set / identity memo on this path:
crates/monty-python/src/convert.rs
- list conversion recurses via
py_to_monty()
- dict conversion recurses via
py_to_monty()
- dataclass conversion delegates to
dataclass_to_monty()
crates/monty-python/src/dataclass.rs
dataclass_to_monty() walks fields and recursively calls py_to_monty() on field values
By contrast, Monty does have cycle handling on the output side when converting Value -> MontyObject via MontyObject::Cycle in crates/monty/src/object.rs.
So this looks like an asymmetry: output conversion is cycle-aware, input conversion is not.
Expected
At minimum, cyclic Python inputs should fail safely with a normal Python/Monty exception.
Even better would be one of:
- reject cyclic inputs explicitly with a clear error
- or add cycle-aware input conversion if that is supposed to be supported
But a process crash from a Python input value seems like a real bug either way.
Bug
pydantic_montyinput conversion does not appear to do any cycle detection on the Python ->MontyObjectpath.As a result:
RecursionErrorlistordictinput can segfault the Python processThis is on commit
a8645d8be07eac3a8b53ea6cb04d512d38024c41.Repros
1. Self-referential dataclass:
RecursionErrorThis raises:
2. Self-referential list: segfault
Running with
python3 -X faulthandlershows the crash recursing in_monty'sconvert::py_to_monty.The same happens with a self-referential dict:
3. Mixed cyclic graphs change the failure mode
Some larger cyclic graphs do not fail the same way as the simple repros.
For example, this dataclass -> dict -> dataclass cycle aborts harder than the simpler dataclass self-cycle:
This produced:
By contrast, these still produced the softer
RecursionErrorpath for me:So the bug is broader than just “dataclasses recurse” or “lists segfault”: the exact crash mode depends on the mix of Python container/object types traversed by
py_to_monty().Why this looks like the root cause
py_to_monty()recursively walks Python containers and dataclass fields, but I don't see any visited-set / identity memo on this path:crates/monty-python/src/convert.rspy_to_monty()py_to_monty()dataclass_to_monty()crates/monty-python/src/dataclass.rsdataclass_to_monty()walks fields and recursively callspy_to_monty()on field valuesBy contrast, Monty does have cycle handling on the output side when converting
Value -> MontyObjectviaMontyObject::Cycleincrates/monty/src/object.rs.So this looks like an asymmetry: output conversion is cycle-aware, input conversion is not.
Expected
At minimum, cyclic Python inputs should fail safely with a normal Python/Monty exception.
Even better would be one of:
But a process crash from a Python input value seems like a real bug either way.