Skip to content

Commit 47b5ecd

Browse files
derek73claude
andauthored
Fix prose the 2.4 changes made stale (pre-release sweep) (#647)
* docs: fix prose the 2.4 changes made stale (pre-release sweep) Step 0 of the release checklist, run over the user docs, the autodoc docstrings and messages, the stage headers, the case-table notes, the design docs and AGENTS.md. Every replacement example was run on this tree, and the migration-guide additions against the 1.4.0, 2.1.0, 2.2.0 and 2.3.0 wheels. User-facing: - AmbiguityKind.SUFFIX_OR_NAME: "Jack MA" is a credential since #289, and a maiden marker after a comma makes no clause since #601; the examples that said otherwise are rewritten. - CONJUNCTION_OR_INITIAL: the default set is "e" and "i". - Lexicon.suffix_acronyms_ambiguous: bare members are read by capitals and the words-to-spare count, not only by periods. - modules.rst / _types.py `joined` example: 'Smith, Ph. D. Smith' now reads a credential run; use 'Smith, Ph. D.'. - customize.rst LEED AP example: the defaults read it after a comma, so the add is shown at the end of a name, with its baseline. - usage.rst: a bare title after a credential is a title, a dotted multi-chunk word reads by shape, and "PhD MD" is one entry. - migrate.rst: eleven more 2.4 shapes to grep fixtures for; X.Y.Z. becomes the real N.C.C. - README: later 2.x releases add breaks of their own. - Messages: the CapsSuffixes bool error names the members and the default (no release had a bool there); the capitalization_exceptions hint offers a tuple, which type-checks, and a test pins it. Contributor-only: rules.md#P3's Accepted pair (the capitals spelling now hits #602's credential run), mechanisms.md's call-site census (is_title_piece is shared since #602/#603; peel_trailing has no caller outside the leaf since #601) and particle count, AGENTS.md's undated counts and a 1.4-only reading, decisions.md's Open blocks for the closed #360/#380 and the #291 note, and case-table notes. The row whose id no longer matched its expectation is renamed, and its one-case spelling is added so S2's reserve stays exercised (field sweep 1156 -> 1159 claims). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(review): fix four defects the sweep's review found - decisions.md: #360 moved mc and ste INTO the never-given set, not out of it (6389f06); the conversion had it backwards. - The capitalization_exceptions hint offered ('key', 'Value'), which type-checks and then fails #459's mask check; it offers ('key', 'KEY'), and the test now executes the offered code (negative control: the old spelling raises). AGENTS.md's pasteable- code rule says the code must run, not only type-check. - The `joined` example shows the role-blind half again: 'Ph. D., Jr.' gives last_list ['Ph. D.']. - SUFFIX_OR_NAME: "Doe, Dr. nee Smith MA" reports, and now says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent e189822 commit 47b5ecd

17 files changed

Lines changed: 185 additions & 87 deletions

‎AGENTS.md‎

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

‎README.rst‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ deprecations 1.3 and 1.4 announced, so if your test suite runs clean on
9191
Two things that check will not catch: four removals 1.4 never warned
9292
about (three raise on contact, the fourth only warns), and one that
9393
changes results silently — ``name == "John Smith"`` is now ``False``.
94+
Later 2.x releases add a few breaks of their own.
9495
`Migrating from HumanName <https://nameparser.readthedocs.io/en/latest/migrate.html>`__
95-
covers both, and translates a v1 customization into the new API whenever
96+
covers all of them, and translates a v1 customization into the new API whenever
9697
that's convenient for you.
9798

9899
See the `release log <https://nameparser.readthedocs.io/en/latest/release_log.html>`__

‎docs/customize.rst‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,17 @@ itself cannot be stored:
8181
'MD PhD'
8282

8383
That has held since 1.4.0. A credential whose words are not in the
84-
default vocabulary is reached by adding those words, not the phrase:
84+
default vocabulary is reached by adding those words, not the phrase.
85+
After a comma the defaults already read an unlisted all-caps run (see
86+
:ref:`all-caps-credentials`), so the difference shows at the end of a
87+
name:
8588

8689
.. doctest::
8790

91+
>>> parse("John Smith LEED AP").suffix
92+
''
8893
>>> lex = Lexicon.default().add(suffix_acronyms={"leed", "ap"})
89-
>>> Parser(lexicon=lex).parse("John Smith, LEED AP").suffix
94+
>>> Parser(lexicon=lex).parse("John Smith LEED AP").suffix
9095
'LEED AP'
9196

9297
Removing works the same way, and drops the word from recognition:

‎docs/design/decisions.md‎

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

‎docs/design/mechanisms.md‎

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

‎docs/design/rules.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,9 +655,9 @@ P3. Rationale: connective words ("y", "of the") bind name words into
655655
after it to the mixed-case rule rather than to the one-case fork,
656656
so a bare Latin capital there reads as an initial while its
657657
lowercase spelling joins — the one shape in which the name's OWN
658-
words read differently in its two one-case spellings. So "JUAN
659-
NÉE JR Y LOPEZ" reads middle "NÉE JR Y" with its "Y" an initial,
660-
while "juan née jr y lopez" reads family "jr y lopez" with its
658+
words read differently in its two one-case spellings. So "DR.
659+
NÉE JUAN Y LOPEZ" reads middle "JUAN Y" with its "Y" an initial,
660+
while "dr. née juan y lopez" reads family "juan y lopez" with its
661661
own "y" joined. A clause's words are read as they always were,
662662
and that is not an exception to this: such a letter still takes
663663
a different tag in the two spellings, so its case repair can

‎docs/migrate.rst‎

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,11 +675,33 @@ grep your fixtures for, with what 1.4 read:
675675
Berg``.
676676
- ``"Josep Carod i Rovira"`` (2.4) — ``i`` links two surnames in a
677677
mixed-case name: 1.4 read middle ``Carod i``, last ``Rovira``.
678-
- ``"John Smith X.Y.Z."`` (2.4) — an unlisted dotted credential after a
679-
full name: 1.4 read last ``X.Y.Z.``, middle ``Smith``. ``"Jack
678+
- ``"Jane Smith N.C.C."`` (2.4) — an unlisted dotted credential after a
679+
full name: 1.4 read last ``N.C.C.``, middle ``Smith``. ``"Jack
680680
X.Y.Z."`` is unchanged.
681-
- ``capitalize()`` on ``"john smith md"`` (2.4) — 1.4 wrote ``M.D.``;
682-
``md`` left the exceptions map and now repairs as an acronym.
681+
- ``"Jane Smith, LEED AP"`` (2.4) — an unlisted all-caps credential
682+
after a comma: 1.4 read first ``LEED``, middle ``AP``, last ``Jane
683+
Smith``.
684+
- ``"Eric H. Holder Jr. Attorney General"`` and ``"Eric H. Holder, Jr.,
685+
Attorney General"`` (2.4) — a title after a credential is a title:
686+
1.4 read middle ``H. Holder Jr. Attorney``, last ``General``, and
687+
suffix ``Jr., Attorney General``.
688+
- ``"Steven Hardman, RN - CRNA"`` (2.4) — a credential after a comma
689+
starts a run of credentials: 1.4 read first ``RN``, middle ``-``,
690+
last ``Steven Hardman``, suffix ``CRNA``.
691+
- ``"John van der Berg Prof."`` (2.4) — 1.4 read last ``van der Berg
692+
Prof.``.
693+
- ``"Liam Ó Murchú"`` (2.4) — the Irish particles ``Ó``, ``Ní`` and
694+
``Ua`` join the surname: 1.4 read middle ``Ó``, last ``Murchú``.
695+
- ``"Herra Väinö Johansson"`` (2.4) — salutations in Finnish,
696+
Icelandic, Malay and other languages are titles: 1.4 read first
697+
``Herra``, middle ``Väinö``.
698+
- ``"山田 タロウ"`` (2.4) — halfwidth katakana reads as katakana: 1.4 read
699+
first ``山田``, last ``タロウ``.
700+
- ``capitalize()`` on ``"john smith md"``, ``"john smith phd"`` and
701+
``"JANE DOE MBA"`` (2.4) — 1.4 wrote ``M.D.``, ``Ph.D.`` and ``Mba``;
702+
credentials are now written the usual way (``MD``, ``PhD``, ``MBA``).
703+
- ``initials()`` on ``"john e smith"`` (2.4) — 1.4 gave ``j. s.``; a
704+
one-letter connective read as an initial now contributes one.
683705

684706
.. doctest::
685707

@@ -700,9 +722,36 @@ grep your fixtures for, with what 1.4 read:
700722
'van der Berg'
701723
>>> HumanName("Josep Carod i Rovira").last
702724
'Carod i Rovira'
703-
>>> HumanName("John Smith X.Y.Z.").suffix
704-
'X.Y.Z.'
725+
>>> HumanName("Jane Smith N.C.C.").suffix
726+
'N.C.C.'
727+
>>> HumanName("Jane Smith, LEED AP").suffix
728+
'LEED AP'
729+
>>> holder = HumanName("Eric H. Holder Jr. Attorney General")
730+
>>> holder.title, holder.last, holder.suffix
731+
('Attorney General', 'Holder', 'Jr.')
732+
>>> HumanName("Eric H. Holder, Jr., Attorney General").title
733+
'Attorney General'
734+
>>> HumanName("Steven Hardman, RN - CRNA").suffix
735+
'RN - CRNA'
736+
>>> HumanName("John van der Berg Prof.").last
737+
'van der Berg'
738+
>>> HumanName("Liam Ó Murchú").last
739+
'Ó Murchú'
740+
>>> HumanName("Herra Väinö Johansson").title
741+
'Herra'
742+
>>> HumanName("山田 タロウ").last
743+
'山田'
705744
>>> md = HumanName("john smith md")
706745
>>> md.capitalize(force=True)
707746
>>> str(md)
708747
'John Smith MD'
748+
>>> phd = HumanName("john smith phd")
749+
>>> phd.capitalize()
750+
>>> str(phd)
751+
'John Smith PhD'
752+
>>> mba = HumanName("JANE DOE MBA")
753+
>>> mba.capitalize()
754+
>>> str(mba)
755+
'Jane Doe MBA'
756+
>>> HumanName("john e smith").initials()
757+
'j. e. s.'

‎docs/modules.rst‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ Results
5656
continuation of the token before it
5757
— within one merged piece the tag is role-blind and every view
5858
joins the pair with a space, so it renders "Ph. D." as one
59-
credential and ``Smith, Ph. D. Smith`` gives ``first_list``
60-
``['Ph. D.']``, and since #436 it also spans the pieces of one
59+
piece in whatever role it lands — ``Ph. D., Jr.`` gives
60+
``last_list`` ``['Ph. D.']`` — and since #436 it also spans the pieces of one
6161
SUFFIX entry, the run of post-nominals the writer wrote without a
6262
comma — "MD PhD" — which is the half the suffix view alone reads
6363
and joins with a space instead of ", "). Every other tag is namespaced

