Python project

Common files for a Python project.

Dependencies

poetry add python-dotenv ipython deescovery

Top-level files

File myproject/config.py

"""Configuration.

The module doesn't depend on the project. The configuration file is initialized
with python-dotenv, and we don't read environment variables in the rest of the
project.


What config.py can import?
--------------------------

The module doesn't import anything from the project.


Who can import config.py?
---------------------------

The module is imported by the app.py to create services. It can also be imported in
the other parts of the application: for example, to get access to PROJECT_ROOT, or to
create project-specific services from requisites, provided in the config.
"""
import os
import pathlib

import dotenv

PROJECT_ROOT = pathlib.Path(__file__).parents[1]

dotenv.load_dotenv((PROJECT_ROOT / ".env").as_posix())

# --------------------------------------------------------------------------------
# Server configuration
# --------------------------------------------------------------------------------
HOST = os.environ.get("HOST", "0.0.0.0")
PORT = int(os.environ.get("PORT", 5000))

File myproject/services.py

"""Services.

The module provides a "registry" or "service locator" for other modules of the
application. It may initialize stub objects for extensions, that are configured
inside the app.py later on.

What services.py can import?
----------------------------

The module doesn't import anything from the project.


Who can import services.py?
--------------------------

The module is imported by different modules across the project, as necessary.
"""
# FastAPI example
from fastapi import FastAPI

fastapi_app = FastAPI()

File myproject/app.py

"""Application.

The module is used to initialize the main project: load configuration and
initialize services.

What app.py can import?
-----------------------

Services, configuration, and everything that is needed to initialize application,
including controllers and models inside apps. In other words, app.py depends on
a lot of things in the project.


Who can import app.py?
----------------------

The module is not imported from anywhere except from a WSGI/ASGI server in
production, and conftests.py for pytest.
"""
from importlib import import_module

from deescovery import ModuleRule, discover
from deescovery.matchers import MatchByPattern

from myproject import config

api_loader = ModuleRule(
    name="FastAPI API endpoints loader",
    module_matches=MatchByPattern(patterns=["myproject.*.api"]),
    module_action=import_module,
)


def init(**kwargs):
    update_config(kwargs)
    discover("myproject", rules=[api_loader])


def update_config(kwargs):
    """Override values of the myproject.config.

    Helpful for tests, when we need to dynamically override some settings.
    """
    for k, v in kwargs.items():
        setattr(config, k, v)

File manage.py

#!/usr/bin/env python
"""Management script."""
import datetime
from typing import Iterable, List

import click
from IPython import embed

from myproject import app


@click.group()
def cli():
    app.init()

@cli.command()
def shell():
    """Open IPython console."""
    embed(colors="neutral")

if __name__ == "__main__":
    cli()