Python decorator function call depth, structured output - python

I am printing inputs and outputs of functions, but this results in hard to read logs, especially with too many nested function calls (not recursive).
Example:
Calling test1()
Calling test2()
Calling test3()
Calling test4()
'test4' returned None
'test3' returned None
'test2' returned None
'test1' returned None
I would like to see the output like this:
Calling test1()
Calling test2()
Calling test3()
Calling test4()
'test4' returned None
'test3' returned None
'test2' returned None
'test1' returned None
If I could keep a call count and transfer it to next calls by incrementing it each time, I could achieve this. Then I could even color the output based on the value of this count and generate easier to read logs.
Is there a way I could keep the depth of current call inside the iterator?
For reference, here is the decorator:
def trace(func):
"""Print the function signature and return value"""
#functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [repr(a) for a in args] # 1
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] # 2
signature = ", ".join(args_repr + kwargs_repr) # 3
print(start + f"Calling {func.__name__}({signature})" + end)
value = func(*args, **kwargs)
print(f"{func.__name__!r} returned {value!r}") # 4
return value
return wrapper_debug

Python knows your call depth, anytime.
You can also print the traceback of where your currently are, anytime.
No need to keep it in a separate variable.
So here you go:
import traceback
len(traceback.format_stack())
Up to you to make the indent and to fix any off-by one errors.

Related

How do I prevent returning None between desired output? [duplicate]

