Skip to content

Commit d6da248

Browse files
committed
Add NON_FIRST_NAME_PREFIXES as a static subset of PREFIXES (#121)
1 parent e3d8b14 commit d6da248

2 files changed

Lines changed: 61 additions & 23 deletions

File tree

nameparser/config/prefixes.py

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
1+
#: The sub-set of :py:data:`PREFIXES` that are *never* a standalone first name.
2+
#: A name that *starts* with one of these has no first name -- the whole thing
3+
#: is a surname (e.g. "de Mesnil" -> last name "de Mesnil"). Curated to exclude
4+
#: anything that can be a given name in some culture (`al`, `van`, `von`,
5+
#: `della`, `di`, `del`, `da`, `vander`, ...) and anything that is also a first
6+
#: name prefix (`abu`). When unsure, leave a word out: a missing member just
7+
#: means that name is not auto-fixed, whereas a wrong member misparses a real
8+
#: person. Must stay a subset of :py:data:`PREFIXES` and disjoint from
9+
#: :py:data:`~nameparser.config.first_name_prefixes.FIRST_NAME_PREFIXES`.
10+
NON_FIRST_NAME_PREFIXES = set([
11+
"'t",
12+
'af',
13+
'auf',
14+
'av',
15+
'bint',
16+
'de',
17+
"de'",
18+
'degli',
19+
'dei',
20+
'delle',
21+
'delli',
22+
'dello',
23+
'dem',
24+
'der',
25+
'dos',
26+
'het',
27+
'ibn',
28+
'op',
29+
'ter',
30+
'vd',
31+
'vom',
32+
'zu',
33+
])
34+
135
#: Name pieces that appear before a last name. Prefixes join to the piece
236
#: that follows them to make one new piece. They can be chained together, e.g
337
#: "von der" and "de la". Because they only appear in middle or last names,
@@ -7,64 +41,47 @@
741
#: appear after a prefixes. So in "pennie von bergen wessels MD", "von" will
842
#: join with all following name pieces until the suffix "MD", resulting in the
943
#: correct parsing of the last name "von bergen wessels".
10-
PREFIXES = set([
11-
"'t",
44+
#:
45+
#: Defined as a static union so every :py:data:`NON_FIRST_NAME_PREFIXES` member
46+
#: is guaranteed to also be a prefix (and still join forward), with no drift --
47+
#: mirroring ``TITLES = FIRST_NAME_TITLES | {...}`` in
48+
#: :py:mod:`nameparser.config.titles`.
49+
PREFIXES = NON_FIRST_NAME_PREFIXES | set([
1250
'aan',
1351
'aen',
1452
'abu',
15-
'af',
1653
'al',
17-
'auf',
18-
'av',
1954
'bar',
2055
'bat',
2156
'bin',
22-
'bint',
2357
'bon',
2458
'da',
2559
'dal',
26-
'de',
27-
"de'",
28-
'degli',
29-
'dei',
3060
'del',
3161
'dela',
3262
'della',
33-
'delle',
34-
'delli',
35-
'dello',
36-
'dem',
3763
'den',
38-
'der',
3964
'di',
4065
'dí',
4166
'do',
42-
'dos',
4367
'du',
4468
'freiherr',
4569
'freiherrin',
4670
'heer',
47-
'het',
48-
'ibn',
4971
'la',
5072
'le',
5173
'mac',
5274
'mc',
53-
'op',
5475
'san',
5576
'santa',
5677
'st',
5778
'ste',
5879
'te',
59-
'ter',
6080
'tho',
6181
'thoe',
6282
'van',
6383
'vande',
6484
'vander',
65-
'vd',
6685
'vel',
67-
'vom',
6886
'von',
69-
'zu',
7087
])

tests/test_prefixes.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import pytest
22

33
from nameparser import HumanName
4+
from nameparser.config.prefixes import PREFIXES, NON_FIRST_NAME_PREFIXES
5+
from nameparser.config.first_name_prefixes import FIRST_NAME_PREFIXES
46

57
from tests.base import HumanNameTestBase
68

@@ -163,6 +165,25 @@ def test_comma_three_conjunctions(self) -> None:
163165
self.m(hn.middle, "Q. Xavier", hn)
164166
self.m(hn.suffix, "III", hn)
165167

168+
def test_non_first_name_prefixes_subset_of_prefixes(self) -> None:
169+
# Every non-first-name prefix must still be a prefix so it joins forward.
170+
self.assertTrue(NON_FIRST_NAME_PREFIXES <= PREFIXES)
171+
172+
def test_non_first_name_prefixes_disjoint_from_first_name_prefixes(self) -> None:
173+
# A word cannot be both "joins to the first name" and "never a first
174+
# name" (e.g. 'abu' is a first_name_prefix, so it is excluded here).
175+
self.assertEqual(NON_FIRST_NAME_PREFIXES & FIRST_NAME_PREFIXES, set())
176+
177+
def test_non_first_name_prefixes_expected_members(self) -> None:
178+
# 'abu' is in PREFIXES but excluded (it is a first_name_prefix);
179+
# 'von'/'van'/'della'/'di'/'del' are excluded (they can be first names).
180+
self.assertIn('de', NON_FIRST_NAME_PREFIXES)
181+
self.assertIn('dos', NON_FIRST_NAME_PREFIXES)
182+
self.assertNotIn('abu', NON_FIRST_NAME_PREFIXES)
183+
self.assertNotIn('von', NON_FIRST_NAME_PREFIXES)
184+
self.assertNotIn('van', NON_FIRST_NAME_PREFIXES)
185+
self.assertNotIn('della', NON_FIRST_NAME_PREFIXES)
186+
166187

167188
class LastNamePrefixSplitTestCase(HumanNameTestBase):
168189

0 commit comments

Comments
 (0)