HumanName("Scott E. Werner").initials() # 'S. W.' -- the middle initial is gone
HumanName("john E. smith").initials() # 'j. s.'
HumanName("Juan Y. Garcia").initials() # 'J. G.'
E and Y are the Latin one-letter members of CONJUNCTIONS, and initials() filters conjunctions out. A middle or family initial that happens to be one of those letters is silently dropped.
A v1 parity break, shipped since 2.0.0
Measured on the released wheels:
| input |
1.4.0 |
2.0.0 |
2.1.0 |
Scott E. Werner |
S. E. W. |
S. W. |
S. W. |
john E. smith |
j. E. s. |
j. s. |
j. s. |
Juan Y. Garcia |
J. Y. G. |
J. G. |
J. G. |
mechanisms.md#FACADE-CONTRACT promises that 1.4-warning-free code keeps working through 2.x "EXCEPT release-log-classified fixes". No release-log bullet classifies this.
The 2.0 core gets it right — only the facade regressed
Same library, same name, two answers:
parse("Scott E. Werner").initials() # 'S. E. W.' correct
HumanName("Scott E. Werner").initials() # 'S. W.' wrong
The boundary is intact, which is what makes this precise
Only the dotted form is affected. A bare one-letter conjunction is still correctly dropped on both versions:
'maria y lopez' -> 'm. l.' 1.4.0 and 2.x agree
'john e smith' -> 'j. s.' 1.4.0 and 2.x agree
So this is not "conjunctions should initial". It is that v1 distinguished the conjunction e from the initial E. by the period, and the facade lost that distinction.
Reach, measured over the differential corpus
Comparing 1.4.0 against 2.2.0dev over the 1094-name corpus, restricted to the 865 names that parse byte-identically on both (so a parse change cannot be mistaken for this one), and comparing flattened letter sequences so 2.x's initials_list regrouping does not count:
14 of those 865 lose an initial, and every one is a middle E:
Amy E Maid · Amy E. Maid · Doctor, Jane E. · E Anne D,Leonardo
JOSE E MARIA SANTOS · Joe E. Smith · John E Smith · Jose E Maria Santos
Jose E. Maria Santos · Rt. Hon. Paul E. Mary · Smith, John E, III, Jr
US Magistrate-Judge Elizabeth E Campbell · john e. smith · scott e. werner
A fifteenth name, de los Santos (lS → S), is not this defect — los is in particles, not conjunctions, and that change is #402/#404's, already classified.
The Cyrillic half is sharper than the Latin one
The shipped single-letter conjunctions are &, e, y, и, й, і, و:
HumanName("Ivan И. Petrov").initials() # 'I. P.' facade
parse("Ivan И. Petrov").initials() # 'I. И. P.' core
_vocab.is_initial narrows by script repertoire (#320) precisely so an initial-shaped Cyrillic letter reads as an initial. The facade's predicate consults neither the shape half nor the repertoire half.
Cause
nameparser/_facade.py:474:
def _is_conjunction(self, text: str) -> bool:
self._resolve()
return _normalize(text) in self._lexicon.conjunctions
v1's is_conjunction excluded initials. _process_initial's comment four lines below states it copies "v1 parser.py:427 verbatim", and the copy dropped the carve-out.
The rule is stated and pinned elsewhere in the tree — nameparser/_render.py's _cap_word comment says "v1's is_conjunction excludes initials: 'E.' in 'Scott E. Werner' is an initial, not the conjunction 'e' (pinned live 2026-07-17)", using this very name. The core honors it; the facade does not.
Suggested fix
Give _facade._is_conjunction the initial carve-out the core already applies, so the facade and parse() agree. nameparser/_render.py's _INITIAL is the same shape test, and tests/v2/test_regex_sync.py already pins it against nameparser/_pipeline/_vocab.py.
Worth checking with it: _is_conjunction is one of the v1 hook names listed in _facade.py's hook roster, so a subclass may override it — whether the carve-out belongs in _is_conjunction or at its call site in _process_initial is a real question, and the hook contract should decide it rather than convenience.
Also worth a look while there: whether any other _facade predicate dropped a v1 carve-out the same way. _is_conjunction was found by checking one path; the roster has seven more.
Found reviewing #458, which fixed the equivalent re-derivation in the core's _cap_word.
EandYare the Latin one-letter members ofCONJUNCTIONS, andinitials()filters conjunctions out. A middle or family initial that happens to be one of those letters is silently dropped.A v1 parity break, shipped since 2.0.0
Measured on the released wheels:
Scott E. WernerS. E. W.S. W.S. W.john E. smithj. E. s.j. s.j. s.Juan Y. GarciaJ. Y. G.J. G.J. G.mechanisms.md#FACADE-CONTRACTpromises that 1.4-warning-free code keeps working through 2.x "EXCEPT release-log-classified fixes". No release-log bullet classifies this.The 2.0 core gets it right — only the facade regressed
Same library, same name, two answers:
The boundary is intact, which is what makes this precise
Only the dotted form is affected. A bare one-letter conjunction is still correctly dropped on both versions:
So this is not "conjunctions should initial". It is that v1 distinguished the conjunction
efrom the initialE.by the period, and the facade lost that distinction.Reach, measured over the differential corpus
Comparing 1.4.0 against
2.2.0devover the 1094-name corpus, restricted to the 865 names that parse byte-identically on both (so a parse change cannot be mistaken for this one), and comparing flattened letter sequences so 2.x'sinitials_listregrouping does not count:14 of those 865 lose an initial, and every one is a middle
E:A fifteenth name,
de los Santos(lS→S), is not this defect —losis inparticles, notconjunctions, and that change is #402/#404's, already classified.The Cyrillic half is sharper than the Latin one
The shipped single-letter conjunctions are
&,e,y,и,й,і,و:_vocab.is_initialnarrows by script repertoire (#320) precisely so an initial-shaped Cyrillic letter reads as an initial. The facade's predicate consults neither the shape half nor the repertoire half.Cause
nameparser/_facade.py:474:v1's
is_conjunctionexcluded initials._process_initial's comment four lines below states it copies "v1 parser.py:427 verbatim", and the copy dropped the carve-out.The rule is stated and pinned elsewhere in the tree —
nameparser/_render.py's_cap_wordcomment says "v1's is_conjunction excludes initials: 'E.' in 'Scott E. Werner' is an initial, not the conjunction 'e' (pinned live 2026-07-17)", using this very name. The core honors it; the facade does not.Suggested fix
Give
_facade._is_conjunctionthe initial carve-out the core already applies, so the facade andparse()agree.nameparser/_render.py's_INITIALis the same shape test, andtests/v2/test_regex_sync.pyalready pins it againstnameparser/_pipeline/_vocab.py.Worth checking with it:
_is_conjunctionis one of the v1 hook names listed in_facade.py's hook roster, so a subclass may override it — whether the carve-out belongs in_is_conjunctionor at its call site in_process_initialis a real question, and the hook contract should decide it rather than convenience.Also worth a look while there: whether any other
_facadepredicate dropped a v1 carve-out the same way._is_conjunctionwas found by checking one path; the roster has seven more.Found reviewing #458, which fixed the equivalent re-derivation in the core's
_cap_word.