Getting Started

Installation

Today, there is a myriad of tools to start a Python project.

The fastlifeweb package can be installed from PyPI with your preferred virtualenv manager.

Note

This section does not explain how to package an application, it explain the philosophy of the framewoks compare to other frameworks such has Pyramid and FastAPI to get started fast.

The cookook has recipes to build properly packaged application using poetry.

First Steps

We can start with a simple hello world app, the FastAPI first step, revisited.

hello world app

# file hello_world.py

from fastlife import Configurator, Response, Settings


async def hello_world() -> Response:
    return Response("Hello World")


def build_app():
    config = Configurator(Settings())
    config.add_route("hello", "/", hello_world, methods=["GET"])
    return config.build_asgi_app()


app = build_app()

In the example above, we can see that Fastlife build a FastAPI application.

In a classical FastAPI application, the application is created an route, routers, are added to it. Using fastlife, the process has been reversed in a dependency injection process, a Configurator object collect all the routes, or any configuration like locale manager for i18n, and create an app after everything has been collected.

The app build is a FastAPI instance.

Note

If you have used the Pyramid framework, you are already familiar with its Configurator. The Fastlife Configurator is a more naive implementation and less feature-rich.

The app can be started with the fastapi dev command or run with uvicorn asgi server.

fastapi dev hello_world.py

hello world app with a template

# file hello_world_with_template.py

from pathlib import Path

from fastlife import Configurator, Settings, TemplateParams

templates_dir = Path(__file__).parent / "templates"


async def hello_world() -> TemplateParams:
    return {}


def build_app():
    config = Configurator(Settings())
    config.add_template_search_path(templates_dir)
    config.add_route(
        "hello", "/", hello_world, template="HelloWorld.jinja", methods=["GET"]
    )
    return config.build_asgi_app()


app = build_app()
{# templates/HelloWorld.jinja #}
<!DOCTYPE html>
<html><body>Hello World</body></html>

To use JinjaX templates, templates path has to be registered using the Configurator.add_template_search_path or using the settings template_search_path.

Settings are pydantic settings, it can be set from a environment variable prefixed by fastlife_. You may also override the settings class to inject your own setting and override the prefix.

On the other hand, adding template search path during the configuration phase makes the app more modular, and kept the module responsible of templates add its own template to the path.

Note

The most concerning can develop and register their own template engine using the fastlife.config.configurator.GenericConfigurator.add_renderer().

modular approach

A maintainable application over time is an application that encourage modularity. In general, any application is divided into layers that have their own responsability.

The http views is one of them and now we are going split those views in a submodule.

Let’s write a simple views module, with our previous view, and nothing more.

# views.py
from pathlib import Path

from fastlife import Configurator, configure, view_config

templates_dir = Path(__file__).parent


@view_config("hello_world", "/", template="HelloWorld.jinja")
async def hello_world() -> dict[str, str]:
    return {}


@configure
def includeme(config: Configurator):
    config.add_template_search_path(templates_dir)

Now, we can use the config.include() method to inject routes in the final application.

# entrypoint.py
from fastlife import Configurator, Settings


def build_app():
    config = Configurator(Settings())
    config.include("views")
    return config.build_asgi_app()


app = build_app()

The config.include() call will import the module and all its submodules and grab all decorated method with a @configure afterwhat, the decorated method will be called with the configurator as first argument in order to configure the module.

Note

If you have used the Pyramid framework, the config.include does not works exactly the same. In Pyramid, there is no decorator on includeme function, and the include does not include submodule. In fastlife, config.include() works like if config.scan() will also call the includeme scanned. In Pyramid, the view and the route are not registered together, views are registered by a scan to set a view_name the the include attach the view name to routes in order to ensure that the routes are always registered in the same order. fastlife use a more traditional approach, and does not respect this strict approach, this is why the @view_config also register the path of the view.

Writing tests

The fastlife.testing.testclient module define a set of class to help writing test for web pages.

Note

This module required the extra testing installable via the command

pip install fastlifeweb[testing]
import pytest
from entrypoint import app

from fastlife.testing import WebTestClient


@pytest.fixture
def client():
    return WebTestClient(app)


def test_views(client: WebTestClient):
    page = client.get("/")
    assert page.html.h1.text == "Hello world!"