Emmett extensions extend the functionality of Emmett in various different ways.
Extensions are Python packages that can be downloaded with pip
. When adding extensions to your application, it is a good habit to declare them as dependencies in your requirements.txt or setup.py file: this way, they can be installed with a simple command or when your application installs.
An extension typically has accompanying documentation that shows how to use it correctly. In general, Emmett extensions should be named with the format emmett-foo
and have a package-name like emmett_foo
, replacing foo with the desired name. If the extension is written according to the suggested pattern, using it in your
application will be quite easy:
from emmett import App
from emmett_foo import Foo
app = App(__name__)
# configure the extension
app.config.Foo.someparam = "something"
# add the extension to our app
app.use_extension(Foo)
# access extension attributes and methods
app.ext.Foo.bar()
That's all.
As you can see, extensions have a namespace that accesses your app's configuration, and after you have added the extension to your application using the use_extension()
method, you can access the extension instance at app.ext.<extension_name>
.
The first step in creating a new extension for Emmett is writing an Extension
subclass:
from emmett.extensions import Extension
class Awesomeness(Extension):
default_config = {}
def on_load(self):
# pass
As you can see, the Extension
class in actually quite simple, since you just have to write down the default configuration (if needed) and override the on_load
method, that will be called by the framework when the extension will be initialised.
You can access three attributes of the extension instance, that are injected by Emmett before calling the on_load
method, in particular you will have:
attribute | description |
---|---|
app | the application on which the extension is loaded |
config | a sdict containing the configuration |
env | a sdict reserved to the extension where data can be stored |
The config
attribute will contain the configuration defined by the developer using the extension, with the default attributes you defined in the default_config
if not specified differently.
The developer will access app.config.Awesomeness
in order configure the extension, in fact Emmett uses the name of your class as namespace for the configuration and environment objects. If you want to specify a different namespace, you can use the relevant attribute:
class Awesomeness(Extension):
namespace = "Foobar"
just remember to update the documentation of your extension accordingly.
Since the extension can access the application object, it can easily do anything, for example it may define a route:
class Awesomeness(Extension):
def on_load(self):
self.app.route('/awesome')(awesome_route)
async def awesome_route():
return {'message': 'Awesome!'}
Changed in version 2.1
Whenever you need to perform more specific actions depending on the status of the application, and the on_load
method is not enough, you can use signals. Emmett provides these signal for extensions:
signal name | parameters | description |
---|---|---|
before_routes | triggered before the first route is defined in the application | |
before_database | triggered before the database is defined in the application | |
after_database | database | triggered after the database has been defined in the application |
before_route | route, f | triggered before a single route is defined |
after_route | route | triggered after a single route is defined |
after_loop | loop | triggered after asyncio loop gets initialized |
Note that the after_database
pass the database instance as parameter, the before_route
the route instance and the decorated method, and the after_route
just the route instance.
Note: all the signals are referred to the application load, none of them will be triggered during the requests flow.
All these signals can be quite handy for specific operations. For example, let's say your extension wants to add a pipe into the application pipeline. Doing this within the on_load
won't be safe, since you don't know if the developer will change the pipeline after the load of your extension on the application. It's, instead, more appropriate using the before_routes
signal for this:
from emmett.pipeline import Pipe
from emmett.extensions import Extension, Signals, listen_signal
class AwesomePipe(Pipe):
# some code
class Awesomeness(Extension):
@listen_signal(Signals.before_routes)
def inject_pipe(self):
self.app.pipeline.append(
AwesomePipe()
)
And if you need the database, you can use the after_database
method:
class Awesomeness(Extension):
@listen_signal(Signals.after_database)
def bind_database(self, database):
self.db = database
Warning: the
listen_signal
decorator is usable only on methods of anExtension
subclass.