Skip to content

Commit 1a7f418

Browse files
committed
Recognize Arabic and fullwidth CJK comma as name-part delimiters
The "Lastname, Firstname" split only matched the ASCII comma, so Arabic (U+060C) and fullwidth (U+FF0C) commas failed to trigger the comma format and leaked into the parsed output. Adds a shared "commas" regex used for both the split and the trailing-comma strip. Fixes #265.
1 parent 17d1946 commit 1a7f418

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

nameparser/config/regexes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
"bidi": re_bidi,
3030
"phd": re.compile(r'\s(ph\.?\s+d\.?)', re.I),
3131
"space_before_comma": re.compile(r'\s+,'),
32+
# ASCII comma plus its Arabic (U+060C) and fullwidth CJK (U+FF0C)
33+
# counterparts, used to split "Last, First" format and to strip a
34+
# trailing comma before parsing (#265).
35+
"commas": re.compile(r'[,،,]'),
3236
"east_slavic_patronymic": re.compile(
3337
r'(ovich|ovna|evich|evna|ichna|ilyich|kuzmich|lukich|fomich|fokich)$',
3438
re.I,

nameparser/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ def _apply_full_name(self, value: str | bytes, *, stacklevel: int) -> None:
923923
def collapse_whitespace(self, string: str) -> str:
924924
# collapse multiple spaces into single space
925925
string = self.C.regexes.spaces.sub(" ", string.strip())
926-
if string.endswith(","):
926+
if string and self.C.regexes.commas.fullmatch(string[-1]):
927927
string = string[:-1]
928928
return string
929929

@@ -1196,7 +1196,7 @@ def parse_full_name(self) -> None:
11961196
self._full_name = self.collapse_whitespace(self._full_name)
11971197

11981198
# break up full_name by commas
1199-
parts = [x.strip() for x in self._full_name.split(",")]
1199+
parts = [x.strip() for x in self.C.regexes.commas.split(self._full_name)]
12001200
self._had_comma = len(parts) > 1
12011201

12021202
log.debug("full_name: %s", self._full_name)

tests/test_comma_variants.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from nameparser import HumanName
2+
3+
from tests.base import HumanNameTestBase
4+
5+
6+
class HumanNameCommaVariantsTests(HumanNameTestBase):
7+
"""Non-ASCII comma characters should split "Last, First" the same as ',' (#265)."""
8+
9+
def test_arabic_comma_splits_lastname_format(self) -> None:
10+
hn = HumanName("سلمان، محمد")
11+
self.m(hn.first, "محمد", hn)
12+
self.m(hn.last, "سلمان", hn)
13+
14+
def test_fullwidth_comma_splits_lastname_format(self) -> None:
15+
hn = HumanName("Smith,John")
16+
self.m(hn.first, "John", hn)
17+
self.m(hn.last, "Smith", hn)
18+
19+
def test_arabic_comma_does_not_pollute_output(self) -> None:
20+
hn = HumanName("سلمان، محمد")
21+
self.assertNotIn("،", hn.last)
22+
self.assertNotIn("،", str(hn))
23+
24+
def test_trailing_arabic_comma_stripped(self) -> None:
25+
# matches ASCII behavior: a single word with a trailing comma has
26+
# nothing after the comma, so it's a bare name, not "Last,"
27+
hn = HumanName("سلمان،")
28+
self.m(hn.first, "سلمان", hn)

0 commit comments

Comments
 (0)