Skip to content

Commit 3d3b8b8

Browse files
authored
Merge pull request #69 from modern-python/review
small fixes and improvements
2 parents 61bd4b7 + 68b9c95 commit 3d3b8b8

14 files changed

Lines changed: 114 additions & 37 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ dist/
1919
.python-version
2020
.venv
2121
uv.lock
22+
plan.md

CLAUDE.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
just install # Update lock file and sync all extras + lint group
9+
just lint # Format and lint (eof-fixer, ruff format, ruff check --fix, ty check)
10+
just lint-ci # CI lint in check-only mode (no auto-fix)
11+
just test # Run pytest with coverage
12+
just test -- -k "test_name" # Run a single test
13+
just test-branch # Run tests with branch coverage
14+
```
15+
16+
All commands use `uv run` — do not invoke tools directly (e.g., use `uv run pytest`, not `pytest`).
17+
18+
## Architecture
19+
20+
**lite-bootstrap** bootstraps Python microservices with pre-configured observability instruments.
21+
22+
### Core pattern
23+
24+
```
25+
BaseConfig (dataclass, frozen, kw_only)
26+
└── Framework configs compose multiple instrument configs via multiple inheritance
27+
28+
BaseInstrument (abstract)
29+
└── Instrument subclasses: lifecycle via bootstrap() / teardown() / is_ready()
30+
31+
BaseBootstrapper (abstract)
32+
├── FastAPIBootstrapper
33+
├── LitestarBootstrapper
34+
├── FastStreamBootstrapper
35+
└── FreeBootstrapper
36+
```
37+
38+
### Key design decisions
39+
40+
- **Optional dependencies**: Each instrument checks for its optional package via `import_checker.py` (`importlib.util.find_spec`). Instruments are skipped silently if the package is absent.
41+
- **Frozen dataclass configs**: All configs are `@dataclasses.dataclass(kw_only=True, frozen=True)`. `from_dict()` and `from_object()` filter unknown keys before constructing.
42+
- **Instrument registry**: `BaseBootstrapper` holds a list of instrument instances; it calls `bootstrap()` on each in order and `teardown()` in reverse during shutdown.
43+
- **Logging ↔ Sentry integration**: `logging_instrument.py` injects structlog context into Sentry events. `sentry_instrument.py` chains `before_send` callbacks via `wrap_before_send_callbacks()`. The `skip_sentry` flag in log context suppresses events.
44+
- **OTel ↔ Logging integration**: The logging instrument injects span/trace IDs from the active OpenTelemetry context into every log record.
45+
46+
### Module layout
47+
48+
- `lite_bootstrap/bootstrappers/` — framework-specific bootstrappers and their config classes
49+
- `lite_bootstrap/instruments/` — individual instrument implementations (one file per tool)
50+
- `lite_bootstrap/helpers/` — utility functions (`fastapi_helpers.py` serves offline Swagger UI assets)
51+
- `lite_bootstrap/import_checker.py` — detects installed optional packages
52+
- `lite_bootstrap/types.py` — shared TypeVars
53+
54+
### Optional dependency groups
55+
56+
Install via `pip install lite-bootstrap[<group>]` or `uv add lite-bootstrap[<group>]`:
57+
58+
| Group | Contents |
59+
|-------|----------|
60+
| `fastapi-all` | fastapi + sentry + otl + logging + metrics |
61+
| `litestar-all` | litestar + sentry + otl + logging |
62+
| `faststream-all` | faststream + sentry + otl + logging |
63+
| `free-all` | sentry + otl + logging |
64+
65+
## Code style
66+
67+
- Line length: 120 characters (ruff enforced)
68+
- Ruff ALL rules enabled; notable ignores: D1 (missing docstrings), S101 (assert), TCH (type-checking imports), FBT (boolean args)
69+
- Type annotations required; checked with `ty`

docs/integrations/fastapi.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22

33
*Another example of usage with FastAPI - [fastapi-sqlalchemy-template](https://github.com/modern-python/fastapi-sqlalchemy-template)*
44

5-
## 1. Install `lite-bootstrapp[fastapi-all]`:
5+
## 1. Install `lite-bootstrap[fastapi-all]`:
66

77
=== "uv"
8-
8+
99
```bash
10-
uv add lite-bootstrapp[fastapi-all]
10+
uv add lite-bootstrap[fastapi-all]
1111
```
12-
12+
1313
=== "pip"
1414

1515
```bash
16-
pip install lite-bootstrapp[fastapi-all]
16+
pip install lite-bootstrap[fastapi-all]
1717
```
1818

1919
=== "poetry"
2020

2121
```bash
22-
poetry add lite-bootstrapp[fastapi-all]
22+
poetry add lite-bootstrap[fastapi-all]
2323
```
2424

2525
Read more about available extras [here](../../../introduction/installation):

docs/integrations/faststream.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
# Usage with `FastStream`
22

3-
## 1. Install `lite-bootstrapp[faststream-all]`:
3+
## 1. Install `lite-bootstrap[faststream-all]`:
44

55
=== "uv"
66

77
```bash
8-
uv add lite-bootstrapp[faststream-all]
8+
uv add lite-bootstrap[faststream-all]
99
```
1010

1111
=== "pip"
1212

1313
```bash
14-
pip install lite-bootstrapp[faststream-all]
14+
pip install lite-bootstrap[faststream-all]
1515
```
1616

1717
=== "poetry"
1818

1919
```bash
20-
poetry add lite-bootstrapp[faststream-all]
20+
poetry add lite-bootstrap[faststream-all]
2121
```
2222

2323
Read more about available extras [here](../../../introduction/installation):

docs/integrations/free.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
# Usage without frameworks
22

3-
## 1. Install `lite-bootstrapp[free-all]`:
3+
## 1. Install `lite-bootstrap[free-all]`:
44

55
=== "uv"
6-
6+
77
```bash
8-
uv add lite-bootstrapp[free-all]
8+
uv add lite-bootstrap[free-all]
99
```
10-
10+
1111
=== "pip"
1212

1313
```bash
14-
pip install lite-bootstrapp[free-all]
14+
pip install lite-bootstrap[free-all]
1515
```
1616

1717
=== "poetry"
1818

1919
```bash
20-
poetry add lite-bootstrapp[free-all]
20+
poetry add lite-bootstrap[free-all]
2121
```
2222

2323
Read more about available extras [here](../../../introduction/installation):

docs/integrations/litestar.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@
77
=== "uv"
88

99
```bash
10-
uv add lite-bootstrapp[litestar-all]
10+
uv add lite-bootstrap[litestar-all]
1111
```
1212

1313
=== "pip"
1414

1515
```bash
16-
pip install lite-bootstrapp[litestar-all]
16+
pip install lite-bootstrap[litestar-all]
1717
```
1818

1919
=== "poetry"
2020

2121
```bash
22-
poetry add lite-bootstrapp[litestar-all]
22+
poetry add lite-bootstrap[litestar-all]
2323
```
2424

2525
Read more about available extras [here](../../../introduction/installation):

docs/introduction/configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ Additional parameters can also be supplied through the settings object:
1717
- `sentry_attach_stacktrace` - if True, stack traces are automatically attached to all messages logged
1818
- `sentry_integrations` - list of integrations to enable
1919
- `sentry_tags` - key/value string pairs that are both indexed and searchable
20-
- `sentry_additional_params** - additional params, which will be passed to `sentry_sdk.init`
20+
- `sentry_additional_params` - additional params, which will be passed to `sentry_sdk.init`
21+
- `sentry_default_integrations` - whether to use sentry's default integrations (default: `True`)
22+
- `sentry_before_send` - optional callback chained after the built-in structlog enricher, passed to `sentry_sdk.init(before_send=...)`
2123

