Skip to content

Commit d4feffc

Browse files
committed
feat: add middle_name_as_last flag to Constants
1 parent 1f2ce4e commit d4feffc

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

nameparser/config/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,32 @@ class Constants:
389389
390390
"""
391391

392+
middle_name_as_last = False
393+
"""
394+
If set, folds middle names into the last name: ``middle_list`` is prepended
395+
to ``last_list`` and ``middle_list`` is cleared, so ``.last`` becomes what
396+
``.surnames`` already was and ``.middle`` becomes empty. Useful for naming
397+
systems with no middle-name concept, where everything after the given name
398+
is lineage/family (e.g. Arabic patronymic chaining: given + father +
399+
grandfather + family).
400+
401+
The fold is uniform across both no-comma and comma ("Last, First Middle")
402+
input, so the two written forms of a name converge on the same result.
403+
404+
For per-instance control without a shared ``Constants``, pass a dedicated
405+
instance: ``HumanName("...", constants=Constants(middle_name_as_last=True))``.
406+
407+
.. doctest::
408+
409+
>>> from nameparser import HumanName
410+
>>> from nameparser.config import Constants
411+
>>> C = Constants(middle_name_as_last=True)
412+
>>> hn = HumanName("Mohamad Ahmad Ali Hassan", constants=C)
413+
>>> hn.first, hn.middle, hn.last
414+
('Mohamad', '', 'Ahmad Ali Hassan')
415+
416+
"""
417+
392418
def __init__(self,
393419
prefixes: Iterable[str] = PREFIXES,
394420
suffix_acronyms: Iterable[str] = SUFFIX_ACRONYMS,
@@ -401,6 +427,7 @@ def __init__(self,
401427
capitalization_exceptions: TupleManager[str] | Iterable[tuple[str, str]] = CAPITALIZATION_EXCEPTIONS,
402428
regexes: RegexTupleManager | TupleManager[re.Pattern[str]] | Iterable[tuple[str, re.Pattern[str]]] = REGEXES,
403429
patronymic_name_order: bool = False,
430+
middle_name_as_last: bool = False,
404431
) -> None:
405432
# These four descriptor assignments call _CachedUnionMember.__set__, which
406433
# calls _invalidate_pst() and establishes self._pst. They must come before
@@ -423,6 +450,7 @@ def __init__(self,
423450
# needing to override parse_nicknames() itself. See issue #112.
424451
self.extra_nickname_delimiters = TupleManager()
425452
self.patronymic_name_order = patronymic_name_order
453+
self.middle_name_as_last = middle_name_as_last
426454

427455
def _invalidate_pst(self) -> None:
428456
self._pst = None

tests/test_middle_name_as_last.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from nameparser.config import Constants
2+
from tests.base import HumanNameTestBase
3+
4+
5+
class MiddleNameAsLastFlagTests(HumanNameTestBase):
6+
7+
def test_default_is_false(self) -> None:
8+
C = Constants()
9+
assert C.middle_name_as_last is False
10+
11+
def test_can_set_true_via_constructor(self) -> None:
12+
C = Constants(middle_name_as_last=True)
13+
assert C.middle_name_as_last is True
14+
15+
def test_does_not_affect_other_instance(self) -> None:
16+
C1 = Constants(middle_name_as_last=True)
17+
C2 = Constants()
18+
assert C1.middle_name_as_last is True
19+
assert C2.middle_name_as_last is False

0 commit comments

Comments
 (0)