Skip to content

Commit caf42fa

Browse files
committed
add gotchas, release logs and process
use uv consistently
1 parent b106d4d commit caf42fa

1 file changed

Lines changed: 40 additions & 8 deletions

File tree

AGENTS.md

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Branch naming: `fix/issue-NNN-short-description` or `feat/short-description`.
1515

1616
# Run all tests (includes --doctest-modules, so doctests in nameparser/ are also run;
1717
# the dual-parametrize fixture doubles the count, so ~370 methods → ~740 results)
18-
uv run pytest
18+
uv run pytest # --doctest-modules is set in pyproject.toml, so doctests run automatically
1919

2020
# Run a single test file / class / method
2121
uv run pytest tests/test_python_api.py
@@ -28,14 +28,32 @@ uv run mypy nameparser/
2828
uv run ruff check nameparser/
2929

3030
# Debug how a specific name string is parsed (prints HumanName repr)
31-
python -m nameparser "Dr. Juan Q. Xavier de la Vega III"
31+
uv run python -m nameparser "Dr. Juan Q. Xavier de la Vega III"
3232

3333
# Build docs
34-
sphinx-build -b html docs dist/docs
35-
36-
# Build package for release
37-
python setup.py sdist bdist_wheel
38-
twine upload dist/*
34+
uv run sphinx-build -b html docs dist/docs
35+
36+
# Maintain docs/release_log.rst as changes land:
37+
# - Keep an "Unreleased" entry at the top: `* X.Y.Z - Unreleased`
38+
# - Add one bullet per notable change; prefix with Add/Fix/Remove/Change
39+
# - Reference the issue or PR in parentheses: (#123) or (#123, #124)
40+
# Use "closes #N" when the change directly resolves the issue
41+
# - Version is decided at release time (patch/minor/major per semver)
42+
# - Format matches existing entries — see 1.3.0 block for a current example
43+
44+
# Release checklist (PyPI publish is triggered automatically by GitHub Actions on release creation)
45+
# 0. Review docs/ for anything stale — especially usage.rst (examples, API surface)
46+
# and any .rst files that reference config constants or HumanName kwargs
47+
# Also review AGENTS.md for stale commands, architecture notes, or gotchas
48+
# 1. Bump VERSION in nameparser/_version.py
49+
# 2. Stamp "Unreleased" → "X.Y.Z - Month DD, YYYY" in docs/release_log.rst
50+
# 3. git commit + git tag -a vX.Y.Z -m "Release X.Y.Z"
51+
# 4. git push origin master && git push origin vX.Y.Z ← tag must be pushed separately before gh release create
52+
# 5. gh release create vX.Y.Z --title "vX.Y.Z" --notes "..."
53+
# 6. Close the vX.Y.Z milestone and create a new "Next Release" one:
54+
# MILESTONE=$(gh api repos/derek73/python-nameparser/milestones --jq '.[] | select(.title=="vX.Y.Z") | .number')
55+
# gh api -X PATCH repos/derek73/python-nameparser/milestones/$MILESTONE -f state=closed
56+
# gh api -X POST repos/derek73/python-nameparser/milestones -f title="Next Release"
3957
```
4058

4159
Enable debug logging to see the parser's internal decisions:
@@ -88,6 +106,20 @@ Each named attribute (`title`, `first`, etc.) is a `@property` that joins its co
88106
3. Add `self.x = x if x is not None else self.C.x` in body — use `is not None`, not `or`, to allow falsy values like `""`
89107
4. conftest auto-restores scalar CONSTANTS between tests, but tests that *set* CONSTANTS mid-run still need their own try/finally
90108

109+
## Gotchas
110+
111+
**`suffix_not_acronyms` vs `is_an_initial` tension** — single-letter roman numeral suffixes (`i`, `v`) are in `suffix_not_acronyms` but also match the `is_an_initial` regex (single uppercase letter), so `is_suffix()` rejects them. Two separate code paths need context-aware workarounds: (1) suffix-comma detection uses `are_suffixes_after_comma()` which bypasses `is_suffix()` for `suffix_not_acronyms` members; (2) lastname-comma post-comma parsing uses `is_suffix_at_lastname_comma_end()` which only fires when `nxt is None` and `len(parts)==2` (no `parts[2]` suffix segment). See issues #136, #144.
112+
113+
**Expected-failure tests use `@pytest.mark.xfail`** — the conftest parametrized fixture breaks `@unittest.expectedFailure`; always use `@pytest.mark.xfail` instead.
114+
115+
**`lc()` strips only trailing periods**`'M.D.'``'m.d'`, not `'md'`. Exception keys in `capitalization_exceptions` are dot-free, so lookups must also try `.replace('.', '')`.
116+
117+
**`docs/usage.rst` contains live doctests** — edits can break `uv run pytest` (run via `--doctest-modules`). Verify new examples with `python3 -c "..."` before committing.
118+
119+
**`initials_separator` is intra-group only** — it controls the joiner between consecutive initials *within* a name group (e.g. two middle names in `middle_list`). Spaces *between* groups come from `initials_format`. To fully concatenate initials you need both `initials_separator=""` and `initials_format="{first}{middle}{last}"`.
120+
121+
**`pr/NNN` local branches** track upstream PRs — don't commit to them by accident. Check `git branch --show-current` before starting work.
122+
91123
### Tests (`tests/`)
92124

93-
Tests run under **pytest** (via `.venv/bin/pytest`) and are split one file per concern (`tests/test_titles.py`, `tests/test_suffixes.py`, etc.). `tests/base.py` holds `HumanNameTestBase` — a plain (non-`unittest`) base whose `m()` helper is a custom assert that prints the original name string on failure (plus thin `assert*` shims so the moved test bodies are unchanged). `tests/conftest.py` defines an autouse fixture that runs **every test twice** — once with `empty_attribute_default = ''` and once with `None` — so reported counts are doubled (e.g. 11 methods → 22 results); it also snapshots/restores the scalar `CONSTANTS` config around each test to keep tests order-independent. `TEST_NAMES` (in `tests/test_variations.py`) is a list of name strings permuted into comma-separated variants as a regression check. Tests that should fail use `@pytest.mark.xfail`. When adding a parsing case, add it to the relevant `tests/test_*.py` file and consider adding the base form to `TEST_NAMES`.
125+
Tests run under **pytest** (via `uv run pytest`) and are split one file per concern (`tests/test_titles.py`, `tests/test_suffixes.py`, etc.). `tests/base.py` holds `HumanNameTestBase` — a plain (non-`unittest`) base whose `m()` helper is a custom assert that prints the original name string on failure (plus thin `assert*` shims so the moved test bodies are unchanged). `tests/conftest.py` defines an autouse fixture that runs **every test twice** — once with `empty_attribute_default = ''` and once with `None` — so reported counts are doubled (e.g. 11 methods → 22 results); it also snapshots/restores the scalar `CONSTANTS` config around each test to keep tests order-independent. `TEST_NAMES` (in `tests/test_variations.py`) is a list of name strings permuted into comma-separated variants as a regression check. Tests that should fail use `@pytest.mark.xfail`. When adding a parsing case, add it to the relevant `tests/test_*.py` file and consider adding the base form to `TEST_NAMES`.

0 commit comments

Comments
 (0)