Skip to content

Commit 17efbbe

Browse files
derek73claude
andcommitted
docs+test: record the comparison_key casefold deviation from 1.4
1.4's comparison_key()/matches() lowercased; 2.0 casefolds (both the new API and the facade). The deviation is strictly more permissive -- ß/SS and final-sigma case pairs now compare equal -- and comparison is the one surface where casefold's aggressive folds are wanted: the key is opaque, so the vocabulary spelling-mutation casefold caused in storage (fixed to lower() in 3e6e08d) cannot happen here. Release-log behavior-changes entry + a pin test on both API paths, so the next casefold audit finds the intent recorded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0fd386c commit 17efbbe

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

docs/release_log.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Release Log
2323
- Fix maiden-name markers (``née``/``nee``/``born``/``geb.``/``roz.``) being folded into ``middle``/``last`` in 1.x (e.g. ``"Jane Smith née Jones"`` → ``middle="Smith née"``); 2.0 recognizes the marker and routes the following name to the new ``maiden`` field (closes #274; ``tests/v2/cases.py`` row ``maiden_marker``; not present in the differential corpus)
2424
- Data change: ``ma``/``do`` added to ``suffix_acronyms_ambiguous`` -- ambiguous acronyms count as suffixes only when written with periods, so a bare common surname (``"Jack Ma"``, ``"Anh Do"``) keeps its family name under 2.0's keep-recognized-suffixes routing, matching 1.4's output (``tests/v2/cases.py`` row ``ambiguous_surname_acronyms``). Side effect: parenthesized/quoted ``"(MA)"``/``"(DO)"`` (no periods) no longer escape to ``suffix`` the way 1.x did -- they now fall through to nickname parsing like any other ambiguous-acronym delimited content. Not present in the differential corpus
2525
- Change suffix-delimiter rendering: with a custom ``Policy``/``Constants`` suffix delimiter configured (e.g. ``suffix_delimiter="/"``, ``"John Smith, RN/CRNA"``), 1.x split the token and rendered ``suffix="RN, CRNA"``; 2.0 keeps the no-space delimiter-core token whole (``suffix="RN/CRNA"``) -- role assignment is unchanged, only rendering differs (anti-#100, migration plan deviation 5; ``tests/v2/cases.py`` row ``suffix_delimiter_no_space_core``). Only fires with a non-default policy, so it does not appear in the (default-policy) differential corpus
26+
- Change ``comparison_key()``/``matches()`` (both APIs) to fold components with ``str.casefold()`` where 1.4 used ``str.lower()``: Unicode case-pair forms now compare equal -- ``"STRASSE"`` matches ``"Straße"``, and a Greek final-sigma variant matches its regular-sigma form. Strictly more permissive (anything 1.4 matched still matches); parse output is unaffected -- vocabulary normalization itself uses ``lower()``, v1-parity (``tests/v2/test_types.py`` row ``test_matches_casefolds_unicode_case_pairs``)
2627

2728
Everything else in the 486-name differential corpus (built from the
2829
v1 test banks as of commit ``2d5d8c2``, pre-dating the M12 test

tests/v2/test_types.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,24 @@ def test_comparison_key_is_casefolded_canonical_seven_tuple() -> None:
303303
assert upper.comparison_key() == lower.comparison_key()
304304

305305

306+
def test_matches_casefolds_unicode_case_pairs() -> None:
307+
# Deliberate 1.4 deviation (release log): comparison folds with
308+
# casefold() where 1.4 used lower(). Comparison is the one surface
309+
# where casefold's aggressive folds are WANTED -- ß/SS and final-
310+
# sigma forms are the same name under different case conventions,
311+
# and the key is opaque, so the spelling-mutation bug casefold
312+
# caused in vocabulary storage (which stays lower(), v1 lc parity
313+
# -- see _lexicon._normalize) cannot happen here.
314+
from nameparser import HumanName, parse
315+
316+
assert parse("Hans Straße").matches("HANS STRASSE") # ß <-> SS
317+
assert parse("Νίκος Παπαδόπουλος").matches("Νίκοσ Παπαδόπουλοσ") # ς <-> σ
318+
# same fold on the facade path -- both APIs share the deviation
319+
assert HumanName("Hans Straße").matches("HANS STRASSE")
320+
key = parse("Hans Straße").comparison_key()
321+
assert key == parse("HANS STRASSE").comparison_key()
322+
323+
306324
def test_token_rejects_bare_string_and_mapping_tags() -> None:
307325
# frozenset("particle") is the set(str) footgun: eight single chars.
308326
with pytest.raises(TypeError, match="bare string"):

0 commit comments

Comments
 (0)