Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions planning/changes/2026-07-19.03-core-zero-dep-typing-extensions.md
Original file line number Diff line number Diff line change
@@ -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.
30 changes: 30 additions & 0 deletions planning/releases/1.3.1.md
Original file line number Diff line number Diff line change
@@ -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`
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading