Applications and modules

Emmett applications are based on the App core class. Let's focus on this fundamental component and on the convenient application modules.

The App object

Changed in version 1.3

As you learned from the Getting Started chapter, your application is defined using Emmett's App class:

from emmett import App

app = App(__name__)

The signature of this class's __init__ method looks like this:

def __init__(
    self,
    import_name: str,
    root_path: Optional[str] = None,
    url_prefix: Optional[str] = None,
    template_folder: str = 'templates',
    config_folder: str = 'config'
):
    # internal code

Let's see the full parameters list in detail:

parameter explanation
import_name gives Emmett an idea of what belongs to your application, usually using __name__ will work out of the box, but you can hardcode the name of your application package if you wish
root_path allows you to set a custom root path for your application, which is usually unnecessary
url_prefix allows you to set a global url prefix for routing
template_folder allows you to set a different folder for your application's templates (by default Emmett uses the templates folder
config_folder allows you to set a different configuration folder for your application, if you wish to load your configuration from files

Since we introduced the config_folder parameter, let's see some details about application configuration.

Application's configuration

The App object provides a config attribute to let you configure your application easily. The config object is something like a Python dictionary, with a friendly syntax and the characteristic of sub-namespace auto-creation.
What does that mean? That you likely want to have the configuration divided into categories, separating the database configuration values from the particulars of your authorization layer or an extension. You can simply write:

from emmett import App
app = App(__name__)

app.config.foo = "bar"
app.config.db.adapter = "mysql"
app.config.db.host = "127.0.0.1"
app.config.Haml.set_as_default = True

without creating dictionaries for db or Haml directly.

You can also load configuration from external files like yaml, so let's see an example. With this application structure:

/app.py
/config
    app.yml
    db.yml

and app.yml looking something like this:

foo: bar
Haml:
  set_as_default: True

and db.yml looking like this:

adapter: mysql
host: 127.0.0.1

you can write in your app.py:

app.config_from_yaml('app.yml')
app.config_from_yaml('db.yml', 'db')

and your configuration will be loaded. As you can see, when calling config_from_yaml(), you can pass the name of the namespace under which Emmett should load the configuration.

Application modules

Changed in version 2.2

When your app's structure starts to come together, you might benefit of packing routes together in common structures, so you can use the same route prefix or hostname, or to have a common pipeline.

Emmett provides application modules for this. You can create new modules using the module method of your application object. For example, let's say you have a minimal blog into your application. Then might be convenient create a module for it:

blog = app.module(__name__, name="blog", url_prefix="blog")

With this single line we created a new module for our blogging system, that will have all the routes prefixed by the /blog url we provided. Now, in order to add a new route to the module, we can use its route method, which has the same syntax of the application one:

@blog.route("/")
async def index():
    # code

The resulting route will respond to the /blog path, since the module have a defined prefix. For instance, if we define an archive route:

@blog.route()
async def archive():
    # code

the final route will be /blog/archive.

A part from the prefix, you can use several parameters when creating modules. Here is the complete list:

parameter explaination
import_name same as we seen for the App object
name name for the module, will be used by Emmett as the namespace for internal routing
template_folder allows you to set a specific sub-folder of your application template path for module templates
template_path allows you to set a specific folder inside your module root path for module templates
static_folder allows you to set a specific sub-folder of your application static path for module assets
static_path allows you to set a specific folder inside your module root path for module assets
url_prefix allows you to set a prefix path for module URLs
hostname allows you to set a specific hostname for module
root_path same as we seen for the App object

As you can see, we can bind the module to a specific host with the hostname parameter, instead of using url_prefix. Considering our blog example, we can bind it to blog.ourhost.tld.

We mentioned that the name parameter of the application modules is instead used by Emmett for the URLs namespacing. To completely understand this and to dive into subsequent considerations, you can read the Routing chapter.

You can also define a specific pipeline for the module, or a list of injectors, so all the routes defined on the model will consequentially use those:

blog.pipeline = [BlogPipe()]
blog.injectors = [BlogInjector()]

And finally, you can create sub-modules starting from other modules.

Sub-modules

One of the benefits of using modules is that you can create a hierarchy of them inside your application, in order to easily manage paths and pipelines.

Let's say, for example, you are building some JSON APIs in your application, and you want every route is prefixed with /apis. Then you can easily create a module for them:

apis = app.module(__name__, 'apis', url_prefix='apis')

Also, since you're using JSON as serialization, you can add the appropriate service pipe to the pipeline:

from emmett.tools import ServicePipe

apis.pipeline = [ServicePipe('json')]

Then, let's say you want to build the first version of your apis, so that they will be accessible from the /apis/v1 endpoint. You can create a submodule from the one you've just created with the same syntax:

v1_apis = apis.module(__name__, 'v1', url_prefix='v1')

Then you can create an endpoint for a specific entity, let's say you want to make operations above the users of your application:

users = v1_apis.module(__name__, 'users', url_prefix='users')

@users.route('/')
async def index():
    # code to return a list of users

@users.route('/<int:user_id>', methods='put')
async def update(user_id):
    # code to edit a user

At this point, the final endpoints for the users' index and update routes will be /apis/v1/users and /apis/v1/users/1.

All the submodule inherit the properties of the parent module, so all the prefixes will be compose to the final one. The same happens for the internal routing, since the names of the two routes will be apis.v1.users.index and apis.v1.users.update.

Every submodule can have its pipeline, that will be added consequentially to the parent one. For example, you might have different authorization systems between the first version of your apis and a new one:

from emmett.tools import ServicePipe

apis = app.module(__name__, 'apis', url_prefix='apis')
apis.pipeline = [ServicePipe('json')]

v1_apis = apis.module(__name__, 'v1', url_prefix='v1')
v1_apis.pipeline = [SomeAuthPipe()]

v2_apis = apis.module(__name__, 'v2', url_prefix='v2')
v2_apis.pipeline = [AnotherAuthPipe()]

Then all the routes defined in these modules or in sub-modules of these modules will have a final pipeline composed by the one of the apis module, and the one of the sub-module.