New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
debugpy triggers an infinite recursion in third party library (pivy) #735
Comments
These are SWIG-generated bindings (can be downloaded prebuilt from https://github.com/apeltauer/FreeCAD/releases/tag/LibPack_12.5.2). The code in question: class SoBaseKit(SoNode):
# ...
def __getattr__(self,name):
c = _coin.SoBaseKit_getNodekitCatalog(self) #line 15299
if c.getPartNumber(name) >= 0:
part = self.getPart(name,1)
return part
return SoNode.__getattr__(self, name) So far as I can tell, the recursion itself is entirely within the library, which would be a bug in that library. It's most likely getting triggered by pydevd by querying for some attribute. @sebjf, can you try setting a breakpoint on that line, and check the name that gets passed to |
That's what I observe too. I spent some time digging around Coin, to see if I could find how getNodekitCatalog decides to terminate, but the only definition I could find just returns a static member:
( It may be being overridden somewhere else, but I am not familiar enough with SWIG to determine if this is the case.
The There are some more oddities to note though. One is that if I leave the print statement in and attach the debugger, then it seems to print to the console as if it were executing, starting with the argument Additionally, I've never seen control flow return to the function. It seems |
|
So it's not actually infinite recursion. I'm still not clear as to why, but even without debugpy in the picture at all, this call recurses 497 times for me. With debugpy, it recurses 1134 times - probably due to attribute accesses that the debugger performs. The problem is that the default recursion limit in Python is 1000. But, it can be changed with What's even weirder is that the number of times it recurses changes if you change the recursion limit! E.g. if you set it to 2285, it recurses 1139 times. I suspect that somewhere inside Pivy, it triggers and catches I don't think we can do much about this. The recursion here is really weird, and probably a perf issue at the very least, so I would recommend filing it as a bug on Pivy (and linking to this one so that they can start with this analysis). But in any case, we can't really gracefully handle Python recursion limits in a debugger that is itself largely written in Python. So the only remedy is to increase the limit. |
One more thing. The recursion is actually triggered by https://github.com/swig/swig/blob/v4.0.2/Lib/python/pyrun.swg#L1266-L1281 Note the call to res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_SoBaseKit, 0 | 0 ); and https://github.com/swig/swig/blob/v4.0.2/Lib/python/pyrun.swg#L1051 And so it recurses. Of note, the call to 'SoBaseKit_getNodekitCatalog' actually comes from Pivy customizations of SWIG: Note that |
Thanks for the additional comments!
I needed to increase the stack size of the debug build of FreeCAD to prevent a crash originally. Though with the debugger attached I went up to something like a 1,000,000 recursion limit/stack size of 1.5Gb and it still crashed, so I really don't know whats going on.
I experimented and think this is correct. I tried handing that case prior but not properly understanding what SWIG was doing, returning the SoNode implementation seems to resolve the issue without breaking anything else in the component however. I will raise an issue on pivy. |
Environment data
Pivy is a library of Coin3D bindings for Python used in FreeCAD.
Actual behavior
Creating a
pivy
object withdebugpy
running will result in anRecursionError
inpivy
.Expected behavior
The pivy object is created normally.
Steps to reproduce:
python -m pip install debugpy
import debugpy; debugpy.listen(5678); debugpy.trace_this_thread(True);
import pivy; pivy.coin.SoDragPointDragger()
Omitting
debugpy.listen(5678)
will create the object successfully, otherwise the instruction will fail with the complete output below.Note that this issue does not occur with PTVSD.
The text was updated successfully, but these errors were encountered: