Skip to content

Commit 409037f

Browse files
author
Nathaniel Henry
committed
Document the SDK conventions and gotchas for contributors
1 parent 6184c1e commit 409037f

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

.claude/CLAUDE.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# closecity (Python SDK)
2+
3+
Python client for the Close travel-time API (https://api.close.city). httpx-based,
4+
returns `geopandas` by default. Sphinx + Furo docs deploy to GitHub Pages; PyPI (via
5+
OIDC trusted publishing) is the eventual target. Public repo:
6+
`henryspatialanalysis/closecity-python`. The R sibling is
7+
`henryspatialanalysis/closecity-r` -- keep the two SDKs behaviourally in step.
8+
9+
## Conventions (these are binding -- do not re-litigate)
10+
11+
- **Prose:** follow the humanizer STYLE (no em dashes, no hype, short active
12+
sentences, Oxford comma). Beginner audience: keep the getting-started glossary
13+
current. Explain, do not sell.
14+
- **ASCII only** in all code and docs (no smart quotes, arrows, or non-ASCII glyphs).
15+
- **Attribution:** copyright and funder is "Henry Spatial Analysis", never "Close".
16+
Author `Nathaniel Henry <nat@henryspatialanalysis.com>`.
17+
- **Code style:** line length 90, spaces around `=` even inside calls (Nat overrides
18+
linters on this). `ruff` clean.
19+
- **API shape:** `Client` with one method per route; `Reply` / `Paginator`. Feature
20+
methods convert to geopandas via `spatial.py` (`to_geopandas()` is also public).
21+
- **Spatial by default:** feature methods (POIs, catchments, areal blocks, isochrones)
22+
return a `GeoDataFrame`. `Client(spatial=True)` is the default; `spatial=False` (on
23+
the client or per call) returns the raw `Reply`. Catalog/places/summaries stay raw.
24+
- **Dependencies:** `geopandas>=0.14` is a hard dependency. `pygris` is the `[tiger]`
25+
extra, used only to fetch block boundaries for the GEOID-only block routes
26+
(`fetch=True`). Keep the runtime footprint minimal otherwise.
27+
- **Tutorials (notebooks):** dead-simple and linear, **no helper functions**. Pull
28+
destination-type ids from the **free catalog** (`client.destination_types()`), never
29+
hardcode numeric codes. Draw a **map at each stage**. **No token-cost talk.** After a
30+
placeholder key, inline `# use your own key here`.
31+
- **Example cities:** Somerville MA (home search), Richmond VA (amenity basket),
32+
Providence RI (competitor walksheds). **No Seattle anywhere.**
33+
- **Docs execute live** at build time (see below), guarded on `CLOSECITY_KEY` so a
34+
keyless / PyPI build stays green (`conf.py` sets `nb_execution_mode` from the key).
35+
36+
## Gotchas (hard-won -- do not re-hit)
37+
38+
- **`blocks_query` needs JSON arrays:** the POST `/v1/blocks/query` body requires list
39+
fields, so scalar `mode`/`type` are normalised with `_as_list()`. A scalar mode
40+
returns 400.
41+
- **CRS:** TIGER blocks arrive in NAD83 (EPSG:4269); `_blocks_gdf` reprojects to 4326
42+
so they match POI/isochrone geometry. geopandas warns / errors on mismatched CRS.
43+
- **Blocks TIGER lacks** (e.g. water blocks) get NaN geometry from the left join;
44+
`_blocks_gdf` drops them (`geometry.notna()`) so plotting and spatial joins work. The
45+
block join renames the key to `geoid` before merging (the R SDK had a bug here; do
46+
not regress).
47+
- **`place_blocks()` (`/v1/places/{geoid}/blocks`) is BROKEN server-side:** it times
48+
out (15s Lambda) for every real place. Tutorials use `blocks_query(center, radius_m)`
49+
instead. Leave the wrapper, but do not build examples on it until the API is fixed.
50+
51+
## Local live doc build (this EFS host)
52+
53+
```bash
54+
python -m pip install -e '.[docs]'
55+
export CLOSECITY_KEY=$(aws ssm get-parameter --name /wtm-api/internal-test-key \
56+
--with-decryption --region us-west-2 --query Parameter.Value --output text) # do not print it
57+
python -m sphinx -b html docs docs/_build/html -W --keep-going
58+
```
59+
60+
Notebooks (`docs/getting_started.md` + `docs/tutorials/*.md`) are myst-nb `.md`
61+
notebooks; they execute when the key is set (else `nb_execution_mode` is off). A hidden
62+
`:tags: [remove-cell]` auth cell builds the real client; a display-only block shows the
63+
placeholder key. `docs.yml` passes the `CLOSECITY_KEY` secret to the build. Tests:
64+
`python -m pytest` (offline, no key).
65+
66+
## Git
67+
68+
Read-only unless Nat says otherwise; Nat runs commit sessions (modular, single-sentence,
69+
present-tense messages, no AI-attribution trailers). Push is a separate gate.

0 commit comments

Comments
 (0)