SPLENT Framework
Table of contents
- Introduction
- How the pieces fit
- What a feature gives the framework
- Finding what you need
- Reference
- See also
Introduction
splent_framework is the library every SPLENT product runs on. It builds one Flask app out of your product’s features, and gives those features what every web app needs, such as configuration, a database, templates, menus, storage and background tasks. Read this section when you write a feature, or when you want to know what your app does as it starts.
Your product’s code to start the app is one call.
from splent_framework.app_factory import create_splent_app
def create_app(config_name=None):
return create_splent_app(__name__, config_name)
How the pieces fit
The factory runs the same 25 steps in every product. One of them loads your features in the order of the UVL model. Each feature registers what it offers, such as services, hooks, menu entries and assets, and the rest of the app reads it from there.
What a feature gives the framework
from splent_framework.blueprints.base_blueprint import create_blueprint
notes_bp = create_blueprint(__name__)
def init_feature(app):
...
A feature brings a blueprint and, when it needs them, inject_config(app) in its config.py, init_feature(app) and inject_context_vars(app). The framework calls each at the right moment. What a feature gives the framework has the full list.
Finding what you need
| You want to | Read |
|---|---|
| Know what happens when the app starts, or change an error page | Application initialization |
| Set or read a setting | Configuration |
| Know in which order features load, or why one did not | Feature loading |
| Write a blueprint, a service, a repository or a seeder | Architecture layers |
| Define models and migrate them | Database |
| Add to another feature’s page, or pass variables to templates | Template system |
| React to what another feature does | Signals |
| Style the public site with a theme or a skin | Theme system |
| Translate your feature | Internationalization (i18n) |
| Open a screen to some roles only | Access control |
| Add an entry to a menu, or a page to a shell | Menus and shells |
| Record who did what, or report a degraded service | Audit and reports |
| Run work outside the request | Background tasks |
| Keep files | Storage |
| Serve an HTTP API | HTTP APIs |
| Limit how often a form is sent | Rate limits and forms |
| Read the site’s name, time zone or address | The site |
| Give items a slug, preview drafts, page lists, fill the sitemap | Content helpers |
| Keep a value that is slow to make | Cache |
| Fetch an address a user typed | Fetching URLs |
| Make your feature’s content searchable | Search |
| Test your feature with the framework’s fixtures | Testing the framework |
Reference
Package layout
Each startup step belongs to one manager in managers/, which runs once, at start. The other packages are what features call while they run.
splent_framework/
├── app_factory.py create_splent_app, _PIPELINE
├── db.py SQLAlchemy singleton (db)
├── optional.py optional_module, optional_attr
├── managers/
│ ├── config_manager.py ConfigManager, resolve_config_name
│ ├── migration_manager.py MigrationManager
│ ├── 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
│ ├── logging_manager.py LoggingManager
│ ├── proxy_manager.py ProxyManager
│ ├── security_headers_manager.py SecurityHeadersManager, csp_nonce
│ ├── cookie_security_manager.py CookieSecurityManager
│ ├── csp.py the Content-Security-Policy of each page
│ ├── static_cache_manager.py StaticCacheManager
│ ├── task_manager.py TaskManager (checks TASKS_MODE at start)
│ ├── health_manager.py HealthManager, the /healthz and /readyz probes
│ ├── csrf_manager.py CSRFManager
│ ├── mapper_guard.py models of features the app does not load
│ └── db_manager.py MigrateManager, an alias of MigrationManager
├── blueprints/
│ └── base_blueprint.py create_blueprint, BaseBlueprint
├── repositories/
│ └── BaseRepository.py BaseRepository[T]
├── services/
│ ├── BaseService.py BaseService
│ ├── service_locator.py register_service, get_service_class, service_proxy
│ └── file_access.py register_file_access_resolver, resolve_file_access, send_protected_file
├── seeders/
│ └── BaseSeeder.py BaseSeeder, SeederError
├── hooks/
│ └── template_hooks.py Template hook registry
├── signals/
│ ├── registry.py Signal registry (register_signal, get_registry)
│ └── signal_utils.py define_signal, connect_signal
├── refinement/
│ ├── __init__.py refine_model, refine_service
│ ├── parser.py parse_extensible, parse_refinement
│ ├── validator.py validate_refinements
│ ├── conflicts.py find_conflicts, RefinerFacts, BaseFacts, Conflict
│ ├── facts.py collect_runtime_facts (boot conflict check)
│ ├── templates.py loader for declared template overrides
│ ├── registry.py RefinementRegistry, RefinementEntry
│ └── model_extender.py apply_model_mixin
├── admin/
│ └── registry.py register_admin_resource, AdminResource
├── audit/
│ └── __init__.py record_event, register_audit_recorder
├── observability/
│ └── __init__.py report_degraded, register_degradation_reporter, request_id
├── health/
│ └── __init__.py register_readiness_check, register_readiness_listener
├── tasks/
│ ├── registry.py task, TaskSpec
│ ├── dispatch.py enqueue, get_run, wait, TaskSettings
│ ├── runs.py TaskRun, its statuses, report_progress, reporting
│ ├── backends.py the sync, thread and rq executors
│ └── worker.py python -m splent_framework.tasks.worker
├── storage/
│ └── __init__.py feature_storage, register_storage_move, /files/<feature>/...
├── cache/
│ └── __init__.py feature_cache
├── fetch/
│ └── __init__.py fetch, Fetched, FetchError
├── api/
│ └── __init__.py Problem, paginate, page_answer, conditional, accepted, deprecate
├── ratelimit/
│ └── __init__.py SharedThrottle, rate_limited, use_table, client_ip
├── guard/
│ └── __init__.py form_guard, Refused
├── keys/
│ └── __init__.py purpose_key, serializer, KeyRing (SPLENT_DERIVED_KEYS)
├── params/
│ ├── schema.py [tool.splent.config_schema]: parse_schema, Param, breaking_changes
│ └── runtime.py param, the parameters of the loaded features
├── settings/
│ └── settings_schema.py register_settings, get_config, setting_value
├── nav/
│ ├── nav_registry.py register_nav_item, get_nav_items
│ └── menu_registry.py register_menu_item, register_user_menu_item, menu_sections
├── layout/
│ └── shells.py shell (the layout a page extends, by shell)
├── search/
│ ├── search_registry.py register_search_source, register_search_field, resolve_search_hits
│ └── schema.py text, keyword, nested..., register_search_facet, register_search_sort
├── ui/
│ ├── __init__.py the page script in layout.scripts
│ └── static/splent-ui.js data-confirm, data-copy, data-reveal, data-busy, SPLENT.fetch/json/notify
├── site/
│ └── __init__.py site_name, site_timezone, local_now, local_today
├── urls/
│ └── __init__.py site_root, external_url_for, has_endpoint, safe_next_url
├── roles/
│ └── __init__.py PANEL_ROLES, ADMIN_ROLES
├── content/
│ └── __init__.py unique_slug, public_or_preview
├── pages/
│ └── __init__.py page_of, pager
├── sitemap/
│ └── __init__.py register_sitemap, /sitemap.xml
├── assets/
│ └── asset_registry.py register_asset, get_assets (per shell)
├── i18n/
│ └── text.py localized, parse_localized
├── context/
│ └── context_manager.py Jinja context builder
├── decorators/
│ └── decorators.py role_required, has_role, register_access_policy
├── markdown/
│ └── render.py render_markdown
├── rich_text/
│ └── sanitize.py sanitize_html (the rich_text filter)
├── dates/
│ └── __init__.py the date, datetime and time_tag filters
├── sizes/
│ └── __init__.py the filesize filter
├── empty/
│ └── __init__.py empty_state
├── icons/
│ └── __init__.py icon, register_icon_set
├── migrations/
│ └── feature_env.py run_feature_migrations (called from every feature's env.py)
├── utils/
│ ├── path_utils.py PathUtils, protected_uploads_dir
│ ├── pyproject_reader.py PyprojectReader
│ ├── feature_utils.py get_features_from_pyproject
│ ├── form_helpers.py form_success, form_error, form_int
│ ├── app_loader.py get_create_app_in_testing_mode
│ ├── text.py slugify, summary (the summary filter)
│ └── uvl_resolver.py resolve_product_uvl (finds the SPL model on disk)
├── translations/ the framework's own catalog (es), for the pages it draws
├── configuration/
│ ├── default_config.py Config / DevelopmentConfig / ..., operator_settings
│ ├── layers.py the trace product:config shows, key by key
│ └── configuration.py uploads_folder_name, get_app_version, is_production
├── environment/
│ └── host.py get_host_for_testing and friends
├── fixtures/
│ └── fixtures.py pytest fixtures
├── helpers/
│ └── test_helpers_auth.py login, logout test helpers
├── bootstraps/
│ └── locustfile_bootstrap.py discover_locustfiles, load_locustfiles
├── locust/
│ └── common.py get_csrf_token
└── selenium/
└── common.py initialize_driver, close_driver
See also
- Feature anatomy, the files a feature is made of.
- Extensibility, how a feature refines, extends or composes another.
Table of contents
- Application initialization
- Configuration
- Feature loading
- Architecture layers
- Database
- Template system
- Signals
- Theme system
- Internationalization (i18n)
- Access control
- Menus and shells
- Audit and reports
- Background tasks
- Storage
- HTTP APIs
- Rate limits and forms
- The site
- Content helpers
- Cache
- Fetching URLs
- Search