Function compositing¶
One of Python’s strengths is how easy it is to manipulate functions and combine
them. However, this typically breaks tools such as Clize which try to inspect
the resulting callable and only get vague information. Fortunately, using the
functions in sigtools
, we can overcome this drawback.
We will look at how you can create decorators that work along with Clize.
Creating decorators is useful if you want to share behaviour across multiple
functions passed to run
, such as extra parameters or input/output formatting.
Using a decorator to add new parameters and modifies the return value¶
Let’s create a decorator that transforms the output of the wrapped function when passed a specific flag.
from sigtools.modifiers import autokwoargs
from sigtools.wrappers import wrapper_decorator
@wrapper_decorator
@autokwoargs
def with_uppercase(wrapped, uppercase=False, *args, **kwargs):
"""
Formatting options:
uppercase: Print output in capitals
"""
ret = wrapped(*args, **kwargs)
if uppercase:
return str(ret).upper()
else:
return ret
wrapper_decorator
lets our
with_uppercase
function decorate other functions:
from clize import run
@with_uppercase
def hello_world(name=None):
"""Says hello world
name: Who to say hello to
"""
if name is not None:
return 'Hello ' + name
else:
return 'Hello world!'
if __name__ == '__main__':
Each time the decorated function is run, with_uppercase
will be run with
the decorated function as first argument wrapped
.
wrapper_decorator
will tell Clize that the combined function has the same
signature as:
@kwoargs('uppercase')
def hello_world(name=None, uppercase=False):
pass
This is the signature you would get by “putting” the parameters of the
decorated function in place of the wrapper’s *args, **kwargs
. It is what wrapper_decorator
expects it to do, but that can be changed.
With the correct signature signalised, the command-line interface matches it:
$ python examples/decorators.py --uppercase
HELLO WORLD!
$ python examples/decorators.py john
Hello john
$ python examples/decorators.py john --uppercase
HELLO JOHN
The help system(provided by clize.help.ClizeHelp
) will also pick up on the
fact that the function is decorated and will read parameter descriptions from
the decorator’s docstring:
$ python decorators.py --help
Usage: decorators.py [OPTIONS] [name]
Says hello world
Positional arguments:
name Who to say hello to
Formatting options:
--uppercase Print output in capitals
Other actions:
-h, --help Show the help
Providing an argument using a decorator¶
from sigtools.modifiers import autokwoargs
from sigtools.wrappers import wrapper_decorator
from clize import run
def get_branch_object(repository, branch_name):
return repository, branch_name
@wrapper_decorator(0, 'branch')
@autokwoargs
def with_branch(wrapped,
repository='.', branch='master',
*args, **kwargs):
"""Decorate with this so your function receives a branch object
repository: A directory belonging to the repository to operate on
branch: The name of the branch to operate on
"""
return wrapped(
*args, branch=get_branch_object(repository, branch), **kwargs)
@with_branch
@autokwoargs
def diff(branch=None):
"""Show the differences between the committed code and the working tree."""
return "I'm different."
@with_branch
@autokwoargs
def commit(branch=None, *text):
"""Commit the changes.
text: A message to store alongside the commit
"""
return "All saved.: " + ' '.join(text)
@with_branch
@autokwoargs
def revert(branch=None):
"""Revert the changes made in the working tree."""
return "There is no chip, John."
run(diff, commit, revert,
description="A mockup version control system(like git, hg or bzr)")