=================================== Setting up aiohttp.web applications =================================== There are many ways on setting up ``aiohttp.web`` application, as well as there are many ways of defining settings for them. Most notable ways of managing settings are: - Importing Python settings module and store it within :class:`aiohttp.web.Application` instance - Reading settings from ``.yaml`` or ``.json`` file and store it again, in application instance With that in mind, *rororo* insists of their way for setting up ``aiohttp.web`` application. Step 1. Settings data structure =============================== The main part of setup is Settings data structure backed by brilliant `environ-config `_ library. It stores all settings values, as well as any other settings related data. Example below illustrates how given Settings data structure may look like, .. code-block:: python import environ @environ.config(prefix=None, frozen=True) class Settings: level: str debug: bool *rororo* provides basic data structure, which covers most of common settings, used withing ``aiohttp.web`` app. Given data structure available as: :class:`rororo.settings.BaseSettings` and it contains next fields, +--------------------+--------------------+----------------------------------------------------------------------------+ | Attrib | Env var | Description | +====================+====================+============================================================================+ | ``host`` | ``AIOHTTP_HOST`` | Host, where ``aiohttp.web`` application expected to run. By default: | | | | ``"localhost"`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``port`` | ``AIOHTTP_PORT`` | Port to run ``aiohttp.web`` application on. By default: ``8080`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``debug`` | ``DEBUG`` | When enabled, means that ``aiohttp.web`` application run in debug mode. By | | | | default: ``False`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``level`` | ``LEVEL`` | Application level (can be used as ``SENTRY_ENVIRONMENT``, for example). | | | | One of: ``"test"``, ``"dev"``, ``"staging"``, ``"prod"``. By default: | | | | ``"dev"`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``time_zone`` | ``TIME_ZONE`` | Time zone to use within application. By default: ``"UTC"`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``first_weekday`` | ``FIRST_WEEKDAY`` | First weekday for calendar and other modules. By default: ``0`` (Monday) | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``locale`` | ``LOCALE`` | Locale to use within app. By default: ``"en_US.UTF-8"`` | | | | | | | | .. note: For best results it is considered to better setup locale and | | | | other ``LC_*`` env vars before running Python executable. | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``sentry_dsn`` | ``SENTRY_DSN`` | Sentry DSN to use. By default: ``None`` | +--------------------+--------------------+----------------------------------------------------------------------------+ | ``sentry_release`` | ``SENTRY_RELEASE`` | Sentry release. By default: ``None`` | +--------------------+--------------------+----------------------------------------------------------------------------+ *rororo* elevates on `12factor `_ configuration principles, which means all values for Settings data structure should come from environment. Step 2. Instantiating settings ============================== After you declare your Settings data structure it is needed to: 1. Instantiate it 2. Assign to the :class:`aiohttp.web.Application` instance In most cases, it should looks like, .. code-block:: python from aiohttp import web from .settings import Settings def create_app( argv: List[str] = None, *, settings: Settings = None ) -> web.Application: # Instantiate settings if needed if settings is None: settings = Settings().from_environ() # Instantiate app app = web.Application(...) # Assign settings to the application for later reusage app["settings"] = settings # Return app return app You also, might want to "apply" settings by running, .. code-block:: python settings.apply() right after instantiation. In that case :func:`rororo.settings.BaseSettings.apply` will call, - :func:`rororo.settings.setup_locale` - :func:`rororo.settings.setup_logging` - :func:`rororo.settings.setup_timezone` functions with respected settings values. Configuring Sentry SDK ---------------------- Even :class:`rororo.settings.BaseSettings` contains values to configure `Sentry `_, it is not designed to call ``sentry_sdk.init`` on :func:`rororo.settings.BaseSettings.apply` run. You must need to setup `Sentry SDK `_ by yourself like, .. code-block:: python import logging import sentry_sdk from sentry_sdk.integrations.aiohttp import AioHttpIntegration from sentry_sdk.integrations.logging import LoggingIntegartion def create_app(argv: List[str] = None) -> web.Application: settings = Settings.from_environ() if settings.sentry_dsn: sentry_sdk.init( settings.sentry_dsn, environment=settings.level, release=settings.sentry_release, integrations=( AioHttpIntegration(), LoggingIntegration(event_level=logging.WARNING), ), ) ... Setup shortcut -------------- There is a :func:`rororo.settings.setup_settings_from_environ` & :func:`rororo.settings.setup_settings` shortcuts, which apply given Settings data structure and put given instance into :class:`aiohttp.web.Application` dict as ``"settings"`` key. In other words given function is a literally shortcut to, .. code-block:: python settings.apply(...) app["settings"] = settings Step 3. Using settings ====================== In ``app.__main__`` script -------------------------- If you run your ``app`` not via ``python -m aiohttp.web``, but via application own ``__main__.py``, it is OK to, 1. Run ``create_app`` factory function 2. Read ``settings`` from resulted app 3. Pass ``host`` / ``port`` and other values to :func:`aiohttp.web.run_app` function In most cases that ``__main__.py`` will look like, .. code-block:: python from aiohttp import web from rororo.aio import ACCESS_LOG_FORMAT from rororo.settings import APP_SETTINGS_KEY from app.app import create_app, logger from app.settings import Settings if __name__ == "__main__": app = create_app() settings: Settings = app[APP_SETTINGS_KEY] is_dev = settings.is_dev if is_dev: import aiohttp_autoreload aiohttp_autoreload.start() web.run_app( host=settings.host, port=settings.port, access_log=logger if is_dev else None, access_log_format=ACCESS_LOG_FORMAT, ) Within view functions --------------------- As :class:`aiohttp.web.Request` instance contains link to ``app``, which requests given view handler, it is straight forward to read the settings within the view as, .. code-block:: python from aiohttp import web from rororo.settings import APP_SETTINGS_KEY async def index(request: web.Request) -> web.Response: if request.app[APP_SETTINGS_KEY].debug: print("Hello, world!") return web.json_response(True) However, as ``aiohttp>=3`` supports sub-apps it is considred to more robust using :attr:`aiohttp.web.Request.config_dict` for accessing Settings data structure, .. code-block:: python if request.config_dict[APP_SETTINGS_KEY].debug: print("Hello, world!")