Package: bumpy
Simplify repetitive project tasks with Python.Bumpy aims to provide a simple and effective way to collect and automate
build, test, and deploy tasks. In order to leverage Python’s powerful
syntax while still maintaining a minimal and readable build file, Bumpy
includes several helper functions to reduce the amount of code and
clutter your build files need to include without sacrificing
functionality. In addition, Bumpy requires only built-in Python
libraries - that means no external dependencies!Bumpy is derived frompynt, and by
extension,microbuild.UsageBumpy is simple to use. Just open abum.pyfile, define your tasks,
and runbumpin the same directory.import bumpy as b

@b.task
def build():
    '''Builds ALL THE CODE.'''
    print 'Building'$ bump build
BuildingAPIBumpy provides a simple function decorator to register and track your
tasks, as well as several helper functions to make your life easier when
interfacing with shell commands and files.TasksIn order to convert your lovely functions into a Bumpy task, it’s as
simple as applying the@taskdecorator:import bumpy as b

@b.task
def build():
    '''Builds ALL THE CODE.'''
    print 'Building'The function name will be used as the task name. The docstring will be
saved and used for the built-in help. Any arguments or keyword arguments
will be parsed and registered as CLI flags.Prerequisite tasks / files can be specified with an optionalreqskeyword argument to@task:@b.task(reqs=build)
def run():
    print 'Running'reqscan be a single requirement or a tuple of requirements. A
dependency will only be executed the first time it’s required, although
it may be explicitly executed multiple times via the command line:$ bump run
Building
Running
$ bump build run
Building
Running
$ bump build build run
Building
Building
Runningstrrequirements will be interpreted as files / paths.Generated files can be specified with an optionalgenskeyword
argument:@b.task(gens='docs')
def docs():
    print 'Documenting'Generated files will be saved and can be automatically deleted with
Bumpy’sclean()helper function. Generated files will also be used
to look up file-based dependency chains.Task aliases can be specified with an optionalaliaskeyword
argument:@b.task(alias='pkg', reqs=(build, docs))
def package():
    print 'Packaging'aliascan be a single alias or a tuple of aliases.A task can be set as the ‘default’ task, ie the task that Bumpy will
invoke if no arguments are provided, by providing'default'as an
optional argument:@b.task('default'):
def build():
    print 'Building'A task can be set as the ‘setup’ task, ie the task that Bumpy will
invoke prior to any other tasks, by providing'setup'as an optional
argument:@b.task('setup')
def setup():
    print 'Setting stuff up'Setup tasks can also accept arguments. More on this later!Similar to ‘setup’ tasks, ‘teardown’ tasks are invoked after every other
task, just before exiting:@b.task('teardown')
def teardown():
    print 'Tearing stuff down'A task can be removed from the lookup table by adding a'private'optional argument or by prefixing its name with an underscore:@b.task('setup', 'private'):
def setup():
    print 'Setting stuff up'

@b.ask('teardown')
def _teardown():
    print 'Tearing stuff down'Private tasks can still be required and executed, but cannot be invoked
from the command line and will not be included in the built-in help.A task can grab a reference to itself by adding a'method'optional
argument:@b.task('method', gens='output.txt')
def output(self):
    print 'Generating {}'.format(self.gens)Function arguments / keyword arguments will be converted into
command-line flags and options.@b.task
def docs(modules, format='markdown'):
    print 'Documenting {!r} as {}'.format(modules, format)Which can then be invoked like this:$ bump docs all
Documenting 'all' as markdown
$ bump docs --format rst bumpy
Documenting 'bumpy' as rst
$ bump docs
abort [bumpy.main]: Too few arguments: [docs] expects 1
$ bump docs --format rst
abort [bumpy.main]: Too few arguments: [docs] expects 1Keyword argumentsmustcome before standard arguments, contrary to
Python’s standards.HelpersTo abort task execution and display an error message, useabort(message, *formatargs):@b.task
def abort():
    b.abort('This task is bad.')If*formatargsare provided,messagewill be used as a string
format forstr.format.To invoke shell commands, useshell(command, *formatargs):@b.task
def echo():
    '''This is an example task that uses a shell command.'''
    print b.shell('echo hi')If*formatargsare provided,commandwill be used as a string
format forstr.format.To require tasks during execution rather than pre-execution, userequire(*reqs):@b.task
def test(fail=False):
    if fail:
        require(abort)

    print 'Flexibly surviving'To check whether requirements are valid without actually executing them,
usevalid(*reqs).To get the youngest age of a collection of files, use:age(*paths):@b.task(reqs='input.txt', gens='output.txt')
def output():
    if b.age('input.txt') < b.age('output.txt'):
        b.shell('cp input.txt output.txt')If you’re meticulous about recording your generated files withgens,
Bumpy will be able to automatically remove all generated files withclean():@b.task
def clean():
    b.clean()NamespacesTasks can be grouped into namespaces by using an optionalnamespacekeyword argument:@b.task(namespace='db')
def init():
    print 'Initialize database'$ bump db.init
Initialize databaseWhen bumpy fires up, it searches for modules in abump/directory
and imports them, assigning each an appropriate namespace. If you want
tasks to be in the global namespace, add them tobump/__bumpy_main__.py,bum.py, orbuild.py, only the first
of which will be imported. If tasks in one namespace depend on tasks in
another, you can use Python’simportto import their containing
module.CLIbump--versionwill print the currently running version of Bumpy.bump-horbump--helpwill print a built-in help message.bump-f<file>will tellbumpto load a different file instead
ofbum.pyorbuild.py.bump-vTASKwill enable verbose mode, printing an enter/exit
message each time a task begins and ends execution.bump TASK1 TASK2 TASK3will execute tasks in sequence