Stack execution (python methods) without decorators - python

I kindly ask for help in solving the following problem: accessing the stack of methods, with the "function name", "arguments" of the function (values) and the return, which has just been executed.
In short: get the chain of execution of methods, with name, arguments and return.
Is there any way to do this without putting decorators in each method?
I have already tried with the traceback module, but it does not work since it shows only the "back" string of the main method below.
I tried with the StackSummary / FrameSummary, but without success too = (
def func_a(param_a1):
print('Executing function A with: {0}'.format(param_a1))
func_b("a", "b")
return "ok"
def func_b(param_b1, param_b2):
print('Executing function B with: {0}, {1}'.format(param_b1, param_b2))
return None
def func_c():
print('Executing function C without args')
return None
if __name__ == '__main__':
# Call function_a()
func_a("testing params")
# Get here???
# call func_a("testing params")
# -> call func_b("a", "b")
# -> call func_c()
# returning None
# returning None
# returning "ok"
Thank you for your attention

Related

Why does a decorator needs a wrapper [duplicate]

If I create a decorator like following:
def my_decorator(some_fun):
def wrapper():
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
return wrapper
#my_decorator
def just_some_function():
print("Wheee!")
Another decorator can be defined as:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
#my_decorator
def just_some_fun():
print("some fun")
Both decorators will work the same. What is the benefit of using "wrapper" function inside decorator. I didn't get the purpose.
The purpose of having a wrapper function is that a function decorator receives a function object to decorate, and it must return the decorated function.
Your 2nd version of my_decorator doesn't have an explicit return statement, so it returns None. When my_decorator is called via the # decorator syntax
before some_function() is called.
some fun
after some_function() is called.
gets printed, and then None gets assigned to the name just_some_fun. So if you add print(just_some_fun) to the end of that code it will print None.
It may be easier to understand what's going on if we get rid of the # syntactic sugar and re-write your code using normal function calling syntax:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
def just_some_fun():
print("some fun")
just_some_fun = my_decorator(just_some_fun)
The decorators in Python are callable objects which in the simplest case is function taking one parameter which is some function or class. The decorator should return again same type which it takes (so if it takes function it should return function). The point is in the time when the decorator is called.
When you import a Python file or just run it directly the Python interpreter goes through the content and gathering information about what classes and function are defined and if it encounter on some code (not declaration) it will execute it.
If the interpreter encounter on decorator it takes the decorated function, call the decorator and replace the decorated function with returned value from the decorator.
Let's say you have this code:
#my_decorator
def my_function()
print("My_function")
It's equivalent to this call:
def my_function()
print("My_function")
my_function = my_decorator(my_function)
If the decorator would be like this one
def my_decorator(func):
print("decorated)
return 42
then the my_function is not even a function it would be an integer (you can try print(my_function))
So when you define decorator as
def my_decorator2(some_fun):
print("before")
some_fun()
print("after")
then this decorator returns nothing (in python it means it returns None).
#my_decorator2
def decorated():
print("inside")
prints
before
inside
after
but calling decorated() will raise exception 'NoneType' object is not callable because the decorated was replaced with None.
You should always create decorator which return something useful like function or class (which is usually the "wrap" function inside). Sometimes can be useful to return from decorator something else then function/class but it usually obfuscate your code and convert it into something totally non-maintainable.
Its already explained why to use wrapper function, out of curiosity I am just giving
examples what we can do if we don't need a wrapper function.
Type 1
Return a small function which returns None or pass
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
def a(): None # or def a(): pass
return (a)
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function()
# equivalent to
#to_be_decorated_function = decorator_func(to_be_decorated_function)
Type 2
This merely tears out the usage of decorators, and just a slight tweak.What if we don't return , as well not use callable object at all.
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)
Output from both the types
Logging IN: Currrently in function
October 16, 2000
Logging OUT: Currrently in function

When is a function decorated, and when isn't a function decorated?

I have a decorator that records the functions present in my script:
registry=[]
def register(func):
print('running register(%s)' % func)
registry.append(func)
return func
I then have a series of decorated functions:
#register
def func1():
print('running f1')
#register
def func2():
print('running f2')
This works, after running the script, print(registry) returns:
[<function func1 at 0x0000000008433950>, <function func2 at 0x0000000008F06AE8>]
However calling the functions individually, for example:
func1()
Returns only 'running f1': just the function, without the decoration.
I was expecting it to return something like 'running register( func1) \n running func1'.
So my question is, when you have a decorated function and call it; when will it call the function in isolation and when will it call the decorated function?
Thanks very much.
Your register (decorator) function is only run once when the code is interpreted.
If you want to alter the function behaviour, try something like:
def register(func):
registry.append(func)
print('adding register(%s)' % func)
def wrap(*args, **kwargs):
print('running register(%s)' % func)
return func(*args, **kwargs)
return wrap
The first print is done once, the second one before each call.
Adding the arguments will make your decorator more transparent.
What we call a "decorator" is just a higher order function, and the #decorator syntax nothing more than syntactic sugar so this:
#decorate
def func():
pass
is strictly equivalent to
def func():
pass
func = decorate(func)
As mentionned by Guillaume Deslandes, if this code is at your module or script's top-level, the decorator is only invoked when the module or script is first loaded by the runtime.
In your case, the decorator function register returns it's argument (the function it's applied to) unchanged, so calling the "decorated" function will work exactly as if it never had been decorated.
If you want to modify the decorated function in any way (by executing code before and or after the original function or whatever), you have to return a new function that will "replace" the original (but - usually - keeping a reference to the original function so this new "wrapper" function can still call the original), which is usually done using the fact that Python functions are closure:
def decorate(fun):
def wrapper(*args, **kw):
res = fun(*args, **kw)
print("function {} called with *{}, *{} returned {}".format(fun, args, kw, res)
return res
return wrapper
#decorate
def fun(a):
return a * 2

Why do we need wrapper function in decorators?

If I create a decorator like following:
def my_decorator(some_fun):
def wrapper():
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
return wrapper
#my_decorator
def just_some_function():
print("Wheee!")
Another decorator can be defined as:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
#my_decorator
def just_some_fun():
print("some fun")
Both decorators will work the same. What is the benefit of using "wrapper" function inside decorator. I didn't get the purpose.
The purpose of having a wrapper function is that a function decorator receives a function object to decorate, and it must return the decorated function.
Your 2nd version of my_decorator doesn't have an explicit return statement, so it returns None. When my_decorator is called via the # decorator syntax
before some_function() is called.
some fun
after some_function() is called.
gets printed, and then None gets assigned to the name just_some_fun. So if you add print(just_some_fun) to the end of that code it will print None.
It may be easier to understand what's going on if we get rid of the # syntactic sugar and re-write your code using normal function calling syntax:
def my_decorator(some_fun):
print("before some_function() is called.")
some_fun()
print("after some_function() is called.")
def just_some_fun():
print("some fun")
just_some_fun = my_decorator(just_some_fun)
The decorators in Python are callable objects which in the simplest case is function taking one parameter which is some function or class. The decorator should return again same type which it takes (so if it takes function it should return function). The point is in the time when the decorator is called.
When you import a Python file or just run it directly the Python interpreter goes through the content and gathering information about what classes and function are defined and if it encounter on some code (not declaration) it will execute it.
If the interpreter encounter on decorator it takes the decorated function, call the decorator and replace the decorated function with returned value from the decorator.
Let's say you have this code:
#my_decorator
def my_function()
print("My_function")
It's equivalent to this call:
def my_function()
print("My_function")
my_function = my_decorator(my_function)
If the decorator would be like this one
def my_decorator(func):
print("decorated)
return 42
then the my_function is not even a function it would be an integer (you can try print(my_function))
So when you define decorator as
def my_decorator2(some_fun):
print("before")
some_fun()
print("after")
then this decorator returns nothing (in python it means it returns None).
#my_decorator2
def decorated():
print("inside")
prints
before
inside
after
but calling decorated() will raise exception 'NoneType' object is not callable because the decorated was replaced with None.
You should always create decorator which return something useful like function or class (which is usually the "wrap" function inside). Sometimes can be useful to return from decorator something else then function/class but it usually obfuscate your code and convert it into something totally non-maintainable.
Its already explained why to use wrapper function, out of curiosity I am just giving
examples what we can do if we don't need a wrapper function.
Type 1
Return a small function which returns None or pass
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
def a(): None # or def a(): pass
return (a)
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function()
# equivalent to
#to_be_decorated_function = decorator_func(to_be_decorated_function)
Type 2
This merely tears out the usage of decorators, and just a slight tweak.What if we don't return , as well not use callable object at all.
def decorator_func(to_be_decorated_function):
print("Logging IN: Currrently in function")
to_be_decorated_function()
print("Logging OUT: Currrently in function")
#decorator_func
def to_be_decorated_function():
print('October 16, 2000')
to_be_decorated_function # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)
Output from both the types
Logging IN: Currrently in function
October 16, 2000
Logging OUT: Currrently in function

Call function or function (python)

I have 4 functions. I want my code to perform the first one AND the second, third, or fourth. I also want at least one (any of them) no matter what unless they all fail.
My initial implementation was:
try:
function1(var)
except:
pass
try:
function2(var) or function3(var) or function4(var)
except:
pass
If function2 doesn't work, it doesn't go to function3, how might this be coded to account for that?
In case the success of failure of a function is determined, whether it raises an exception or not, you could write a helper method, that would try to call a list of functions until a successful one returns.
#!/usr/bin/env python
# coding: utf-8
import sys
def callany(*funs):
"""
Returns the return value of the first successfully called function
otherwise raises an error.
"""
for fun in funs:
try:
return fun()
except Exception as err:
print('call to %s failed' % (fun.__name__), file=sys.stderr)
raise RuntimeError('none of the functions could be called')
if __name__ == '__main__':
def a(): raise NotImplementedError('a')
def b(): raise NotImplementedError('b')
# def c(): raise NotImplementedError('c')
c = lambda: "OK"
x = callany(a, b, c)
print(x)
# call to a failed
# call to b failed
# OK
The toy implementation above could be improved by adding support for function arguments.
Runnable snippet: https://glot.io/snippets/ehqk3alcfv
If the functions indicate success by returning a boolean value, you can use them just as in an ordinary boolean expression.

Python Decorator Self-firing

I am fairly new to Python and have been learning about decorators. After messing around with Flask, I am trying to write some code that simulates their route handler/decorators, just to understand how decorators (with arguments) work.
In the code below, the route decorator seems to call itself once the script runs. My question is, how is it possible that app.route() gets called when i run this script, and what is really happening here? Notice i don't call my index() function anywhere directly.
# test.py
class Flask(object):
def __init__(self, name):
self.scriptname = name
def route(self, *rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
f(args[0])
return decorator
app = Flask(__name__)
#app.route("/", methods = ["GET","PUT"])
def index(rt):
print('route: ' + rt)
the above prints this in my terminal:
$ python test.py
['GET', 'PUT']
route: /
Any insight would be appreciated.
#app.route("/", methods = ["GET","PUT"]) is an executable statement: it calls the route() method of the app object. Since it's at module level, it will be executed when the script is imported.
Now, the result of calling app.route(...) is a function, and because you've used the # to mark it as a decorator, that function will wrap index. Note that the syntax is just a shortcut for this:
index = app.route(...)(index)
in other words, Python will call the function returned by app.route() with index as a parameter, and store the result as the new index function.
However, you're missing a level here. A normal decorator, without params, is written like this:
#foo
def bar()
pass
and when the module is imported, foo() is run and returns a function that wraps bar. But you're calling your route() function within the decorator call! So actually your function needs to return a decorator function that itself returns a function that wraps the original function... headscratching, to be sure.
Your route method should look more like this:
def route(self, *rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
def wrapped(index_args):
f(args[0])
return wrapped
return decorator
Basically... app.route(index, "/", ["GET", "PUT"]) is a function. And this is the function which is going to be called instead of index.
In your code, when you call index(), it calls app.route(index, "/", ["GET", "PUT"]). This starts by printing kargs['methods'], then creates the decorator function:
def decorator(f):
f(args[0])
This decorator will call the decorated function (f) with one argument, args[0], which here is "/". This prints route: /.
The best explanation of decorators I've found is here: How to make a chain of function decorators?
If you dont want the self-firing, you can define your decorator this way:
def route(*rargs, **kargs):
args = list(rargs)
if kargs:
print(kargs['methods'])
def decorator(f):
f(args[0])
return decorator
#app.route("/", methods = ["GET","PUT"])
def index(rt):
print('route: ' + rt)
However, the rt argument of index will never be used, because route always calls index with args[0] which is always \...

Categories

Resources