I have a problem in Python, for which I cannot find any clean solution ...
When calling some methods, I want to execute some code before the method execution and after. In order (among many other things) to automatically set and clean a context
variable.
In order to achieve this, I have declared the following metaclass :
class MyType(type):
def __new__(cls, name, bases, attrs):
#wraps the 'test' method to automate context management and other stuff
attrs['test'] = cls.other_wrapper(attrs['test'])
attrs['test'] = cls.context_wrapper(attrs['test'])
return super(MyType, cls).__new__(cls, name, bases, attrs)
@classmethod
def context_wrapper(cls, operation):
def _manage_context(self, *args, **kwargs):
#Sets the context to 'blabla' before the execution
self.context = 'blabla'
returned = operation(self, *args, **kwargs)
#Cleans the context after execution
self.context = None
return returned
return _manage_context
@classmethod
def other_wrapper(cls, operation):
def _wrapped(self, *args, **kwargs):
#DO something with self and *args and **kwargs
return operation(self, *args, **kwargs)
return _wrapped
This works like a charm :
class Parent(object):
__metaclass__ = MyType
def test(self):
#Here the context is set:
print self.context #prints blabla
But as soon as I want to subclass Parent
, problems appear, when I call the parent method with super
:
class Child(Parent):
def test(self):
#Here the context is set too
print self.context #prints blabla
super(Child, self).test()
#But now the context is unset, because Parent.test is also wrapped by _manage_context
#so this prints 'None', which is not what was expected
print self.context
I have thought of saving the context before setting it to a new value, but that only solves partially the problem...
Indeed, (hang on, this is hard to explain), the parent method is called, the wrappers are executed, but they receive *args
and **kwargs
addressed to Parent.test
, while self
is a Child
instance, so self
attributes have irrelevant values if I want to challenge them with *args
and **kwargs
(for example for automated validation purpose), example :
@classmethod
def validation_wrapper(cls, operation):
def _wrapped(self, *args, **kwargs):
#Validate the value of a kwarg
#But if this is executed because we called super(Child, self).test(...
#`self.some_minimum` will be `Child.some_minimum`, which is irrelevant
#considering that we called `Parent.test`
if not kwarg['some_arg'] > self.some_minimum:
raise ValueError('Validation failed')
return operation(self, *args, **kwargs)
return _wrapped
So basically, to solve this problem I see two solutions :
preventing the wrappers to be executed when the method was called with super(Child, self)
having a self
that is always of the "right" type
Both solutions seem impossible to me ... Do somebody has an idea on how to solve this ? A suggestion ?
Well, can't you just check if the context is already set in _manage_context
? Like this:
def _manage_context(self, *args, **kwargs):
#Sets the context to 'blabla' before the execution
if self.context is None:
self.context = 'blabla'
returned = operation(self, *args, **kwargs)
#Cleans the context after execution
self.context = None
return returned
else:
return operation(self, *args, **kwargs)
Also, this should probably be wrapped in a try-catch block, to ensure resetting of the context in case of exceptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With