You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parser.matches(a, b) → parses str arguments with THIS parser
Parser.revise(name, **fields) → classifies each value with THIS parser
There is no Parser.initials. ParsedName.initials() takes (spec, delimiter, separator) and nothing else
(nameparser/_types.py:856), so there is no supported way to tell that
view which vocabulary the name was parsed under.
Why it matters now
initials() reads token tags, and tags are the parse's own record, so
for a name the parser produced the missing parameter costs nothing —
the tags already carry the custom lexicon's decisions. It costs
something as soon as a field is spliced in after the parse:
>>>fromnameparserimportParser, Lexicon>>>p=Parser()
>>>p.parse("john de la vega").initials()
'j. v.'>>>p.parse("john smith").replace(family="de la vega").initials()
'j. d. l. v.'
The spliced tokens carry no reading, so every word of the field
contributes an initial. The facade agrees with the parse, not with
the core (HumanName("john de la vega").initials() is 'j. v.', and
so is 1.4.0's), because HumanName.initials_list() computes from the
field STRINGS with its own vocabulary. So the divergence is v2-core
only, and it has shipped since 2.0.0.
Why the obvious fix was written and dropped
PR #463 added a fallback: where a token carries no reading, ask a
vocabulary the way the parser would have. capitalized() can do this
honestly — it is handed a lexicon. initials() is not, so the
fallback had to reach for Lexicon.default() and guess. Under a
caller's own vocabulary the guess is wrong in the worst direction —
it does not merely fail to fix the defect, it erases a field that was
answering correctly:
>>>fromnameparserimportParser, Lexicon>>>p=Parser(lexicon=Lexicon.default().add(particles={"y"}))
>>>p.parse("Juan de y").initials()
'J. d. y.'>>>p.parse("Juan Perez").replace(family="de y").initials()
'J. d. y.'# today'J.'# with the Lexicon.default() fallback
Under the caller's lexicon de y is an all-particle family, so all of
its words are name words and initial (rules.md#R2/#R3). Under the
default vocabulary y is a conjunction and de a particle, so both
are skipped and the whole field's initials vanish. The fallback was
reverted for that reason; the underlying defect is unfixed and
recorded as accepted in docs/design/rules.md#R3 and docs/design/decisions.md.
What this issue asks for
A crossing that lets initials() be answered under the parse's own
vocabulary, so a future fallback is answerable rather than a guess.
The obvious shape, matching the three crossings that already exist:
Open questions worth deciding on the issue rather than in review:
Does the crossing alone close the defect, or is a second change
needed?Parser.initials would give the view a vocabulary; whether
the view should then fall back for spliced text is the decision PR Render views honor the parser's decisions instead of re-deciding (#458) #463 got wrong once, and it should be taken explicitly. Note that ParsedName.initials() — the no-parser call — would still have no
vocabulary and would still answer from tags alone, so the two
surfaces would disagree by design. capitalized() already has that
shape (lexicon=None means the default), which is a precedent for
either answer.
Or should ParsedName.initials() grow a lexicon= parameter
instead, matching ParsedName.capitalized(lexicon=None, *, force)
exactly, with Parser.initials as the thin delegate? That is the
smaller surface change and the one that makes the two methods
symmetric. It is also the one that makes ParsedName.initials()
able to guess Lexicon.default() on an omitted argument — which is
the same guess this issue says is wrong, only now the caller has a
way to avoid it.
Is Parser.revise() the answer we want to keep recommending?
It already crosses this correctly — p.revise(name, family="de y")
classifies the value with the parser's own lexicon and initials J. d. y. — so the crossing exists for anyone who edits through
the parser. This issue is about replace(), which deliberately does
NOT classify, and about the many callers who reach for it first.
Measured on
fix/458-461-render-honors-the-parse @ e39d8a3, 2026-08-29, and on the
released 1.4.0 wheel for the facade comparison.
Parsercarries three crossings that let a name parsed under a customlexicon be rendered or compared under that same lexicon:
Parser.capitalized(name, force=...)→ParsedName.capitalized(lexicon=...)Parser.matches(a, b)→ parsesstrarguments with THIS parserParser.revise(name, **fields)→ classifies each value with THIS parserThere is no
Parser.initials.ParsedName.initials()takes(spec, delimiter, separator)and nothing else(
nameparser/_types.py:856), so there is no supported way to tell thatview which vocabulary the name was parsed under.
Why it matters now
initials()reads token tags, and tags are the parse's own record, sofor a name the parser produced the missing parameter costs nothing —
the tags already carry the custom lexicon's decisions. It costs
something as soon as a field is spliced in after the parse:
The spliced tokens carry no reading, so every word of the field
contributes an initial. The facade agrees with the parse, not with
the core (
HumanName("john de la vega").initials()is'j. v.', andso is 1.4.0's), because
HumanName.initials_list()computes from thefield STRINGS with its own vocabulary. So the divergence is v2-core
only, and it has shipped since 2.0.0.
Why the obvious fix was written and dropped
PR #463 added a fallback: where a token carries no reading, ask a
vocabulary the way the parser would have.
capitalized()can do thishonestly — it is handed a lexicon.
initials()is not, so thefallback had to reach for
Lexicon.default()and guess. Under acaller's own vocabulary the guess is wrong in the worst direction —
it does not merely fail to fix the defect, it erases a field that was
answering correctly:
Under the caller's lexicon
de yis an all-particle family, so all ofits words are name words and initial (rules.md#R2/#R3). Under the
default vocabulary
yis a conjunction anddea particle, so bothare skipped and the whole field's initials vanish. The fallback was
reverted for that reason; the underlying defect is unfixed and
recorded as accepted in
docs/design/rules.md#R3anddocs/design/decisions.md.What this issue asks for
A crossing that lets
initials()be answered under the parse's ownvocabulary, so a future fallback is answerable rather than a guess.
The obvious shape, matching the three crossings that already exist:
Open questions worth deciding on the issue rather than in review:
Does the crossing alone close the defect, or is a second change
needed?
Parser.initialswould give the view a vocabulary; whetherthe view should then fall back for spliced text is the decision PR
Render views honor the parser's decisions instead of re-deciding (#458) #463 got wrong once, and it should be taken explicitly. Note that
ParsedName.initials()— the no-parser call — would still have novocabulary and would still answer from tags alone, so the two
surfaces would disagree by design.
capitalized()already has thatshape (
lexicon=Nonemeans the default), which is a precedent foreither answer.
Or should
ParsedName.initials()grow alexicon=parameterinstead, matching
ParsedName.capitalized(lexicon=None, *, force)exactly, with
Parser.initialsas the thin delegate? That is thesmaller surface change and the one that makes the two methods
symmetric. It is also the one that makes
ParsedName.initials()able to guess
Lexicon.default()on an omitted argument — which isthe same guess this issue says is wrong, only now the caller has a
way to avoid it.
Is
Parser.revise()the answer we want to keep recommending?It already crosses this correctly —
p.revise(name, family="de y")classifies the value with the parser's own lexicon and initials
J. d. y.— so the crossing exists for anyone who edits throughthe parser. This issue is about
replace(), which deliberately doesNOT classify, and about the many callers who reach for it first.
Measured on
fix/458-461-render-honors-the-parse@ e39d8a3, 2026-08-29, and on thereleased 1.4.0 wheel for the facade comparison.