Decorator metaclass

2008-05-22 Thread thomas . karolski
Hi,
I would like to create a Decorator metaclass, which automatically
turns a class which inherits from the "Decorator" type into a
decorator.
A decorator in this case, is simply a class which has all of its
decorator implementation inside a decorator() method. Every other
attribute access is being proxied to decorator().getParent().

Here's my attempt:
---
from new import classobj

class DecoratorType(type):
def __new__(cls, name, bases, dct):
dct2 = {}
for key, val in dct.iteritems():
dct2[key] = val

# create a new class which will store all of the implementation
impl = classobj('%sImpl'%name,(),dct2)

# update the old class to implement this implementation
def __init__(self, *args, **dargs):
object.__setattr__(self, '__impl', impl(*args, **dargs))
def decorator(self):
return object.__getattribute__(self,'__impl')
def __getattribute__(self, attr):
if attr=="decorator":
return object.__getattribute__(self,'decorator')
return getattr(object.__getattribute__(self, 
'decorator')
().getParent(), attr)
dct = {}
dct['__init__'] = __init__
dct['decorator'] = decorator
dct['__getattribute__'] = __getattribute__

return type.__new__(cls, name, bases, dct)

class Decorator(object):
__metaclass__ = DecoratorType

class HBar(Decorator):
def __init__(self, number):
Decorator.__init__(self)
self._number = number
def inc(self):
self._number += 1
def p(self):
print self._number

hbar = HBar(10)
for each in dir(hbar.decorator()):
print each

hbar.decorator().p()
hbar.decorator().inc()
hbar.decorator().p()
---
Unfortunately this does not work. The newly defined __init__ method
inside __new__, does a call to impl(*args, **dargs). However, since
the HBar.__init__ calls the Decorator.__init__ method, but the
HBar.__init__ method no longer resides inside HBar, but rather inside
HBarImpl (which is no longer a subtype of Decorator), the compiler
complains that Decorator.__init__ is not being called with a Decorator
instance as its first argument (which is true).
I tried changing the definition of impl inside __new__ to have
Decorator as one of its bases, but then for some reason impl(*args,
**dargs) asks for 4 arguments (just like __new__) and I have no clue
as to why that happens.

Any help on this?

Regards,
Thomas K.
--
http://mail.python.org/mailman/listinfo/python-list


Re: Decorator metaclass

2008-05-23 Thread Thomas Karolski
Thanks for pointing out all those mistakes. I think I'm already starting 
to grasp all of the python magic going on in there.



Parenthetical: I don't normally recommend this style, since it
obscures the fact that you're using a custom metaclass to the user.
That is something the user probably would benefit from knowing, if for
no other reason than so they can make a mental note about where to
look first if something goes wrong.  I prefer to make the user use the
__metaclass__ attribute.


Really just personal preference I think. I'm not really a friend of 
declaring variables if there is a more "intuitive" way.



class HBar(Decorator):
def __init__(self, number):
Decorator.__init__(self)


Ok, at this point you have to ask yourself what you want to do,
because the solution you choose will involve trade-offs.


Yes, it was probably a bad example. I decided not to call the Decorator 
's __init__ method in my new version (which I have posted as a reply to 
the reply of Maric Michaud).



You will note that Decorator does not define __init__.  In fact,
object.__init__ will be called, which does nothing.  If you think that
all classes with DecoratorType as their metaclass will be a direct
subclass of Decorator, you can get away with not calling
Decorator.__init__ at all.


Now, inside my new version, I have a class which inherits from both 
Decorator and Window, out of which the __init__ for Decorator is not 
called. Does this prove to be a problem?



Probably the best piece of advice is "Don't try to use Decorator
pattern".  :)


Well, I decided on the decorator pattern, because I want to be able to 
change the behavior of classes during run-time. I did not really find 
any other pattern which would let me do that.


Regards,
Thomas K.

--
http://mail.python.org/mailman/listinfo/python-list


Re: Decorator metaclass

2008-05-23 Thread Thomas Karolski
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