Skip to content

fastack.app

Fastack (FastAPI)

Fastack application aims to support:

  • App settings
  • Adding a plugin
  • Adding a command
  • Adding a controller to create a REST APIs
  • Access app, request, websocket, state objects globally (like Flask)
Source code in fastack/app.py
class Fastack(FastAPI):
    """
    Fastack application aims to support:

    * App settings
    * Adding a plugin
    * Adding a command
    * Adding a controller to create a REST APIs
    * Access ``app``, ``request``, ``websocket``, ``state`` objects globally (like Flask)

    """

    # Storage for all commands and will be added to the "fastack" command, so you can access it.
    cli = Typer()

    def set_settings(self, settings: ModuleType):
        """
        Set settings for the application.

        Args:
            settings (ModuleType): settings module
        """

        self.state.settings = settings

    def get_setting(self, name: str, default: Any = None):
        """
        Get setting value by name.

        Args:
            name (str): Setting name
            default (Any, optional): Default value if setting is not found. Defaults to None.

        Returns:
            Any: Setting value
        """

        assert self.state.settings is not None
        return getattr(self.state.settings, name, default)

    def load_plugins(self):
        """
        Load plugins from settings.
        """

        for plugin_str in self.get_setting("PLUGINS", []):
            plugin_str += ".setup"
            plugin = import_attr(plugin_str)
            plugin(self)

    def load_commands(self):
        """
        Load commands from settings.
        """

        for command_str in self.get_setting("COMMANDS", []):
            command: Union[Callable, Typer] = import_attr(command_str)
            if isinstance(command, Typer):
                self.cli.add_typer(command)
            else:
                self.cli.command(command.__name__)(command)

    def include_controller(
        self,
        controller: Controller,
        *,
        prefix: str = "",
        tags: Optional[List[str]] = None,
        dependencies: Optional[Sequence[params.Depends]] = None,
        default_response_class: Type[Response] = Default(JSONResponse),
        responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
        callbacks: Optional[List[BaseRoute]] = None,
        routes: Optional[List[BaseRoute]] = None,
        redirect_slashes: bool = True,
        default: Optional[ASGIApp] = None,
        dependency_overrides_provider: Optional[Any] = None,
        route_class: Type[APIRoute] = APIRoute,
        on_startup: Optional[Sequence[Callable[[], Any]]] = None,
        on_shutdown: Optional[Sequence[Callable[[], Any]]] = None,
        deprecated: Optional[bool] = None,
        include_in_schema: bool = True,
    ):
        """
        Include controller to the application.

        Args:
            controller (Controller): Controller instance

        For other parameters, please see the documentation of ``fastapi.APIRouter.add_api_route``.
        """

        assert isinstance(
            controller, Controller
        ), f"Controller must be an instance of {Controller!r}"
        router = controller.build(
            prefix=prefix,
            tags=tags,
            dependencies=dependencies,
            default_response_class=default_response_class,
            responses=responses,
            callbacks=callbacks,
            routes=routes,
            redirect_slashes=redirect_slashes,
            default=default,
            dependency_overrides_provider=dependency_overrides_provider,
            route_class=route_class,
            on_startup=on_startup,
            on_shutdown=on_shutdown,
            deprecated=deprecated,
            include_in_schema=include_in_schema,
        )
        self.include_router(router)

    @property
    def middleware(self) -> MiddlewareManager:  # type: ignore[override]
        return MiddlewareManager(self)

    def app_context(self, with_lifespan: bool = True):
        return AppContext(self, with_lifespan=with_lifespan)

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        req_token: Optional[Token] = None
        ws_token: Optional[Token] = None
        try:
            async with self.app_context(with_lifespan=False):
                scope_type = scope["type"]
                # If the scope is http we will create a request instance object and add it to the global stack,
                # so that it can be accessed via ``fastack.globals.request``
                if scope_type == "http":
                    request = Request(scope, receive)
                    req_token = _request_ctx_stack.set(request)

                # Same as above, but for websocket
                elif scope_type == "websocket":
                    websocket = WebSocket(scope, receive, send)
                    ws_token = _websocket_ctx_stack.set(websocket)

                await super().__call__(scope, receive, send)
        finally:
            # Clean global stack, when app finish processing request
            if req_token:
                _request_ctx_stack.reset(req_token)
            if ws_token:
                _websocket_ctx_stack.reset(ws_token)

