Bugs item #532646, was opened at 2002-03-20 10:56 Message generated for change (Comment added) made by bcannon You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=532646&group_id=5470
Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Python Interpreter Core Group: Python 2.3 >Status: Open >Resolution: None Priority: 5 Submitted By: Gregor Mirai (gregmi) Assigned to: Guido van Rossum (gvanrossum) Summary: Recursive class instance "error" Initial Comment: If one writes the following code (tested on Python 2.1, 2.2 on platforms MacOS X Server, MacOS X, Windows 98, NT, 2000) one can easily produce several "errors". MacOS X, MacOS X Server (Python 2.1, 2.2) ------------------------------------------ class A: def __getattr__(self, name): print name, return A() ------------------------------------------ >>> a=A() >>> a.foo Segmentation fault Win98, NT, 2000 (Python 2.1, 2.2) ------------------------------------------ class A: def __getattr__(self, name): print name return A() ------------------------------------------ >>> a=A() >>> a.foo foo __repr__ __call__ __call__ __call__ ... ad inf ---------------------------------------------------------------------- >Comment By: Brett Cannon (bcannon) Date: 2006-04-17 17:00 Message: Logged In: YES user_id=357491 This was not fixed for new-style classes and still segfaults the interpreter at least back to 2.4. Reopening. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-06-13 14:50 Message: Logged In: YES user_id=6380 It was very specific to __call__ after all, and I found an example that didn't involve __getattr__. See the comments I checked in as part of the fix in classobject.c. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-06-13 13:41 Message: Logged In: YES user_id=6380 Hm. I'm tracing this in the debugger now, and it appears that the problem is when trying to *print* an A instance. The statement a.foo causes the problem simply because it returns an A instance. (Proof: "a=A().foo; print a" fails in the print.) I think that instance_call may not be the real cause of the problem... ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2002-06-12 15:38 Message: Logged In: YES user_id=33168 The problem is that there is mutual recursion between instance_call() and PyObject_Call(). The recursion apparently goes through the eval_frame() loop. This patch increases the recursion depth so the mutual recursion will eventually end (otherwise, the stack grows without bounds). ISTM the first check SHOULD be reached, but it is not. I don't understand why. The recursion error must be set in eval_frame(). The first block in the patch could be just this line: ++tstate->recursion_depth; I didn't do it that way, since I didn't expect returning to eval_frame(). I'm not sure if it's guaranteed to return to eval_frame() which is why I left the first condition in with the comment. I suppose the comment should say something like: /* this condition doesn't seem to be triggered, the recursion depth limit is exceeded in eval_frame */ The test for recursion_depth is necessary to ensure that the recursion error isn't overwritten. If this check is removed, the follow exception is raised: AttributeError: A instance has no __call__ method Hopefully this makes sense. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-06-10 09:08 Message: Logged In: YES user_id=6380 Can you explain how the fix works? Why does the first comment say /* this shouldn't be reached, but just in case */ and why is this test necesary if (tstate->recursion_depth < Py_GetRecursionLimit()) { ??? And who *does* report the recursion error? ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2002-06-07 15:52 Message: Logged In: YES user_id=33168 The recursion fields are shared in the new patch. ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2002-06-07 13:35 Message: Logged In: YES user_id=33168 That sounds more reasonable. I'll take a look and see if they can be integrated. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-06-07 13:27 Message: Logged In: YES user_id=6380 Fair enough. Ideally this should share the recursion_limit variable from ceval.c and the recursion_depth field of the stack frame. ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2002-06-07 13:23 Message: Logged In: YES user_id=33168 I don't know. I tried to come up with other test cases and failed. Tried __str__ and __getitem__. Also tried new-style classes. The problem was that the code bounced back and forth between PyObject_Call() & instance_call() (mutual recursion). ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-06-07 13:08 Message: Logged In: YES user_id=6380 Is this fix enough? Aren't there lots of other ways to generate the same error? E.g. new-style classes (where this particular example doesn't apply, but others might). Or is this specific to instance_call? ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2002-06-07 12:21 Message: Logged In: YES user_id=33168 The attached patch stops the segfault and raises an exception: TypeError: 'A' max recursion limit (100000) exceeded I'm not sure if this is genarally applicable, or what a reasonable # is. 100,000 was a guess and should probably be made a #def w/comment. ---------------------------------------------------------------------- Comment By: Nobody/Anonymous (nobody) Date: 2002-03-21 01:47 Message: Logged In: NO >>> a=A() >>> a.foo foo __repr__ __call__ __call__ __call__ ... ad inf This is normal behavior. The code at the top is buggy. The correct one is: class A: def __getattr__(self, name): if name == '__repr__': return self.__repr__() print name return A() ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2002-03-20 14:10 Message: Logged In: YES user_id=6380 This is buggy user code. But the segfault should be fixed if possible. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=532646&group_id=5470 _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com