Dynamically override all parent class methods - python

I want to be able to dynamically override all calls sent to methods on the parent class. So far I've only been able to do it for methods not defined in a parent class.
I.e. Given a parent class:
class A:
def foo(self):
print("foo")
def bar(self):
print("bar")
I want to create a Spy class that inherits from A will print "This is a spy" before calling any method on A, including foo.
class Spy(A):
pass # What do I do here?
s = Spy()
>>> s.foo()
This is a spy
foo
Current implementation
My current implementation of Spy is this:
class Spy(A):
def __getattr__(self, method):
print("This is a spy")
return getattr(super(A, self), method)
However, this only works for methods not defined in the parent class:
>>> s.baz()
This is a spy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getattr__
AttributeError: 'super' object has no attribute 'baz'
When I call a method that exists already, it doesn't work:
>>> s.foo()
foo
>>> s.bar()
bar

The following code snippet should do what you want. I have excluded methods starting with __ because these can be problematic (for example, overriding __class__ with a function will cause an error).
class A:
def foo(self):
print("foo")
def bar(self, x):
print(x)
class Spy(A):
def functionwrapper(self, functionname):
originalfunc = getattr(super(), functionname)
def wrap(*args, **kwargs):
print("This is a spy: ", end="")
originalfunc(*args, **kwargs)
return wrap
def __init__(self):
for methodname in [method for method in dir(A) if (method[0:2] != "__")]:
setattr(self, methodname, self.functionwrapper(methodname))
s = Spy()
s.foo()
s.bar("variable")
Output
This is a spy: foo
This is a spy: variable

When I call a method that exists already, it doesn't work
That's because __getattr__ is only gonna get called when the default attribute access fails. So if the parent has that method, it gets found and your __getattr__ won't get called.
You need to intercept the __getattribute__ instead:
from types import MethodType
from inspect import isfunction
class A:
def foo(self):
print("foo")
class Spy(A):
def bar(self):
print("barrrrrrrrr")
def __getattribute__(self, name):
# check it instance's namespace
instance_dict = object.__getattribute__(self, "__dict__")
if name in instance_dict:
return instance_dict[name]
# check its class' namespace
if name in type(self).__dict__:
return object.__getattribute__(self, name)
# check parents
for cls in type(self).mro()[1:]:
if name in cls.__dict__:
member = cls.__dict__[name]
if isfunction(member):
# your code here
print("This is a spy")
return MethodType(cls.__dict__[name], self)
return member
raise AttributeError(f"{type(self)} object has no attribute '{name}'")
s = Spy()
s.foo()
s.bar()
print("-----------------------------------")
s.boo()
output:
This is a spy
foo
barrrrrrrrr
-----------------------------------
Traceback (most recent call last):
File "...", line 32, in <module>
s.boo()
File "...", line 25, in __getattribute__
raise AttributeError(f"{type(self)} object has no attribute '{name}'")
AttributeError: <class '__main__.Spy'> object has no attribute 'boo'
So this is one way of doing it and I agree that it might be overkill.
You do that in three steps:
Check to see it is in the instance's namespace: like the data attributes you usually set in __init__.
Check to see it is in its class' namespace: You don't want yout print statement here because it's not sent to the parent.
Check its parents

