Implementing a generic and dynamic facade for python classes - python

I wanted to implement a sort of facade pattern in python. However because I need to do the same for all methods, I'd like to do it in a generic way. Let me use an example:
class MyObject:
def __init__(self, *args, **kwargs):
# do something with args/kwargs
def method1(self, x):
# do something
def method2(self, x, a):
# do something
def method3(self, x, a, b):
# do something
class MyFacade:
def __init__(self, *args, **kwargs):
self.x = SOMETHING
self.obj = MyObject(*args, **kwargs)
def method1(self):
return self.obj.method1(self.x)
def method2(self, a):
return self.obj.method2(self.x, a)
def method3(self, a, b):
return self.obj.method3(self.x, a, b)
Now because I have several classes like MyObject, I'd like a generic way of creating a MyFacade for each of them without having to write code for each method (they all do more or less the same). Also if MyObject changes, I'd like MyFacade not being impacted and rather handle any interface change in MyObject transparently.
Thanks for the help!
EDIT:
This works but methods inherited from MyInterface raise TypeError because of the extra argument.
class MyObject:
def method1(self, x):
print(x)
def method2(self, x, a):
print(x, a)
def method3(self, x, a, b):
print(x, a, b)
class MyInterface:
def methodX(self):
print("YAY!")
class MyFacade(MyInterface, MyObject):
def __init__(self):
self.x= "WHATEVER"
def __getattribute__(self, item):
result = super().__getattribute__(item)
if callable(result):
return lambda *args, **kwargs: result(self.x, *args, **kwargs)
return result
EDIT:
I modified condition this way and now problem with MyInterface is gone:
if callable(result) and result.__name__ in MyObject.__dict__:

The obvious way of doing this is to use the fact that class and function names are variables and can be assigned so MyFacade could be defined as follows:
class MyFacade:
def __init__(self,obj, *args, **kwargs):
self.x = SOMETHING
self.obj = obj(*args, **kwargs)
def method1():
return self.obj.method1(self.x)
def method2(a):
return self.obj.method2(self.x, a)
def method3(a, b):
return self.obj.method1(self.x, a, b)
and the set-up call would be eg:
fasc = MyFscade(MyOject,*args,**kwargs)

Related

Log common method among classes in python

I am importing several classes from a library with a common method, like
class BarClass1:
def __init__(self):
pass
def bar(self, x):
return x + 1
class BarClass2:
def __init__(self):
pass
def bar(self, x):
return x + 2
class BarClass3:
def __init__(self):
pass
def bar(self, x):
return x + 3
I want to add logging to the bar method of each class, and for that purpose I create children for these classes in the following way:
def log_something(x):
print(f'input is {x}')
class DerivedBarClass1(BarClass1):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass2(BarClass2):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass3(BarClass3):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
I feel I am doing a lot of code repetition, is there a simpler way of doing this? My main constraint is not being able to modify the code in BarClass1, BarClass2 or BarClass3.
If you can't modify the code, you can always monkey-patch the classes...
import functools
def add_logging_single_arg(f): # maybe a better name...
#functools.wraps(f)
def wrapper(self, x):
log_something(x)
return f(x)
return wrapper
for klass in [BarClass1, BarClass2, BarClass3]:
klass.bar = add_logging_single_arg(bar)

Is there a simpler syntax for member decorators for methods?

