Adding a decorator to an abstract method - python

I'm trying to add a decorator on an abstractmethod such that when the method is called in the subclasses, the decorator function is also called. This is for a framework, so I'm trying to limit the amount of extra code later users have to code. The decorator is pretty specific to the framework and isn't meant to be used by the general users of the framework - hope that makes sense.
I know there are a few other ways of doing this, but most of them involve the user copying some boilerplate code when they create their own subclasses. The decorator helps prevent having to copy the boilerplate.
from abc import abstractmethod, ABC
def prepost(fn):
# The new function the decorator returns
def wrapper(*args, **kwargs):
print("In wrap 1")
fn(*args, **kwargs)
print("In wrap 2")
return
wrapper.__isabstractmethod__ = True
return wrapper
class Base(ABC):
pass
class Foo(Base):
#prepost
#abstractmethod
def dosomething(self):
raise NotImplementedError
class Bar(Foo):
def dosomething(self):
print("I'm doing something")
Test = Bar()
Test.dosomething()
When I try this I just get:
I'm doing something
rather than the extra output from the decorator.

Related

Manipulating function docstring and signature in base class

I have an abstract base class Base that provides an abstract method _run() that needs to be implemented by derived classes, as well as a method run() that will call _run() and do some extra work that is common to all derived classes.
In all derived classes, I am setting the function docstring for the _run() method. As this function is not part of the public API, I want the same docstring (and function signature) to instead show up for the run() method.
Consider the following example:
import inspect
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def _run(self):
return
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""new_doc"""
return
My initial idea was to manipulate the docstring in Base.__init__ or Base.__new__. This works to some extent, but presents a number of problems:
I want to be able to override these two methods (at the very least __init__) in derived classes.
This requires the class to be instantiated before the docstring is available.
By setting the docstring for Base.run when instantiating the derived class, it would in fact set the docstring for all derived classes.
class Base(ABC):
def __init__(self):
type(self).run.__doc__ = type(self)._run.__doc__
type(self).run.__signature__ = inspect.signature(type(self)._run)
...
What I am hoping for:
>>> Derived.run.__doc__
'new_doc'
What I get so far:
>>> Derived.run.__doc__
'old_doc'
>>> Derived().run.__doc__
'new_doc'
Are there any solutions to this?
Don't modify the docstring of Base.run; instead, document what it does: it invokes a subclass-defined method.
class Base(ABC):
#abstractmethod
def _run(self):
"Must be replaced with actual code"
return
def run(self, *args, **kwargs):
"""Does some setup and runs self._run"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""Does some work"""
return
There is no need to generate a new docstring for Derived.run, because Derived.run and Base.run evaluate to the exact same object: the run method defined by Base. Inheritance doesn't change what Base.run does just because it is invoked from an instance of Derived rather than an instance of Base.
The best workaround I have come up with is to create a decorator instead:
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
def extra_work(func):
# Do some extra work and modify func.__doc__
...
return func
class Derived(Base):
#extra_work
def run(self):
"""new_doc"""
return
This way the extra work can still be defined outside the derived class to avoid duplicating it in every class derived from Base, and I am able to automatically update the docstring to reflect the added functionality.

How to externalize the decorator in python flask application

I have written a python Flask application, which has a class and methods as below.
class PythonSample:
def method1():
pass # does something
def method2():
pass # does something
Now I have written another class which has decorator functions as below.
class PythonAuth:
def oauthAuth():
pass
Now I'm wiring oauthAuth decorator for all the methods of PythonSample class as below
import oauthAuth from PythonAuth
class PythonSample
#oauthAuth
def method1():
pass # does something
#oauthAuth
def method2():
pass # does something
Applying decorator at each method works fine.
Question: Instead of applying oauthAuth decorator to each of the methods. Is there a way to configure in python, as apply oauthAuth decorator to all the methods in a class and exclude certain methods.
Something like include auth for certain URLs and exclude authentication for certain urls
Please ignore the syntax of the python code here.
You can use a class decorator plus some magic.
Decorating Functions
Assume you have a decorator that just logs a string before calling the function.
def log(func):
def logged_func(*args, **kwargs):
print('logged')
func(*args, **kwargs)
return logged_func
Decorating classes
You can use the same trick, but with a class. log_all is a class decorator, cls is a class type. We use vars to walk the class dictionary, and look for methods by using callable(v). Decorate the method with log(v) and use setattr to change the cls definition to the new decorated method. Just like function decorators, return the class in the end.
def log_all(cls):
for k, v in vars(cls).items():
if callable(v):
setattr(cls, k, log(v))
return cls
I am ignoring k essentially, but k is the method name, you could leverage it to achieve your usage scenario.
Full code
Here is a full example, that should make some sense now.
def log(func):
def logged_func(*args, **kwargs):
print('logged')
func(*args, **kwargs)
return logged_func
def log_all(cls):
for k, v in vars(cls).items():
if callable(v):
setattr(cls, k, log(v))
return cls
#log_all
class A:
def method(self):
pass
Every method in class A should be decorated with the log decorator.
>>> a = A()
>>> a.method()
logged

