Skip to content

Commit 6fd116e

Browse files
authored
Merge pull request #315 from derek73/docs/period-abbreviation-scoping
docs: correct the scope of the period-abbreviation title rule
2 parents 6a9283e + e661a59 commit 6fd116e

4 files changed

Lines changed: 42 additions & 16 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ Add a dedicated `copy.deepcopy()` round-trip test for it too (see `test_regexes_
172172

173173
**Titles permanently shadow first names — be conservative** — any word in `TITLES` is always consumed as a title and can never be parsed as a first name. `"Dean"` is the canonical example: it's a common academic title *and* a common given name, so it is intentionally absent from the default titles (see `docs/customize.rst` — users who need it add it via opt-in `Constants`). Before adding a word to `TITLES`, ask: "Could this plausibly be someone's given name in any culture?" If yes, don't add it globally; it belongs in caller-supplied `Constants` instead. This same caution applies to international honorifics — `Prince`, `Sheikh`, `Frau` are all first names in some contexts. It also applies to any prefix sub-set gated on "never a first name": obscure-looking foreign particles are surprisingly often real given names — `Von` (Von Miller), `Vander` (Brazilian, also the Arcane character). When unsure, exclude — a missing member just means that name isn't auto-handled, whereas a wrong member misparses a real person.
174174

175-
**`is_leading_title()` infers titles beyond the `TITLES` set, but only in leading position** — an unrecognized multi-letter word ending in a single trailing period (matched via the `period_abbreviation` regex, `{2,}` letters) is treated as a title when it appears before the first name is set, e.g. `"Major. Dona Smith"``title='Major.'`. It's distinct from `is_title()` and does not mutate `C.titles`, so the periodless form (`"Major"`) is unaffected elsewhere. The `{2,}` length requirement — not a separate initials check — is what excludes single-letter initials like `"J."` from being swallowed as titles; the same word after the first name is left as a middle name. (#109; see `docs/usage.rst` "Leading Period-Abbreviation Titles")
175+
**The period-abbreviation title inference runs at the head of the GIVEN-NAME part, not the head of the name** — an unrecognized multi-letter word ending in a single trailing period (`_assign._PERIOD_ABBREV`, a hand copy of the `period_abbreviation` regex, `{2,}` letters) is treated as a title in the leading title run, e.g. `"Insp. Jane Morse"` → `title='Insp.'`. "Leading" is per SEGMENT: `_peel_leading_titles` is called for NO_COMMA segment 0, SUFFIX_COMMA segment 0, and FAMILY_COMMA **segment 1**, so `"Morse, Det. Insp. Jane"` → `title='Det. Insp.'` and a lone `"Smith, Xyz."` → `title='Xyz.'` — long-standing, verified against 1.4.0, and the mechanism behind #296 (`"Smith, Jr."` → title, which the shape rule claims even once `jr` leaves `TITLES`). The docs said "leading word" until 2026-08-01 and were wrong for every comma path. It does not mutate `C.titles`, so the periodless form (`"Insp"`) is unaffected elsewhere. The `{2,}` length requirement — not a separate initials check — is what excludes single-letter initials like `"J."`; the same word after the given name is left as a middle name. **The inference OUTRANKS vocabulary where it runs**: `"Esq. Smith"` → `title='Esq.'` even though `esq` is suffix-only vocabulary, because the shape rule fires before anything consults the suffix sets. **And it runs in one direction only**: a trailing abbreviation has no structural counterpart and is matched against the suffix vocabulary alone, so a trailing TITLE word is not a title (`"John Smith Prof."` → `family='Prof.'`, and the comma path disagrees — `"Smith, Prof."` → `title='Prof.'`). Meanwhile `period_joined_vocab` resolves INTERIOR-period tokens (`Lt.Gov.`, `Msc.Ed.`) to title-or-suffix by vocabulary, and `_extract._suffix_shaped` treats any period-final delimited content as not-a-nickname. Four trailing-period behaviors, four different resolutions; unifying them is open design work, not settled. (#109; see `docs/usage.rst` "Titles you didn't configure")
176176

177177
**Cyrillic suffix regexes need `re.I` even when the pattern is suffix-only** — a Latin title-cased word (`Ivanovich`) keeps its suffix lowercase, so `re.I` seemed skippable; but an irregular Cyrillic suffix can be nearly the whole word (`ильич`), so title-casing capitalizes into the suffix itself (`Ильич`). `east_slavic_patronymic_cyrillic` shipped without `re.I` on the Latin reasoning and silently failed on capitalized irregular forms — don't assume Latin's title-case safety transfers to Cyrillic. (#185)
178178

@@ -216,7 +216,7 @@ Don't use the bare `python3 -m doctest <file>.rst` CLI (no `optionflags`) to che
216216

217217
**Prefix-join uses value-based `list.index()`** in `join_on_conjunctions` — fragile when a token value repeats (e.g. a trailing title that's also a suffix acronym, or two `van`s); constrain such lookups to start at `i + 1`. See #100.
218218

219-
**Title vs suffix is purely positional** — a word matching `TITLES` at the front of a name becomes `title`; the same word matching `SUFFIX_ACRONYMS`/`SUFFIX_NOT_ACRONYMS` at the end becomes `suffix` (never both, regardless of the word's real-world meaning). External test sources (old issue gists, etc.) sometimes assert `suffix` for a leading professional abbreviation like `RA`/`PD`/`Dipl.-Ing.` — that's the source data being wrong, not a parser bug. Verify position before "fixing" it.
219+
**Title vs suffix is positional for BARE words, and the leading period-abbreviation rule overrides even that** — a word matching `TITLES` at the front of a name becomes `title`; the same word matching `SUFFIX_ACRONYMS`/`SUFFIX_NOT_ACRONYMS` at the end becomes `suffix` (never both, regardless of the word's real-world meaning). External test sources (old issue gists, etc.) sometimes assert `suffix` for a leading professional abbreviation like `RA`/`PD`/`Dipl.-Ing.` — that's the source data being wrong, not a parser bug. Verify position before "fixing" it. Two qualifications the older "purely positional" wording papered over, both measured 2026-08-01: a PERIOD-marked leading word is claimed by the shape rule before any vocabulary is read (`"Esq. Smith"` → `title`, though `esq` is suffix-only), and trailing position has no such rule at all, so a title word there is neither title nor suffix but a NAME part (`"John Smith Prof."` → `family='Prof.'`) — which is what the comma path already disagrees with. Why it is not simply inverted to "vocabulary decides": `TITLES` holds 692 words that are in no suffix set, and many are ordinary surnames (`king`, `bishop`, `prince`, `pope`, `judge`, `sheriff`, `baron`, `master`, ...), so a vocabulary-first trailing rule would read `"Mary Jane King"` as `title='King'`, `family='Jane'`. The period is what separates the safe case from that one — `King` is a surname, `King.` is not.
220220

221221
### Tests (`tests/`)
222222

docs/customize.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ go together:
7070
'Hon'
7171

7272
Emptying the vocabulary does not switch titles off entirely, though. A
73-
leading word that ends in a period is read as a title structurally,
74-
without consulting ``titles`` at all — that is what lets unfamiliar
75-
ranks and abbreviations work (see :ref:`abbreviated-titles`):
73+
word ending in a period, standing at the front of the part that carries
74+
the given name, is read as a title structurally, without consulting
75+
``titles`` at all — that is what lets unfamiliar ranks and
76+
abbreviations work (see :ref:`abbreviated-titles`):
7677

7778
.. doctest::
7879

docs/release_log.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Release Log
3131
- Fix NFD-decomposed input missing the East Asian defaults entirely: script classification now normalizes to NFC before deciding, so a Korean or Japanese name typed on macOS — where decomposed text is routine — gets the same order rule as its composed twin, which it silently did not before. Segmentation MATCHING deliberately stays raw, so an unspaced NFD hangul name is ordered correctly but not split, rather than being split in the wrong place. One gotcha worth stating plainly: parse output preserves the encoding it was given, so for NFD input ``name.family == "김"`` is ``False`` even though it is the same name — compare NFC-normalized text when comparing across encodings (#272)
3232
- Fix the Ukrainian conjunction ``й`` not joining the pieces around it: it is the euphonic alternate of ``і``, the two chosen by the surrounding vowel and consonant rather than by meaning (``"Олесь і Олена"`` but ``"Марія й Петро"``), so real Ukrainian data carries both spellings and shipping only ``і`` recognized just one of them. ``"Олесь й Олена Коваленки"`` now gives given ``"Олесь й Олена"`` where the ``й`` previously landed in ``middle``. Same treatment as the ``и``/``і`` entries added in 2.0.0, single-letter carve-out included: the conjunction joins only once the name has enough pieces, and a punctuated initial still wins, so ``"Й. Сліпий"`` is unaffected. Raised in a comment on #267
3333

34+
**Documentation**
35+
36+
- Correct the documented scope of the period-abbreviation title rule. It was described as applying to "a leading word", which was never true of any comma form: the rule runs at the front of the part that carries the given name, which after a family comma is the part *after* the comma — so ``"Morse, Det. Insp. Jane"`` gives title ``Det. Insp.``. Behavior is unchanged and matches 1.4.0; only the description was wrong. The examples now use real abbreviations absent from the shipped vocabulary (``Det. Insp.``) rather than whole words carrying a stray period, so they demonstrate the structural inference instead of merely surviving it
37+
3438
* 2.0.0 - July 27, 2026
3539

3640
Two release candidates preceded this release (rc1 on 2026-07-23,

docs/usage.rst

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -530,34 +530,55 @@ what that field marks and how to add to it.
530530
Titles you didn't configure
531531
-----------------------------
532532

533-
A leading word that ends in a period is read as a title even when it is
534-
in no vocabulary list, which is what lets unfamiliar ranks, honorifics
535-
and abbreviations work without configuring anything:
533+
A trailing period marks an abbreviation, and the parts of a name that
534+
get abbreviated are the ones standing outside it — titles and
535+
post-nominals. So a word ending in a period, in no vocabulary list at
536+
all, is read as a title when it stands where a title stands: at the
537+
front of the part that carries the given name. That is what lets
538+
unfamiliar ranks, honorifics and abbreviations work without configuring
539+
anything:
536540

537541
.. doctest::
538542

539-
>>> parse("Major. Dona Smith").title
540-
'Major.'
541-
>>> parse("Foo. Xyz. John Smith").title # chains
542-
'Foo. Xyz.'
543+
>>> parse("Insp. Jane Morse").title
544+
'Insp.'
545+
>>> parse("Det. Insp. Jane Morse").title # chains
546+
'Det. Insp.'
547+
548+
Neither ``det`` nor ``insp`` is in the shipped vocabulary; the periods
549+
are doing all the work.
550+
551+
The part that carries the given name is not always the front of the
552+
string. After a family comma it is the part *after* the comma, and the
553+
rule applies there in exactly the same way:
554+
555+
.. doctest::
556+
557+
>>> parse("Morse, Det. Insp. Jane").title
558+
'Det. Insp.'
543559

544560
The rule is bounded in three ways, so it doesn't swallow ordinary
545561
names. Single initials are left alone, so are abbreviations with
546-
interior periods, and the rule only applies to the leading run — the
547-
same word after the given name is a middle name:
562+
interior periods, and it applies only to that leading run — the same
563+
word after the given name is a middle name:
548564

549565
.. doctest::
550566

551567
>>> parse("J. Smith").given
552568
'J.'
553569
>>> parse("E.T. Jones").given
554570
'E.T.'
555-
>>> parse("John Major. Smith").middle
556-
'Major.'
571+
>>> parse("Jane Insp. Morse").middle
572+
'Insp.'
557573

558574
Because this is structural rather than vocabulary-driven, emptying
559575
``titles`` does not switch it off; see :doc:`customize`.
560576

577+
Only the title direction is inferred this way. A *trailing*
578+
abbreviation is matched against the suffix vocabulary and nothing more,
579+
so an abbreviated post-nominal is recognized only if it is a word the
580+
parser already knows.
581+
561582
Comparing names
562583
----------------
563584

0 commit comments

Comments
 (0)