Hi I would like to mock my decorator since I don't want to be actually calling/executing this function. But I can't seem to find the solution for this below are my code
# This is the decorator located in my project
# This is located in custom.mydecorators.decorator_file.custom_decorator
Base = declarative_base()
def custom_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
print("This message is still printed even when I try to patch this function")
try:
#code here
my_var = CoolClass()
retval = func(*args, **kwargs)
except Exception as e:
#rollback code here
raise e
return retval
return wrapper
Now I'm trying to patch this using this code
patch('custom.mydecorators.decorator_file.custom_decorator', lambda x: x).start()
class TestMockDecoratorsCallingClass(unittest.TestCase):
def test_should_return_success_if_decorators_are_mocked(self):
# Code here
My decorators work properly in a non unittest file. But if I mock this decorator it fails saying that the local variable 'my_var' referenced before assignment
Note: my_var is inside the decorator I'm trying to mock/patch also the print message is still executed even when I try to patch it
Related
I have a class that handles the API calls to a server. Certain methods within the class require the user to be logged in. Since it is possible for the session to run out, I need some functionality that re-logins the user once the session timed out. My idea was to use a decorator. If I try it like this
class Outer_Class():
class login_required():
def __init__(self, decorated_func):
self.decorated_func = decorated_func
def __call__(self, *args, **kwargs):
try:
response = self.decorated_func(*args, **kwargs)
except:
print('Session probably timed out. Logging in again ...')
args[0]._login()
response = self.decorated_func(*args, **kwargs)
return response
def __init__(self):
self.logged_in = False
self.url = 'something'
self._login()
def _login(self):
print(f'Logging in on {self.url}!')
self.logged_in = True
#this method requires the user to be logged in
#login_required
def do_something(self, param_1):
print('Doing something important with param_1')
if (): #..this fails
raise Exception()
I get an error. AttributeError: 'str' object has no attribute '_login'
Why do I not get a reference to the Outer_Class-instance handed over via *args? Is there another way to get a reference to the instance?
Found this answer How to get instance given a method of the instance? , but the decorated_function doesn't seem to have a reference to it's own instance.
It works fine, when Im using a decorator function outside of the class. This solves the problem, but I like to know, if it is possible to solve the this way.
The problem is that the magic of passing the object as the first hidden parameter only works for a non static method. As your decorator returns a custom callable object which is not a function, it never receives the calling object which is just lost in the call. So when you try to call the decorated function, you only pass it param_1 in the position of self. You get a first exception do_something() missing 1 required positional argument: 'param_1', fall into the except block and get your error.
You can still tie the decorator to the class, but it must be a function to have self magic work:
class Outer_Class():
def login_required(decorated_func):
def inner(self, *args, **kwargs):
print("decorated called")
try:
response = decorated_func(self, *args, **kwargs)
except:
print('Session probably timed out. Logging in again ...')
self._login()
response = decorated_func(self, *args, **kwargs)
return response
return inner
...
#this method requires the user to be logged in
#login_required
def do_something(self, param_1):
print('Doing something important with param_1', param_1)
if (False): #..this fails
raise Exception()
You can then successfully do:
>>> a = Outer_Class()
Logging in on something!
>>> a.do_something("foo")
decorated called
Doing something important with param_1
You have the command of
args[0]._login()
in the except. Since args[0] is a string and it doesn't have a _login method, you get the error message mentioned in the question.
Context:
I'm writing a personal python module to simplify some scripts I have lying around. One of the functions I have is untested and may have undesirable edge cases that I still have to consider. In order to not allow myself from relying on it from other modules or functions, I was wondering whether I could enforce it to raise an error if not called directly from the REPL.
I'm not asking whether this is a good idea or not. It obviously isn't because it defeats the purpose of writing a function in the first place. I'm wondering if is is possible in Python, and how to do it.
Question:
Is it possible to have a function raise an error if not called interactively? For example:
def is_called_from_top_level():
"How to implement this?"
pass
def shady_func():
"Only for testing at the REPL. Calling from elsewhere will raise."
if not is_called_from_top_level():
raise NotImplementedError("Shady function can only be called directly.")
return True
def other_func():
"Has an indirect call to shady."
return shady_func()
And then at a REPL:
[In:1] shady_func()
[Out:1] True
[In:2] other_func()
[Out:2] NotImplementedError: "Shady function can only be called directly."
Try checking for ps1 on sys.
import sys
def dangerous_util_func(a, b):
is_interactive = bool(getattr(sys, 'ps1', False))
print(is_interactive) # Prints True or False
return a + b
You can even get fancy and make a decorator for this to make it more reusable.
import sys
from functools import wraps
def repl_only(func):
#wraps(func)
def wrapped(*args, **kwargs):
is_interactive = bool(getattr(sys, 'ps1', False))
if not is_interactive:
raise NotImplementedError("Can only be called from REPL")
return func(*args, **kwargs)
return wrapped
#repl_only
def dangerous_util_func(a, b):
return a + b
DISCLAIMER: This is a bit of a hack, and may not work across different Python / IPython / Jupyter versions, but the underlying idea still holds, i.e. use inspect to get an idea of who is calling.
The code below was tested with Python 3.7.3, IPython 7.6.1 and Jupyter Notebook Server 5.7.8.
Using inspect (obviously), one can look for distinctive features of the REPL frame:
inside a Jupyter Notebook you can check if the repr() of the previous frame contain the string 'code <module>';
using Python / IPython you can check for the code representation of the previous frame to start at line 1.
In code, this would look like:
import inspect
def is_called_from_top_level():
"How to implement this?"
pass
def shady_func():
"Only for testing at the REPL. Calling from elsewhere will raise."
frame = inspect.currentframe()
is_interactive = (
'code <module>' in repr(frame.f_back) # Jupyter
or 'line 1>' in repr(frame.f_back.f_code)) # Python / IPython
if not is_interactive:
raise NotImplementedError("Shady function can only be called directly.")
return True
def other_func():
"Has an indirect call to shady."
return shady_func()
shady_func()
# True
other_func()
# raises NotImplementedError
(EDITED to include support for both Jupyter Notebook and Python / IPython).
As suggested by #bananafish, this is actually a good use case for a decorator:
import inspect
import functools
def repl_only(func):
#functools.wraps(func)
def wrapped(*args, **kwargs):
frame = inspect.currentframe()
is_interactive = (
'code <module>' in repr(frame.f_back) # Jupyter
or 'line 1>' in repr(frame.f_back.f_code)) # Python / IPython
if not is_interactive:
raise NotImplementedError('Can only be called from REPL')
return func(*args, **kwargs)
return wrapped
#repl_only
def foo():
return True
def bar():
return foo()
print(foo())
# True
print(bar())
# raises NotImplementedError
You can do something like that:
import inspect
def other():
shady()
def shady():
curfrm = inspect.currentframe()
calframe = inspect.getouterframes(curfrm, 2)
caller = calframe[1][3]
if not '<module>' in caller::
raise Exception("Not an acceptable caller")
print("that's fine")
if __name__ == '__main__':
import sys
args = sys.argv[1:]
shady()
other()
The module inspect allows you to get information such as the function's caller. You may have to dig a bit deeper if you have edge cases....
Inspired by the comment to the OP suggesting looking at the stack trace, #norok2 's solution based on direct caller inspection, and by #bananafish 's use of the decorator, I came up with an alternative solution that does not require inspect nor sys.
The idea is to throw and catch to get a handle on a traceback object (essentially our stack trace), and then do the direct caller inspection.
from functools import wraps
def repl_only(func):
#wraps(func)
def wrapped(*args, **kwargs):
try:
raise Exception
except Exception as e:
if "module" not in str(e.__traceback__.tb_frame.f_back)[-10:]:
raise NotImplementedError(f"{func.__name__} has to be called from the REPL!")
return func(*args, **kwargs)
return wrapped
#repl_only
def dangerous_util_func(a, b):
return a + b
def foo():
return dangerous_util_func(1, 2)
Here dangerous_util_func will run and foo will throw.
I have some blocks of code which need to be wrapped by function.
try:
if config.DEVELOPMENT == True:
# do_some_stuff
except:
logger.info("Config is not set for development")
Then I'll do again:
try:
if config.DEVELOPMENT == True:
# do_some_another_stuff
except:
logger.info("Config is not set for development")
So, how can I wrap this "do_some_stuff" and "do_some_another_stuff"?
I'm trying to write function with contextmanager:
#contextmanager
def try_dev_config(name):
try:
if name is not None:
yield
except Exception as e:
print "not dev config"
with try_dev_config("config.DEVELOPMENT"):
# do_some_stuff
And I got an error:
RuntimeError: generator didn't yield
You could pass in a function.
boolean = True
def pass_this_in():
print("I just did some stuff")
def the_try_except_bit(function):
try:
if boolean:
function()
except:
print("Excepted")
# Calling the above code
the_try_except_bit(pass_this_in)
If you want to reduce the "pass_this_in" definition bit, then you can use lambda function definitions:
pass_this_in = lambda : print("I just did some stuff")
I am not sure that a context manager is the good method to achieve what you want. The context manager goal is to provide a mecanism to open/instantiate a resource, give access to it (or not) and close/clean it automatically when you no more need it.
IMHO, what you need is a decorator.
A decorator aims at executing code around a function call. It would force you to put each block of code in a function but I don't think it is so difficult. You can implement it like this:
class Config(object):
"""for demonstration purpose only: used to have a config.DEVELOPMENT value"""
DEVELOPMENT = True
class Logger(object):
"""for demonstration purpose only: used to have a logger.info method"""
#staticmethod
def info(msg):
print("Logged: {}".format(msg))
def check_dev_config(config, logger):
def dev_config_checker(func):
def wrapper(*args, **kwargs):
try:
if config.DEVELOPMENT:
func(*args, **kwargs)
except Exception as err:
logger.info(
"Config is not set for developpement: {}".format(err))
return wrapper
return dev_config_checker
#check_dev_config(Config, Logger)
def do_stuff_1():
print("stuff 1 done")
#check_dev_config(Config, Logger)
def do_stuff_2():
raise Exception("stuff 2 failed")
do_stuff_1()
do_stuff_2()
This code prints
stuff 1 done
Logged: Config is not set for developpement: stuff 2 failed
Explanations:
The check_dev_config function is actually a decorator generator which accepts the config and the logger as arguments.
It returns the dev_config_checker function which is an actual (and parameterised) decorator, and which accepts a function to decorate as argument.
This decorator returns a wrapper function which will actually run code around the decorated function call. In this function, the decorated function is called inside a try/except structure and only if the config.DEVELOPMENT is evaluated to True. In case of exception, the logger is used to log an information.
Each block of code to decorate is put into a function (do_stuff_1, do_stuff_2 and decorated with the check_dev_config decorator generator, giving it the config and the logger.
When decorated functions are called, they are called via their decorator and not directly. As you can see, the do_stuff_2 exception has been catched and the a message has been logged.
I am using pytest to automate project test. I want to take some unique actions like "save_snapshot()" only when a test case fails.
Do we have something like that in pytest?
I have tried to achieve this using the teardown_method() But this method is not getting executed when a test case fails.
without using fixtures please.
I found a solution for this issue by using python decorator for each test in class:
def is_failed_decorator(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except AssertionError:
cls_obj = args[0]
cur_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
func_name = func.__name__
# Save_snapshot().
raise
return wrapper
# Tests class
#is_failed_decorator
def test_fail(self):
assert False
worked for me :D
I want to use a decorator to handle auditing of various functions (mainly Django view functions, but not exclusively). In order to do this I would like to be able to audit the function post-execution - i.e. the function runs as normal, and if it returns without an exception, then the decorator logs the fact.
Something like:
#audit_action(action='did something')
def do_something(*args, **kwargs):
if args[0] == 'foo':
return 'bar'
else:
return 'baz'
Where audit_action would only run after the function has completed.
Decorators usually return a wrapper function; just put your logic in the wrapper function after invoking the wrapped function.
def audit_action(action):
def decorator_func(func):
def wrapper_func(*args, **kwargs):
# Invoke the wrapped function first
retval = func(*args, **kwargs)
# Now do something here with retval and/or action
print('In wrapper_func, handling action {!r} after wrapped function returned {!r}'.format(action, retval))
return retval
return wrapper_func
return decorator_func
So audit_action(action='did something') is a decorator factory that returns a scoped decorator_func, which is used to decorate your do_something (do_something = decorator_func(do_something)).
After decorating, your do_something reference has been replaced by wrapper_func. Calling wrapper_func() causes the original do_something() to be called, and then your code in the wrapper func can do things.
The above code, combined with your example function, gives the following output:
>>> do_something('foo')
In wrapper_func, handling action 'did something' after wrapped function returned 'bar'
'bar'
Your decorator can handle it here itself, like
def audit_action(function_to_decorate):
def wrapper(*args, **kw):
# Calling your function
output = function_to_decorate(*args, **kw)
# Below this line you can do post processing
print "In Post Processing...."
return output
return wrapper