CodeVetter · Repo Unpack · Extension points
pallets/flask
Flask is built almost entirely out of swap points. Class-level attributes on `App`/`Flask` (`request_class`, `response_class`, `json_provider_class`, `config_class`, `app_ctx_globals_class`, `url_rule_class`, `jinja_environment`, `aborter_class`, `session_interface`, `test_client_class`) let subclasses replace nearly every collaborator. Registration flows through `@setupmethod`-guarded decorators that append into dicts keyed by blueprint name or `None`. Third-party CLI commands load from the `flask.commands` entry point group; extensions stash state in `app.extensions[name]`. The JSON layer has two independent extension seams: `JSONProvider` for request/response JSON and `JSONTag`/`TaggedJSONSerializer.register` for session payload types.
- Swappable classes are declared as plain class attributes and instantiated lazily: `aborter_class`, `jinja_environment`, `app_ctx_globals_class`, `config_class`, `json_provider_class`, `url_rule_class`, `url_map_class`, `test_client_class`, `test_cli_runner_class`.
- Hook factories are explicitly designed for override: `make_config`, `make_aborter`, `create_jinja_environment`, `create_global_jinja_loader`, `create_url_adapter`, `auto_find_instance_path`, `select_jinja_autoescape`, `make_shell_context`, `ensure_sync`, `async_to_sync`.
- Third-party CLI commands plug in through the `flask.commands` entry point group, loaded once by `FlaskGroup._load_plugin_commands` before any built-in or app command lookup.
- Extensions store per-app state in `app.extensions`, keyed by the extension module's short name — the celery example demonstrates the convention with `app.extensions["celery"] = celery_app`.
- JSON behaviour is swapped by subclassing `JSONProvider` (implement `dumps`/`loads`; `dump`, `load` and `response` have defaults) and setting `json_provider_class` or assigning `app.json` directly.
- Session payload types extend via `JSONTag` subclasses registered with `TaggedJSONSerializer.register(tag, force=..., index=...)`; the module docstring walks through adding an `OrderedDict` tag at index 0.
- Session storage backends plug in by subclassing `SessionInterface` (implementing `open_session`/`save_session`, which raise `NotImplementedError` in the base) and assigning `app.session_interface`; `null_session_class` and `should_set_cookie` are also override points.
- The registries new handlers append into are all name-keyed dicts on `Scaffold`/`App`: `view_functions`, `error_handler_spec`, `before_request_funcs`, `after_request_funcs`, `teardown_request_funcs`, `url_value_preprocessors`, `url_default_functions`, `template_context_processors`, with `None` meaning app-level.
- `@setupmethod` wraps every registration decorator with `_check_setup_finished`, which raises an `AssertionError` once `_got_first_request` is true — so extensions must complete registration before the first request.
- Custom CLI groups are supported end-to-end: `AppGroup.command` auto-wraps callbacks in `with_appcontext` (disable with `with_appcontext=False`) and `AppGroup.group` defaults nested groups to `AppGroup`; `FlaskGroup(create_app=...)` builds a bespoke script entrypoint.
- Blueprint CLI grouping is a three-way switch in `Blueprint.register`: `cli_group=None` merges commands into `app.cli` flat, the `_sentinel` default nests them under the blueprint name, and an explicit string nests them under that name.
- Template extension points: `template_filter`, `template_test`, `template_global` (usable with or without parentheses as of 3.2) on the app, mirrored by `app_template_filter`/`app_template_test`/`app_template_global` on blueprints, plus `DispatchingJinjaLoader._iter_loaders` for custom template discovery.
- URL building failures are extensible through `app.url_build_error_handlers`, a list consulted by `handle_url_build_error`; functions returning None or re-raising `BuildError` are skipped.
- Blueprints can be nested arbitrarily via `Blueprint.register_blueprint`, with `register` composing parent and child `url_prefix` and `subdomain` and threading `name_prefix` to produce dotted names.
- Deferred setup is available for extensions that need the app object later: `Blueprint.record` / `record_once` append callbacks that receive a `BlueprintSetupState` at registration time.