Compare commits

...

2 Commits

Author SHA1 Message Date
John Lancaster 85eb75d188 logging 2026-06-19 17:40:04 -05:00
John Lancaster 5c4de7b721 pytest skill 2026-06-19 17:39:57 -05:00
4 changed files with 35 additions and 48 deletions
-1
View File
@@ -51,7 +51,6 @@ Use this for all stacks first:
Load next reference only if needed: Load next reference only if needed:
- Baseline details and rationale: [pytest-docs.md](./references/pytest-docs.md) - Baseline details and rationale: [pytest-docs.md](./references/pytest-docs.md)
- Condensed quick path variant: [pytest-docs copy.md](./references/pytest-docs copy.md)
### Level 2: FastAPI branch (only for HTTP/dependency/lifespan concerns) ### Level 2: FastAPI branch (only for HTTP/dependency/lifespan concerns)
Escalate here when testing API routes, dependency injection boundaries, or app lifespan behavior. Escalate here when testing API routes, dependency injection boundaries, or app lifespan behavior.
@@ -1,35 +0,0 @@
# Pytest Documentation Notes
Primary references used:
- https://docs.pytest.org/en/stable/explanation/goodpractices.html
- https://docs.pytest.org/en/stable/how-to/fixtures.html
- https://docs.pytest.org/en/stable/example/markers.html
- https://docs.pytest.org/en/stable/reference/customize.html
- https://docs.pytest.org/en/stable/explanation/flaky.html
## Agent Quick Path
Use this file when you need fast pytest scaffolding defaults without framework-specific details.
1. Mirror source layout under `tests/`.
2. Keep fixtures small and explicit; default to `function` scope.
3. Register markers up front in `pyproject.toml`.
4. Validate structure first with `uv run pytest --collect-only -q`.
5. Run fast lane with `uv run pytest -m unit -q`.
Load other references only when needed:
- FastAPI routes/dependency injection/lifespan: `fastapi-testing.md`
- SQLAlchemy sessions/transactions/DB fixtures: `sqlalchemy-testing.md`
## Practical Guidance For This Skill
- Use src-aligned test layout and keep test discovery conventional.
- Keep fixtures small, composable, and explicit; use `yield` for teardown.
- Register custom markers and keep strict marker validation on.
- Separate quick unit runs from slower integration/external runs.
- Minimize flakiness by controlling shared state and avoiding hidden dependencies.
- Use `--collect-only` and marker-filtered runs to validate scaffold quality early.
## Commands Worth Remembering
- `uv run pytest --collect-only -q`
- `uv run pytest -m unit -q`
- `uv run pytest -m "not external" -q`
- `uv run pytest -q`
@@ -7,6 +7,19 @@
- [Configuration reference](https://docs.pytest.org/en/stable/reference/customize.html) - [Configuration reference](https://docs.pytest.org/en/stable/reference/customize.html)
- [Flaky tests](https://docs.pytest.org/en/stable/explanation/flaky.html) - [Flaky tests](https://docs.pytest.org/en/stable/explanation/flaky.html)
## Agent Quick Path
Use this file when you need fast pytest scaffolding defaults without framework-specific details.
1. Mirror source layout under `tests/`.
2. Keep fixtures small and explicit; default to `function` scope.
3. Register markers up front in `pyproject.toml`.
4. Validate structure first with `uv run pytest --collect-only -q`.
5. Run fast lane with `uv run pytest -m unit -q`.
Load other references only when needed:
- FastAPI routes/dependency injection/lifespan: `fastapi-testing.md`
- SQLAlchemy sessions/transactions/DB fixtures: `sqlalchemy-testing.md`
## Practical Guidance For This Skill ## Practical Guidance For This Skill
- Use src-aligned test layout and keep test discovery conventional. - Use src-aligned test layout and keep test discovery conventional.
- Keep fixtures small, composable, and explicit; use `yield` for teardown. - Keep fixtures small, composable, and explicit; use `yield` for teardown.
+22 -12
View File
@@ -12,12 +12,14 @@ Load references only when needed:
- Python logging overview and hierarchy: [Python logging references](./references/python-logging-docs.md) - Python logging overview and hierarchy: [Python logging references](./references/python-logging-docs.md)
## When to Use ## When to Use
- A project configures logging ad hoc with `basicConfig` across multiple modules. - A project configures logging ad hoc with `basicConfig` across multiple modules.
- You need one canonical logging configuration for app startup. - You need one canonical logging configuration for app startup.
- You need consistent formatting and levels across console/file handlers. - You need consistent formatting and levels across console/file handlers.
- You want library modules to use named loggers without configuring logging themselves. - You want library modules to use named loggers without configuring logging themselves.
## Inputs To Collect ## Inputs To Collect
1. Runtime type: script, library, web app, worker, CLI. 1. Runtime type: script, library, web app, worker, CLI.
2. Destinations: stdout only, file only, or both. 2. Destinations: stdout only, file only, or both.
3. Desired default level: `INFO`, `DEBUG`, etc. 3. Desired default level: `INFO`, `DEBUG`, etc.
@@ -30,6 +32,7 @@ If missing, assume:
- `disable_existing_loggers: False` - `disable_existing_loggers: False`
## Procedure ## Procedure
1. Define a single `LOGGING` dictionary in one startup-oriented module (for example `logging_config.py`). 1. Define a single `LOGGING` dictionary in one startup-oriented module (for example `logging_config.py`).
2. Include `version: 1` and set `disable_existing_loggers: False` unless there is a specific reason to silence existing loggers. 2. Include `version: 1` and set `disable_existing_loggers: False` unless there is a specific reason to silence existing loggers.
3. Define formatters first, then handlers, then logger routing (`root` and optional named `loggers`). 3. Define formatters first, then handlers, then logger routing (`root` and optional named `loggers`).
@@ -38,24 +41,28 @@ If missing, assume:
6. Keep libraries configuration-free: libraries should emit logs, applications decide routing. 6. Keep libraries configuration-free: libraries should emit logs, applications decide routing.
7. Verify behavior with a quick smoke check at multiple levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`). 7. Verify behavior with a quick smoke check at multiple levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`).
## Minimal Baseline Template ## Minimal Baseline Templates
```python
# logging_config.py ### Configuration
from logging.config import dictConfig
!!! warning "Don't use the name `logging.py` because it will conflict
```python title="logging_config.py"
import logging.config
LOGGING = { LOGGING = {
"version": 1, "version": 1,
"disable_existing_loggers": False, "disable_existing_loggers": False,
"formatters": { "formatters": {
"standard": { "basic": {
"format": "%(asctime)s %(levelname)s %(name)s: %(message)s" "format": "%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
} }
}, },
"handlers": { "handlers": {
"console": { "console": {
"class": "logging.StreamHandler", "class": "logging.StreamHandler",
"level": "INFO", "formatter": "basic",
"formatter": "standard",
"stream": "ext://sys.stdout", "stream": "ext://sys.stdout",
} }
}, },
@@ -66,22 +73,24 @@ LOGGING = {
} }
def configure_logging() -> None: def configure_logging() -> None:
dictConfig(LOGGING) logging.config.dictConfig(LOGGING)
``` ```
```python ```python title="app.py"
# app startup # app startup
from .logging_config import configure_logging from .logging_config import configure_logging
configure_logging() configure_logging()
``` ```
### Usage
The preferred way of instantiating loggers is at the top of modules like this:
```python ```python
# any module
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.info("module initialized")
``` ```
## Completion Checks ## Completion Checks
@@ -90,6 +99,7 @@ logger.info("module initialized")
3. Modules use `getLogger(__name__)`. 3. Modules use `getLogger(__name__)`.
4. Logs appear at expected level and destination. 4. Logs appear at expected level and destination.
5. Third-party logger noise is intentionally configured or left at defaults. 5. Third-party logger noise is intentionally configured or left at defaults.
6. No module named `logging.py` in the project.
## Branching Guidance ## Branching Guidance
- If structured logs are required: switch formatter output to JSON while keeping `dictConfig` topology unchanged. - If structured logs are required: switch formatter output to JSON while keeping `dictConfig` topology unchanged.