Skip to content

Commit cb2576f

Browse files
committed
Expose Constants.non_first_name_prefixes SetManager (#121)
1 parent d6da248 commit cb2576f

3 files changed

Lines changed: 27 additions & 1 deletion

File tree

nameparser/config/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from typing_extensions import Self
3737

3838
from nameparser.util import lc
39-
from nameparser.config.prefixes import PREFIXES
39+
from nameparser.config.prefixes import PREFIXES, NON_FIRST_NAME_PREFIXES
4040
from nameparser.config.first_name_prefixes import FIRST_NAME_PREFIXES
4141
from nameparser.config.capitalization import CAPITALIZATION_EXCEPTIONS
4242
from nameparser.config.conjunctions import CONJUNCTIONS
@@ -264,6 +264,11 @@ class Constants:
264264
:py:attr:`conjunctions` wrapped with :py:class:`SetManager`.
265265
:param set first_name_prefixes:
266266
:py:attr:`~first_name_prefixes.FIRST_NAME_PREFIXES` wrapped with :py:class:`SetManager`.
267+
:param set non_first_name_prefixes:
268+
:py:attr:`~prefixes.NON_FIRST_NAME_PREFIXES` wrapped with :py:class:`SetManager`.
269+
The subset of prefixes that are never a first name, so a *leading* one
270+
marks the whole name as a surname. Must stay disjoint from
271+
``first_name_prefixes``.
267272
:type capitalization_exceptions: tuple or dict
268273
:param capitalization_exceptions:
269274
:py:attr:`~capitalization.CAPITALIZATION_EXCEPTIONS` wrapped with :py:class:`TupleManager`.
@@ -289,6 +294,7 @@ class Constants:
289294
first_name_titles: SetManager
290295
conjunctions: SetManager
291296
first_name_prefixes: SetManager
297+
non_first_name_prefixes: SetManager
292298
suffix_acronyms_ambiguous: SetManager
293299
capitalization_exceptions: TupleManager[str]
294300
regexes: RegexTupleManager
@@ -463,6 +469,7 @@ def __init__(self,
463469
first_name_titles: Iterable[str] = FIRST_NAME_TITLES,
464470
conjunctions: Iterable[str] = CONJUNCTIONS,
465471
first_name_prefixes: Iterable[str] = FIRST_NAME_PREFIXES,
472+
non_first_name_prefixes: Iterable[str] = NON_FIRST_NAME_PREFIXES,
466473
capitalization_exceptions: TupleManager[str] | Iterable[tuple[str, str]] = CAPITALIZATION_EXCEPTIONS,
467474
regexes: RegexTupleManager | TupleManager[re.Pattern[str]] | Iterable[tuple[str, re.Pattern[str]]] = REGEXES,
468475
patronymic_name_order: bool = False,
@@ -478,6 +485,7 @@ def __init__(self,
478485
self.first_name_titles = SetManager(first_name_titles)
479486
self.conjunctions = SetManager(conjunctions)
480487
self.first_name_prefixes = SetManager(first_name_prefixes)
488+
self.non_first_name_prefixes = SetManager(non_first_name_prefixes)
481489
self.suffix_acronyms_ambiguous = SetManager(suffix_acronyms_ambiguous)
482490
self.capitalization_exceptions = TupleManager(capitalization_exceptions)
483491
self.regexes = RegexTupleManager(regexes)

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"first_name_titles",
3636
"conjunctions",
3737
"first_name_prefixes",
38+
"non_first_name_prefixes",
3839
"capitalization_exceptions",
3940
"regexes",
4041
"nickname_delimiters",

tests/test_prefixes.py

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

33
from nameparser import HumanName
4+
from nameparser.config import CONSTANTS, Constants
45
from nameparser.config.prefixes import PREFIXES, NON_FIRST_NAME_PREFIXES
56
from nameparser.config.first_name_prefixes import FIRST_NAME_PREFIXES
67

@@ -184,6 +185,22 @@ def test_non_first_name_prefixes_expected_members(self) -> None:
184185
self.assertNotIn('van', NON_FIRST_NAME_PREFIXES)
185186
self.assertNotIn('della', NON_FIRST_NAME_PREFIXES)
186187

188+
def test_constants_exposes_non_first_name_prefixes(self) -> None:
189+
self.assertEqual(set(CONSTANTS.non_first_name_prefixes), NON_FIRST_NAME_PREFIXES)
190+
191+
def test_non_first_name_prefixes_disjoint_from_titles(self) -> None:
192+
# A member that is also a title is consumed as a title before the fold
193+
# would run, making it inert (the st/ste footgun). Pin the invariant on
194+
# the live default sets.
195+
self.assertEqual(
196+
set(CONSTANTS.non_first_name_prefixes) & set(CONSTANTS.titles),
197+
set(),
198+
)
199+
200+
def test_non_first_name_prefixes_constructor_arg(self) -> None:
201+
c = Constants(non_first_name_prefixes={'zzz'})
202+
self.assertEqual(set(c.non_first_name_prefixes), {'zzz'})
203+
187204

188205
class LastNamePrefixSplitTestCase(HumanNameTestBase):
189206

0 commit comments

Comments
 (0)