I changed a class which had a function that had to be ran prior to running a number of other functions. The "prior-to-others" function is now a decorator. But the syntax, which I came up with, seems very unintuitive.
It used to be something like this:
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
def request1(self):
self._wait_on_throttlers()
...
def request2(self):
self._wait_on_throttlers()
...
And now it's like this:
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
def _throttled(f):
def inner(self, *args, **kwargs):
self._wait_on_throttlers()
return f(self, *args, **kwargs)
return inner
#_throttled
def request1(self):
...
#_throttled
def request2(self):
...
And, while I think the use of this decorator made the code more clear, the implementation of this decorator took some doing. It's also very fragile and hard to read. For example, if the inner return line return f(self, *args, **kwargs) is changed to return self.f(*args, **kwargs), then it won't work anymore.
This seems to do with the order in which the elements of the class are compiled. I am also afraid that this would break in future versions of Python. I am using Python 3.6.8.
Is there an accepted and/or recommended way to make such class-member decorators of class methods which would be less counter-intuitive and less fragile?
For the sake of a minimal reproducible example, the ... can be considered to be a pass statement and the class TempThrottler can be defined as below (this isn't the actual implementation, but it's enough to satisfy the example above):
class TempoThrottler:
def __init__(self, t):
pass
def isallowed(self):
from random import randint
return (True, False)[randint(0,1)]
def mustwait(self):
return 1
def consume(self):
pass
Below is a runnable example that illustrates my suggestion of how it would be possible to move the decorator function completely out of the class:
from random import randint
import time
class TempoThrottler:
def __init__(self, t):
pass
def isallowed(self):
# return [True, False](randint(0,1))
return [True, False][randint(0,1)]
def mustwait(self):
return 1
def consume(self):
pass
# Decorator not in class.
def _throttled(f):
def inner(self, *args, **kwargs):
self._wait_on_throttlers()
return f(self, *args, **kwargs)
return inner
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
#_throttled
def request1(self):
print('in request1()')
...
#_throttled
def request2(self):
print('in request2()')
...
s = Session(range(3))
s.request1() # -> in request1()
s.request2() # -> in request2()
While this maybe more complicated, at the first glance, in order to address that
the decorator is not bound to an instance of the class
this decorator is coupled with the class
I am going to make the decorator parametrized and move it outside of the class. Then the instance of the decorator specialized for a specific pre-call method can be assigned to a class variable. And then this class variable can be used as the actual decorator.
def precall_decorator(precall_f):
def decor(f):
def inner(self, *args, **kwargs):
precall_f(self)
return f(self, *args, **kwargs)
return inner
return decor
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
_throttled = precall_decorator(_wait_on_throttlers)
#_throttled
def request1(self):
...
#_throttled
def request2(self):
...

Nested class decorators which are also descriptor classes

I have a situation where I need to use nested decorators like below,
class A(object):
def __init__(self,v):
print("A.init")
#deco1
#deco2
def m(self, a):
print("A.m")
Decorators are implemented like below,
class deco1(object):
def __init__(self, f):
print("deco1.init")
self.f = f
def __call__(self, *args, **kwargs):
print("deco1.call.before")
r = self.f(*args, **kwargs)
print("deco1.call.after")
return r
def __get__(self, o, c):
print("deco1.get")
return MethodType(self, o)
class deco2(object):
def __init__(self, f):
print("deco2.init")
self.f = f
def __call__(self, *args, **kwargs):
print("deco2.call.before")
r = self.f(*args, **kwargs)
print("deco2.call.after")
return r
def __get__(self, o, c):
print("deco2.get")
return MethodType(self, o)
Problem is descriptor method on deco2 class isn't being called and I require that to be called.
When I do something like below,
aa = A(100)
aa.m(10)
Actual,
deco1.get
deco1.call.before
deco2.call.before
A.m
deco2.call.after
deco1.call.after
Expected
deco1.get
deco1.call.before
deco2.get #deco2.__get__ to be called
deco2.call.before
A.m
deco2.call.after
deco1.call.after
I need to have separate decorators for a reason. With that in mind, how could I make this work ? Also, if someone can explain why deco2.get isn't called, that would be great ! Thanks.
Using Python 3.7.x
deco2.get was called [once] when the decorated m was created.
The decoration
#deco1
#deco2
def m
can be rewritten as
m = deco1(deco2(m))
so when deco1 argument was calculated deco2.get was called.

Working around decorator and getattr

