Generated: 2026-01-11 | Scan Level: Exhaustive
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.12 | See pyproject.toml (>=3.12,<3.13) |
| Poetry | Latest | Package management |
| Docker | Latest | Optional, for local services |
| Git | Latest | Version control |
git clone https://github.com/DeerHide/fastapi_factory_utilities.git
cd fastapi_factory_utilitiesIf not already installed:
curl -sSL https://install.python-poetry.org | python3 -poetry config virtualenvs.in-project true# Install all dependencies including test group
poetry install --with test --extras allThis will:
- Create
.venv/in the project root - Install all production dependencies
- Install all test dependencies
source .venv/bin/activatepoetry run pre-commit install
poetry run pre-commit install --hook-type pre-pushUse the provided setup script:
./scripts/setup_dev_env.shpoetry run python -m fastapi_factory_utilities.example# Start MongoDB and OpenTelemetry Collector
docker-compose up -d mongo otel_collector
# Run the application
poetry run python -m fastapi_factory_utilities.exampleThe example app will be available at: http://localhost:8000
# Format code with Ruff
poetry run ruff format src tests
# Check and auto-fix linting issues
poetry run ruff check --fix src tests# Run mypy in strict mode
poetry run mypy# Run all tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=src --cov-report=html --cov-report=term
# Run specific test file
poetry run pytest tests/units/fastapi_factory_utilities/core/test_exceptions.py
# Run tests in parallel
poetry run pytest -n auto
# Run with verbose output
poetry run pytest -vv# Run all hooks on all files
poetry run pre-commit run --all-files
# Run specific hook
poetry run pre-commit run ruff-check --all-files
poetry run pre-commit run mypy --all-filessrc/fastapi_factory_utilities/
├── core/ # Main library code
│ ├── app/ # Extend ApplicationAbstract here
│ ├── plugins/ # Add new plugins here
│ ├── security/ # Security extensions
│ ├── services/ # New services
│ └── utils/ # Utility functions
└── example/ # Reference implementation
mkdir -p src/fastapi_factory_utilities/core/plugins/my_plugin
touch src/fastapi_factory_utilities/core/plugins/my_plugin/__init__.py
touch src/fastapi_factory_utilities/core/plugins/my_plugin/plugins.py# plugins.py
from fastapi_factory_utilities.core.plugins.abstracts import PluginAbstract
class MyPlugin(PluginAbstract):
"""My custom plugin."""
def on_load(self) -> None:
"""Sync initialization."""
# Validate configuration
# Setup logging
pass
async def on_startup(self) -> None:
"""Async initialization."""
# Connect to services
# Register with StatusService
pass
async def on_shutdown(self) -> None:
"""Cleanup."""
# Close connections
pass# __init__.py
from .plugins import MyPlugin
__all__ = ["MyPlugin"]mkdir -p tests/units/fastapi_factory_utilities/core/plugins/my_plugin
touch tests/units/fastapi_factory_utilities/core/plugins/my_plugin/test_plugins.pyPlugin fixtures (ODMPlugin, RedisPlugin, TaskiqPlugin, AiopikaPlugin)
are wired to testcontainers. That is the shape consumers already use; FFU
does not ship a shared pytest plugin of driver-seam doubles.
Container-only (do not fake): Mongo transactions / multi-doc atomicity; all AMQP broker semantics (topic routing, publisher confirms, consumer acks, dead-letter / TTL retry).
AbstractRepositoryInMemory is a public testing helper for unit tests that
do not need a Mongo driver. Prefer mongomock or a real Mongo testcontainer
when the test needs Beanie/AbstractRepository to run against a driver.
RepositoryContract lives in tests/fixtures/repository_contract.py. The
library CI runs it twice (mongomock + real Mongo). Divergence fails the build.
class TestMyFeature:
"""Tests for MyFeature."""
def test_specific_scenario(self) -> None:
"""Test description."""
# Arrange
# Act
# Assertclass TestAsyncFeature:
"""Tests for async features."""
async def test_async_operation(self) -> None:
"""Test async operation."""
result = await async_function()
assert result is not Nonefrom fastapi_factory_utilities.core.plugins.aiohttp_plugin.mockers import (
build_mocked_aiohttp_response,
build_mocked_aiohttp_resource,
)
async def test_with_mocked_http() -> None:
response = build_mocked_aiohttp_response(
status=HTTPStatus.OK,
json={"data": "value"},
)
resource = build_mocked_aiohttp_resource(get=response)
service = MyService(http_resource=resource)| Fixture | Scope | Description |
|---|---|---|
mongodb_server_as_container |
session | MongoDB Testcontainer |
mongodb_database_name |
function | Unique database name |
async_motor_database |
function | Async MongoDB database |
odm_plugin_factory |
function | ODM plugin factory |
redis_container |
session | Redis Testcontainer |
taskiq_plugin |
function | Taskiq plugin |
rabbitmq_container |
session | RabbitMQ Testcontainer |
aiopika_plugin |
function | Aiopika plugin |
minio_container |
session | MinIO Testcontainer |
s3_buckets |
function | Created logical→physical buckets |
s3_plugin_factory |
function | S3 plugin factory |
./scripts/dev-in-container.sh# Available services in docker-compose.yml
services:
otel_collector: # OpenTelemetry Collector (ports 4317, 4318)
mongo: # MongoDB (port 27017)
application: # Example application (port 8000)# MongoDB only
docker-compose up -d mongo
# OpenTelemetry stack
docker-compose up -d otel_collector
# All services
docker-compose up -dapplication:
service_namespace: "my-company"
service_name: "my-service"
description: "My microservice description"
version: "1.0.0"
environment: "development"
server:
host: "0.0.0.0"
port: 8000
workers: 1
# CORS is opt-in: omit this block (or leave allow_origins empty) to disable it.
# Never combine allow_origins: ["*"] with allow_credentials: true.
cors:
allow_origins: ["https://app.example"]
allow_credentials: true
allow_methods: ["*"]
allow_headers: ["*"]
development:
debug: true
reload: true
logging_mode: "console"The CI pipeline (.github/workflows/ci.yml) runs:
- Setup - Install dependencies, generate
poetry.lock - SBOM Creation - Generate Software Bill of Materials
- Dependency Scan - Grype vulnerability scanning
- Pre-commit - Run all linting, formatting, type checking, tests
- Release (on tags) - Build and publish to PyPI
# Simulate CI pre-commit step
poetry run pre-commit run --all-files# 1. Standard library
import logging
from typing import Any
# 2. Third-party
from fastapi import FastAPI
from pydantic import BaseModel
# 3. Local application
from fastapi_factory_utilities.core.app import ApplicationAbstract| Type | Convention | Example |
|---|---|---|
| Classes | PascalCase | ApplicationAbstract |
| Functions | snake_case | get_config() |
| Constants | UPPER_SNAKE | DEFAULT_PORT |
| Private | Leading underscore | _internal_method() |
| Type Variables | Descriptive + Type | DocumentGenericType |
def my_function(param1: str, param2: int) -> bool:
"""Brief description.
Longer description if needed.
Args:
param1: Description of param1.
param2: Description of param2.
Returns:
Description of return value.
Raises:
ValueError: When something is wrong.
"""poetry.lock is committed. It pins what CI installs. The version ranges in
pyproject.toml are what consumers resolve against, and they are allowed to
differ. Do not run poetry update as a side effect of pushing — pre-push
only checks that the lock still matches pyproject.toml. Fresh resolution
runs on a weekly canary (.github/workflows/deps-canary.yml).
poetry check --lock
poetry install --sync --with test --extras allpre-commit clean
pre-commit install# Check if MongoDB is running
docker-compose ps mongo
# View MongoDB logs
docker-compose logs mongoEnsure PYTHONPATH includes src and tests:
export PYTHONPATH=./src:./tests:$PYTHONPATH| Task | Command |
|---|---|
| Install deps | poetry install --with test --extras all |
| Run tests | poetry run pytest |
| Format code | poetry run ruff format src tests |
| Lint code | poetry run ruff check --fix src tests |
| Type check | poetry run mypy |
| Pre-commit | poetry run pre-commit run --all-files |
| Run example | poetry run python -m fastapi_factory_utilities.example |
| Check lock | poetry check --lock |
Generated by BMAD Document Project Workflow