I am refactoring a Python signal processing framework because of a maximum recurrence depth error we experienced.
The most likely explanation for the error is that sometimes a large number of instances of a single class are created recursively from the class' its init method. Indeed experiments simulating this situation lead to a Exception RuntimeError: 'maximum recursion depth exceeded'.
When I move creating the next element in the chain to the container managing these objects the problem seems to disappear, this despite building in my naive understanding a call stack of the same depth. I call create through all the previously created objects. (see example code).
I am inclined to conclude that the recursion depth limit is somehow set per object (be it a class or an instance). Which when creating recursively via init probably puts everything on the stack of the class, where as if we create them recursively through a line of instances of the same class all the objects will have very limited recursion depth.
I am seeking some confirmation for this hypothesis, but find it hard to get either confirmation or refutation.
import sys
class Chunk(object):
pre=None
post=None
def __init__(self, container,pre):
print 'Chunk Created'
self.pre=pre
self.container=container
def create(self,x):
if self.post == None:
self.post=Chunk(self.container,self)
newChunk =self.post
else:
newChunk =self.post.create(x+1)
return newChunk
def droprefs(self):
print 'refcount droprefs start: ', sys.getrefcount(self)
if not self.pre ==None:
self.pre.droprefs()
self.pre=None
self.post=None
self.container=None
print 'refcount droprefs end: ', sys.getrefcount(self)
def claimContainer(self):
self.container.setmasterchunk(self)
self.pre.droprefs()
self.pre=None
class Container(object):
masterchunk=None
def __init__(self):
self.masterchunk=Chunk(self, None)
def setmasterchunk(self, chunk):
print 'refcount 5: ', sys.getrefcount(self.masterchunk)
self.masterchunk=chunk
print 'refcount 6: ', sys.getrefcount(self.masterchunk)
def testrecursion(self,n):
for i in range(n):
print 'refcount 6: ', sys.getrefcount(self.masterchunk)
newChunk=self.masterchunk.create(1)
return newChunk
def testhistoryremoval(self,chunk):
chunk.claimContainer()
if __name__ == '__main__':
C=Container()
middle=C.testrecursion(300)
last=C.testrecursion(600)
last=C.testhistoryremoval(middle)
if middle== C.masterchunk:
print 'SUCCESS'
Note added:
Code showing the maximum recursion depth:
import sys
from time import sleep
class Chunk(object):
pre=None
post=None
def __init__(self, container,pre,n):
print 'Chunk Created'
self.pre=pre
self.container=container
if n>0:
self.post=Chunk(self.container,self,n-1)
def droprefs(self):
print 'refcount droprefs start: ', sys.getrefcount(self)
if not self.pre ==None:
self.pre.droprefs()
self.pre=None
self.post=None
self.container=None
print 'refcount droprefs end: ', sys.getrefcount(self)
def claimContainer(self):
self.container.setmasterchunk(self)
self.pre.droprefs()
self.pre=None
class Container(object):
masterchunk=None
def __init__(self):
pass
def setmasterchunk(self, chunk):
print 'refcount 5: ', sys.getrefcount(self.masterchunk)
self.masterchunk=chunk
print 'refcount 6: ', sys.getrefcount(self.masterchunk)
def testrecursion(self,n):
self.masterchunk=Chunk(self,None,n)
if __name__ == '__main__':
print('Try 10')
C0=Container()
C0.testrecursion(10)
sleep(2)
print('Try 1000')
C1=Container()
C1.testrecursion(1000)