bgg-search 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ exclude_dirs:
2
+ - tests
3
+ - .venv
@@ -0,0 +1,7 @@
1
+ [run]
2
+ source = bgg_search
3
+ branch = True
4
+
5
+ [report]
6
+ fail_under = 95
7
+ show_missing = True
@@ -0,0 +1,44 @@
1
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/streetsidesoftware/cspell/main/packages/cspell-types/cspell.schema.json
2
+
3
+ version: "0.2"
4
+ language: en-US
5
+
6
+ dictionaries:
7
+ - python
8
+ - html
9
+ - xml
10
+ - bash
11
+ - markdown
12
+ - software-terms
13
+
14
+ words:
15
+ - bgg
16
+ - boardgame
17
+ - boardgamegeek
18
+ - xmlapi
19
+ - httpx
20
+ - pydantic
21
+ - pytest
22
+ - mypy
23
+ - ruff
24
+ - pyproject
25
+ - editable
26
+ - geek
27
+ - conftest
28
+ - stdlib
29
+ - integ
30
+ - Arnauld
31
+ - Muysewinkel
32
+ - coveragerc
33
+ - pytest-cov
34
+
35
+ ignorePaths:
36
+ - .git/**
37
+ - .venv/**
38
+ - "**/__pycache__/**"
39
+ - "**/*.egg-info/**"
40
+ - dist/**
41
+ - build/**
42
+
43
+ ignoreRegExpList:
44
+ - "https?://[^\\s]+"
@@ -0,0 +1,16 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ tox:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-python@v5
13
+ with:
14
+ python-version: "3.13"
15
+ - run: pip install tox~=4.55.0
16
+ - run: tox
@@ -0,0 +1,21 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["version/*"]
6
+
7
+ permissions:
8
+ id-token: write
9
+ contents: read
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.13"
19
+ - run: pip install build
20
+ - run: python -m build
21
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,13 @@
1
+ # Virtualenvs
2
+ .venv/
3
+
4
+ # Build artifacts
5
+ dist/
6
+ *.egg-info/
7
+
8
+ # Caches
9
+ __pycache__/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .tox/
13
+ .coverage
@@ -0,0 +1,9 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: tox
5
+ name: tox
6
+ entry: .venv/bin/tox
7
+ language: system
8
+ pass_filenames: false
9
+ always_run: true
@@ -0,0 +1,268 @@
1
+ # BGG Search — Agent Guide
2
+
3
+ ## Project context
4
+
5
+ `bgg-search` is a personal Python package for querying [BoardGameGeek](https://boardgamegeek.com) (BGG).
6
+ It wraps the BGG XML API2 and exposes:
7
+
8
+ - a clean, reusable Python API for searching games, retrieving game details, and filtering results;
9
+ - a generic CLI for interacting with the BGG API from the command line.
10
+
11
+ This is a personal project; prioritize clarity and correctness over enterprise-grade robustness.
12
+
13
+ Rationale behind key choices is documented in [DECISIONS.md](DECISIONS.md).
14
+ The project initiation workflow and branch/version map are documented in [PROCESS.md](PROCESS.md).
15
+
16
+ ### Repository layout (target structure)
17
+
18
+ ```
19
+ bgg-search/
20
+ ├── src/
21
+ │ └── bgg_search/ # installable package
22
+ │ ├── __init__.py # explicit public API surface
23
+ │ ├── models.py # pure domain dataclasses
24
+ │ ├── exceptions.py # all domain exceptions
25
+ │ ├── _protocol.py # BggClientProtocol (typing.Protocol)
26
+ │ ├── _client.py # concrete httpx client + XML parsing
27
+ │ ├── search.py # use-case layer
28
+ │ └── cli.py # CLI adapter (I/O only)
29
+ ├── tests/
30
+ │ ├── conftest.py
31
+ │ ├── unit/ # fast, purely local tests
32
+ │ │ ├── conftest.py
33
+ │ │ └── test_*.py
34
+ │ └── integ/ # slow, hits the real BGG API
35
+ │ ├── conftest.py
36
+ │ └── test_*.py
37
+ ├── requirements/
38
+ │ ├── runtime.in # package runtime deps (unpinned spec)
39
+ │ ├── runtime.txt # locked runtime deps
40
+ │ ├── dev.in # tooling: tox, ruff, mypy, bandit (unpinned spec)
41
+ │ ├── dev.txt # locked tooling deps
42
+ │ ├── unit.in # unit test deps (unpinned spec)
43
+ │ ├── unit.txt # locked unit test deps
44
+ │ ├── integ.in # integ test deps (unpinned spec)
45
+ │ ├── integ.txt # locked integ test deps
46
+ │ ├── audit.in # pip-audit (unpinned spec)
47
+ │ └── audit.txt # locked audit deps
48
+ ├── pyproject.toml # package metadata and build system only
49
+ ├── CHANGELOG.md # user-facing change history (Keep a Changelog format)
50
+ ├── DECISIONS.md # rationale behind key design and package choices
51
+ ├── PROCESS.md # project initiation workflow and branch/version map
52
+ ├── README.md # public-facing project documentation
53
+ └── AGENTS.md # guide for AI agents (this file)
54
+ ```
55
+
56
+ Transient files (not always present):
57
+
58
+ | File | Scope | Purpose |
59
+ |------|-------|---------|
60
+ | `ROADMAP.md` | `main` | succession of phases toward MVP |
61
+ | `PHASE_PLAN.md` | `main` | decomposition of the current phase |
62
+ | `PLAN.md` | feature branch | steps for the current feature/change |
63
+
64
+ ## Development environment
65
+
66
+ Dependencies are managed with **uv**. Each context has its own requirements file (see `requirements/`).
67
+ `.in` files are the unpinned specs; `.txt` files are the locked versions generated from them.
68
+
69
+ ```bash
70
+ # create and activate a virtual environment
71
+ uv venv --python 3.13 && source .venv/bin/activate
72
+
73
+ # install the package in editable mode with locked dev deps
74
+ uv pip install -e . -r requirements/dev.txt
75
+
76
+ # register the pre-commit hook (once per clone)
77
+ pre-commit install
78
+ ```
79
+
80
+ There is no `setup.py`. `pyproject.toml` contains package metadata and build system only; all dependency declarations live in `requirements/`.
81
+
82
+ ### Commands
83
+
84
+ All tasks run through **tox**, which executes each step in an isolated virtualenv.
85
+
86
+ | Task | Command |
87
+ |------|---------|
88
+ | Full quality gate (lint + type + security + unit tests) | `tox` |
89
+ | Lint / format | `tox -e lint` |
90
+ | Type-check | `tox -e type` |
91
+ | Security scan (bandit) | `tox -e security` |
92
+ | Unit tests | `tox -e unit` |
93
+ | Integration tests | `tox -e integ` |
94
+ | Dependency audit (pip-audit) | `tox -e audit` |
95
+ | Re-lock all dependencies | `tox -e lock` |
96
+
97
+ Run `tox` before every commit.
98
+ Before each release, also run: `tox -e lock`, then `tox -e audit`, then `tox -e integ`.
99
+
100
+ To fix formatting issues reported by `tox -e lint`, run `ruff format .` locally then re-run tox.
101
+
102
+ ### Tool configuration
103
+
104
+ Prefer individual configuration files per tool (e.g., `.bandit`, `mypy.ini`, `ruff.toml`) over consolidating everything into `pyproject.toml`. Use `pyproject.toml` only for package metadata and build system configuration.
105
+
106
+ ## Development workflow
107
+
108
+ Every change (feature, bug fix, refactoring, …) follows this process:
109
+
110
+ 1. **Create a branch**: cut a short-lived branch from `main`, named after the intent (e.g., `feat/search-by-rank`).
111
+ 2. **Write a plan**: create `PLAN.md` on the branch; describe the steps before writing any code. One step = one future commit. Commit `PLAN.md` immediately so it is tracked and stays branch-local. (`PLAN.md` is the feature-level plan; the phase-level plan lives in `PHASE_PLAN.md` on `main` — see [PROCESS.md](PROCESS.md).)
112
+ 3. **Review the plan**: adapt it before starting execution.
113
+ 4. **Execute step by step** — for each step in `PLAN.md`:
114
+ - Edit code and adapt tests.
115
+ - Review the changes.
116
+ - Run `tox` (full quality gate).
117
+ - Loop until clean.
118
+ - Commit.
119
+ 5. **Remove `PLAN.md`**: delete it in a dedicated commit (`chore(PLAN.md): remove branch-local plan before merge`). This ensures it is never merged into `main`.
120
+ 6. **Merge and delete the branch**: merge into `main` with `--no-ff`, then delete the branch (`git branch -d <branch>`).
121
+ 7. **Run integration tests**: `tox -e integ`; loop until clean.
122
+ 8. **Release**: bump version (Y for a feature or significant fix; Z for a hotfix on a past version), update `CHANGELOG.md`, run `tox -e lock && tox -e audit`, tag `version/X.Y.Z`.
123
+
124
+ ### Step quality rules
125
+
126
+ Each step (commit) must be:
127
+ - **Localized**: touch as few files as possible — ideally one; as few sections within that file as possible.
128
+ - **Consistent**: all changes in the step serve a single, coherent purpose.
129
+ - **Clean**: the codebase must pass `tox` at the end of every step, with no known errors left behind.
130
+
131
+ `PLAN.md` is branch-local and must **not** be merged into `main` (enforced by step 5 above).
132
+
133
+ ## Architecture
134
+
135
+ ### Modularization
136
+
137
+ Modules are organized in layers. Inner layers never import from outer layers:
138
+
139
+ ```
140
+ cli.py → search.py → _protocol.py → models.py
141
+ ↘ exceptions.py
142
+ _client.py → _protocol.py, models.py, exceptions.py
143
+ ```
144
+
145
+ Rules:
146
+
147
+ - `models.py` and `exceptions.py`: no imports from this package.
148
+ - `_protocol.py`: imports `models.py` only; defines `BggClientProtocol` (`typing.Protocol`).
149
+ - `_client.py`: internal (underscore prefix); never imported directly by callers; implements `BggClientProtocol`.
150
+ - `search.py`: depends on `BggClientProtocol`, not on `_client.py`; receives a client instance via parameter.
151
+ - `cli.py`: pure I/O adapter — parse args, call `search.py`, format output; no business logic.
152
+ - `__init__.py`: explicitly re-exports the public API; adding internal modules never accidentally becomes public.
153
+
154
+ ### Code conventions
155
+
156
+ - **Python 3.13**; use built-in `tomllib`, `match`/`case`, `TypeAlias`, etc. where appropriate.
157
+ - Public functions and classes must have type annotations; internal helpers may omit them only when obvious.
158
+ - No comments that restate what the code already says. Comment the *why*, not the *what*.
159
+ - Raise domain-specific exceptions (subclass `BggSearchError`) instead of leaking `httpx` errors.
160
+
161
+ ### Package selection rules
162
+
163
+ When choosing packages, apply these rules in order:
164
+
165
+ 1. **Prefer stdlib** over external packages when the functionality is equivalent.
166
+ 2. **Prefer well-maintained external packages**: large community, frequent and recent releases.
167
+ 3. **Prefer packages with fewer transitive dependencies** when other criteria are equal.
168
+
169
+ Concretely for this project:
170
+ - XML parsing → `xml.etree.ElementTree` (stdlib), not `lxml` or `beautifulsoup4`
171
+ - HTTP → `httpx` (active community, minimal deps) over `requests` (heavier dep tree) or `aiohttp`
172
+ - Data models → `dataclasses` (stdlib) when validation is not needed; `pydantic>=2` only when input validation is required
173
+ - Date/time → `datetime` (stdlib), not `arrow` or `pendulum`
174
+
175
+ ### Testing
176
+
177
+ Use `pytest`; never use `unittest` directly.
178
+ Aim for behavior coverage, not line coverage — test what the public API promises, not implementation details.
179
+
180
+ #### Unit tests (`tests/unit/`)
181
+
182
+ - Purely local: no network, no filesystem side-effects.
183
+ - Mock HTTP with `httpx.MockTransport`; do **not** add `pytest-httpx`.
184
+ - One file per source module: `tests/unit/test_client.py`, `tests/unit/test_search.py`, etc.
185
+ - Must be fast enough to run after every code change.
186
+
187
+ #### Integration tests (`tests/integ/`)
188
+
189
+ - Hit the real BGG API; require network access.
190
+ - Run only explicitly (e.g., before a release) — never triggered automatically.
191
+ - Keep the number of requests minimal: one test must not make more API calls than strictly necessary.
192
+ - Add a `time.sleep` between requests to avoid flooding the BGG API.
193
+ - One file per high-level scenario: `tests/integ/test_search_flow.py`, etc.
194
+
195
+ ### Things to avoid
196
+
197
+ - Do **not** add logging configuration at module level; leave that to the caller.
198
+ - Do **not** cache BGG responses unless caching is explicitly requested.
199
+ - Do **not** commit `.venv/`, `__pycache__/`, or `*.egg-info/` (ensure `.gitignore` covers them).
200
+
201
+ ## Conventions
202
+
203
+ ### Language
204
+
205
+ Use American English for text (messages, documentation, commits...)
206
+
207
+ ### Commit format
208
+
209
+ Use commits of the form `<action>(<scope>):<description>`, where:
210
+
211
+ - `<action>` is one of "add", "upd", "feat", "refactor", "chore"...
212
+ - `<scope>` is a compact spec of the files full path (relative to project root)
213
+ (exceptionally it may be omitted, when the commit concerns the whole project);
214
+ optionally followed by ` > <location>` to pinpoint the change within the file
215
+ (e.g. a class name, function name, or config section): `src/bgg_search/client.py > BggClient.search`
216
+ - `<description>` is a short sentence describing the modification (do not repeat the action)
217
+
218
+ Do not add `Co-Authored-By` trailers. AI involvement is documented once in this file.
219
+
220
+ ### Version management
221
+
222
+ Versions follow [Semantic Versioning](https://semver.org) (`MAJOR.MINOR.PATCH`):
223
+
224
+ - **MAJOR**: incompatible API change.
225
+ - **MINOR**: new feature, or a significant fix on the latest version.
226
+ - **PATCH**: hotfix backported to a past version (branch cut from an old release tag, not `main`).
227
+
228
+ Between releases the version carries the `.dev0` suffix (PEP 440), signalling that the code is not yet a stable build. The canonical workflow:
229
+
230
+ 1. Before release: set version to `X.Y.Z` in `pyproject.toml`, update `CHANGELOG.md`, commit, tag `version/X.Y.Z`.
231
+ 2. After release: immediately bump to `X.(Y+1).0.dev0` and commit.
232
+
233
+ The version is declared once, in `pyproject.toml` (`version = "..."`); the package exposes it via `importlib.metadata`:
234
+
235
+ ```python
236
+ from importlib.metadata import version
237
+ __version__ = version("bgg-search")
238
+ ```
239
+
240
+ Do **not** hard-code the version string anywhere else in the source.
241
+
242
+ ### Changelog
243
+
244
+ `CHANGELOG.md` follows the [Keep a Changelog](https://keepachangelog.com) format (version 1.0.0).
245
+
246
+ Rules:
247
+
248
+ - Always keep an `## [Unreleased]` section at the top.
249
+ - Add an entry under `[Unreleased]` for every user-facing change (new feature, fix, removed behavior). Internal refactors and tooling changes do not need an entry.
250
+ - Use the standard subsections: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`.
251
+ - On release: rename `[Unreleased]` to `[x.y.z] - YYYY-MM-DD` and open a fresh `[Unreleased]` section above it.
252
+ - Do **not** edit past release sections.
253
+
254
+ ## Reference
255
+
256
+ ### BGG XML API2
257
+
258
+ Base URL: `https://boardgamegeek.com/xmlapi2/`
259
+
260
+ Key endpoints used in this project:
261
+
262
+ | Endpoint | Purpose |
263
+ |----------|---------|
264
+ | `search?query=<q>&type=boardgame` | Search games by name |
265
+ | `thing?id=<id>&stats=1` | Fetch full game details |
266
+ | `collection?username=<u>&own=1` | Fetch a user's owned games |
267
+
268
+ The API is unauthenticated and rate-limited; add a short `time.sleep` between bulk requests.
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-07
11
+
12
+ ### Added
13
+
14
+ - Initial release of `bgg-search`: a Python client and CLI for the BoardGameGeek XML API.
15
+ - Package installable from PyPI; requires Python ≥ 3.13.
16
+ - `bgg_search.__version__` exposes the installed package version.
17
+ - MIT license.
@@ -0,0 +1,154 @@
1
+ # Design decisions
2
+
3
+ Rationale behind key choices made in this project.
4
+ See [AGENTS.md](AGENTS.md) for the actionable rules derived from these decisions.
5
+
6
+ ---
7
+
8
+ ## uv as dependency manager
9
+
10
+ `uv` replaces `pip` + `venv` for environment and dependency management.
11
+
12
+ - **Speed**: written in Rust; resolves and installs orders of magnitude faster than pip.
13
+ - **Single tool**: handles virtualenv creation (`uv venv`), installation (`uv pip install`), and locking (`uv pip compile`) — no need for pip-tools alongside pip.
14
+ - **pip-compatible**: `uv pip compile` produces standard `requirements.txt` files; `uv pip install -r` accepts them. No lock-in to a proprietary format.
15
+ - **Actively maintained**: developed by Astral (same team as ruff), large and fast-growing community.
16
+
17
+ The `.in` / `.txt` split (spec vs. lock) follows the pip-tools convention, which uv fully supports.
18
+
19
+ ---
20
+
21
+ ## Individual tool configuration files over pyproject.toml
22
+
23
+ Each tool (`ruff`, `mypy`, `bandit`, `tox`, …) gets its own configuration file rather than a consolidated `[tool.*]` section in `pyproject.toml`.
24
+
25
+ Reasons:
26
+
27
+ - **Discoverability**: a developer (or agent) looking for ruff's config opens `ruff.toml` directly; they do not need to know which tools happen to store config in `pyproject.toml` and scroll through an unrelated file.
28
+ - **Separation of concerns**: `pyproject.toml` is the package manifest (metadata, dependencies, build system). Mixing tool config into it conflates two distinct responsibilities.
29
+ - **Diff clarity**: changes to a tool's config appear in that tool's file, not buried in `pyproject.toml` alongside unrelated edits.
30
+ - **Portability**: individual config files work even when the tool is invoked outside the Python packaging context (e.g., in a pre-commit hook, a CI step, or a standalone script).
31
+
32
+ `pyproject.toml` retains only what belongs there: `[project]` metadata (including runtime `dependencies`) and `[build-system]`.
33
+
34
+ ---
35
+
36
+ ## Python 3.13 as target version
37
+
38
+ Criteria for choosing a Python version (in order):
39
+
40
+ 1. **Active support window**: target a version with several years of security fixes remaining; avoid versions nearing EOL.
41
+ 2. **Ecosystem readiness**: all dependencies must support the target version.
42
+ 3. **Feature set**: prefer newer versions for language improvements and performance gains.
43
+ 4. **Project type**: a published library must support older versions for broad compatibility; a personal tool can freely track the latest stable.
44
+
45
+ As of June 2026, Python 3.13 is the latest stable release (EOL Oct 2029), all project dependencies support it, and 3.14 is still pre-release. There is no reason to stay on an older version for a personal tool.
46
+
47
+ The version pin (`3.13`, not `≥ 3.13`) is intentional: it makes the runtime explicit and reproducible. Upgrade deliberately when 3.14 stabilizes and the ecosystem catches up.
48
+
49
+ ---
50
+
51
+ ## Modular architecture and separation of concerns
52
+
53
+ The package is organized in layers with a strict inward dependency direction. Each decision below addresses a specific maintainability or testability concern.
54
+
55
+ ### Domain models and exceptions are dependency-free
56
+
57
+ `models.py` and `exceptions.py` import nothing from this package or from external libraries.
58
+ This makes them the stable core: any other module can import them without risk of circular imports,
59
+ and they can be read and understood without knowledge of httpx, XML, or argparse.
60
+
61
+ ### Abstract client protocol (`_protocol.py`)
62
+
63
+ `search.py` (the use-case layer) depends on `BggClientProtocol` — a `typing.Protocol` — rather
64
+ than on the concrete `_client.py`. This decouples business logic from HTTP and XML details:
65
+
66
+ - Unit tests for `search.py` pass a fake/stub client with no httpx involved.
67
+ - The concrete client can be replaced (e.g., async variant, cached variant) without touching `search.py`.
68
+ - The protocol is the contract; the client is an implementation detail.
69
+
70
+ ### Concrete client is internal (`_client.py`)
71
+
72
+ The underscore prefix signals that `_client.py` is not part of the public API.
73
+ Callers (including `cli.py`) never import it directly; they receive a client instance via
74
+ dependency injection. This means:
75
+
76
+ - XML parsing is fully encapsulated: swapping the parser is a one-file change.
77
+ - The HTTP layer can evolve (e.g., connection pooling, retries) without rippling through the codebase.
78
+
79
+ ### CLI is a pure I/O adapter (`cli.py`)
80
+
81
+ `cli.py` contains no business logic. It only:
82
+ 1. Parses command-line arguments.
83
+ 2. Calls `search.py` functions.
84
+ 3. Formats and prints output.
85
+
86
+ This separation means the Python API and the CLI are independently testable and independently
87
+ evolvable. A future GUI or REST wrapper would reuse `search.py` without touching `cli.py`.
88
+
89
+ ### Explicit public API (`__init__.py`)
90
+
91
+ `__init__.py` explicitly re-exports everything that is public. Adding a new internal module
92
+ (e.g., a caching layer) never accidentally leaks into the package's public surface.
93
+ Consumers of the library import from `bgg_search`, not from `bgg_search._client`.
94
+
95
+ ---
96
+
97
+ ## dataclasses over pydantic (default)
98
+
99
+ Use `dataclasses` (stdlib) for internal data structures where no input validation is needed.
100
+ Reach for `pydantic>=2` only when validating external input (e.g., deserializing API responses
101
+ where field types or constraints must be enforced at runtime).
102
+
103
+ This applies the **prefer stdlib** rule from the [Package selection policy](#package-selection-policy).
104
+
105
+ ---
106
+
107
+ ## Package selection policy
108
+
109
+ **Rules (in priority order):**
110
+
111
+ 1. Prefer stdlib over external packages when the functionality is equivalent.
112
+ 2. Among external packages, prefer those with a large community and frequent recent releases.
113
+ 3. Among equally suitable packages, prefer those with fewer transitive dependencies.
114
+
115
+ ---
116
+
117
+ ## pytest over unittest
118
+
119
+ `unittest` is stdlib, but its functionality is not equivalent to `pytest`:
120
+
121
+ - **Fixtures**: `pytest` fixture injection is composable and scope-aware; `unittest` setUp/tearDown is flat and class-scoped.
122
+ - **Parametrize**: `@pytest.mark.parametrize` is concise; the `unittest` equivalent (`subTest` or external libs) is verbose.
123
+ - **Assertions**: `pytest` rewrites plain `assert` statements and produces detailed diffs; `unittest` requires `assertEqual`, `assertIn`, etc.
124
+ - **Ecosystem**: plugins (`pytest-cov`, `pytest-xdist`, …) integrate naturally.
125
+
126
+ `pytest` has a very large community, is released frequently, and has minimal transitive dependencies.
127
+ The "prefer stdlib" rule does not apply here because the functionality is not equivalent.
128
+
129
+ ---
130
+
131
+ ## httpx.MockTransport over pytest-httpx
132
+
133
+ `pytest-httpx` is a thin convenience layer on top of `httpx`'s own `MockTransport`.
134
+ The built-in transport covers the same use case (intercept requests, return canned responses)
135
+ with no extra dependency — applying the **prefer stdlib/built-in** and **fewer transitive dependencies**
136
+ rules from the [Package selection policy](#package-selection-policy).
137
+
138
+ The trade-off is a few more lines of fixture boilerplate in `tests/conftest.py`.
139
+
140
+ ---
141
+
142
+ ## httpx over requests
143
+
144
+ `httpx` has a smaller transitive dependency tree than `requests` (**fewer transitive dependencies**),
145
+ supports both sync and async out of the box, and is actively maintained with a large community
146
+ (**well-maintained, frequent releases**) — see [Package selection policy](#package-selection-policy).
147
+
148
+ ---
149
+
150
+ ## xml.etree.ElementTree over lxml / beautifulsoup4
151
+
152
+ The BGG XML API returns well-formed XML. The stdlib parser handles it without issues.
153
+ Adding `lxml` or `beautifulsoup4` would introduce external dependencies for no gain —
154
+ applying the **prefer stdlib** rule from the [Package selection policy](#package-selection-policy).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arnauld Van Muysewinkel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.