Decorator metaclass

Thomas Karolski Thomas.Karolski at googlemail.com
Fri May 23 15:25:19 EDT 2008


Turns out the first msg I sent did not reach the list, so I'll just post 
what I've achieved by now:
------------------------------------------
class DecoratorDummy(object): pass

class InheritedDecoratorType(type):
	def __new__(cls, name, bases, dct):
		# return if its a class which inherited from Decorator
		if Decorator in bases:
			return type.__new__(cls, name, bases, {})
		
		# if its a class which did not directly inherit from Decorator,
		# then it inherited from a class which has been manipulated using the
		# Decorator class.
		# in that case we change the bases of the inheriting class.
		# We'll split the manipulated class into Decorator and its implementation
		for b in bases:
			if type(b) is InheritedDecoratorType:
				break
		newbases = [x for x in bases]
		# remove the manipulated base class
		newbases.remove(b)
		# and add the impl of the manipulated base class
		newbases.append(b._impl_type)
		# and add the Decorator class
		newbases.append(Decorator)
		# now we'll have to make sure the dict of the new class shows the 
original base class
		# (which has been removed) as the implementation class
		dct[b.__name__] =  b._impl_type
		# since we have added a Decorator class, we ought to get rid of it
		# through the DecoratorType metaclass
		r = DecoratorType.__new__(cls, name, tuple(newbases), dct)
		
		return r

class DecoratorType(type):
	def __new__(cls, name, bases, dct):

		# if the first class is DecoratorDummy, then we're handling the
		# Decorator class, which is not supposed to be modified
		if bases[0] is DecoratorDummy:
			return type.__new__(cls, name, bases, {})
		
		# if one of the bases is the Decorator class
		b = [x for x in bases]
		if Decorator in b:
			# create a new impl type which inherits from every but the decorator 
class
			b.remove(Decorator)
			
		impl_type = type('%sImpl'%name, tuple(b), dict(dct))
		# make the returned type no longer a DecoratorType, but rather a 
normal type
		# Types which inherit from a class which inherited from Decorator, 
will thus
		# *not* be put through this metaclass.
		#dectype = type.__new__(type, name, tuple(b), {'_impl_type' : impl_type })
		dectype = type.__new__(InheritedDecoratorType, name, tuple(b), 
{'_impl_type' : impl_type })
		
		# update the old class to implement this implementation
		def __init__(self, *args, **dargs):
			new_impl = impl_type(*args, **dargs)
			super(dectype._impl_type, new_impl).__init__(*args,
															 **dargs)
			object.__setattr__(self, '_impl', new_impl)
		def decorator(self):
			return object.__getattribute__(self, '_impl')
		def __getattribute__(self, attr):
			if attr=="decorator":
				return object.__getattribute__(self, 'decorator')
			
			# if we have a specified method inside the decorator().__decorate__ var,
			# then call decorator().attr(), otherwise proxy the call
			d = object.__getattribute__(self, 'decorator')()
			if attr in d.__decorate__:
				return getattr(d, attr)
			return getattr(d.getParent(), attr)
		dectype.__init__ = __init__
		dectype.decorator = decorator
		dectype.__getattribute__ = __getattribute__
		
		return dectype

class Decorator(DecoratorDummy):
		__metaclass__ = DecoratorType

class Window(object):
	def __init__(self, parent):
		print "Window::__init__(%s)"%self
		self._parent = parent
	
	def setParent(self, parent):
		self._parent = parent
	
	def getParent(self):
		return self._parent

class Textarea(Window):
	def draw(self):
		print "Textarea::draw()"

class HBar(Decorator, Window):
	__decorate__ = ["draw"]
	def __init__(self, parent):
		print "HBar::__init__(%s)"%self
		Window.__init__(self, parent=parent)
		
		self._progress = 0.0
		
	def setProgress(self, p):
		print "setting progress to %s"%p
		self._progress= p
		
	def getProgress(self):
		return self._progress
	
	def draw(self):
		self.getParent().draw()
		print "HBar::draw()"
		
class HBarTrue(HBar):
	# HBar's bases: Window (Decorator removed by metaclass)
	# HBar's methods: __init__, decorator, __getattribute__
	# everything else is inside decorator()
	# we thus need to make calls to self within HBarTrue,
	# calls to self.decorator() - this is not possible
	# Otherwise we could also let HBarTrue inherit from HBarImpl,
	# however then a call to HBar.__init__ would be invalid inside
	# HBarTrue.__init__ unless we specify HBar internally as being HBarImpl
	# - this however is not possible
	# if we inherit from HBarImpl, then we no longer have the decorator
	# functionality. We'd thus have to include Decorator in the list of bases
	
	# Inherit normally from HBar and not Decorator.
	# Move all methods from HBarTrue to HBar.decorator()
	# create a custom __init__ method which calls HBar.decorator().__init__
	
	def __init__(self, parent):
		print "HBarTrue::__init__(%s)"%self
		for each in dir(self):
			print each
		HBar.__init__(self, parent)
		
		self.setProgress(0.0)

myTextarea = Textarea("main.parent")
myTextarea.draw()
myTextarea = HBarTrue(myTextarea)
myTextarea.draw()
myTextarea.decorator().setProgress(100.0)
------------------------------------------
The code above works only if I don't inherit from HBar like I did.
As it is now, the HBar class, which inherits from Decorator and Window, 
is going to be manipulated by the metaclass. During this process the 
Decorator base-class is removed, the implementation of HBar moved into 
_impl_type and the methods __init__, decorator and __getattribute__ are 
being assigned to the new class.
Everything works so far if I don't subclass HBar.
If I do subclass HBar, then the subclass goes through the second 
metaclass (InheritedDecoratorType). In there I replace the base class 
HBar with HBarImpl and Decorator. This way the subclass inherits the 
implementation of HBar and through the Decorator class then the subclass 
goes through the same metaclass magic as HBar in the first step. Of 
course however, since HBarTrue is no longer a subclass of HBar (since 
that baseclass has been removed), the HBar.__init__ method won't accept 
the non HBar self parameter.

Now the reason why I'm using decorators, is because I want to be ably to 
add the functionality dynamicly - without the need of construction 
classes for the different possibilities. Composite pattern does not help 
in this case, since I lose the ability to choose in what order I call 
the decorator's and the decorated's methods.

I'll just keep trying. Any input greatly appreciated.

Regards,
Thomas K.




More information about the Python-list mailing list