Call a Function by alias in a Decorator - python

The current code I have, allows the function to call the wrapper decorator, and uses the function name in its code. However, I'm looking for a way to give the function a 'alias' in a way as an argument. Here's the current code:
import os, sys
# Take user input
message = input('type command: ')
# Command wrapper
ALLCOMMANDS = {}
def command(function):
ALLCOMMANDS[function.__name__] = function
return function
# Commands
#command
def foo():
print("bar")
#command
def goo():
print('ber')
# Run appropriate command
if message in ALLCOMMANDS:
ALLCOMMANDS[message]()
For example I would want to be able to call the function by a name such as '!foo' from the user input, so maybe the argument would look like #command(name='!foo'), I just don't know where to go from there to use that argument in the decorator since it already has an argument.
I attempted
# Command wrapper
ALLCOMMANDS = {}
def command(name):
ALLCOMMANDS[name] = name
return name
but keep getting errors and I assume I am missing something

You should read up a bit more on python decorators. You're getting an error with:
def command(name):
ALLCOMMANDS[name] = name
return name
Because of the return name.
Decorators are just syntactic sugar. This:
#command
def foo():
print('bar')
Is equivalent to:
def foo():
print('bar')
foo = command(foo)
From this you can see why your original decorator works. At the end you return function.
Things get a little tricker when you have a decorator that takes arguments. Desugared the following:
#command('nickname')
def foo():
print('bar')
Looks like this:
def foo():
print('bar')
foo = command('nickname')(foo)
So, to write a decorator that takes arguments, the decorator needs to return a function that takes the function to decorate as an argument:
def command(nickname):
def wrapped(f):
ALLCOMMANDS[nickname] = f
return f
return wrapped
Also consider making ALLCOMMANDS an attribute on your command instead of a global (UPPER_SNAKE is usually reserved for constants):
def command(nickname):
def wrapped(f):
command._functions[nickname] = f
return f
return wrapped
command._functions = {}

Related

How to write a decorator for a function with an argument?

I would like to write a decorator to redirect stdout for the main function to a specific log file. The argument that the main function takes is - item, and I want the decorator to allocate a different log path with each item for the main function. How do I achieve this?
Currently I have:
def redirect_stdout(func):
def wrapper():
with open(f"{log_path}{item}.log", "w") as log, contextlib.redirect_stdout(log), contextlib.redirect_stderr(log):
func(item)
return wrapper()
#redirect_stdout
def main(item):
But I am not sure how the item argument goes into the decorator. Thanks!
What you are looking for is something like below
def redirect_stdout(func):
def wrapper(item):
with open(f"{log_path}{item}.log", "w") as log, contextlib.redirect_stdout(log), contextlib.redirect_stderr(log):
func(item)
return wrapper
To understand how this works you need to properly understand how the decorator works. Check below I have tried to explain how the decorator works. ==> I have used to indicate this is equivalent to.
#redirect_stdout
def main(item):
pass
== >
def main(item):
pass
main = redirect_stdout(main) = wrapper
---------
main(item) => wrapper(item)

Python: Indent print output from class

How can I indent the print output on the command line from a class that is called? I can't edit the class file to add tabs to each print().
So I would call the imported class in mypythonthing.py:
print('Calling class')
MyClass()
All the print output would then be indented, or have something prepended to it.
e.g.
$ python mypythonthing.py
$ Running your python script...
$ Calling class
$ > The print output from MyClass is indented
$ > Exiting MyClass
$
Patch the built-in print function to prefix each line with your indentation.
import builtins
def print(*args, **kwargs):
builtins.print(" > ", *args, **kwargs)
If you can put the code that should be indented inside (one or more) functions, then you can use a decorator to wrap these functions.
Then any invocation of print inside these function will be indented.
Also, you will only need to declare this function in your main script, and not anywhere else.
Example -
import builtins
import another # for demo purposes only
# This will override the default `print` function.
# Invoking it as a decorator will automatically perform
# initialisation and cleanup. There is also never a need
# to modify this.
def indent(f):
def closure():
old = builtins.print
builtins.print = lambda x, *args, **kwargs: old("\t>", x, *args, **kwargs)
f()
builtins.print = old
return closure
some_number = "100"
# Example function, note decorator usage.
# This function may **not** take any parameters!
# It may however, use any variables declared before it.
#indent
def indentedStuffGoesHere():
print("Inside `indentedStuffGoesHere`")
print(some_number)
another.Foo().bar()
another.stuff()
print("entering special block")
indentedStuffGoesHere()
print("done")
another.py
def stuff():
print("TESTING stuff")
class Foo:
def bar(self):
print("HELLO FROM FOO")
Output:
entering special block
> Inside `indentedStuffGoesHere`
> 100
> HELLO FROM FOO
> TESTING stuff
done
i think what you might be looking for is textwrap:
textwrap docs
so as an example:
wrapper = textwrap.TextWrapper(width=preferredWidth, subsequent_indent='\t')
message = "asdf" * 50
print wrapper.fill(message)