test.py:
import functools
import inspect
TXT = "This is a spy"
def deco(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(TXT)
return func(*args, **kwargs)
return wrapper
class A:
def foo(self):
print("foo")
def bar(self):
print("bar")
class Spy(A):
def __init__(self):
self.patch()
#classmethod
def patch(cls):
superclass = cls.__bases__[0]
for name, value in inspect.getmembers(superclass, inspect.isfunction):
setattr(cls, name, deco(value))
def main():
s = Spy()
s.foo()
s.bar()
if __name__ == "__main__":
main()
Test:
$ python test.py
This is a spy
foo
This is a spy
bar

Related

Python read-only wrapper class that denies access to certain methods and all attributes

I have the following base class.
class BaseWithMethod:
def __init__(self, prop=None):
self.prop = prop
def evil_method(self):
print(f"%##&ç? {self.prop}")
I want to create a wrapper class ReadonlyWrapperSelectedMethods that shows the same functionality as the base class but does not allow certain methods (evil_method in this example) to be called. Further, wrapped instances should be read-only, as discussed in my other SO question here. That means that calls to __setattr__ should raise an error once the instance is initialized. The behavior is demonstrated in the following code:
# Instantiate the wrapper class
readonly_instance = ReadonlyWrapperSelectedMethods()
# I can access properties
prop = readonly_instance.prop
# This should raise a PermissionError
readonly_instance.prop = 23
# This should also raise a PermissionError
readonly_instance.evil_method()
Is there a way to implement this behavior without modifying the base class? See below how it can be done when the base class may be changed.
Attempt 1: Modifying the base class
So far I have tried the following. I added an attribute _initialized to the base class and set it to True at the end of __init__:
class BaseWithMethodModified:
_initialized = False
def __init__(self, prop=None):
self.prop = prop
self._initialized = True
def evil_method(self):
print(f"%##&ç? {self.prop}")
In this case the following wrapper class should do the job. It overrides the __getattribute__ method and delegates calls to methods that are allowed to the super class.
class ReadonlyWrapperSelectedMethods(BaseWithMethodModified):
"""Read-only wrapper class."""
def __getattribute__(self, name: str):
if "evil" in name:
raise PermissionError()
else:
return super().__getattribute__(name)
def __setattr__(self, key, value) -> None:
if self.__getattribute__("_initialized"):
raise PermissionError()
else:
super().__setattr__(key, value)
The issue with this attempt is that I do not want to modify the base class and if the attribute _initialized is defined in the wrapper class, it cannot be accessed since all attribute accesses are delegated to the base class through __getattribute__. Maybe this can be circumvented in some way?
You could simply override the __init__ method:
class ReadonlyWrapperSelectedMethods(BaseWithMethod):
"""Read-only wrapper class."""
def __init__(self, prop=None):
super().__init__(prop)
self._initialized = True
def __getattribute__(self, name: str):
if "evil" in name:
raise PermissionError()
else:
return super().__getattribute__(name)
def __setattr__(self, key, value) -> None:
if hasattr(self, "_initialized"):
raise PermissionError()
else:
super().__setattr__(key, value)
After __init__ returns, the object is readonly:
>>> readonly_instance = ReadonlyWrapperSelectedMethods()
>>> vars(readonly_instance)
{'prop': None, '_initialized': True}
>>> prop = readonly_instance.prop
>>> readonly_instance.prop = 23
Traceback (most recent call last):
File "<pyshell#126>", line 1, in <module>
readonly_instance.prop = 23
File "<pyshell#121>", line 16, in __setattr__
raise PermissionError()
PermissionError
>>> readonly_instance.evil_method()
Traceback (most recent call last):
File "<pyshell#127>", line 1, in <module>
readonly_instance.evil_method()
File "<pyshell#121>", line 10, in __getattribute__
raise PermissionError()
PermissionError
Don't use inheritance, use composition. Take advantage of __slots__:
class Foo:
def __init__(self, prop=None):
self.prop = prop
def evil_method(self):
print(f"%##&ç? {self.prop}")
class ReadOnlyWrapper:
__slots__ = ('_foo',)
def __init__(self, foo: Foo):
self._foo = foo
def __getattr__(self, name: str):
if "evil" in name:
raise PermissionError()
else:
return getattr(self._foo, name)
wrapper = ReadOnlyWrapper(Foo())

Python class constructor (static)

Does Python have a mechanism for class constructors, i.e. a function that is called whenever the class is first referenced (as opposed to when an instance of that object is created)? I know this exists in some other languages, but I haven't come across it in Python.
Basically, I would like to initialise some static attributes in that function. I put an example below of what I would expect. Of course, the example returns None, but I would like it return 'foo'.
class T:
arg = None
def __class_constructor__():
T.arg = 'foo'
print(T.arg) # returns None
To avoid confusion: I am well aware of the object constructor, but that's not what I want, because it is only called once the first object is created, not before:
class T:
arg = None
def __init__(self):
type(self).arg = 'foo'
print(T.arg) # returns None
obj = T()
print(T.arg) # returns 'foo'
You can use a class decorator:
def add_arg(cls):
if not hasattr(cls, "arg"):
cls.arg = 'foo'
return cls
#add_arg
class T(object):
pass
Or a custom metaclass:
class WithArg(type):
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
if not hasattr(cls, "arg"):
cls.arg = "foo"
return cls
# python 2
class T(object):
__metaclass__ = WithArg
# python 3
class T(metaclass=WithArg):
pass
But as others already mention this won't give you much more than plainly setting the class attribute in the class statement.
NB : if you want a computed attribute on the class itself, you'll have to either set it as a property on a custom metaclass
class WithProp(type):
#property
def arg(cls):
return "foo"
class T(object):
__metaclass__ = WithProp
T.arg
=> 'foo'
But arg will only be available on the class object itself, not on it's instances:
T().arg
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'T' object has no attribute 'arg'
or write your own custom descriptor:
class ArgDescriptor(object):
def __get__(self, obj, cls=None):
return 42
class T(object):
arg = ArgDescriptor()
T.arg
=> 42
T().arg
=> 42
You simply have to initialise the class variable when declaring it within the class
class T:
arg = 'foo' #this initialises the class instance
def __init__(self):
self.arg = 'bar' #this initialises the object instance
print(T.arg) # class instance returns 'foo'
obj = T()
print(T.arg) # class instance still returns 'foo'
print(obj.arg) # object instance returns 'bar'
I create a static_init decorator which calls a static_init class method if it exists. This static_init class method will run when the class decorator is evaluated which is when the class is defined - so not quite when the class is first referenced - but it is analogous to static initialization in other languages like Java.
Here is the decorator and example of how to use it to initialize a class variable on an enum class:
# pylint: disable=missing-docstring,no-member
import enum
def static_init(cls):
if getattr(cls, "static_init", None):
cls.static_init()
return cls
#static_init
class SomeEnum(enum.Enum):
VAL_A = enum.auto()
VAL_B = enum.auto()
VAL_C = enum.auto()
VAL_D = enum.auto()
#classmethod
def static_init(cls):
text_dict = {}
setattr(cls, 'text_dict', text_dict)
for value in cls:
text_dict[value.name.lower().replace("_", " ").title()] = value
def test_static_init():
assert SomeEnum.text_dict["Val A"] == SomeEnum.VAL_A
assert SomeEnum.text_dict["Val B"] == SomeEnum.VAL_B
assert SomeEnum.text_dict["Val C"] == SomeEnum.VAL_C
assert SomeEnum.text_dict["Val D"] == SomeEnum.VAL_D

Decorate class that has no self in method signature in Python

I am trying to apply decorator dynamically to classes.
It works if I have a class method including self in method signature.
Working example:
from functools import wraps
def debug(func):
#wraps(func)
def wrapper(*args, **kwargs):
print('awesome')
f = func(*args, **kwargs)
return f
return wrapper
def debugclass(cls):
# cls is a class
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
return cls
class Dude:
def test(self):
#def test(): # this does not work
pass
debugclass(Dude)
dude = Dude()
dude.test()
How could I change Dude class method signature so that it would work without self being part of signature?
class Dude:
def test(): # without self
pass
debugclass(Dude)
dude = Dude()
dude.test()
Getting error:
Traceback (most recent call last):
File "withoutself.py", line 33, in <module>
dude.test()
File "withoutself.py", line 7, in wrapper
f = func(*args, **kwargs)
TypeError: test() takes no arguments (1 given)
For your test() method to be callable without a self or cls parameter you need to make it a staticmethod.
class Dude:
#staticmethod
def test():
pass
Then you also need to update debugclass to wrap staticmethod objects as they aren't callables. Unfortunately you'll need a different way to wrap the staticmethod objects:
def debugclass(cls):
# cls is a class
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
elif isinstance(val, staticmethod):
setattr(cls, key, staticmethod(debug(val.__func__)))
return cls
>>> class Dude:
def test(self):
pass
#staticmethod
def test1():
pass
>>> debugclass(Dude)
<class __main__.Dude at 0x7ff731842f58>
>>> Dude().test()
awesome
>>> Dude.test1()
awesome
Shortly, you can't.
Elaboratedely, python passes object instance as a first argument when a method is called. Therefore if you define a method with no arguments you get the execption. The 1 given argument is your "dude" object.
For more information please see https://docs.python.org/2/tutorial/classes.html#method-objects

Infinite recursion in decorator (Class seems to inherit from itself)

I'm seeing an infinite recursion in a decorated class:
def my_decorator():
def class_decorator(cls):
class NewClass(cls):
#classmethod
def func(cls):
print "decorator"
super(NewClass, cls).func()
return NewClass
return class_decorator
class B(object):
#classmethod
def func(cls):
print "Root"
#my_decorator()
class A(B):
#classmethod
def func(cls):
print "func"
super(A, cls).func()
x = A()
x.func()
Outputs:
decorator
func
func
func
func
... <lots of 'func'> ...
lots of these:
File "test.py", line 22, in func
super(A, cls).func()
File "test.py", line 22, in func
super(A, cls).func()
File "test.py", line 22, in func
super(A, cls).func()
RuntimeError: maximum recursion depth exceeded while calling a Python object
Without the decorator, it returns:
func
Root
...as expected.
What's going on?
After you've decorated the original A, which is equivalent to:
A = my_decorator()(A)
The name A refers to the newly created NewClass, which also inherits from the original A, not the original A. Therefore super(A, cls).func always resolves to super(NewClass, cls).func, which is A.func.
Unwrapping the decorator and assigning the result to a different name, you get the correct behaviour:
X = my_decorator()(A)
x = X()
x.func()
gives:
decorator
func
Root
Code like this:
#my_decorator()
class A(B):
# ...
is semantically the same thing as:
A = my_decorator(A)
(according to PEP 3129)
That means that the symbol with name A, which normally would hold a Type value of 'class A' is actually being redefined to be whatever is returned from my_decorator, which is the Type NewClass.
So at the point where you call
super(A, cls).func()
A actually refers to NewClass (as does cls), resulting in a call to the same method.

Python inheritance - how to disable a function

In C++ you can disable a function in parent's class by declaring it as private in the child class. How can this be done in Python? I.E. How can I hide parent's function from child's public interface?
There really aren't any true "private" attributes or methods in Python. One thing you can do is simply override the method you don't want in the subclass, and raise an exception:
>>> class Foo( object ):
... def foo( self ):
... print 'FOO!'
...
>>> class Bar( Foo ):
... def foo( self ):
... raise AttributeError( "'Bar' object has no attribute 'foo'" )
...
>>> b = Bar()
>>> b.foo()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 3, in foo
AttributeError: 'Bar' object has no attribute 'foo'
kurosch's method of solving the problem isn't quite correct, because you can still use b.foo without getting an AttributeError. If you don't invoke the function, no error occurs. Here are two ways that I can think to do this:
import doctest
class Foo(object):
"""
>>> Foo().foo()
foo
"""
def foo(self): print 'foo'
def fu(self): print 'fu'
class Bar(object):
"""
>>> b = Bar()
>>> b.foo()
Traceback (most recent call last):
...
AttributeError
>>> hasattr(b, 'foo')
False
>>> hasattr(b, 'fu')
True
"""
def __init__(self): self._wrapped = Foo()
def __getattr__(self, attr_name):
if attr_name == 'foo': raise AttributeError
return getattr(self._wrapped, attr_name)
class Baz(Foo):
"""
>>> b = Baz()
>>> b.foo() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
AttributeError...
>>> hasattr(b, 'foo')
False
>>> hasattr(b, 'fu')
True
"""
foo = property()
if __name__ == '__main__':
doctest.testmod()
Bar uses the "wrap" pattern to restrict access to the wrapped object. Martelli has a good talk dealing with this. Baz uses the property built-in to implement the descriptor protocol for the attribute to override.
A variation on the answer of kurosch:
class Foo( object ):
def foo( self ):
print 'FOO!'
class Bar( Foo ):
#property
def foo( self ):
raise AttributeError( "'Bar' object has no attribute 'foo'" )
b = Bar()
b.foo
This raises an AttributeError on the property instead of when the method is called.
I would have suggested it in a comment but unfortunately do not have the reputation for it yet.
class X(object):
def some_function(self):
do_some_stuff()
class Y(object):
some_function = None
This may lead to some nasty and hard to find exceptions being thrown though, so you might try this:
class X(object):
def some_function(self):
do_some_stuff()
class Y(object):
def some_function(self):
raise NotImplementedError("function some_function not implemented")
That could be even simpler.
#property
def private(self):
raise AttributeError
class A:
def __init__(self):
pass
def hello(self):
print("Hello World")
class B(A):
hello = private # that short, really
def hi(self):
A.hello(self)
obj = A()
obj.hello()
obj = B()
obj.hi() # works
obj.hello() # raises AttributeError
This is the cleanest way I know to do it.
Override the methods and have each of the overridden methods call your disabledmethods() method. Like this:
class Deck(list):
...
#staticmethod
def disabledmethods():
raise Exception('Function Disabled')
def pop(self): Deck.disabledmethods()
def sort(self): Deck.disabledmethods()
def reverse(self): Deck.disabledmethods()
def __setitem__(self, loc, val): Deck.disabledmethods()
Another approach is define an descriptor that errors on access.
class NotHereDescriptor:
def __get__(self, obj, type=None):
raise AttributeError
class Bar:
foo = NotHereDescriptor()
This is similar in nature to the property approach a few people have used above. However it has the advantage that hasattr(Bar, 'foo') will return False as one would expect if the function really didn't exist. Which further reduces the chance of weird bugs. Although it does still show up in dir(Bar).
If you are interested in what this is doing and why it works check out the descriptor section of the data model page https://docs.python.org/3/reference/datamodel.html#descriptors and the descriptor how to https://docs.python.org/3/howto/descriptor.html

Categories