SPLENT Framework
splent_framework is the core library that every SPLENT product runs on. It wires together Flask, SQLAlchemy, Alembic, Flask-Session, and a feature loading pipeline into a single coherent application factory.
Developers building features or products almost never call framework internals directly — the CLI scaffolds the right hooks, and the factory wires everything at startup. This section documents the internals for contributors, advanced users, and anyone who wants to understand exactly what happens when create_app() runs.
Architecture
The framework is organised around a manager pattern: each subsystem is owned by a single manager class that is instantiated once during create_app() and never touched again at runtime.
splent_framework/
├── managers/
│ ├── config_manager.py ConfigManager
│ ├── migration_manager.py MigrationManager (+ db alias)
│ ├── feature_manager.py FeatureManager
│ ├── feature_loader.py FeatureLoader + collaborators
│ ├── feature_order.py FeatureLoadOrderResolver
│ ├── namespace_manager.py NamespaceManager
│ ├── session_manager.py SessionManager
│ ├── jinja_manager.py JinjaManager
│ ├── error_handler_manager.py ErrorHandlerManager
│ ├── locale_manager.py LocaleManager
│ └── task_queue_manager.py TaskQueueManager
├── blueprints/
│ └── base_blueprint.py BaseBlueprint
├── repositories/
│ └── BaseRepository.py BaseRepository[T]
├── services/
│ └── BaseService.py BaseService
├── seeders/
│ └── BaseSeeder.py BaseSeeder
├── resources/
│ └── generic_resource.py GenericResource
├── serialisers/
│ └── serializer.py Serializer
├── hooks/
│ └── template_hooks.py Template hook registry
├── context/
│ └── context_manager.py Jinja context builder
├── utils/
│ ├── path_utils.py PathUtils
│ ├── pyproject_reader.py PyprojectReader
│ └── feature_utils.py get_features_from_pyproject
├── configuration/
│ └── default_config.py Config / DevelopmentConfig / ...
├── fixtures/
│ └── fixtures.py pytest fixtures
└── db.py SQLAlchemy singleton
Initialization sequence
create_app() in every SPLENT product runs the same pipeline, in this order:
create_app(config_name)
│
├── NamespaceManager.init_app() set up Python namespace packages
├── ConfigManager.init_app() load product or default config
├── MigrationManager(app) init SQLAlchemy + Alembic
├── SessionManager(app) init Flask-Session
├── LoggingManager(app) configure logging
├── ErrorHandlerManager(app) register HTTP error handlers
├── LocaleManager(app) init Flask-Babel, locale selector
├── JinjaManager(app, context) context processors + template hooks
└── FeatureManager(app).register_features()
│
├── read pyproject.toml feature list
├── resolve topological load order via UVL
└── for each feature (in order):
├── locate symlink in features/
├── validate src/ structure
├── add to sys.path + import
├── call feature.config.inject_config(app)
├── call feature.init_feature(app)
├── register Blueprint instances
└── register translations/ directory with LocaleManager
See Application initialization for the full breakdown.
What features must provide
A feature loaded by FeatureManager must expose certain hooks in its __init__.py. The framework calls them in order:
| Hook | Required | Purpose |
|---|---|---|
<bp_name> = BaseBlueprint(...) |
Yes | Blueprint the framework registers with Flask |
init_feature(app) |
No | One-time setup (register extensions, configure services) |
inject_context_vars(app) |
No | Return a dict injected into every Jinja template as globals |
In config.py (optional, at the feature root):
| Hook | Required | Purpose |
|---|---|---|
inject_config(app) |
No | Modify app.config before any blueprint is registered |
Manager reference
| Manager | File | Responsibility |
|---|---|---|
ConfigManager |
managers/config_manager.py |
Load product or default config into app.config |
MigrationManager |
managers/migration_manager.py |
SQLAlchemy + Flask-Migrate + splent_migrations table |
FeatureManager |
managers/feature_manager.py |
High-level feature registration orchestrator |
FeatureLoader |
managers/feature_loader.py |
Full pipeline for a single feature (resolve → validate → import → integrate) |
FeatureLoadOrderResolver |
managers/feature_order.py |
Kahn topological sort using UVL constraints |
NamespaceManager |
managers/namespace_manager.py |
Python namespace packages for .splent_cache/features/ |
SessionManager |
managers/session_manager.py |
Flask-Session (Redis-backed or filesystem) |
JinjaManager |
managers/jinja_manager.py |
Context processors + template hook globals |
ErrorHandlerManager |
managers/error_handler_manager.py |
400/401/404/500 error handlers with product override |
LocaleManager |
managers/locale_manager.py |
Flask-Babel initialization, per-feature translation directory registration |
TaskQueueManager |
managers/task_queue_manager.py |
RQ (Redis Queue) singleton for background tasks |
Base classes reference
| Class | File | What it gives you |
|---|---|---|
BaseBlueprint |
blueprints/base_blueprint.py |
Blueprint with automatic asset routing |
BaseRepository[T] |
repositories/BaseRepository.py |
Generic CRUD over a SQLAlchemy model |
BaseService |
services/BaseService.py |
Service layer wrapping a repository |
BaseSeeder |
seeders/BaseSeeder.py |
Database seeding abstraction with error handling |
Docs in this section
| Page | What it covers |
|---|---|
| Application initialization | create_app() factory, manager wiring, initialization order |
| Configuration | Config hierarchy: framework defaults → product → feature → service |
| Feature loading | FeatureManager, FeatureLoader, FeatureLoadOrderResolver, FeatureRef |
| Architecture layers | BaseBlueprint, BaseRepository, BaseService, BaseSeeder |
| Database | db singleton, MigrationManager, splent_migrations table |
| Template system | JinjaManager, template hooks, inject_context_vars |
| Internationalization (i18n) | LocaleManager, per-feature translations, Flask-Babel integration |
| Testing | pytest fixtures, test client, DB isolation |