Skip to content

Commit 7a00311

Browse files
committed
feat: implement middle_name_as_last fold (#133)
1 parent d4feffc commit 7a00311

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

nameparser/parser.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,15 @@ def handle_patronymic_name_order(self) -> None:
753753
self.first_list,
754754
)
755755

756+
def handle_middle_name_as_last(self) -> None:
757+
"""
758+
When middle_name_as_last is enabled, fold middle_list into last_list
759+
(prepended, preserving order) and clear middle_list. No-op when
760+
middle_list is already empty.
761+
"""
762+
self.last_list = self.middle_list + self.last_list
763+
self.middle_list = []
764+
756765
def post_process(self) -> None:
757766
"""
758767
This happens at the end of the :py:func:`parse_full_name` after
@@ -762,6 +771,8 @@ def post_process(self) -> None:
762771
self.handle_firstnames()
763772
if self.C.patronymic_name_order:
764773
self.handle_patronymic_name_order()
774+
if self.C.middle_name_as_last:
775+
self.handle_middle_name_as_last()
765776
self.handle_capitalization()
766777

767778
def fix_phd(self) -> None:

tests/test_middle_name_as_last.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from nameparser import HumanName
12
from nameparser.config import Constants
23
from tests.base import HumanNameTestBase
34

@@ -17,3 +18,64 @@ def test_does_not_affect_other_instance(self) -> None:
1718
C2 = Constants()
1819
assert C1.middle_name_as_last is True
1920
assert C2.middle_name_as_last is False
21+
22+
23+
class MiddleNameAsLastFoldTests(HumanNameTestBase):
24+
25+
def setup_method(self) -> None:
26+
self.C = Constants(middle_name_as_last=True)
27+
28+
def hn(self, name: str) -> HumanName:
29+
return HumanName(name, constants=self.C)
30+
31+
def test_fold_no_comma(self) -> None:
32+
n = self.hn("Mohamad Ahmad Ali Hassan")
33+
self.m(n.first, "Mohamad", n)
34+
self.m(n.middle, "", n)
35+
self.m(n.last, "Ahmad Ali Hassan", n)
36+
37+
def test_fold_comma_converges(self) -> None:
38+
no_comma = self.hn("Mohamad Ahmad Ali Hassan")
39+
comma = self.hn("Hassan, Mohamad Ahmad Ali")
40+
self.m(comma.first, no_comma.first, comma)
41+
self.m(comma.last, no_comma.last, comma)
42+
43+
def test_title_and_suffix_preserved(self) -> None:
44+
n = self.hn("Dr. Mohamad Ahmad Hassan Jr")
45+
self.m(n.title, "Dr.", n)
46+
self.m(n.last, "Ahmad Hassan", n)
47+
self.m(n.suffix, "Jr", n)
48+
49+
def test_no_middle_is_noop(self) -> None:
50+
n = self.hn("John Doe")
51+
self.m(n.first, "John", n)
52+
self.m(n.middle, "", n)
53+
self.m(n.last, "Doe", n)
54+
55+
def test_single_token_is_noop(self) -> None:
56+
n = self.hn("Cher")
57+
self.m(n.first, "Cher", n)
58+
self.m(n.last, "", n)
59+
60+
def test_given_names_and_surnames_track_fold(self) -> None:
61+
n = self.hn("Mohamad Ahmad Ali Hassan")
62+
self.m(n.given_names, n.first, n)
63+
self.m(n.surnames, n.last, n)
64+
65+
def test_last_prefixes_still_split_after_fold(self) -> None:
66+
# Unfolded this is first="Miguel", middle="da Silva do Amaral",
67+
# last="de Souza" (last_prefixes="de"). Folded, last_list becomes
68+
# ["da","Silva","do","Amaral","de","Souza"]; _split_last() strips
69+
# leading contiguous prefix words from the start, so only the
70+
# leading "da" is stripped ("Silva" is not a prefix, so scanning
71+
# stops there) — last_prefixes="da", not "de".
72+
n = self.hn("Miguel da Silva do Amaral de Souza")
73+
self.m(n.last_prefixes, "da", n)
74+
75+
76+
class MiddleNameAsLastFlagOffTests(HumanNameTestBase):
77+
78+
def test_default_constants_unaffected(self) -> None:
79+
n = HumanName("Mohamad Ahmad Ali Hassan")
80+
self.m(n.middle, "Ahmad Ali", n)
81+
self.m(n.last, "Hassan", n)

0 commit comments

Comments
 (0)