‎docs/usage.rst‎

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ Every piece of each is optional:
4646
A title written with a period (``Prof.``) is also read at the end of a
4747
name in forms 1 and 3 and at the end of the given name in form 2;
4848
written bare there (``Prof``), it is a name word, because many title
49-
words are also surnames.
49+
words are also surnames — unless a credential stands before it, which
50+
starts a run of post-nominals and titles (``John Smith PhD Prof`` reads
51+
title ``Prof``; see :ref:`trailing-titles`).
5052

5153
The last two differ in what the comma is doing. In form 2 it separates
5254
the family name from the rest, so the family name comes first; in form
@@ -146,7 +148,7 @@ name to its full vocabulary set:
146148
* - :mod:`Suffixes <nameparser.config.suffixes>`
147149
- adjacent suffixes
148150
- ``suffix``
149-
- ``John Smith PhD MD`` → ``PhD, MD``
151+
- ``John Smith PhD MD`` → ``PhD MD``
150152
* - :mod:`Bound given names <nameparser.config.bound_given_names>`
151153
- the following word
152154
- ``given``
@@ -885,17 +887,21 @@ Because this is structural rather than vocabulary-driven, emptying
885887
At the end of a name
886888
~~~~~~~~~~~~~~~~~~~~
887889

888-
Only the leading slot INFERS. At the back of a name a period-marked
889-
word is read by vocabulary alone: a word the parser already knows as a
890-
post-nominal or as a title reads as one, and an unfamiliar abbreviation
891-
stays a name word.
890+
Only the leading slot INFERS. At the back of a name a word ending in a
891+
single period is read by vocabulary alone: a word the parser already
892+
knows as a post-nominal or as a title reads as one, and an unfamiliar
893+
abbreviation stays a name word. A word of two or more period-separated
894+
chunks is the exception, read as a credential by its shape (see
895+
:ref:`dotted-credentials`).
892896

