Repository navigation
Rename the vocabulary data modules to the 2.0 terminology (prefixes.py → particles.py), with a 2.x deprecation bridge #293
Description
The 2.0 API named its concepts for what they are — particles, bound_given_names, given_name_titles, suffix_words — but the nameparser/config data modules that feed them still carry the 1.x names. A v2 user who follows a Lexicon field docstring's "full default list" link lands in a module named for the old model (prefixes.py), and the naming mismatch has to be re-explained in every docstring that crosses the boundary.
The data modules are semi-public: the docs point at them by path (``:data:`~nameparser.config.prefixes.PREFIXES```), so direct imports exist in the wild and a silent rename would hard-break them. Every 2.0 removal got a 1.x warning first; this rename deserves the same bridge.
Rename table (one themed sweep — the point is that the data layer matches the Lexicon fields)
| Current | Renamed | Feeds |
|---|---|---|
prefixes.py / PREFIXES |
particles.py / PARTICLES |
Lexicon.particles |
NON_FIRST_NAME_PREFIXES |
NON_GIVEN_NAME_PARTICLES |
complement of Lexicon.particles_ambiguous — renamed, not inverted; see below |
bound_first_names.py / BOUND_FIRST_NAMES |
bound_given_names.py / BOUND_GIVEN_NAMES |
Lexicon.bound_given_names |
FIRST_NAME_TITLES (stays in titles.py) |
GIVEN_NAME_TITLES |
Lexicon.given_name_titles |
SUFFIX_NOT_ACRONYMS (stays in suffixes.py) |
SUFFIX_WORDS |
Lexicon.suffix_words |
Why the data layer keeps the never-given set instead of storing PARTICLES_AMBIGUOUS directly
The stored set and the Lexicon field are a source and a derivation, not one concept under two names — the v2 API flipped the presentation deliberately, and each layer's encoding serves its audience:
- Safe-by-default for new vocabulary. Under the current encoding, a particle newly added to
PARTICLESis automatically may-be-given (leading occurrence reads as a given name, with theparticle-or-givenflag) unless it is deliberately added to the never-given list. Storing the ambiguous set instead would require two-place additions to get the safe behavior, and forgetting the second place silently produces the unsafe one — the particle swallows leading given names unflagged. Wrongly claiming "never a given name" misparses real people (Von Miller, Vander), so the risky claim must stay the explicit opt-in. - The v1 shim needs the never-given set through 2.x anyway.
Constants.non_first_name_prefixesis mutable v1 surface; today the complement translation runs one direction (particles_ambiguous = prefixes − non_first_name_prefixesat snapshot time). Inverting the stored set would require both directions of the derivation to exist simultaneously. - The import-time invariants in
prefixes.py(e.g. disjointness from the bound given names) are naturally stated about the never-given set, and a rename-plus-inversion diff can't be reviewed as a behavior-preserving rename.
The one comment explaining the complement lives at the derivation site in _default_lexicon() (the # FLIPPED from v1 note).
Also in scope: freeze the vocabulary constants (set → frozenset)
The rename is the cheap moment to make the data-module set constants immutable, and it closes a real latent hazard, not just a hygiene itch: Lexicon.default() is functools.cached — it reads these sets once — while the v1 shim's Constants instances copy from them at each construction. Runtime mutation of a module constant (TITLES.add("dean") as a global-default hack) therefore silently desyncs the two APIs: fresh Constants see the entry, the cached default Lexicon never does. Frozen constants make that unrepresentable — the mutation raises AttributeError at the mutation site instead of splitting the defaults.
Why here: the new names have zero existing users and can be born frozen; the old names already warn through the PEP 562 bridge, so a mutation attempt fails loudly in code that is already being told to migrate; and everything internal treats the sets as immutable today (_default_lexicon() wraps each in frozenset(...) at the call site — that wrapping can then drop; the shim's SetManagers copy at construction; module-level | compositions and the import-time subset/disjoint asserts work identically on frozensets).
Scope: all vocabulary set constants across the data modules (SUFFIX_ACRONYMS, SUFFIX_WORDS, CONJUNCTIONS, MAIDEN_MARKERS, TITLES, GIVEN_NAME_TITLES, the renamed particle/bound sets, …) — freezing only the renamed five would leave an inconsistent trap. CAPITALIZATION_EXCEPTIONS (a dict) stays out: MappingProxyType at module level has pickling wrinkles, it isn't part of the rename table, and it can be its own decision later.
Lands as its own commit inside this bundle, after the renames — the rename commits stay provable as pure renames ("same sets, new names"), and the freeze commit is a separate, trivially-reviewable diff (mutation raises; composition and asserts still hold). One semantic change per commit, same as the PARTICLES_AMBIGUOUS scoping call above.
Mechanism
- The new module/constant names become canonical; all internal imports (
_lexicon._default_lexicon(),_config_shim._snapshot(), the import-time subset asserts) move to them. - The old module names remain as thin re-export shims using module-level
__getattr__(PEP 562, the same pattern asnameparser/locales/__init__.py), emitting aDeprecationWarningthat names the new path. Old constant names within surviving modules (FIRST_NAME_TITLES,SUFFIX_NOT_ACRONYMS) get the same treatment via__getattr__on their module. - The v1
Constantsattribute names (prefixes,first_name_titles, …) are facade surface and do not change — the shim simply imports from the renamed modules. - Docs sweep: every
:data:cross-reference moves to the new paths;customize.rst/migrate.rstnote the rename and the 3.0 endpoint.
Sequencing
- 2.1: canonical rename + deprecated aliases (this issue).
- 3.0: aliases removed with the rest of the facade layer (add to the 3.0 milestone checklist on merge).
Import of an old name must warn exactly once per process per name, and the suite's filterwarnings = ["error"] means any internal import still using an old path fails CI immediately — the bridge can't rot silently.
Activity
Warn once per read-location, not once per process (#293)
Keep missing-attribute checking on the modules that survive (#293)
Close the gaps the vocabulary guards left open (#293)
Correct eight claims the review measured false (#293)
Rename the particle vocabulary to the 2.0 terminology (#293)
Bridge the 1.x particle names through 2.x (#293)
Rename the bound given-name vocabulary to the 2.0 terminology (#293)
Bridge the 1.x bound given-name constant (#293)
Rename FIRST_NAME_TITLES to GIVEN_NAME_TITLES (#293)
Rename SUFFIX_NOT_ACRONYMS to SUFFIX_WORDS (#293)
1 remaining item
Freeze the vocabulary constants (#293)
Sharpen the retired-name guard (#293)
Move the docs to the renamed vocabulary, and fix four claims (#293)
Cover star imports in the deprecation bridge (#293)
Warn once per read-location, not once per process (#293)
Keep missing-attribute checking on the modules that survive (#293)
Close the gaps the vocabulary guards left open (#293)
Correct eight claims the review measured false (#293)
Merge pull request #354 from derek73/claude/issue-293-vocabulary-rename
Fix three claims this branch got wrong while fixing others
Merge pull request #377 from derek73/claude/2-2-work-prioritization-7…
docs(rules): topic-review amendments from the 2.0-vocabulary session …
Name the join in the vocabulary the constants now use
chore(config,tests): the derek73#293 rename housekeeping deferred out…