This question already has answers here:
Why is "None" printed after my function's output?
(7 answers)
Closed 7 months ago.
I am trying to return the function with arguments and the functions results in the format of the print statement. The code works except I am getting a "None" between each answer when a test is run. How do I prevent the None from printing?
def debug(func):
"""
:param func: function
"""
def inner_func(*args,**kwargs):
answer = func(*args,**kwargs)
return print(f"{func.__name__}{args} was called and returned {answer}")
return inner_func
And the test:
def add(a, b):
return a + b
#debug
def sub(a, b=100):
return a - b
print(add(3, 4))
print(sub(3))`
print(sub(3,4))
add(3, 4) was called and returned 7
None
sub(3,) was called and returned -97
None
sub(3, 4) was called and returned -1
None
Expected Output:
add(3, 4) was called and returned 7
sub(3) was called and returned -97
sub(3, 4) was called and returned -1
I think you meant to write your debug decorator like this:
(properly indented)
def debug(func):
"""
:param func: function
"""
def inner_func(*args,**kwargs):
answer = func(*args,**kwargs)
print(f"{func.__name__}{args} was called and returned {answer}")
return answer
return inner_func
You meant to return answer rather than the result of a print() which is always None.
Also, if you don't want to see the returned answer, then don't print it. Since the decorator calls print(), just call the functions:
add(3, 4)
sub(3)
sub(3,4)
Also, in particular, if you want this line:
sub(3) was called and returned -97
you can change the print line to this:
print(f"{func.__name__}({','.join([str(arg) for arg in args])}) was called and returned {answer}")

Pass arguments only when needed

As an example I have a function f and a variable a=1.
Sometimes f needs a and I want to call f(a=a) and other times f has no arguments. How can I deal with that?
When I try to call f(a=a) (expecting that it will silently ignore the case where a is not used in f), I get the following TypeError:
f got an unexpected keyword argument 'a'
Edit
I want to implement a function g such that, given:
def f1(a):
return a
def f2():
return 1
we have:
g(f1, a) == a
g(f2, a) == 1
It seems you want to inspect a function's arguments.
For that, use the inspect module:
import inspect
def g(somefunc, *params):
num_args = len(inspect.getargspec(somefunc).args)
return somefunc(*params[:num_args])
Testing, with your functions:
def f1(a):
return a
def f2():
return 1
>>> print(g(f1, 3))
3
>>> print(g(f2, 3))
1
use keyword dictionary in f:
def f(**kwargs):
if 'a' in kwargs:
print("a passed with value {}".format(kwargs['a']))
else:
print('a not passed')
f(a=12)
f()
prints:
a passed with value 12
a not passed
it makes argument testing/retrieving completely manual. You can do anything you want, and it can be generalized to several arguments.
This also forbids to pass arguments as positional like f(12) which is probably a good thing in your case.
>>> f(12)
Traceback (most recent call last):
TypeError: f() takes 0 positional arguments but 1 was given
The drawback is that the caller cannot rely on parameter names to know what to pass. Either create a docstring, or people will have to read the code/guess...
Now to apply that to your edit, which kind of changes the problem I must say. So f2 cannot be changed, in that cas the work must be done in the wrapper function:
def f1(a):
return a
def f2():
return 1
def g(f,arg):
try:
return f(a=arg)
except TypeError:
return f()
a=12
print(g(f1, a))
print(g(f2, a))
prints:
12
1
so "better ask forgiveness than permission": if we get a TypeError (raised when the parameter isn't known by the function), we just call the function without the parameter. The drawback if that if the first function returns a TypeError, we cannot know if it was the parameters or inside the function.
A workaround would be to inspect the error message and only call the function without parameters if the error message is about parameters:
def g(f,arg):
try:
return f(a=arg)
except TypeError as e:
if "unexpected keyword" in str(e):
return f()
else:
raise e # another TypeError, let it pass
Simply specify the default argument in the function def line...
def f(a=1):
return(a)
print(f()) #returns 1
print(f(a = 2)) #returns 2
For your next part (as you seemed to have switched your question with edits...
def g(f, a):
return(f(a))

How to apply class decorator at base of all decorators on methods

I am using this way of decorating all methods
import inspect
def decallmethods(decorator, prefix='test_'):
def dectheclass(cls):
for name, m in inspect.getmembers(cls, inspect.ismethod):
if name.startswith(prefix):
setattr(cls, name, decorator(m))
return cls
return dectheclass
#decallmethods(login_testuser)
class TestCase(object):
def setUp(self):
pass
def test_1(self):
print "test_1()"
def test_2(self):
print "test_2()"
This is working but it applies at the top , if i have other decorators.
I mean
Now the result is
#login_testuser
#other
def test_2(self):
print "test_2()"
But i want
#other
#login_testuser
def test_2(self):
print "test_2()"
This is most certainly a bad idea, but what you want to do can be done in some extent, and this is going to take a lot of time to explain. First off, rather than thinking of decorators as a syntax sugar, think of them as what they really are: a function (that is a closure) with a function that exist inside it. Now this is out of the way, supposed we have a function:
def operation(a, b):
print('doing operation')
return a + b
Simply it will do this
>>> hi = operation('hello', 'world')
doing operation
>>> print(hi)
helloworld
Now define a decorator that prints something before and after calling its inner function (equivalent to the other decorator that you want to decorator later):
def other(f):
def other_inner(*a, **kw):
print('other start')
result = f(*a, **kw)
print('other finish')
return result
return other_inner
With that, build a new function and decorator
#other
def o_operation(a, b):
print('doing operation')
return a + b
Remembering, this is basically equivalent to o_operation = other(operation)
Run this to ensure it works:
>>> r2 = o_operation('some', 'inner')
other start
doing operation
other finish
>>> print(r2)
someinner
Finally, the final decorator you want to call immediately before operation but not d_operation, but with your existing code it results in this:
def inject(f):
def injected(*a, **kw):
print('inject start')
result = f(*a, **kw)
print('inject finish')
return result
return injected
#inject
#other
def i_o_operation(a, b):
print('doing operation')
return a + b
Run the above:
>>> i_o_operation('hello', 'foo')
inject start
other start
doing operation
other finish
inject finish
'hellofoo'
As mentioned decorators are really closures and hence that's why it's possible to have items inside that are effectively instanced inside. You can reach them by going through the __closure__ attribute:
>>> i_o_operation.__closure__
(<cell at 0x7fc0eabd1fd8: function object at 0x7fc0eabce7d0>,)
>>> i_o_operation.__closure__[0].cell_contents
<function other_inner at 0x7fc0eabce7d0>
>>> print(i_o_operation.__closure__[0].cell_contents('a', 'b'))
other start
doing operation
other finish
ab
See how this effectively calls the function inside the injected closure directly, as if that got unwrapped. What if that closure can be replaced with the one that did the injection? For all of our protection, __closure__ and cell.cell_contents are read-only. What needs to be done is to construct completely new functions with the intended closures by making use of the FunctionType function constructor (found in the types module)
Back to the problem. Since what we have now is:
i_o_operation = inject(other(operation))
And what we want is
o_i_operation = other(inject(operation))
We effectively have to somehow strip the call to other from i_o_operation and somehow wrap it around with inject to produce o_i_operation. (Dragons follows after the break)
First, construct a function that effectively calls inject(operation) by taking the closure to level deep (so that f will contain just the original operation call) but mix it with the code produced by inject(f):
i_operation = FunctionType(
i_o_operation.__code__,
globals=globals(),
closure=i_o_operation.__closure__[0].cell_contents.__closure__,
)
Since i_o_operation is the result of inject(f) we can take that code to produce a new function. The globals is a formality that's required, and finally take the closure of the nested level, and the first part of the function is produced. Verify that the other is not called.
>>> i_operation('test', 'strip')
inject start
doing operation
inject finish
'teststrip'
Neat. However we still want the other to be wrapped outside of this to finally produce o_i_operation. We do need to somehow put this new function we produced in a closure, and a way to do this is to create a surrogate function that produce one
def closure(f):
def surrogate(*a, **kw):
return f(*a, **kw)
return surrogate
And simply use it to construct and extract our closure
o_i_operation = FunctionType(
i_o_operation.__closure__[0].cell_contents.__code__,
globals=globals(),
closure=closure(i_operation).__closure__,
)
Call this:
>>> o_i_operation('job', 'complete')
other start
inject start
doing operation
inject finish
other finish
'jobcomplete'
Looks like we finally got what we need. While this doesn't exactly answer your exact problem, this started down the right track but is already pretty hairy.
Now for the actual problem: a function that will ensure a decorator function be the most inner (final) callable before a given original, undecorated function - i.e. for a given target and a f(g(...(callable)), we want to emulate a result that gives f(g(...(target(callable)))). This is the code:
from types import FunctionType
def strip_decorators(f):
"""
Strip all decorators from f. Assumes each are functions with a
closure with a first cell being the target function.
"""
# list of not the actual decorator, but the returned functions
decorators = []
while f.__closure__:
# Assume first item is the target method
decorators.append(f)
f = f.__closure__[0].cell_contents
return decorators, f
def inject_decorator(decorator, f):
"""
Inject a decorator to the most inner function within the stack of
closures in `f`.
"""
def closure(f):
def surrogate(*a, **kw):
return f(*a, **kw)
return surrogate
decorators, target_f = strip_decorators(f)
result = decorator(target_f)
while decorators:
# pop out the last one in
decorator = decorators.pop()
result = FunctionType(
decorator.__code__,
globals=globals(),
closure=closure(result).__closure__,
)
return result
To test this, we use a typical example use-case - html tags.
def italics(f):
def i(s):
return '<i>' + f(s) + '</i>'
return i
def bold(f):
def b(s):
return '<b>' + f(s) + '</b>'
return b
def underline(f):
def u(s):
return '<u>' + f(s) + '</u>'
return u
#italics
#bold
def hi(s):
return s
Running the test.
>>> hi('hello')
'<i><b>hello</b></i>'
Our target is to inject the underline decorator (specifically the u(hi) callable) into the most inner closure. This can be done like so, with the function we have defined above:
>>> hi_u = inject_decorator(underline, hi)
>>> hi_u('hello')
'<i><b><u>hello</u></b></i>'
Works with undecorated functions:
>>> def pp(s):
... return s
...
>>> pp_b = inject_decorator(bold, pp)
>>> pp_b('hello')
'<b>hello</b>'
A major assumption was made for this first-cut version of the rewriter, which is that all decorators in the chain only have a closure length of one, that one element being the function being decorated with. Take this decorator for instance:
def prefix(p):
def decorator(f):
def inner(*args, **kwargs):
new_args = [p + a for a in args]
return f(*new_args, **kwargs)
return inner
return decorator
Example usage:
>>> #prefix('++')
... def prefix_hi(s):
... return s
...
>>> prefix_hi('test')
'++test'
Now try to inject a bold decorator like so:
>>> prefix_hi_bold = inject_decorator(bold, prefix_hi)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in inject_decorator
ValueError: inner requires closure of length 2, not 1
This is simply because the closure formed by decorator within prefix has two elements, one being the prefix string p and the second being the actual function, and inner being nested inside that expects both those to be present inside its closure. Resolving that will require more code to analyse and reconstruct the details.
Anyway, this explanation took quite a bit of time and words, so I hope you understand this and maybe get you started on the actual right track.
If you want to turn inject_decorator into a decorator, and/or mix it into your class decorator, best of luck, most of the hard work is already done.

Wrap function without clobbering default arguments

Is there a way to forward function arguments without hiding the fact that the original call did or did not provide optional arguments?
def func1(a=x):
# do stuff
def func2(b=y):
# pass args to func1 without masking func1 defaults
return func1(?)
A call to func2() should result in func1() being called without arguments or at least with its default arguments, whatever they may be.
The following almost works but fundamentally I don't know if there is a way for func2 to determine if its defaults were invoked on not.
def func2(b=y):
# this comes close but what if func2(y) is called?
if b == y:
return func1()
else:
return func1(b)
The usual way of determining if a parameter is left off is to use None as the default. It's unlikely that you'll be calling a function with None so it's a useful marker.
def func2(b=None):
if b is None:
return func1()
else:
return func1(b)
I suspect the right way to do this is to have your func2 function use a sentinel value as its default argument, so you can recognize it easily. If you get that sentinel, you can set up the arguments you'll pass on to func1 however you want (e.g. not passing any argument). You can use argument unpacking to handle passing a variable number of arguments (such as 0-1).
A common sentinel is None, though if that could be a meaningful value for a caller to pass, you may want to use something else (an instance of object is a common choice). Here's an example:
def func1(a="default value"): # lets assume we don't know what this default is
# do stuff with a
# later, perhaps in a different module
_sentinel = object() # our sentinel object
def func2(b=_sentinel):
if b is _sentinel: # test for the sentinel
b = "some useful value"
a_args = () # arguments to func1 is an empty tuple
else:
a_args = (b,) # pack b into a 1-tuple
# do stuff with b perhaps
func1(*a_args) # call func1 with appropriate arguments (either b or nothing)
Note that this design is relatively silly. Most of the time you'll either call func1 with an argument in all cases, or you'll call it without an argument in all cases. You rarely need to conditionally pass an argument like this.
See this answer:
https://stackoverflow.com/a/2088101/933416
There is no way to get the information you want from the internals. To detect whether defaults were used, you would need to re-implement the internal default argument processing within the function, i.e.:
def func2(*args, **kwargs):
if len(args) == 0 and "b" not in kwargs:
b = y
return func1()
else:
return func1(b)
Now from the first check we guarantee that func2() was called as opposed to func2(y) or func2(b=y). In almost every case, the unique object sentinel is good enough to avoid having to truly guarantee how it was called, but it can be done.
But judging from the fact that you immediately return the result of func1, I see no reason why func2 even has default arguments. In the default call (func2()), that y is never used. So why is it there? Why don't you just use define func2(*a, **k) and pass them directly to func1?
Argument forwarding should be done with variadic arguments:
def func2(*args, **kwargs):
func1(*args, **kwargs)
Everything will just work, although introspection can suffer a bit.
If you need to sometimes not pass on an argument, you can remove an argument whenever:
del kwargs["name"]
An example:
def print_wrapper(*args, extrabig=False, **kwargs):
if extrabig:
args = [arg*2 for arg in args]
kwargs["sep"] = kwargs.get("sep", " ") * 2
print(*args, **kwargs)
print_wrapper(2, 4, 8, end="!!!\n")
#>>> 2 4 8!!!
print_wrapper(2, 4, 8, sep=", ", end="!!!\n")
#>>> 2, 4, 8!!!
print_wrapper(2, 4, 8, extrabig=True, end="!!!\n")
#>>> 4 8 16!!!
If you really don't want to do this (although you'd be wrong), you can use object to generate a unique sentinel.
# Bad! Won't let you print None
def optionally_print_one_thing(thing=None):
if thing is not None:
print(thing)
# Better
_no_argument = object()
def optionally_print_one_thing(thing=_no_argument):
if thing is not _no_argument:
print(thing)
What is your exact use case? func2 should be smart enough to only pass on the appropriate params to func1, and that should rely on the default values of any parameters.
The only time I have ever found it necessary to change how func2 calls func1 is when func1 is a c function with a screwy signature:
def func2(this, that, those=None):
if those is None:
return func1(this, that)
else:
return func1(this, that, those)

Python - Passing a function into another function [duplicate]

This question already has answers here:
Python function as a function argument?
(10 answers)
Closed last month.
I am solving a puzzle using python and depending on which puzzle I am solving I will have to use a special set of rules. How can I pass a function into another function in Python?
Example
def Game(listA, listB, rules):
if rules == True:
do...
else:
do...
def Rule1(v):
if "variable_name1" in v:
return False
elif "variable_name2" in v:
return False
else:
return True
def Rule2(v):
if "variable_name3" and "variable_name4" in v:
return False
elif "variable_name4" and variable_name1 in v:
return False
else:
return True
This is just a pseudo code and therefore not specific but I get the code to compile but I need to know how to call the function Game and whether it's correctly defined since rules will be switched for either Rule1(v) or Rule2(v).
Just pass it in like any other parameter:
def a(x):
return "a(%s)" % (x,)
def b(f,x):
return f(x)
print b(a,10)
Treat function as variable in your program so you can just pass them to other functions easily:
def test ():
print "test was invoked"
def invoker(func):
func()
invoker(test) # prints test was invoked
For passing both a function, and any arguments to the function:
from typing import Callable
def looper(fn: Callable, n:int, *args, **kwargs):
"""
Call a function `n` times
Parameters
----------
fn: Callable
Function to be called.
n: int
Number of times to call `func`.
*args
Positional arguments to be passed to `func`.
**kwargs
Keyword arguments to be passed to `func`.
Example
-------
>>> def foo(a:Union[float, int], b:Union[float, int]):
... '''The function to pass'''
... print(a+b)
>>> looper(foo, 3, 2, b=4)
6
6
6
"""
for i in range(n):
fn(*args, **kwargs)
Depending on what you are doing, it could make sense to define a decorator, or perhaps use functools.partial.
Just pass it in, like this:
Game(list_a, list_b, Rule1)
and then your Game function could look something like this (still pseudocode):
def Game(listA, listB, rules=None):
if rules:
# do something useful
# ...
result = rules(variable) # this is how you can call your rule
else:
# do something useful without rules
A function name can become a variable name (and thus be passed as an argument) by dropping the parentheses. A variable name can become a function name by adding the parentheses.
In your example, equate the variable rules to one of your functions, leaving off the parentheses and the mention of the argument. Then in your game() function, invoke rules( v ) with the parentheses and the v parameter.
if puzzle == type1:
rules = Rule1
else:
rules = Rule2
def Game(listA, listB, rules):
if rules( v ) == True:
do...
else:
do...

Categories

Resources