pschanely / CrossHair

An analysis tool for Python that blurs the line between testing and type systems.
Other
996 stars 47 forks source link

`RecursionError` in `_issubclass` #294

Closed Zac-HD closed 1 month ago

Zac-HD commented 1 month ago

Going through https://github.com/HypothesisWorks/hypothesis/pull/4034, the most common error remaining (CI logs) is

Traceback (most recent call last):
  File ".../crosshair/libimpl/builtinslib.py", line 4308, in _dict
    if isinstance(arg, Mapping):
  File ".../crosshair/libimpl/builtinslib.py", line 4500, in _isinstance
    return _issubclass(type(obj), types)
  File ".../crosshair/libimpl/builtinslib.py", line 4496, in _issubclass
    return issubclass(subclass, superclass)
  File ".../crosshair/libimpl/builtinslib.py", line 4496, in _issubclass
    return issubclass(subclass, superclass)
  File ".../crosshair/libimpl/builtinslib.py", line 4496, in _issubclass
    return issubclass(subclass, superclass)
  [Previous line repeated 1980 more times]
  ...
RecursionError: maximum recursion depth exceeded while calling a Python object

Frustratingly I still can't reproduce this locally, but from the CI logs it's specifically isinstance(arg, Mapping) inside _dict() which causes this problem, so my best guess is that this fails if we somehow construct an arg where checking

https://github.com/pschanely/CrossHair/blob/23d5f113a5f754af55a0f76b56a4a354477e85e5/crosshair/libimpl/builtinslib.py#L4305-L4308

https://github.com/pschanely/CrossHair/blob/23d5f113a5f754af55a0f76b56a4a354477e85e5/crosshair/libimpl/builtinslib.py#L4470-L4496

pschanely commented 1 month ago

Yup yup, I was trying to get into this over the weekend. I CAN reproduce locally if I run enough other tests beforehand. It's definitely some set of behaviors that puts my tracing infrastructure into a bad state and then causes downstream failures. This bug causes a lot of failures (when running enough tests), so I'm prioritizing this investigation. Even though a lot of the other issues are likely much easier to diagnose & fix 😆

It's a ~busy week for me but more to come soon.

Zac-HD commented 1 month ago

woohoo! If there's anything more I can do to help, let me know 😁

pschanely commented 1 month ago

So, I believe this is ultimately a result of attempting to nest the CrossHair contexts.

hypothesis-crosshair v0.0.12 should prevent the cross-test badness. (but we still don't support nesting)

Zac-HD commented 1 month ago

Awesome! Failing loudly is totally fine, let's see what's left with the new versions...

Zac-HD commented 1 month ago

This went beautifully, almost every RecursionError was fixed.

...but this CI job still has two crosshair-only recursion errors. Note that the tests were previously marked as known failures due to recursionerror, so I presume this is an additional mode which already existed and simply went undiagnosed when I saw the exception type without checking the traceback:

Traceback (most recent call last):
  File ".../hypothesis/hypothesis-python/tests/common/debug.py", line 125, in assert_examples
    msg = f"Found {s!r} using strategy {strategy} which does not match"
  File ".../crosshair/opcode_intercept.py", line 132, in __format__
    self.formatted = format(self.value, fmt)
  File ".../crosshair/libimpl/builtinslib.py", line 4366, in _format
    obj = deep_realize(obj)
  File ".../crosshair/core.py", line 258, in deep_realize
    return deepcopyext(value, CopyMode.REALIZE, {} if memo is None else memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 112, in _deepconstruct
    return _reconstruct(obj, memo, *reduct, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 84, in _deepconstruct
    return creator(obj, memo, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 112, in _deepconstruct
    return _reconstruct(obj, memo, *reduct, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 84, in _deepconstruct
    return creator(obj, memo, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 112, in _deepconstruct
    return _reconstruct(obj, memo, *reduct, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 84, in _deepconstruct
    return creator(obj, memo, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File ".../crosshair/copyext.py", line 77, in subdeepcopy
    return deepcopyext(obj, mode, memo)
  File ".../crosshair/copyext.py", line 53, in deepcopyext
    cpy = _deepconstruct(obj, mode, memo)
  File ".../crosshair/copyext.py", line 112, in _deepconstruct
    return _reconstruct(obj, memo, *reduct, deepcopy=subdeepcopy)
  File ".../lib/python3.10/copy.py", line 272, in _reconstruct
    if hasattr(y, '__setstate__'):
  File ".../lark/lark.py", line 159, in __getattr__
    return self.options[name]
  File ".../lark/lark.py", line 159, in __getattr__
    return self.options[name]
  File ".../lark/lark.py", line 159, in __getattr__
    return self.options[name]
  [Previous line repeated 1959 more times]
RecursionError: maximum recursion depth exceeded