decorator inside class Python

Sorry for my english. I want to create a decorator method that can check each step methods and write it db.
This is my method:
class Test:
#StepStatusManager.logger_steps("GET_LIST") # TypeError: logger_steps() missing 1 required positional argument: 'type'
def get_mails(self):
print("GET_MAIL")
This is my decorator class:
class StepStatusManager:
def __init__(self):
self.db = DB()
def logger_steps(self, type):
def logger_steps(func):
#functools.wraps(func)
def wrapper(*args):
try:
func(*args)
self.db.setStatus(type)
except BaseException as e:
print(e)
return wrapper
return logger_steps
You are trying to call the instance method, logger_steps, directly from the class StepStatusManager, and Python is taking the value "GET_LIST" as the self parameter instead of type. You should create an instance of StepStatusManager and then make the decorator calling the method of the instance instead. It can be as simple as:
manager = StepStatusManager()
class Test:
#manager.logger_steps("GET_LIST")
def get_mails(self):
print("GET_MAIL")
This is now creating an instance of the class and then calling the method on the instance, instead of trying to call the method directly from the class. You can now use manager to decorate as many methods as you want. Also, this would make all decorated methods use the same StepStatusManager, but if you want you can create different instances and use them to decorate different methods; that would allow you to use different self.db for different methods, if you need it.
Another approach could be having the db variable in the class, and make logger_steps a class method instead:
class StepStatusManager:
db = DB()
#classmethod
def logger_steps(cls, type):
def logger_steps(func):
#functools.wraps(func)
def wrapper(*args):
try:
func(*args)
cls.db.setStatus(type)
except BaseException as e:
print(e)
return wrapper
return logger_steps
class Test:
#StepStatusManager.logger_steps("GET_LIST")
def get_mails(self):
print("GET_MAIL")
Note however that this is less flexible, in that it will not allow you to have methods decorated with different managers, should you ever need to. Also, this is mostly equivalent to have, instead of a class, a StepStatusManager module, where db is a module variable and logger_steps is a module function, and that would probably clearer if you want this functionality:
# StepStatusManager.py
# ...
db = DB()
def logger_steps(type):
def logger_steps(func):
#functools.wraps(func)
def wrapper(*args):
try:
func(*args)
cls.db.setStatus(type)
except BaseException as e:
print(e)
return wrapper
return logger_steps
# test.py
import StepStatusManager
class Test:
#StepStatusManager.logger_steps("GET_LIST")
def get_mails(self):
print("GET_MAIL")
Again this is maybe more straightforward but less flexible as your first proposed class-based solution.
EDIT:
Just for completeness and comparison, here is yet another version, similar to the one with #classmethod, but using #staticmethod instead (to understand the subtle difference between these two decorators, check one of the many SO questions about it, e.g. What is the difference between #staticmethod and #classmethod? or Meaning of #classmethod and #staticmethod for beginner?):
class StepStatusManager:
db = DB()
#staticmethod
def logger_steps(type):
def logger_steps(func):
#functools.wraps(func)
def wrapper(*args):
try:
func(*args)
StepStatusManager.db.setStatus(type)
except BaseException as e:
print(e)
return wrapper
return logger_steps
class Test:
#StepStatusManager.logger_steps("GET_LIST")
def get_mails(self):
print("GET_MAIL")
As it frequently happens with #classmethod and #staticmethod, the difference is quite minimal. Their behavior might differ if you are using inheritance, or if you are using a metaclass, or a decorator, or something like that, but otherwise they pretty much the same.

Using decorator from base class both in base class and derived class

