|
| 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 | +# Install dev dependencies |
| 9 | +pip install -r dev-requirements.txt |
| 10 | + |
| 11 | +# Run all tests |
| 12 | +python tests.py |
| 13 | + |
| 14 | +# Run a single test by class/method |
| 15 | +python -m unittest tests.HumanNamePythonTests.test_utf8 |
| 16 | + |
| 17 | +# Debug how a specific name string is parsed (prints HumanName repr) |
| 18 | +python tests.py "Dr. Juan Q. Xavier de la Vega III" |
| 19 | + |
| 20 | +# Build docs |
| 21 | +sphinx-build -b html docs dist/docs |
| 22 | + |
| 23 | +# Build package for release |
| 24 | +python setup.py sdist bdist_wheel |
| 25 | +twine upload dist/* |
| 26 | +``` |
| 27 | + |
| 28 | +Enable debug logging to see the parser's internal decisions: |
| 29 | + |
| 30 | +```python |
| 31 | +import logging |
| 32 | +logging.getLogger('HumanName').setLevel(logging.DEBUG) |
| 33 | +``` |
| 34 | + |
| 35 | +## Architecture |
| 36 | + |
| 37 | +The library has two layers: `nameparser/config/` (data) and `nameparser/parser.py` (logic). |
| 38 | + |
| 39 | +### Configuration layer (`nameparser/config/`) |
| 40 | + |
| 41 | +Each module defines a plain Python set of known name pieces: |
| 42 | + |
| 43 | +- `titles.py` — `TITLES` (prenominals) and `FIRST_NAME_TITLES` (e.g. "Sir", which treat the following name as first, not last) |
| 44 | +- `suffixes.py` — `SUFFIX_ACRONYMS` (with periods, e.g. "M.D.") and `SUFFIX_NOT_ACRONYMS` (e.g. "Jr.") |
| 45 | +- `prefixes.py` — `PREFIXES` (lastname particles, e.g. "de", "van") |
| 46 | +- `conjunctions.py` — `CONJUNCTIONS` (e.g. "and", "of") used to chain multi-word titles |
| 47 | +- `capitalization.py` — `CAPITALIZATION_EXCEPTIONS` mapping (e.g. `{'phd': 'Ph.D.'}`) |
| 48 | +- `regexes.py` — compiled regular expressions wrapped in a `TupleManager` |
| 49 | + |
| 50 | +`config/__init__.py` wraps everything into `SetManager` and `TupleManager` instances inside a `Constants` class. A module-level singleton `CONSTANTS` is shared across all `HumanName` instances by default. |
| 51 | + |
| 52 | +**Two-tier config pattern**: `CONSTANTS` is global; passing `None` as the second arg to `HumanName` creates a fresh per-instance `Constants()`. After modifying per-instance config you must call `hn.parse_full_name()` again. `SetManager.add()`/`remove()` normalizes inputs to lowercase with no periods, so callers don't need to worry about case. |
| 53 | + |
| 54 | +### Parser (`nameparser/parser.py`) |
| 55 | + |
| 56 | +`HumanName` is the single public class. Assigning to `full_name` (or instantiating with a string) triggers `parse_full_name()`. |
| 57 | + |
| 58 | +Parse flow: |
| 59 | +1. `pre_process()` — strips nicknames (parenthesis/quotes) and emoji, fixes "Ph.D." variant spellings |
| 60 | +2. Split on commas → 1 part (no comma), 2 parts (suffix-comma or lastname-comma), 3+ parts |
| 61 | +3. `parse_pieces()` — splits on spaces, detects dotted abbreviations like "Lt.Gov." and adds them to constants dynamically |
| 62 | +4. `join_on_conjunctions()` — merges pieces adjacent to conjunctions into single tokens (e.g. `['Secretary', 'of', 'State']` → `['Secretary of State']`); also joins prefix particles to the following lastname token |
| 63 | +5. Iterates pieces, assigning to `title_list`, `first_list`, `middle_list`, `last_list`, `suffix_list` |
| 64 | +6. `post_process()` — `handle_firstnames()` swaps first/last when only a title + one name; `handle_capitalization()` applies optional auto-cap |
| 65 | + |
| 66 | +Each named attribute (`title`, `first`, etc.) is a `@property` that joins its corresponding `_list`. Setters call `_set_list()` which runs the value through `parse_pieces()`, so assigning `hn.last = "de la Vega"` correctly re-parses prefix tokens. |
| 67 | + |
| 68 | +### Tests (`tests.py`) |
| 69 | + |
| 70 | +All tests live in a single file. `HumanNameTestBase.m()` is a custom assert helper that prints the original name string on failure. Many test classes group cases by name format type. `TEST_NAMES` is a list of name strings that gets automatically permuted into comma-separated variants as a regression check. When adding a new parsing case, add it to the relevant test class and consider adding the base form to `TEST_NAMES`. |
0 commit comments