2224
Read more about sentry_sdk params [here](https://docs.sentry.io/platforms/python/configuration/options/).
2325

docs/introduction/installation.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ For example, if you want to bootstrap litestar with structlog and opentelemetry
2525
=== "uv"
2626

2727
```bash
28-
uv add lite-bootstrapp[litestar-logging,litestar-otl]
28+
uv add lite-bootstrap[litestar-logging,litestar-otl]
2929
```
3030

3131
=== "pip"
3232

3333
```bash
34-
pip install lite-bootstrapp[litestar-logging,litestar-otl]
34+
pip install lite-bootstrap[litestar-logging,litestar-otl]
3535
```
3636

3737
=== "poetry"
3838

3939
```bash
40-
poetry add lite-bootstrapp[litestar-logging,litestar-otl]
40+
poetry add lite-bootstrap[litestar-logging,litestar-otl]
4141
```

lite_bootstrap/bootstrappers/base.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020

2121
class BaseBootstrapper(abc.ABC, typing.Generic[ApplicationT]):
22-
SLOTS = "bootstrap_config", "instruments", "is_bootstrapped"
2322
instruments_types: typing.ClassVar[list[type[BaseInstrument]]]
2423
instruments: list[BaseInstrument]
2524
bootstrap_config: BaseConfig

lite_bootstrap/bootstrappers/fastapi_bootstrapper.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
class FastAPIConfig(
3939
CorsConfig, HealthChecksConfig, LoggingConfig, OpentelemetryConfig, PrometheusConfig, SentryConfig, SwaggerConfig
4040
):
41-
application: "fastapi.FastAPI" = dataclasses.field(default=None) # type: ignore[assignment]
41+
application: "fastapi.FastAPI" = dataclasses.field(default=None) # ty: ignore[invalid-assignment]
4242
application_kwargs: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
4343
opentelemetry_excluded_urls: list[str] = dataclasses.field(default_factory=list)
4444
prometheus_instrumentator_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
@@ -59,12 +59,12 @@ def __post_init__(self) -> None:
5959

6060

6161
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
62-
class FastApiCorsInstrument(CorsInstrument):
62+
class FastAPICorsInstrument(CorsInstrument):
6363
bootstrap_config: FastAPIConfig
6464

6565
def bootstrap(self) -> None:
6666
self.bootstrap_config.application.add_middleware(
67-
CORSMiddleware, # ty: ignore[invalid-argument-type]
67+
CORSMiddleware,
6868
allow_origins=self.bootstrap_config.cors_allowed_origins,
6969
allow_methods=self.bootstrap_config.cors_allowed_methods,
7070
allow_headers=self.bootstrap_config.cors_allowed_headers,
@@ -152,7 +152,7 @@ def bootstrap(self) -> None:
152152

153153

154154
@dataclasses.dataclass(kw_only=True, frozen=True)
155-
class FastApiSwaggerInstrument(SwaggerInstrument):
155+
class FastAPISwaggerInstrument(SwaggerInstrument):
156156
bootstrap_config: FastAPIConfig
157157

158158
def bootstrap(self) -> None:
@@ -172,13 +172,13 @@ class FastAPIBootstrapper(BaseBootstrapper["fastapi.FastAPI"]):
172172
__slots__ = "bootstrap_config", "instruments"
173173

174174
instruments_types: typing.ClassVar = [
175-
FastApiCorsInstrument,
175+
FastAPICorsInstrument,
176176
FastAPIOpenTelemetryInstrument,
177177
FastAPISentryInstrument,
178178
FastAPIHealthChecksInstrument,
179179
FastAPILoggingInstrument,
180180
FastAPIPrometheusInstrument,
181-
FastApiSwaggerInstrument,
181+
FastAPISwaggerInstrument,
182182
]
183183
bootstrap_config: FastAPIConfig
184184
not_ready_message = "fastapi is not installed"

0 commit comments

Comments
 (0)