Override a function' sub-function from a decorator?

Let's consider this piece of code where I would like to create bar dynamically with a decorator
def foo():
def bar():
print "I am bar from foo"
print bar()
def baz():
def bar():
print "I am bar from baz"
print bar()
I thought I could create bar from the outside with a decorator:
def bar2():
print "I am super bar from foo"
setattr(foo, 'bar', bar2)
But the result is not what I was expecting (I would like to get I am super bar from foo:
>>> foo()
I am bar from foo
Is it possible to override a sub-function on an existing function with a decorator?
The actual use case
I am writing a wrapper for a library and to avoid boilerplate code I would like to simplify my work.
Each library function has a prefix lib_ and returns an error code. I would like to add the prefix to the current function and treat the error code. This could be as simple as this:
def call():
fname = __libprefix__ + inspect.stack()[1][3]
return_code = getattr(__lib__, fname)(*args)
if return_code < 0: raise LibError(fname, return_code)
def foo():
call()
The problem is that call might act differently in certain cases. Some library functions do not return an error_code so it would be easier to write it like
this:
def foo():
call(check_status=True)
Or much better in my opinion (this is the point where I started thinking about decorators):
#LibFunc(check_status=True)
def foo():
call()
In this last example I should declare call inside foo as a sub-function created dynamically by the decorator itself.
The idea was to use something like this:
class LibFunc(object):
def __init__(self,**kwargs):
self.kwargs = kwargs
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
def call(*args):
fname = __libprefix__ + original_func.__name__
return_code = getattr(__lib__, fname)(*args)
if return_code < 0: raise LibError(fname, return_code)
print original_func
print call
# <<<< The part that does not work
setattr(original_func, 'call', call)
# <<<<
original_func(*args,**kwargs)
return wrappee
Initially I was tempted to call the call inside the decorator itself to minimize the writing:
#LibFunc():
foo(): pass
Unfortunately, this is not an option since other things should sometime be done before and after the call:
#LibFunc():
foo(a,b):
value = c_float()
call(a, pointer(value), b)
return value.value
Another option that I thought about was to use SWIG, but again this is not an option because I will need to rebuild the existing library with the SWIG wrapping functions.
And last but not least, I may get inspiration from SWIG typemaps and declare my wrapper as this:
#LibFunc(check_exit = true, map = ('<a', '>c_float', '<c_int(b)')):
foo(a,b): pass
This looks like the best solution to me, but this is another topic and another question...
Are you married to the idea of a decorator? Because if your goal is bunch of module-level functions each of which wraps somelib.lib_somefunctionname, I don't see why you need one.
Those module-level names don't have to be functions, they just have to be callable. They could be a bunch of class instances, as long as they have a __call__ method.
I used two different subclasses to determine how to treat the return value:
#!/usr/bin/env python3
import libtowrap # Replace with the real library name.
class Wrapper(object):
'''
Parent class for all wrapped functions in libtowrap.
'''
def __init__(self, name):
self.__name__ = str(name)
self.wrapped_name = 'lib_' + self.__name__
self.wrapped_func = getattr(libtowrap, self.wrapped_name)
self.__doc__ = self.wrapped_func.__doc__
return
class CheckedWrapper(Wrapper):
'''
Wraps functions in libtowrap that return an error code that must
be checked. Negative return values indicate an error, and will
raise a LibError. Successful calls return None.
'''
def __call__(self, *args, **kwargs):
error_code = self.wrapped_func(*args, **kwargs)
if error_code < 0:
raise LibError(self.__name__, error_code)
return
class UncheckedWrapper(Wrapper):
'''
Wraps functions in libtowrap that return a useful value, as
opposed to an error code.
'''
def __call__(self, *args, **kwargs):
return self.wrapped_func(*args, **kwargs)
strict = CheckedWrapper('strict')
negative_means_failure = CheckedWrapper('negative_means_failure')
whatever = UncheckedWrapper('whatever')
negative_is_ok = UncheckedWrapper('negative_is_ok')
Note that the wrapper "functions" are assigned while the module is being imported. They are in the top-level module namespace, and not hidden by any if __name__ == '__main__' test.
They will behave like functions for most purposes, but there will be minor differences. For example, I gave each instance a __name__ that matches the name they're assigned to, not the lib_-prefixed name used in libtowrap... but I copied the original __doc__, which might refer to a prefixed name like lib_some_other_function. Also, testing them with isinstance will probably surprise people.
For more about decorators, and for many more annoying little discrepancies like the ones I mentioned above, see Graham Dumpleton's half-hour lecture "Advanced Methods for Creating Decorators" (PyCon US 2014; slides). He is the author of the wrapt module (Python Package Index; Git Hub; Read the Docs), which corrects all(?) of the usual decorator inconsistencies. It might solve your problem entirely (except for the old lib_-style names showing up in __doc__).

How to use decorators in Python

I recently started learning about Decorators in Python and found the following piece of code which got me confused.
HANDLERS = {}
def handler_for(template):
def decorator(f):
HANDLERS[template] = f
return f
return decorator
def get_template(template, **kwargs):
#This function got me all confused. How does HANDLERS.get(template, None) works and how does it return demo_func() function?
return HANDLERS.get(template, None)
def email(template, **kwargs):
handler = get_template(template, **kwargs)
contents = handler(**kwargs)
print contents
#handler_for('demo')
def demo_func(**kwargs):
#Do something and return the String
if __name__ == "__main__":
email('demo')
I tried debugging with Python Debugger but I'm still getting confused as how the get_template() function calls the decorator which returns the function name.
I tried debugging with Python Debugger but I'm still getting confused as how the get_template() function calls the decorator which returns the function name.
Well… it doesn't. The decorator gets called by you using #handler_for('demo').
Decorators aren't as complicated as they seem, once you keep in mind that the following are equivalent:
#beans
def spam(eggs):
print(eggs)
def spam(eggs):
print(eggs)
spam = beans(spam)
That beans doesn't have to be just an identifier, it can be (almost) any expression, even a function call. So, your code is doing this:
demo_func = handler_for('demo')(demo_func)
handler_for takes 'demo' as the value of its template parameter. It then defines and returns a local function, decorator. So, when it's called, it has access to its own argument, f, and also to the closed-over template value from when it was constructed. That function is immediately called with demo_func as its f value, and it just stashes the function in the HANDLERS dictionary and returns it unchanged. So, inlining everything:
def demo_func(**kwargs):
#Do something and return the String
template = 'demo'
HANDLERS[template] = demo_func
demo_func = demo_func
So, because get_template just looks things up in HANDLERS, when you later call get_template('demo') it will find demo_func.

Dictionary of functions for all function in class

I'm relatively new to python and I have a class that has a bunch of different function. I read in the user input and depending on the user input I call a different function. Instead of having a bunch of if else statements I thought it would be better to have a dictionary of functions so currently my class looks like this:
class Foo:
def func1(self):
#do something
def func2(self, arg1):
#do something else
def func3(self, arg1, arg2):
#do something
def func4(self, arg1):
#do something
def __init__(self):
self.functions = {"FUNC2": func2, "FUNC4":func4}
def run_loop(self):
while 1:
user_input = raw_input()
cmd = user_input.split(' ')[0]
if cmd in self.functions:
self.functions[cmd].__get__(self, type(self))()
else:
#call other functions
I'm calling this in a main.py like so:
c = Class()
c.run_loop()
My issue is that I'm getting the following error NameError: global name 'func2' is not defined`. I'm not really sure why this is happening. I get the error in the constructor. Any ideas?
You need to specify that the function is within the class by adding self before it.
def __init__(self):
self.functions = {"FUNC2": self.func2, "FUNC4":self.func4}
You need to use self to access class functions from other function in same class. The corrected code will be
self.functions = {"FUNC2": self.func2, "FUNC4":self.func4}
The function is not identified within the class. Function "func2" & "func4" are part of the class and can be referred using the self...

Categories

Resources