strange error whilst porting to 2.6

Robin Becker robin at reportlab.com
Mon Jan 26 10:45:25 EST 2009


Robin Becker wrote:
> I found that this error
> 
>> Exception RuntimeError: 'maximum recursion depth exceeded in 
>> __subclasscheck__' in <type 'exceptions.AttributeError'> ignored 
> 
> occurs when attempting to copy (copy.copy(inst)) an instance of a class 
> that looks like this
> 
> class LazyParagraph(_LazyMixin,TTParagraph):
>     SUPER=TTParagraph
>     _CLEAN_SPACE=1
........
> 
> I have attempted to abstract the problem, but so far I haven't found the 
> vital bits.

OK this turns out to be one of those useful exercises after all. After 
instrumenting the copy instance copy func my colleague and I found the problem 
occurs in this innocuous looking example

###################
import copy
class _LazyMixin:
	"""don't do any initialization until later"""
	def __init__(self,*args):
		self._args = args
		self._initialized = 0

	def __getattr__(self,a):
		if not self._initialized:
			self._Initialize()
			return getattr(self,a)
		raise AttributeError("No attribute '%s'" % a)

	def _Initialize(self):
		if not self._initialized:
			self._initialized = 1
			del self._args

l=_LazyMixin()
print l._initialized
copy.debug=1
copy.copy(l)
###################


it turns out that in the absence of other info _copy_inst creates a dummy 
instance and changes its class to the incoming class. It then asks the new 
unpopulated  instance if it has an attribute __setstate__, that triggers the 
_LazyMixin __getattr__ which fails because there's no _initialize member so 
__getattr__ gets recalled etc etc. Presumably in earlier pythons this bad 
behaviour is just hidden. The fix for us is to provide a __setstate__ which does 
what the no __setstate__ code does ie update __dict__.

However, since the _copy_inst code knows there can be no members on the new 
instance should it not be asking the __class__ for __setstate__?
-- 
Robin Becker




More information about the Python-list mailing list