Does the class itself need to be changed? Or is the goal just to replace what object.method () does at a certain point at runtime?
I ask because I circumvented the problem of actually changing the class in calling the patch method for monkey in my structure using getattribute and Runtime Decorator on my Base inheritance object.
The methods received by the base object in getattribute are wrapped in a Runtime_Decorator, which parses a method that invokes method keyword arguments to fix decorators / monkeys.
This allows you to use the syntax object.method (monkey_patch = "mypatch"), object.method (decorator = "mydecorator") and even object.method (decorators = my_decorator_list).
This works for any individual method call (I do not take magic methods into account), does it without actually changing the attributes of the class / instance, can use arbitrary, even foreign methods to fix it, and will work transparently on sublayers that inherit from Base (if they do not override getattribute , of course).
import trace def monkey_patched(self, *args, **kwargs): print self, "Tried to call a method, but it was monkey patched instead" return "and now for something completely different" class Base(object): def __init__(self): super(Base, self).__init__() def testmethod(self): print "%s test method" % self def __getattribute__(self, attribute): value = super(Base, self).__getattribute__(attribute) if "__" not in attribute and callable(value): value = Runtime_Decorator(value) return value class Runtime_Decorator(object): def __init__(self, function): self.function = function def __call__(self, *args, **kwargs): if kwargs.has_key("monkey_patch"): module_name, patch_name = self._resolve_string(kwargs.pop("monkey_patch")) module = self._get_module(module_name) monkey_patch = getattr(module, patch_name) return monkey_patch(self.function.im_self, *args, **kwargs) if kwargs.has_key('decorator'): decorator_type = str(kwargs['decorator']) module_name, decorator_name = self._resolve_string(decorator_type) decorator = self._get_decorator(decorator_name, module_name) wrapped_function = decorator(self.function) del kwargs['decorator'] return wrapped_function(*args, **kwargs) elif kwargs.has_key('decorators'): decorators = [] for item in kwargs['decorators']: module_name, decorator_name = self._resolve_string(item) decorator = self._get_decorator(decorator_name, module_name) decorators.append(decorator) wrapped_function = self.function for item in reversed(decorators): wrapped_function = item(wrapped_function) del kwargs['decorators'] return wrapped_function(*args, **kwargs) else: return self.function(*args, **kwargs) def _resolve_string(self, string): try:
The disadvantage of this approach is that getattribute intercepts all access to attributes, so checking and potential packaging of methods even for attributes that are not + methods will not use the function for a specific call. And using getattribute in general is a bit trickier.
The actual impact of this overhead in my experience / for my purposes was negligible, and the dual-core Celeron runs on my machine. In a previous implementation, I used introspective methods for an init object and associated Runtime_Decorator with methods. Running this method eliminated the need to use getattribute and reduced the overhead mentioned earlier ... however, it also breaks up a pickle (maybe not dill) and is less dynamic than this approach.
The only use cases that I actually encountered βin the wildβ using this technique were with the timing and tracing of decorators. However, the opportunities that it opens up are extremely broad.
If you have a pre-existing class that cannot be inherited from another base (or use the method that it owns the class definition or in its base class), then all this simply does not apply to your problem at all, unfortunately.
I donβt think that setting / removing non-invoking attributes for a class at runtime is necessarily a daunting task? if you do not want the classes that inherit from the changed class to automatically reflect the changes in themselves ... This would be a "black worm" worms throughout the sound.