Configuration is a required step before the application can be served by an ASGI server such as uvicorn.
Configuration occurs when the
configure() helper is called with the
app that should be served. This has the effect of calling all registered plugins, which in turn set up features such as CORS, HTTPS redirection, etc.
Since it prepares the
app for serving,
configure() can only be called once.
Besides the mandatory
configure() can be given one or both of the following:
- A settings object or module, e.g.
configure(app, settings). In this case, individual settings are obtained from upper-cased constants, e.g.
ALLOWED_HOSTS = ["example.com"].
You can prevent Bocadillo from treating intermediary variables as settings by prefixing them with a leading underscore, e.g.
- Arbitrary settings passed as keyword arguments, e.g.
If you ever need to access settings at runtime, you can use the
This object can be imported from the
bocadillo package, and gets populated once
configure() is called (which has a few gotchas — see warning below). Besides regular dot notation, you can use the dict-like
.get() method to retrieve a setting with an optional default value.
Accessing an attribute of
configure() was called will raise an error.
For this reason, you should only access settings inside a function or a method. In other words, you should not access settings at the top level of a Python module.
Here is an example:
# myproject/app.py from bocadillo import App, settings app = App() @app.route("/") async def index(req, res): if settings.get("PUBLIC", False): res.text = "Hello, world!" else: res.text = "Coming soon!"
Accessing settings is typically needed when implementing plugins.
The recommended way to define configuration is through a settings module, i.e. a
.py file which declares settings as upper-cased constants.
We recommend you use Starlette's Config helper to define settings. It reads configuration from environment variables and also supports
.env files located at the project root directory. This way, you only need one settings module for all environments (production, local development, etc.).
Here's an example:
# myproject/settings.py from starlette.config import Config config = Config(".env") ALLOWED_HOSTS = ["localhost", "example.com"] HSTS = config("HSTS", default=False)
And the corresponding
# myproject/app.py from bocadillo import App, configure from . import settings app = App() configure(app, settings)
from . import settings statement above is a relative import. It only works if the code is located inside a package — here,
myproject. If you don't use a package, you will need to use absolute imports, e.g.
For more information on Python imports, see Absolute vs Relative Imports In Python (Real Python).
Server entry point
Although the minimal working app instanciates and configures the
app within the same script, it is recommended to perform these operations separately. This allows you to change settings at runtime, and encourages more decoupling between settings and applications.
To achieve this, you should create a server entry point, i.e. a Python script typically named
configure() is called to prepare the
app before it is served by the ASGI server.
For example, remove the call
# myproject/app.py from bocadillo import App app = App() # ...
and move it to a new
# myproject/asgi.py from bocadillo import configure from .app import app from . import settings configure(app, settings)
You must now serve the app using
$ uvicorn myproject.asgi:app instead of
$ uvicorn myproject.app:app.