Author: cepedus
README version: 2023-06-25
This repository contains the setup, configuration and make
targets to develop and deploy the simplest FastAPI app, providing scalable utils to type-check, lint and test your code.
๐ฆ Requirements
Assuming you have a Unix shell with make
and curl
- This repository works by default with Python
3.11
. Details are provided on how to change this on the sections below. - A Python 3.11 executable of your choice: install using pyenv, official installers, brew, conda, mamba, etc.
๐ฅพ Want to set up a Poetry-managed replicable environment?
make init
๐งช Want to test the code and configuration?
make app-ci
โจ Want to build and run the app?
make app-run
You're wondering what those 3 commands actually do. The main control over the repo is hosted under make
targets that link commonly used utils when developing a Python app:
- Environment setup: Poetry
- Code quality: mypy, pylint, pytest
- Server framework: FastAPI
- Container deployment: Docker
The package-specific configurations are gathered on a single TOML file (Poetry, mypy, pylint and isort). In particular, we use:
- Pytorch's
mypy-strict.ini
rules - Minimal disable rules for pylint (no docstring whatsoever => keep your code as clean as possible!)
- Strict
asyncio
mode for pytest.
Dissecting Makefile
init
installs Poetry using the official installer (if not present on your system), creates a project-specific virtual environment and installs the dependencies of your.lock
file.app-ci
launches pylint, mypy and pytest on your source code.app-run
builds and deploys locally your app. The 2 services (app and database) are launched on the same virtual network.clean
clears bytecode, poetry/pip caches and Docker cache, dangling images and volumes. Use with caution.
Dissecting .python-version
- A single entrypoint for changing your build defining, for example,
3.11.3
. Works best algonside withpyenv
Dissecting Dockerfile
It consists of a standard 2-layer image. On the first one we install poetry and on the 2nd one we install our requirements and run the app, passing environment variables individually.
Why a custom entrypoint?
To properly propagate process signals to the container. As the spawned shell to run our app runs on Bash, we spawns a child process to run node, but signals sent to the parent process are not forwarded to the child processes (a more detailed explanation in found here and there).
In order to have a proper signal managmement, we trap and handle SIGINT
and SIGTERM
: see server/entrypoint.sh.
Dissecting docker-compose.yaml
We explicitly define an internal network in order for the containers of Mongo and our app to work properly.
Dissecting pyproject.toml
Here we centralise all of the relevant configuration for:
- Poetry
- Mypy
- Ruff
- isort
- Black
- Pytest
Setting up a replicable environment, creating your Dockerfiles and making sure everything is coherent when developing is, well, not actual development of new features for your app. If some of those preliminary, yet necessary steps fail it can easily take down your deployment or give place to strange dependency bugs, Docker not finding what's supposed to or even security implications in your containers.
In this repo I tried to gather all of these generic building bricks to allow focusing on what's important if you're developing: writing code rather than configurations. If the entrypoint to land on your app is simplified, you can onboard contributors much more easily and your app's "control tower" is abstracted.
Also, with the right building blocks is easier to follow coding good practices and thus maintain your codebase away from spaghettification.
- Repository configuration: direct commits to the main branch are forbidden, only possible to do so through PRs.
- GitHub Actions: workflows to CI check each commit to the main branch and on PRs.