Application Fundamentals
The core of this project is built around a hierarchical structure of application objects that separate resource management, core logic, and the WSGI interface. This architecture allows for a shared interface between the main application and components like Blueprints.
The Application Hierarchy
The application logic is distributed across three primary classes, each adding a layer of functionality:
- Scaffold: Defined in
src/flask/sansio/scaffold.py, this is the base class for both the main application and Blueprints. It provides the common interface for registering routes, error handlers, and lifecycle hooks. - App (Sans-IO): Defined in
src/flask/sansio/app.py, this class inherits fromScaffoldand implements the core application logic (configuration, blueprint management, URL mapping) without being tied to a specific IO or WSGI environment. - Flask: Defined in
src/flask/app.py, this is the final WSGI-compliant application class. It inherits from the Sans-IOAppand adds request/response handling, session management, and CLI integration.
Scaffold: The Shared Foundation
The Scaffold class establishes the "setup" interface used by developers to build their applications. It manages:
- Resource Discovery: Uses
import_nameandroot_pathto locate static files and templates. - Registration Decorators: Provides methods like
@app.route,@app.before_request, and@app.errorhandler.
# src/flask/sansio/scaffold.py
class Scaffold:
def __init__(
self,
import_name: str,
static_folder: str | os.PathLike[str] | None = None,
static_url_path: str | None = None,
template_folder: str | os.PathLike[str] | None = None,
root_path: str | None = None,
):
self.import_name = import_name
self.static_folder = static_folder
self.template_folder = template_folder
self.root_path = root_path or get_root_path(self.import_name)
# Internal registries for handlers
self.view_functions: dict[str, ft.RouteCallable] = {}
self.before_request_funcs: dict[ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable]] = defaultdict(list)
# ...
App (Sans-IO): Core Logic
The App class extends Scaffold to manage the application's state. It introduces the Config object and the url_map (using Werkzeug's routing system).
# src/flask/sansio/app.py
class App(Scaffold):
def __init__(self, import_name: str, ...):
super().__init__(import_name=import_name, ...)
self.config = self.make_config(instance_relative_config)
self.blueprints: dict[str, Blueprint] = {}
self.extensions: dict[str, t.Any] = {}
self.url_map = self.url_map_class(host_matching=host_matching)
Flask: The WSGI Application
The Flask class is what most developers interact with. It implements the __call__ method to satisfy the WSGI interface and manages the request and application contexts.
# src/flask/app.py
class Flask(App):
def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> cabc.Iterable[bytes]:
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app`, which can be
wrapped with middleware.
"""
return self.wsgi_app(environ, start_response)
Application Lifecycle: Setup vs. Runtime
This project strictly enforces a separation between the Setup Phase and the Request Phase.
The Setup Phase and setupmethod
Methods used to configure the application (like add_url_rule or register_blueprint) are decorated with @setupmethod. This decorator checks the _got_first_request flag. If the application has already started handling requests, calling these methods will raise an AssertionError.
# src/flask/sansio/scaffold.py
def setupmethod(f: F) -> F:
def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any:
self._check_setup_finished(f.__name__)
return f(self, *args, **kwargs)
return update_wrapper(wrapper_func, f)
In src/flask/sansio/app.py, _check_setup_finished is implemented to prevent late configuration:
# src/flask/sansio/app.py
def _check_setup_finished(self, f_name: str) -> None:
if self._got_first_request:
raise AssertionError(
f"The setup method '{f_name}' can no longer be called"
" on the application. It has already handled its first"
" request..."
)
Request Lifecycle Hooks
The Scaffold class defines several hooks that allow developers to inject logic at different points in the request lifecycle:
before_request: Runs before the view function. If it returns a value, that value is treated as the response, and the view is skipped.after_request: Runs after the view function, receiving the response object. It must return a response object.teardown_request: Runs after the response is sent, even if an exception occurred. Used for cleanup (e.g., closing database connections).
Resource Discovery and the Instance Path
The import_name passed to the Flask constructor is critical. It is used to determine the root_path, which Flask uses to find the static and templates folders.
Additionally, the project supports an Instance Path, which is a dedicated folder for configuration and resources that should not be version-controlled (like database files or secrets).
# src/flask/sansio/app.py
def auto_find_instance_path(self) -> str:
"""Locates the 'instance' folder next to the package or module."""
prefix, package_path = find_package(self.import_name)
if prefix is None:
return os.path.join(package_path, "instance")
return os.path.join(prefix, "var", f"{self.name}-instance")
Application Factory Pattern
While a global Flask object can be used, the codebase frequently utilizes the Application Factory Pattern. This involves wrapping the creation of the Flask instance in a function, allowing for multiple instances with different configurations (e.g., for testing).
A standard implementation can be seen in the tutorial example:
# examples/tutorial/flaskr/__init__.py
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY="dev",
DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"),
)
if test_config is None:
app.config.from_pyfile("config.py", silent=True)
else:
app.config.from_mapping(test_config)
# ... registration of blueprints and routes ...
return app