893897
.. doctest::
894898

895899
>>> parse("John Smith Prof.").title
896900
'Prof.'
897901
>>> parse("John Smith Xyz.").family
898902
'Xyz.'
903+
>>> parse("Jane Smith N.C.C.").suffix
904+
'N.C.C.'
899905

900906
That asymmetry is deliberate. The title vocabulary holds hundreds of
901907
words that are ordinary surnames — ``king``, ``judge``, ``bishop`` —
@@ -904,6 +910,16 @@ names their family field. The period is what separates the two: a
904910
trailing title word written *without* one is a name word, so
905911
``parse("Mary Jane King").family`` is ``'King'``.
906912

913+
A credential in front changes that. It starts a run of post-nominals
914+
and titles that lasts to the end of the name, so a bare title word
915+
behind it is a title:
916+
917+
.. doctest::
918+
919+
>>> john_prof = parse("John Smith PhD Prof")
920+
>>> (john_prof.title, john_prof.suffix)
921+
('Prof', 'PhD')
922+
907923
Comparing names
908924
----------------
909925

‎nameparser/_lexicon.py‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,9 +594,13 @@ class Lexicon:
594594
#: default list:
595595
#: :data:`~nameparser.config.suffixes.SUFFIX_WORDS`.
596596
suffix_words: frozenset[str] = frozenset()
597-
#: Subset of suffix_acronyms counted as suffixes only when written
598-
#: WITH periods -- their bare forms are common surnames ("ma",
599-
#: "do": "Jack Ma" keeps his family name). Full default list:
597+
#: Subset of suffix_acronyms whose bare forms are also common
598+
#: surnames ("ma", "do"). Written with periods one is a suffix
599+
#: outright; written bare, capitals decide in a mixed-case name
600+
#: ("Jack MA" is the credential, "Jack Ma" keeps his family name)
601+
#: and otherwise the word counts as a suffix only where a name
602+
#: word is left beside it ("John Smith ma"), rules.md#S2. Full
603+
#: default list:
600604
#: :data:`~nameparser.config.suffixes.SUFFIX_ACRONYMS_AMBIGUOUS`.
601605
suffix_acronyms_ambiguous: frozenset[str] = frozenset()
602606
#: Family-name particles that chain onto the following piece
@@ -888,7 +892,9 @@ def _edit(self, op: str, entries: Mapping[str, Iterable[str]]) -> Lexicon:
888892
raise TypeError(
889893
"capitalization_exceptions holds key->value pairs; "
890894
"use dataclasses.replace(lexicon, "
891-
"capitalization_exceptions={...}) instead of "
895+
"capitalization_exceptions=(*lexicon."
896+
"capitalization_exceptions, ('key', 'KEY'))) "
897+
"instead of "
892898
f"{op}()"
893899
)
894900
if name not in _VOCAB_FIELDS:

0 commit comments

Comments
 (0)