Modular Architecture
Modular architecture in this codebase is centered around the Blueprint system, which allows developers to organize applications into distinct, reusable components. This system enables the definition of routes, error handlers, and middleware in isolation, which are later "recorded" onto a central application instance.
The Blueprint Hierarchy
The modularity system is built on a three-tier class hierarchy located in src/flask/sansio/ and src/flask/:
- Scaffold (
src/flask/sansio/scaffold.py): The base class shared by both the mainFlaskapplication andBlueprint. it provides the core decorators for routing and request lifecycle management, such as@route,@before_request, and@errorhandler. - SansioBlueprint (
src/flask/sansio/blueprints.py): A "Sans-I/O" implementation of the blueprint logic. It introduces the ability to "record" setup operations (like adding a route) into adeferred_functionslist rather than applying them immediately. - Blueprint (
src/flask/blueprints.py): The standard implementation used by developers. It extendsSansioBlueprintwith WSGI-specific features, such as serving static files viasend_static_fileand providing acligroup for blueprint-specific terminal commands.
Defining and Registering Blueprints
A blueprint acts as a template for a section of an application. It is defined independently and then registered on the application instance, often within an application factory.
Basic Definition
In the flaskr tutorial example (examples/tutorial/flaskr/auth.py), a blueprint is used to isolate authentication logic:
from flask import Blueprint
# Define the blueprint with a name and URL prefix
bp = Blueprint("auth", __name__, url_prefix="/auth")
@bp.route("/register", methods=("GET", "POST"))
def register():
# View logic here
...
Application Registration
The blueprint is then attached to the application in the factory function (examples/tutorial/flaskr/__init__.py):
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
from . import auth
app.register_blueprint(auth.bp)
return app
The Registration Lifecycle
When app.register_blueprint(bp) is called, the following process occurs within src/flask/sansio/blueprints.py:
- State Creation: The blueprint creates a
BlueprintSetupStateobject. This object holds the configuration for this specific registration, including theurl_prefix,subdomain, and a reference to theapp. - Prefixing: The
BlueprintSetupState.add_url_rulemethod automatically prepends the blueprint'surl_prefixto all routes and prefixes theendpointwith the blueprint's name (e.g.,auth.register). - Execution: All functions stored in
bp.deferred_functions(populated by decorators like@bp.route) are executed, passing thestateobject to each. - Merging: The
_merge_blueprint_funcsmethod merges the blueprint's error handlers and request hooks into the application's global collections.
Hierarchical Structures (Nested Blueprints)
Blueprints can be nested within other blueprints to create complex URL and logic hierarchies. When a blueprint is registered on another blueprint, prefixes and subdomains are concatenated.
As demonstrated in tests/test_blueprints.py:
parent = Blueprint("parent", __name__)
child = Blueprint("child", __name__)
grandchild = Blueprint("grandchild", __name__)
# Nesting blueprints
child.register_blueprint(grandchild, url_prefix="/grandchild")
parent.register_blueprint(child, url_prefix="/child")
app.register_blueprint(parent, url_prefix="/parent")
In this structure, a route defined on grandchild at /index would be accessible at /parent/child/grandchild/index. The endpoint would be registered as parent.child.grandchild.index.
Scope and Isolation
The Scaffold class allows blueprints to maintain their own isolated request lifecycle and error handling. However, blueprints also provide "app-level" variants of these decorators to register handlers globally from within a module.
Local vs. Global Handlers
- Local:
@bp.before_requestor@bp.errorhandler(404)only trigger for requests handled by that specific blueprint. - Global: Methods prefixed with
app_(defined insrc/flask/sansio/blueprints.py) affect the entire application.
| Local Decorator | Global Equivalent | Description |
|---|---|---|
@bp.before_request | @bp.before_app_request | Runs before every request in the app. |
@bp.errorhandler | @bp.app_errorhandler | Handles errors for the entire application. |
@bp.context_processor | @bp.app_context_processor | Injects variables into all templates. |
Template and Static File Isolation
Blueprints can define their own template_folder and static_folder.
- Templates: Blueprint templates have lower precedence than the main application's templates, allowing users to override blueprint-provided templates by placing a file with the same name in the app's template directory.
- Static Files: If a blueprint has a
static_folderbut nourl_prefix, the application's main static route takes precedence. It is recommended to use aurl_prefixwhen serving blueprint-specific static assets.