Skip to content

Commit 6bba2eb

Browse files
committed
test: add regex boundary and nickname-interaction coverage; clarify docs (#109)
Add tests for the leading-period-abbreviation feature's remaining untested boundaries per PR #196 review: digit/apostrophe exclusion, case-insensitivity, and interaction with parenthetical nicknames. Also tighten doc wording that implied only the literal first token in a name could become a title, when the rule applies to the whole leading title run (chained abbreviations included).
1 parent b4c672a commit 6bba2eb

4 files changed

Lines changed: 40 additions & 12 deletions

File tree

docs/release_log.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Release Log
3030
``CONSTANTS.first_name_prefixes.clear()``. **Default-on: changes parsing
3131
output for names with these prefixes.** (#150)
3232
- Add ``middle_name_as_last`` flag to ``Constants`` and ``HumanName`` for opt-in folding of middle names into the last name, for naming systems with no middle-name concept (e.g. Arabic patronymic chaining) (#133)
33-
- Treat an unrecognized, multi-letter token ending in a period at the start of a name (e.g. ``"Major."``) as a ``title`` instead of a ``first`` name; internal-period abbreviations (``"E.T."``) and single-letter initials (``"J."``) are unaffected. **Default-on: changes parsing of names with a leading unknown period-abbreviation** (closes #109)
33+
- Treat an unrecognized, multi-letter token ending in a period in the leading title run (before the first name is set), e.g. ``"Major."``, as a ``title`` instead of a ``first`` name; internal-period abbreviations (``"E.T."``) and single-letter initials (``"J."``) are unaffected. **Default-on: changes parsing of names with a leading unknown period-abbreviation** (closes #109)
3434
* 1.2.1 - June 19, 2026
3535
- Fix ``initials()`` interpolating the literal ``None`` for empty name parts when ``empty_attribute_default = None`` (e.g. ``"J. None D."``); empty parts now render as an empty string and a fully-empty result returns ``empty_attribute_default``
3636
- Add ``python -m nameparser "Name String"`` command-line helper that prints a parsed name

docs/usage.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,13 @@ reading in that ambiguous context:
191191
Leading Period-Abbreviation Titles
192192
-----------------------------------
193193

194-
An unrecognized, multi-letter word ending in a period, appearing before the
195-
first name, is treated as a title -- this covers military ranks and other
196-
abbreviations that aren't in the built-in titles list. Single-letter
197-
initials (``"J."``) and internal-period abbreviations (``"E.T."``) are not
198-
affected, and the same word appearing after the first name is left as a
199-
middle name.
194+
An unrecognized, multi-letter word ending in a period, found anywhere in the
195+
leading title run (i.e. before the first name is set), is treated as a title
196+
-- this covers military ranks and other abbreviations that aren't in the
197+
built-in titles list, including chained abbreviations like
198+
``"Foo. Xyz. John Smith"``. Single-letter initials (``"J."``) and
199+
internal-period abbreviations (``"E.T."``) are not affected, and the same
200+
word appearing after the first name is left as a middle name.
200201

201202
.. doctest:: leading_period_titles
202203
:options: +NORMALIZE_WHITESPACE

nameparser/parser.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -543,11 +543,14 @@ def is_title(self, value: str) -> bool:
543543
def is_leading_title(self, piece: str) -> bool:
544544
"""
545545
True if ``piece`` is a known title, or an unrecognized multi-letter
546-
word ending in a single trailing period (e.g. ``"Major."``). Only
547-
meaningful for pieces in the title position (before the first name is
548-
set) — a period-abbreviation appearing later in the name is left as a
549-
middle name. Does not mutate ``C.titles``, so the periodless form
550-
(``"Major"``) is never affected in later parses.
546+
word ending in a single trailing period (e.g. ``"Major."``). The
547+
``{2,}`` in the ``period_abbreviation`` regex, not a separate
548+
``is_an_initial()`` check, is what excludes single-letter initials
549+
like ``"J."``. Only meaningful for pieces in the title position
550+
(before the first name is set) — a period-abbreviation appearing
551+
later in the name is left as a middle name. Does not mutate
552+
``C.titles``, so the periodless form (``"Major"``) is never affected
553+
in later parses.
551554
"""
552555
return self.is_title(piece) or bool(self.C.regexes.period_abbreviation.match(piece))
553556

tests/test_titles.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,27 @@ def test_middle_initial_with_period_unaffected(self) -> None:
334334
self.m(hn.first, "John", hn)
335335
self.m(hn.middle, "Q.", hn)
336336
self.m(hn.last, "Smith", hn)
337+
338+
def test_leading_period_abbreviation_excludes_digits(self) -> None:
339+
hn = HumanName("No1. John Smith")
340+
self.m(hn.title, "", hn)
341+
self.m(hn.first, "No1.", hn)
342+
self.m(hn.last, "Smith", hn)
343+
344+
def test_leading_period_abbreviation_excludes_apostrophe(self) -> None:
345+
hn = HumanName("O'B. John Smith")
346+
self.m(hn.title, "", hn)
347+
self.m(hn.first, "O'B.", hn)
348+
self.m(hn.last, "Smith", hn)
349+
350+
def test_leading_period_abbreviation_case_insensitive(self) -> None:
351+
hn = HumanName("xyz. John Smith")
352+
self.m(hn.title, "xyz.", hn)
353+
self.m(hn.first, "John", hn)
354+
self.m(hn.last, "Smith", hn)
355+
356+
def test_leading_period_abbreviation_with_nickname(self) -> None:
357+
hn = HumanName("Xyz. (Bud) Smith")
358+
self.m(hn.title, "Xyz.", hn)
359+
self.m(hn.first, "Smith", hn)
360+
self.m(hn.nickname, "Bud", hn)

0 commit comments

Comments
 (0)