get_setting(self, name, default=None)

Get setting value by name.

Parameters:

Name Type Description Default
name str

Setting name

required
default Any

Default value if setting is not found. Defaults to None.

None

Returns:

Type Description
Any

Setting value

Source code in fastack/app.py
def get_setting(self, name: str, default: Any = None):
    """
    Get setting value by name.

    Args:
        name (str): Setting name
        default (Any, optional): Default value if setting is not found. Defaults to None.

    Returns:
        Any: Setting value
    """

    assert self.state.settings is not None
    return getattr(self.state.settings, name, default)

include_controller(self, controller, *, prefix='', tags=None, dependencies=None, default_response_class=<fastapi.datastructures.DefaultPlaceholder object at 0x7f0df3659790>, responses=None, callbacks=None, routes=None, redirect_slashes=True, default=None, dependency_overrides_provider=None, route_class=<class 'fastapi.routing.APIRoute'>, on_startup=None, on_shutdown=None, deprecated=None, include_in_schema=True)

Include controller to the application.

Parameters:

Name Type Description Default
controller Controller

Controller instance

required

For other parameters, please see the documentation of fastapi.APIRouter.add_api_route.

Source code in fastack/app.py
def include_controller(
    self,
    controller: Controller,
    *,
    prefix: str = "",
    tags: Optional[List[str]] = None,
    dependencies: Optional[Sequence[params.Depends]] = None,
    default_response_class: Type[Response] = Default(JSONResponse),
    responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
    callbacks: Optional[List[BaseRoute]] = None,
    routes: Optional[List[BaseRoute]] = None,
    redirect_slashes: bool = True,
    default: Optional[ASGIApp] = None,
    dependency_overrides_provider: Optional[Any] = None,
    route_class: Type[APIRoute] = APIRoute,
    on_startup: Optional[Sequence[Callable[[], Any]]] = None,
    on_shutdown: Optional[Sequence[Callable[[], Any]]] = None,
    deprecated: Optional[bool] = None,
    include_in_schema: bool = True,
):
    """
    Include controller to the application.

    Args:
        controller (Controller): Controller instance

    For other parameters, please see the documentation of ``fastapi.APIRouter.add_api_route``.
    """

    assert isinstance(
        controller, Controller
    ), f"Controller must be an instance of {Controller!r}"
    router = controller.build(
        prefix=prefix,
        tags=tags,
        dependencies=dependencies,
        default_response_class=default_response_class,
        responses=responses,
        callbacks=callbacks,
        routes=routes,
        redirect_slashes=redirect_slashes,
        default=default,
        dependency_overrides_provider=dependency_overrides_provider,
        route_class=route_class,
        on_startup=on_startup,
        on_shutdown=on_shutdown,
        deprecated=deprecated,
        include_in_schema=include_in_schema,
    )
    self.include_router(router)

load_commands(self)

Load commands from settings.

Source code in fastack/app.py
def load_commands(self):
    """
    Load commands from settings.
    """

    for command_str in self.get_setting("COMMANDS", []):
        command: Union[Callable, Typer] = import_attr(command_str)
        if isinstance(command, Typer):
            self.cli.add_typer(command)
        else:
            self.cli.command(command.__name__)(command)

load_plugins(self)

Load plugins from settings.

Source code in fastack/app.py
def load_plugins(self):
    """
    Load plugins from settings.
    """

    for plugin_str in self.get_setting("PLUGINS", []):
        plugin_str += ".setup"
        plugin = import_attr(plugin_str)
        plugin(self)

set_settings(self, settings)

Set settings for the application.

Parameters:

Name Type Description Default
settings ModuleType

settings module