I have some python objects with some methods in which i would like to do some check at the beggining, depending of this check, the method's code would run, or an execption would be raised. Instead of replicating the "check" code at the beginning of every method I though of doing a decorator, I also want the decorator to be embedded inside the class itself, since it is closely related to it. So basically:
instead of this
class A(object):
def a_method(self):
if self.check_var is True:
(some_code)
else:
raise Exception
I would like to have this
class A(object):
def decorator(function):
def function_wrapper(self, *args, **kwargs):
if self.check_var is True:
return function(self, *args, **kwargs)
else:
raise Exception
return function_wrapper
#decorator
def a_method(self):
(some_code)
My first question is, am I going about this right? or is there a better way. I have many methods of the A class that need to have this check, so that is why I don't want to replicate the code unnecessarily.
My second question is, if I go about this the way I described, I run into a problem when I want to derive a class from class A and performe the same decorator checks. Again I don't want to replicate the code, so I want to reuse the decorator in the base class A to performe checks in the derived class. I read about turning the decorator into a #classmethod however when I do this I am able to use the decorator in the derived class but not in the base class anymore!
So basically I would like something like this:
class A(object):
#classmethod #maybe
def decorator(function):
def function_wrapper(self, *args, **kwargs):
if self.check_var is True:
return function(self, *args, **kwargs)
else:
raise Exception
return function_wrapper
#decorator
def a_method(self):
(some_code)
class B(A):
#decorator
def b_method(self):
(some_code)
Does anybody know of any clean way to do this?
Since you would prefer to put the decorator inside the class (rather than outside both of them as I suggested in a comment), below shows a way to do it. It makes the decorator a staticmethod instead of a classmethod, and requires using it in a slightly unusual manner, but only within the class.
For more information regarding the necessity of using the decorator like this, see my question Calling class staticmethod within the class body?
class A(object):
#staticmethod
def decorator(function):
def function_wrapper(*args, **kwargs):
print('in function_wrapper')
return function(*args, **kwargs)
return function_wrapper
#decorator.__func__ #### Note unusual decorator usage inside defining class
def a_method(self):
print('in a_method')
class B(A):
#A.decorator #### Normal decorator usage outside defining class
def b_method(self):
print('in b_method')
One way to avoid having to use __func__ and still keep the definition in the first class would be to postpone turning it into a staticmethod until the very end of the class definition:
class A(object):
def decorator(function):
def function_wrapper(*args, **kwargs):
print('in function_wrapper')
return function(*args, **kwargs)
return function_wrapper
#decorator
def a_method(self):
print('in a_method')
decorator = staticmethod(decorator) #### convert for use outside this class
class B(A):
#A.decorator
def b_method(self):
print('in b_method')
Yet another way to avoid the __func__ is something like this:
class A(object):
class Check:
#staticmethod
def decorator(function):
def function_wrapper(*args, **kwargs):
print('in function_wrapper')
return function(*args, **kwargs)
return function_wrapper
#Check.decorator
def a_method(self):
print('in a_method')
class B(A):
Check = A.Check
#Check.decorator
def b_method(self):
print('in b_method')
Which has the additional advantage of making usage of the decorator very uniform.
My first question is, am I going about this right?
As martineau said below, the good practice is put classic decorator outside class.
def get_decorator(function, argument):
def function_wrapper(*args, **kwargs):
if argument is True:
return function(*args, **kwargs)
else:
raise Exception
return function_wrapper
class A(object):
def __init__(self):
self.check_var = True
self.a_method = get_decorator(self.a_method, self.check_var)
def a_method(self):
(whatever)
class B(A):
def __init__(self):
super(B, self).__init__()
self.b_method = get_decorator(self.b_method, self.check_var)
def b_method(self):
(whatever)
Classic decorator is called during class creation time, which is long before an instance is created. Reference

How can I mock/patch a decorator in python?

WORKAROUND:
I was unable to patch the decorator and still have access to the wrapped function. but the workaround for me to test the function wrapped in a decorator was the following:
def un_some_method(self):
...
some_method = some_var.some_decorator('somestring')(un_some_method)
This still decorates my function, but gives me access to the function if I wish to test it, reuse it elsewhere...
This is the problem:
I have a module, which has a class in it, and variables that instantiate a class which expose a decorator.
Then within the class inside my module, I use this variable with the instance of my class and decorate a method in my class... To be clear, lets look at some code:
some_var = ClassX()
class SomeClass(object):
#some_var.some_decorator('somestring')
def some_method(self):
...
in my test I want to test the code within some_method and I don't care about the decorator... although my attempts to mock the decorator out was useless... below is some things I tried:
#patch('path_to_classx.ClassX.some_decorator')
#patch('path_to_someclassmodule.some_var')
#patch('path_to_someclassmodule.ClassX')
none of the attempts above prevented the function from being decorated... any idea why?
One option is to patch some_var after you have instantiated it, replacing some_decorator with an identity function so that the decorated method remains unchanged.
import mock
class ClassX(object):
def some_decorator(self, f):
def g(*args, **kwargs):
print("Hello")
return f(*args, **kwargs)
return g
some_var = ClassX()
with mock.patch.object(some_var, 'some_decorator', side_effect=lambda f: f):
class SomeClass(object):
#some_var.some_decorator
def some_method(self):
print "World"
SomeClass().some_method()

Categories