Skip to content

Commit 0908325

Browse files
committed
Fix repeated-prefix-chain data loss in join_on_conjunctions prefix loop
Replace value-based pieces.index() lookup with a position-tracked forward scan. The old code re-found each prefix by searching for its string value after the list had already been mutated, which silently dropped one repetition of a chain when the same prefix word appeared more than once (e.g. 'Juan de la de la Vega' -> last 'de la Vega', missing the first 'de la'). Fixes #208.
1 parent cc01385 commit 0908325

2 files changed

Lines changed: 27 additions & 42 deletions

File tree

nameparser/parser.py

Lines changed: 17 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,51 +1304,26 @@ def shift_conj_index(past: int, by: int) -> None:
13041304
shift_conj_index(past=i, by=end - start - 1)
13051305

13061306
# join prefixes to following lastnames: ['de la Vega'], ['van Buren']
1307-
prefixes = list(filter(self.is_prefix, pieces))
1308-
if prefixes:
1309-
for prefix in prefixes:
1310-
try:
1311-
i = pieces.index(prefix)
1312-
except ValueError:
1313-
# If the prefix is no longer in pieces, it's because it has been
1314-
# combined with the prefix that appears right before (or before that when
1315-
# chained together) in the last loop, so the index of that newly created
1316-
# piece is the same as in the last loop, i==i still, and we want to join
1317-
# it to the next piece.
1318-
pass
1307+
i = 0
1308+
while i < len(pieces):
1309+
if not self.is_prefix(pieces[i]) or (i == 0 and total_length >= 1):
1310+
# If it's the first piece and there are more than 1 rootnames,
1311+
# assume it's a first name rather than a prefix.
1312+
i += 1
1313+
continue
13191314

1320-
new_piece = ''
1315+
# absorb any immediately-adjacent prefixes into one contiguous run
1316+
# e.g. "von und zu der" ==> chain them all before looking further
1317+
j = i + 1
1318+
while j < len(pieces) and self.is_prefix(pieces[j]):
1319+
j += 1
13211320

1322-
# join everything after the prefix until the next prefix or suffix
1321+
# then join everything after the run until the next prefix or suffix
1322+
while j < len(pieces) and not self.is_prefix(pieces[j]) and not self.is_suffix(pieces[j]):
1323+
j += 1
13231324

1324-
try:
1325-
if i == 0 and total_length >= 1:
1326-
# If it's the first piece and there are more than 1 rootnames, assume it's a first name
1327-
continue
1328-
next_prefix = next(iter(filter(self.is_prefix, pieces[i + 1:])))
1329-
j = pieces.index(next_prefix, i + 1)
1330-
if j == i + 1:
1331-
# if there are two prefixes in sequence, join to the following piece
1332-
j += 1
1333-
new_piece = ' '.join(pieces[i:j])
1334-
pieces[i:j] = [new_piece]
1335-
except StopIteration:
1336-
try:
1337-
# if there are no more prefixes, look for a suffix to stop at
1338-
stop_at = next(iter(filter(self.is_suffix, pieces[i + 1:])))
1339-
# search from i + 1: filter() finds the value of stop_at
1340-
# in pieces[i+1:] but pieces.index() without a start
1341-
# argument searches from 0, so an earlier occurrence of
1342-
# the same token (e.g. a suffix token that also appears
1343-
# before the prefix) would be matched instead.
1344-
j = pieces.index(stop_at, i + 1)
1345-
new_piece = ' '.join(pieces[i:j])
1346-
pieces[i:j] = [new_piece]
1347-
except StopIteration:
1348-
# if there were no suffixes, nothing to stop at so join all
1349-
# remaining pieces
1350-
new_piece = ' '.join(pieces[i:])
1351-
pieces[i:] = [new_piece]
1325+
pieces[i:j] = [' '.join(pieces[i:j])]
1326+
i += 1
13521327

13531328
log.debug("pieces: %s", pieces)
13541329
return pieces

tests/test_prefixes.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,16 @@ def test_non_first_name_prefix_with_custom_title(self) -> None:
307307
self.m(hn.first, "", hn)
308308
self.m(hn.last, "de Mesnil", hn)
309309

310+
def test_repeated_prefix_chain_de_la(self) -> None:
311+
hn = HumanName("Juan de la de la Vega")
312+
self.m(hn.first, "Juan", hn)
313+
self.m(hn.last, "de la de la Vega", hn)
314+
315+
def test_repeated_prefix_chain_van_der(self) -> None:
316+
hn = HumanName("Charles van der van der Berg")
317+
self.m(hn.first, "Charles", hn)
318+
self.m(hn.last, "van der van der Berg", hn)
319+
310320
# --- safety: excluded / ambiguous particles are unchanged ---
311321

312322
def test_leading_von_is_unchanged(self) -> None:

0 commit comments

Comments
 (0)