I have had a situation arise where most methods of a class need to raise an exception if called except for one, if a certain condition is False. It would be possible to go into most methods and write if not condition such that each method will raise the exception if the condition is not true, but I think it is probably possible to do this somehow with a single decorator on the top of the class.
This question is similar but it involves decorating every method separately, and if I were going to do that, I might as well just put the if statement into each method.
Here is some code and comments to help communicate it:
CONDITION = True # change to False to test
def CheckMethods():
if CONDITION:
# run all the methods as usual if they are called
pass
else:
# raise an exception for any method which is called except 'cow'
# if 'cow' method is called, run it as usual
pass
@CheckMethods
class AnimalCalls:
def dog(self):
print("woof")
def cat(self):
print("miaow")
def cow(self):
print("moo")
def sheep(self)
print("baa")
a = AnimalCalls()
a.dog()
a.cat()
a.cow()
a.sheep()
Does anyone know how to do this? Have never decorated a class before or tried to check its methods like this.
Implementing a proxy is as simple as that
class Proxy:
def __init__(self, inst):
self.__inst = inst
def __getattr__(self, name):
return getattr(self.__inst, name)
Instead of obj = SomeClass() you'd use obj = Proxy(SomeClass()). All accesses to obj.attribute get intercepted by Proxy.__getattr__. That's the method you may add more logic to, e.g.:
class MethodChecker:
def __init__(self, inst, check):
self.__inst = inst
self.__check = check
def __getattr__(self, name):
self.__check()
return getattr(self.__inst, name)
The proxy would be my pick, but here is a decorator as requested.
I added a test to exclude any methods starting with an underscore. You might want to include _internal methods, but take care not to mess with any special __dunder__ methods.
# cond = lambda attr: True # full access
cond = lambda attr: attr == 'cow'
def methodcheck(cls):
def cond_getattribute(self, name):
if name.startswith('_') or cond(name):
return saved_gettattribute(self, name)
raise AttributeError("access forbidden")
saved_gettattribute = cls.__getattribute__
cls.__getattribute__ = cond_getattribute
return cls
@methodcheck
class AnimalCalls:
def dog(self):
print("woof")
def cat(self):
print("miaow")
def cow(self):
print("moo")
def sheep(self):
print("baa"
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