From 649f04523be9ffb35673163bc0cc7ced12433df5 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sun, 19 Jul 2026 17:02:09 +0300 Subject: [PATCH] fix: declare typing-extensions as core's runtime dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare `pip install lite-bootstrap` + `import lite_bootstrap` (no extras) crashed on undeclared typing_extensions: base.py uses `typing_extensions.Self` and healthchecks_instrument.py's TypedDict is a FastAPI response model that pydantic requires be a `typing_extensions.TypedDict` on Python < 3.12 (stdlib typing.TypedDict is rejected there). So genuine zero-dependency core is not achievable while supporting 3.10/3.11 — declare typing-extensions (pure Python, free-threaded-friendly). Pre-existing (bare 1.2.3 failed identically); surfaced by a clean-room 3.14t install of published 1.3.0. Ships as 1.3.1. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...7-19.03-core-zero-dep-typing-extensions.md | 72 +++++++++++++++++++ planning/releases/1.3.1.md | 30 ++++++++ pyproject.toml | 7 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md create mode 100644 planning/releases/1.3.1.md diff --git a/planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md b/planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md new file mode 100644 index 0000000..d82e2a5 --- /dev/null +++ b/planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md @@ -0,0 +1,72 @@ +--- +summary: Declare `typing-extensions` as core's runtime dependency so a bare `import lite_bootstrap` (no extras) works — the code uses it at runtime and pydantic requires `typing_extensions.TypedDict` on Python < 3.12, so genuine zero-dependency is not achievable while supporting 3.10/3.11. +--- + +# Design: Declare the runtime `typing_extensions` core dependency + +## Summary + +`lite-bootstrap` core imports `typing_extensions` at runtime but never declared +it, so a bare `pip install lite-bootstrap` + `import lite_bootstrap` (no extras) +crashes with `ModuleNotFoundError: No module named 'typing_extensions'`. Declare +`typing-extensions` as core's one dependency. (An attempt to instead *remove* the +usage and keep core zero-dependency failed on Python < 3.12 — see below.) + +## Motivation + +Found by a clean-room install of the published `1.3.0` on a fresh 3.14t venv: +core installed with **zero** declared dependencies (after orjson became opt-in), +but `import lite_bootstrap` immediately raised on `base.py`'s top-level +`import typing_extensions`. Pre-existing (bare `lite-bootstrap==1.2.3` fails +identically — its only core dep, `orjson`, doesn't pull `typing_extensions`); +every install with any extra masked it, since extras pull `typing_extensions` in +transitively. 1.3.0's "zero-dependency core" claim was therefore false. + +Two runtime uses: + +- `base.py:4,24,30` — `typing_extensions.Self` return annotations. +- `healthchecks_instrument.py:8` — `class HealthCheckTypedDict(typing_extensions.TypedDict, ...)`, + used as a FastAPI response model (`health_check_handler() -> HealthCheckTypedDict`). + +## Design + +`pyproject.toml`: `dependencies = ["typing-extensions"]`. `typing-extensions` is +pure Python, so core stays free-threaded-friendly; it is the leanest possible +core (one small, ubiquitous dependency). + +## Rejected: remove the usage to keep core zero-dependency + +Attempted (deferred `Self` annotations via `from __future__ import annotations` + +`TYPE_CHECKING`, and `typing.TypedDict` in healthchecks). It passed locally on +3.12 and on an isolated 3.10 TypedDict construction, but **failed the CI matrix +on 3.10 and 3.11**: pydantic rejects a stdlib `typing.TypedDict` model on Python +< 3.12 — + +``` +PydanticUserError: Please use `typing_extensions.TypedDict` instead of +`typing.TypedDict` on Python < 3.12. +``` + +Because `HealthCheckTypedDict` is a FastAPI/pydantic response model, it must be a +`typing_extensions.TypedDict` for as long as 3.10/3.11 are supported. So core +genuinely needs `typing_extensions` at runtime; declaring it is the honest fix. + +## Non-goals + +- Dropping Python 3.10/3.11 (which would make zero-dep achievable). Out of scope. +- Amending the published `1.3.0` note (immutable); `1.3.1` states the corrected + "one pure-Python dependency" story. + +## Testing + +- Clean-room bare install on 3.14t: `pip install lite-bootstrap` now pulls + `typing-extensions`, and `import lite_bootstrap` + `FreeBootstrapper.bootstrap()` + succeed. +- `just test` 100% coverage on the full 3.10-3.14 matrix (the FastAPI healthcheck + schema builds on every version); `just lint-ci` clean. + +## Risk + +- **None material.** Adds one pure-Python dependency that the code already + required at runtime; strictly fixes a crash. `typing-extensions` is already + present transitively in every non-bare install. diff --git a/planning/releases/1.3.1.md b/planning/releases/1.3.1.md new file mode 100644 index 0000000..226d417 --- /dev/null +++ b/planning/releases/1.3.1.md @@ -0,0 +1,30 @@ +# lite-bootstrap 1.3.1 — fix bare-install import (`typing-extensions`) + +**1.3.1 is a patch release. Fully backward compatible with 1.3.0.** + +## Bug fixes + +- **Bare `import lite_bootstrap` no longer crashes on a missing + `typing_extensions`.** Core uses `typing_extensions` at runtime (a `Self` + return annotation, and a `TypedDict` FastAPI response model that pydantic + requires be a `typing_extensions.TypedDict` on Python < 3.12) but never + declared it, so `pip install lite-bootstrap` with no extras followed by + `import lite_bootstrap` raised `ModuleNotFoundError: No module named + 'typing_extensions'`. `typing-extensions` is now declared as core's single + dependency. It is pure Python, so free-threaded support is unchanged, and this + is the leanest possible core. + + This corrects 1.3.0's note, which called core "zero-dependency": the code + genuinely needs `typing_extensions` while Python 3.10/3.11 are supported. The + bug was pre-existing (bare `1.2.3` failed the same way); every install with any + extra masked it, because extras pull `typing_extensions` in transitively. + +## Backwards compatibility + +Fully compatible with 1.3.0. No API or configuration changes; the only +difference is that a bare-core install now resolves `typing-extensions` and +imports cleanly. + +## References + +- `planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md` diff --git a/pyproject.toml b/pyproject.toml index 4e7bbcf..cfd2792 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,12 @@ classifiers = [ "Topic :: Software Development :: Libraries", ] version = "0" -dependencies = [] +dependencies = [ + # Runtime use: `typing_extensions.Self` (base.py) and `typing_extensions.TypedDict` + # (healthchecks) — the latter is required by pydantic/FastAPI on Python < 3.12 + # (`typing.TypedDict` is rejected there). Pure Python, so ft-friendly. + "typing-extensions", +] [project.urls] Homepage = "https://lite-bootstrap.modern-python.org"