pallets/flask
Read the sans-IO README first — it defines the hardest rule in this repo: nothing in `src/flask/sansio/` may do IO or touch the Flask globals, so context-dependent code belongs in `app.py`/`blueprints.py`. This is the 3.2 development line, so the second thing to internalise is that `RequestContext` no longer exists as a distinct class and that dispatch-related `Flask` methods take `ctx: AppContext` as their first parameter. Every user-visible change needs a `CHANGES.rst` entry with a `:pr:`/`:issue:` reference and a `.. versionadded:: 3.2` / `.. versionchanged:: 3.2` directive in the docstring — this is followed consistently throughout the codebase. Verify with `tox run -e py3.13` (or plain `pytest`), `tox run -e typing`, and `tox run -e style`; `filterwarnings = error` means a new warning without a matching test will break the suite.
- Read first, in order: `src/flask/sansio/README.md` (the IO boundary rule), `src/flask/ctx.py` (the merged `AppContext`), `src/flask/app.py` `wsgi_app`/`full_dispatch_request` (the dispatch spine), and `docs/lifecycle.rst` (the maintainers' own narrative of setup → serving → request).
- Safe edit boundary: code in `src/flask/sansio/` must not import `flask.globals` or perform IO; if a change needs `current_app`, `request` or the filesystem, it belongs in the IO subclass in `src/flask/app.py` or `src/flask/blueprints.py`.
- Trap: if you add or change a `Flask` dispatch method, it must take `ctx: AppContext` as the first parameter after `self`, or `__init_subclass__`'s signature sniffing will treat subclass overrides as legacy and silently rewrap base-class methods.
- Trap: `filterwarnings = ["error"]` turns every warning into a test failure — any new `DeprecationWarning` must be paired with a test that expects it.
- Convention: every public change gets a `CHANGES.rst` bullet under the unreleased version with a `:pr:` or `:issue:` role, as the 3.2.0 section demonstrates for all 13 current entries.
- Convention: docstrings carry Sphinx `.. versionadded::` / `.. versionchanged::` directives naming the exact release — e.g. `select_jinja_autoescape` documents the 3.2 case-insensitivity change and `do_teardown_request` documents the 3.2 all-callbacks change.
- Tests to run: `pytest` (or `tox run` for a specific Python), `tox run -e typing` for mypy+pyright, and `tox run -e style` for the full pre-commit set; docs changes need `tox run -e docs`, which builds with `-W` so any Sphinx warning fails.
- Style is enforced by ruff with `select = ["B", "E", "F", "I", "UP", "W"]` and isort configured `force-single-line = true`, `order-by-type = false` — every import is on its own line throughout `src/`, matching that setting.
- Typing is strict and non-optional: `[tool.mypy] strict = true` over `src` and `tests/type_check`, plus pyright in basic mode; the package ships `src/flask/py.typed`, so any missing annotation is a public-API regression.
- Shared return/callback types live in `src/flask/typing.py` (`ResponseReturnValue`, `ResponseValue`, `HeadersValue`, `AppOrBlueprintKey`); extend these rather than inlining unions, and note `ResponseValue` deliberately includes `AsyncIterable` for Quart's benefit.
- Trap: `Blueprint.get_send_file_max_age` and `Blueprint.send_static_file` duplicate the `Flask` versions by design — a fix in one must be mirrored in the other.
- Trap: registration methods are frozen after the first request by `@setupmethod`/`_check_setup_finished`, so any new registration API must be decorated with `@setupmethod` to stay consistent.
- Trap: `conftest.py`'s autouse `leak_detector` fails any test that leaves a context pushed — when adding context manipulation, make sure pushes and pops balance even on the error path.
- Dependency changes require regenerating `uv.lock` — the `uv-lock` pre-commit hook and the `update-requirements` tox env (`uv lock -U`) both enforce it, and the lock file is bundled into the sdist.
- Don't add new runtime dependencies casually: the six pinned runtime deps are all Pallets-maintained, and optional features (async, dotenv) are gated behind extras with graceful `ImportError` handling at the call site. (inferred)