I have problem solving this question, I have the following class:
class test:
#auth
def method1(self, x):
return x
#auth
def method2(self, x, y):
return x+y
def method3(self, z):
return z
I applied the decorator in both methods, follow:
class auth:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
self.f(*args, **kwargs)
So far no problem, however I need (NEED) to use the following code:
def run():
klass = globals()["test"]()
method1 = getattr(klass, "method1")
print(method1.__code__.co_varnames)
# should print (self, x)
method2 = getattr(klass, "method2")
print(method2.__code__.co_varnames)
# should print (self, x, y)
method3 = getattr(klass, "method3")
print(method3.__code__.co_varnames)
# i get (self, z) < without decorator
But I get now:
AttributeError: 'auth' object has no attribute '__code__'
What makes sense if we think that the signature of method "method1 and method2" is now "auth".
So how do I get the arguments with or without decorators.
I started reading about the "inspect" but there are many reports about being slow.
The "original" method is stored in the f attribute of the auth object. Instead of method1.__code__.co_varnames use method1.f.__code__.co_varnames
Annotations just contain an object and are not the object itsself, it is an object of class auth and not function. To access the function itsself, you can write methodN.f.__code__.co_varnames or assign a copy of the __dict__ object of the function to the auth-object in __init__ itsself.
class auth:
def __init__(self, f):
self.__dict__.update(f.__dict__)
# now the initialisations
self.f = f
def __call__(self, *args, **kwargs):
self.f(*args, **kwargs)
Edit:
You should initialize the members/call super after updating the dict, because f could be overriden by the update, eg. you define another decorator-class and it has also a member f.

Chaining decorators that are defined as classes?

For the sake of learning, I'm trying to chain decorators that are defined by classes. I read this question about decorators, which has a lot of good information about chaining them with functions. They also link to the documentation, but I'm trying to figure out simpler examples.
Basically, I'm trying to mimic similar behaviour using classes. Here is my first decorator definition, which works perfectly.
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5)) #Outputs 49, as expected
Then, I add another decorator to create this code snippet:
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
class append_abc(object):
def __init__(self, f):
pass
def __call__(self, *args):
return str(*args) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))
#Ideally, this should print "49abc" but prints "(2,5)abc" instead
what is the proper way of doing this? I guess what I want to do is create a decorator in the form of a class that takes the output of the function it decorates (in this case square_result) and appends "abc" to it.
I know that when I add a decorator to my code, the add_two_numbers() function is compiled and that function object is passed to the square_result class, which does something to create a function-like object which is substituted for the original add_two_numbers(). However, I'm not sure how to chain this.
This does what you want:
class square_result(object):
def __init__(self, f):
pass
def __call__(self, x, y):
return (x+y)**2
class append_abc(object):
def __init__(self, f):
self.f = f
def __call__(self, *args):
return str(self.f(*args)) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))
You need to actually run the inner function in the decorator if you want to use its output in the result of the decorator.
I didn't edit your first decorator, as it does what you want, but it actually isn't useful as a decorator. Since its output is not related in any way to the function it's decorating, it's just replacing the function. If you wanted to replace the function with that class, the way to do it would be
class square_result(object):
def __call__(self, x, y):
return (x+y)**2
# this has no effect at all after the reassignment
def add_two_numbers(x, y):
return x + y
add_two_numbers = square_result()
PEP8 also suggests CamelCase for your class names (SquareResult).
square_result doesn't "decorate" the add_two_numbers result, but overrides it (doing the addition as well as the squaring). It should instead treat the decorated function the same way that append_abc does, by storing it and then making use of the decorated function in its own implementation. Thus:
class square_result(object):
def __init__(self, f):
self.function = f
def __call__(self, *args):
return self.function(*args)**2
class append_abc(object):
def __init__(self, f):
self.function = f
def __call__(self, *args):
return str(self.function(*args)) + "abc"
#append_abc
#square_result
def add_two_numbers(x, y):
return x + y
print(add_two_numbers(2,5))

Categories

Resources