Ampho User Manual

What is Ampho

Ampho is a Python library that provides simple and convenient way to develop applications by splitting them into small, easily maintainable parts called “bundles”.

Bundle is a regular Python package designed according to some rules and contains request handlers, templates, CLI commands, static files, configuration files, application logic, etc.

Using Ampho you don’t have to create an application class and instantiate it separately. All you have to do is to create one or more bundles and then start ready to use application provided by Ampho, using traditional python -m command or your favorite application server like uWSGI or Gunicorn.

Installation

To install latest version of the Ampho, use one simple command:

(env) $ pip install -U ampho

Ampho and Flask

It is important to notice, that Ampho is not a standalone framework. It should be considered as a helper library, that may help make development process of Flask applications a little bit easier. It means that you don’t have to change your development workflow significantly, as well as you don’t have to learn new concepts besides couple ones, which are described in this document. For all other information please check Flask’s official documentation.

What is a bundle

Technically bundle is a regular Python package. The main purpose of bundles is reducing developers efforts by splitting application into small and maintainable parts. Bundle is like Flask blueprint with slightly improved functionality.

Every Ampho application usually consists of one or more bundles, where one bundle may use another ones, other bundles may use another ones and so on. Because bundles are just regular Python packages, you can separately develop, test and distribute them via GitHub, PyPi and other ways you like, being free to develop your application’s architecture in any way you want.

Each bundle has a name which is actually a package name. Obviously you can name your bundles anyhow you like, since any valid Python package name is suitable.

Bootstrap bundle

When Ampho starts it needs to know what to do after initialization. Because any Ampho application are splitted into bundles, it’s obviously that a some “special” bootstrap bundle should exist, allowing Ampho to boot it at first. Usually it’s not necessary to specify that bundle name manually, because Ampho searches for a bundle named app and uses it as an entry point. If you for some reason want to name your bootstrap bundle differently, you should notice Ampho about that using environment variable AMPHO_ENTRY.

Root Directory

Despite almost any modern web application usually consists of many parts, it’s a good practice to retain all that parts under a single location on the filesystem, called root directory. Ampho automatically selects parent directory of the bootstrap bundle as a root one.

Instance directory

As you may know, Flask has a concept of instance directory. Unlike Flask, Ampho automatically creates the application object and configures location of the instance directory, which is by default located at ${root_dir}/instance. You don’t have to create this directory manually, it’ll be created by Ampho automatically at start time.

Creating a bundle

To create a bundle you just need to create a regular python package which can be properly imported afterwards. Let’s assume you use directory structure mentioned above, so after creating the app package in the root directory, you’ll have following files layout:

/hello-world
    /app
        __init__.py
    /instance

Notice, that at this point the app bundle doesn’t have anything inside, it’s just an empty Python package. But this is already enough to start the application.

Start the application

Now, when you have at least one bundle, you can start Ampho application:

(env) $ ampho run

And voila, you have your application running!

* Serving Flask app "ampho.main:APPLICATION"
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

As you can notice, starting an Ampho application is almost the same as starting a Flask one, except instead of flask CLI command, ampho should be used. This is only the difference between Ampho and Flask at this point.

Bundle initialization process

When Ampho loads a bundle, it does this operation in two steps. At first, bundle is registered, and then it is loaded. If you need to perform actions during bundle registration, you should define on_register() hook function in the bundle module’s __init__.py code and it’ll be called automatically by Ampho. Similarly, you may define on_load() function, if you need actions to be performed at bundle loading time.

def on_register():
    print('Bundle is registered')

def on_load():
    print('Bundle is loaded')

Bundle requirements

A bundle can depend on other bundles. In that case it is important, that required bundles be properly loaded and initialized before dependant bundle. To define requirements for your bundle, use REQUIRES list or tuple of strings property in bundle’s __init__.py, i. e.:

REQUIRES = ('ampho_locale', 'ampho_db')

Application configuration

Ampho applications are configured the same way as Flask ones. In addition to Flask’ configuration mechanism, Ampho provides another convenient way to handle and distribute application’s configuration using JSON files with pre-defined names, located in the instance directory.

When Ampho starts, it searches for configuration files in the following order:

  1. default.json
  2. {environment}.json
  3. {username}@{hostname}.json