required
Source code in fastack/app.py
def set_settings(self, settings: ModuleType):
    """
    Set settings for the application.

    Args:
        settings (ModuleType): settings module
    """

    self.state.settings = settings

create_app(settings, routes=None, title='Fastack', description='Fastack Framework', version='0.1.0', openapi_url='/openapi.json', openapi_tags=None, servers=None, dependencies=None, default_response_class=<fastapi.datastructures.DefaultPlaceholder object at 0x7f0df36da6d0>, docs_url='/docs', redoc_url='/redoc', swagger_ui_oauth2_redirect_url='/docs/oauth2-redirect', swagger_ui_init_oauth=None, middleware=None, exception_handlers=None, on_startup=None, on_shutdown=None, terms_of_service=None, contact=None, license_info=None, openapi_prefix='', root_path='', root_path_in_servers=True, responses=None, callbacks=None, deprecated=None, include_in_schema=True, swagger_ui_parameters=None, **extra)

Create a Fastack application.

Source code in fastack/app.py
def create_app(
    settings: ModuleType,
    routes: Optional[List[BaseRoute]] = None,
    title: str = "Fastack",
    description: str = "Fastack Framework",
    version: str = "0.1.0",
    openapi_url: Optional[str] = "/openapi.json",
    openapi_tags: Optional[List[Dict[str, Any]]] = None,
    servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
    dependencies: Optional[Sequence[Depends]] = None,
    default_response_class: Type[Response] = Default(JSONResponse),
    docs_url: Optional[str] = "/docs",
    redoc_url: Optional[str] = "/redoc",
    swagger_ui_oauth2_redirect_url: Optional[str] = "/docs/oauth2-redirect",
    swagger_ui_init_oauth: Optional[Dict[str, Any]] = None,
    middleware: Optional[Sequence[Middleware]] = None,
    exception_handlers: Optional[
        Dict[
            Union[int, Type[Exception]],
            Callable[[Request, Any], Coroutine[Any, Any, Response]],
        ]
    ] = None,
    on_startup: Optional[Sequence[Callable[[], Any]]] = None,
    on_shutdown: Optional[Sequence[Callable[[], Any]]] = None,
    terms_of_service: Optional[str] = None,
    contact: Optional[Dict[str, Union[str, Any]]] = None,
    license_info: Optional[Dict[str, Union[str, Any]]] = None,
    openapi_prefix: str = "",
    root_path: str = "",
    root_path_in_servers: bool = True,
    responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
    callbacks: Optional[List[BaseRoute]] = None,
    deprecated: Optional[bool] = None,
    include_in_schema: bool = True,
    swagger_ui_parameters: Optional[Dict[str, Any]] = None,
    **extra: Any,
):
    """
    Create a Fastack application.
    """

    if not contact:
        contact = {
            "name": "Fastack",
            "url": "https://github.com/fastack-dev/fastack",
        }

    app = Fastack(
        debug=settings.DEBUG,
        routes=routes,
        title=title,
        description=description,
        version=version,
        openapi_url=openapi_url,
        openapi_tags=openapi_tags,
        servers=servers,
        dependencies=dependencies,
        default_response_class=default_response_class,
        docs_url=docs_url,
        redoc_url=redoc_url,
        swagger_ui_oauth2_redirect_url=swagger_ui_oauth2_redirect_url,
        swagger_ui_init_oauth=swagger_ui_init_oauth,
        middleware=middleware,
        exception_handlers=exception_handlers,
        on_startup=on_startup,
        on_shutdown=on_shutdown,
        terms_of_service=terms_of_service,
        contact=contact,
        license_info=license_info,
        openapi_prefix=openapi_prefix,
        root_path=root_path,
        root_path_in_servers=root_path_in_servers,
        responses=responses,
        callbacks=callbacks,
        deprecated=deprecated,
        include_in_schema=include_in_schema,
        swagger_ui_parameters=swagger_ui_parameters,
        **extra,
    )
    app.set_settings(settings)
    app.load_plugins()
    app.load_commands()
    return app

Last update: January 17, 2022 13:26:42