where parameters from each next file are merged with a previous one. The default.json file is being loaded always. The {environment}.json is loaded only if {environment} corresponds to current ${FLASK_ENV} environment variable. And the {username}@{hostname}.json will be loaded only if {username} and {hostname} are correspond to the ${USER} and ${HOSTNAME} environment variables.

For example, some application can have following configuration files set:

/hello-world
    /instance
        default.json
        development.json
        production.json
        home_user@home_host.json
        prod_user@prod_host.json

Using this approach, you can store all the application configuration in one place, while Ampho will choose appropriate configuration set automatically depending on environment where application runs.

Routing

In general it doesn’t matter where exactly views code is located, but Ampho proposes a convenient way to organize views and map them to URLs.

When Ampho loads a bundle, it checks for the views module presence in the bundle’s package, and, if it’s present, Ampho automatically imports it within bundle’s context, so you can easily use views module to define views and map them as routes.

Let’s look how this works. At first, of course, we need to create views module inside a bundle:

/hello-world
    /app
        __init__.py
        views.py

And place some code there:

from ampho import route

@route('/')
def home() -> str:
    """Home page
    """
    return 'Hello, world!'

As you can see, there is the ampho.route decorator used to make the home() function responsible for processing requests to the / URL path.

Since Ampho uses Flask under the hood, you are free to use any features of the Flask routing, including variable rules, different HTTP methods and so on.

Note

Don’t forget to use route() decorator from the ampho package instead of the flask’s one, because it does some kind of magic while dealing with bundles.

For all other aspects of working with routing, please refer to the Flask routing guide.

Template rendering

Template rendering in Ampho works almost the same way as in Flask, except two moments:

  1. Template files should be located inside the tpl directory of the bundle.
  2. To render templates the ampho.render() function should be user instead if flask.render_template(). The first one has exactly same signature as the flask.render_template(), but injects _bundle variable into each template, which is current bundle object.

CLI commands

In general it doesn’t matter where exactly CLI commands code is located, but Ampho proposes a convenient to organize commands code by placing them into separate module named commands.

/hello-world
    /app
        __init__.py
        commands.py  <-- Here is the module with commands
        views.py

Once you have module named commands in a bundle, Ampho will import it automatically at bundle loading time, so everything you need to do is to place commands’ functions into it, wrapping them with ampho.cli.command() decorator.

from ampho import cli, echo_info

@cli.command('hello')
def hello():
    echo_info('Hello, world')

That’s all. Now, you can run your command from CLI:

(env) $ ampho app hello
Hello, world

Notice, that hello command was automatically placed to the app group, which name is the name of the bundle where command was defined. If you need to change command group’s name, it could be done via CLI_GROUP module-level property. Additionally, using the CLI_HELP property, you can set group’s description shown when you run ampho command without arguments.

from ampho import cli, echo_info

CLI_GROUP = 'my_app'
CLI_HELP = 'Set of extremely useful commands'

@cli.command('hello')
def hello():
    echo_info('Hello, world')

For all other aspects of working with CLI commands, please refer to the Flask CLI guide.

Application Context

When you use pure Flask, you create application object by yourself. But when you use Ampho, this object created by Ampho for you. To access this object use ampho.current_app attribute, i. e.:

from ampho import current_app
from flask.logging import default_handler

current_app.logger.removeHandler(default_handler)

Logging

If FLASK_ENV configuration parameter is development or FLASK_DEBUG is 1, logging level automatically is set to DEBUG.

Besides of Flask logging capabilities, Ampho additionally adds TimedRotatingFileHandler by default. This logger is configured to write one file per day into the ${root_dir}/log by default and retains last 30 files.

If you don’t need this logger to be enabled, set LOG_FILES_ENABLED configuration parameter to 0.

If it’s necessary to change log messages format of this logger, you can do this via LOG_FILES_MSG_FORMAT configuration parameter.

Number of retained files is controlled via LOG_FILES_BACKUP_COUNT configuration parameter.

Deployment

Generally deploying Ampho application to a web server is the same as deploying a Flask application.

One thing should be noted, that when deploying to a uWSGI server, the ampho.main module name should be used as the application container, i. e.:

uwsgi --http :8080 --plugin=python --venv=./env --module=ampho.main