Skip to content

Commit 3d02563

Browse files
derek73claude
andcommitted
Move the docs that said FAMILY_FIRST splits at the particle (derek73#359)
Four sites asserted, correctly until the previous commit, that Policy(name_order=FAMILY_FIRST) reads "de Mesnil" as family 'de', given 'Mesnil'. derek73#354 wrote two of them and derek73#358 scoped the other two to the default order precisely so they would not bless that output. Now that the fold is order-independent, the first two are false and the scoping on the others is over-cautious in the one place a reader is most likely to test it. - config/particles.py: both docstrings. NON_GIVEN_NAME_PARTICLES no longer scopes its "the whole thing is a surname" reading to the default order, and PARTICLES now splits the sentence where the behavior splits -- a leading member is the surname under every order, while a leading particle OUTSIDE the set is genuinely order-dependent and still reads as family 'van', given 'Gogh' under FAMILY_FIRST. - _lexicon.py: particles_ambiguous said a non-member is folded "under the default given-first order". It is folded under all of them; what stays name_order's question is where a MEMBER's piece lands. - AGENTS.md's config-layer entry, same correction, plus the mechanism (the fold keys on position) since that is what a future reader needs to know before touching the rule. - docs/customize.rst and docs/usage.rst: no false claim to fix, but both fenced the reading off to the default order in a sentence that is now about two different things. Each says which half name_order still governs. customize.rst's particles_ambiguous section gets the point of derek73#359 as well: taking a word out of the set now changes the parsed fields under a family-first order, where before it moved only the ambiguity report. Every replacement claim was measured by parsing, not reasoned: FAMILY_FIRST gives family 'de Mesnil' and 'de la Vega' with no given name, family 'van' + given 'Gogh' for "van Gogh", family 'de' for the bare "de", and family 'van Gogh' once 'van' is removed from particles_ambiguous. FAMILY_FIRST_GIVEN_LAST agrees with FAMILY_FIRST on all of them. The release log bullet records the FAMILY_FIRST change and the "Mesnil de" shape that moves the other way; the 2.2.0 preamble's "no parse changes" is narrowed to the default order, which is what its own 751-name measurement covers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent dacc700 commit 3d02563

6 files changed

Lines changed: 50 additions & 41 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ Most modules define a `frozenset` of known name pieces; `capitalization.py` and
133133

134134
- `titles.py``TITLES` (prenominals) and `GIVEN_NAME_TITLES` (e.g. "Sir", which treat the following name as given, not family)
135135
- `suffixes.py``SUFFIX_ACRONYMS` (with periods, e.g. "M.D.") and `SUFFIX_WORDS` (e.g. "Jr."), plus `GLUED_HONORIFICS` (#308), the subset of `SUFFIX_WORDS` the peel may split off the END of a name token — a separate, harsher set, since the glued position has no writer-drawn boundary to lean on
136-
- `particles.py``PARTICLES` (family-name particles, e.g. "de", "van") and `NON_GIVEN_NAME_PARTICLES`, the curated subset that is *never* a standalone given name (under the DEFAULT given-first order a name starting with one is all surname: "de Mesnil" — but that is `name_order`'s half of the sentence, not this set's, and `Policy(name_order=FAMILY_FIRST)` reads the same input as family "de", given "Mesnil"; what the set decides under either order is that a leading particle outside it records a `PARTICLE_OR_GIVEN` ambiguity and one inside it records none); `Lexicon.particles_ambiguous` is its complement within `PARTICLES`, so the two mark OPPOSITE sets — see the flip warning in `docs/migrate.rst` before translating either
136+
- `particles.py` — `PARTICLES` (family-name particles, e.g. "de", "van") and `NON_GIVEN_NAME_PARTICLES`, the curated subset that is *never* a standalone given name (a name starting with one is all surname — "de Mesnil" — under EVERY `name_order` since #359: the post_rules fold keys on the token's opening POSITION, not on the GIVEN role it happened to get, so `Policy(name_order=FAMILY_FIRST)` reads "de Mesnil" as the family name too, and the degenerate bare "de" with nothing to fold into still stays as it is. A leading particle OUTSIDE the set is genuinely order-dependent and still splits — "van Gogh" is family "van", given "Gogh" under `FAMILY_FIRST` — since a word that CAN be a given name leaves `name_order` a real question to answer; what the set decides under either order is that such a leading particle records a `PARTICLE_OR_GIVEN` ambiguity and one inside it records none); `Lexicon.particles_ambiguous` is its complement within `PARTICLES`, so the two mark OPPOSITE sets — see the flip warning in `docs/migrate.rst` before translating either
137137
- `bound_given_names.py``BOUND_GIVEN_NAMES` (bound given-name prefixes, e.g. "abdul", "abu"); a group-stage rule joins the first non-title piece to its following piece before roles are assigned (v1's `_join_bound_first_name`, ported into `_pipeline/_group.py` and gone from the tree — the v1 descriptions further down are history, not current code)
138138
- `conjunctions.py``CONJUNCTIONS` (e.g. "and", "of") used to chain multi-word titles
139139
- `maiden_markers.py``MAIDEN_MARKERS` (e.g. "née", "geb.") routing the following name to `maiden`

docs/customize.rst

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,13 @@ a suffix only when written with periods:
168168
``particles_ambiguous`` is the same idea for surname particles. A
169169
particle listed there may also be a given name, which is what makes a
170170
leading one a decision to take; a particle *not* listed there never
171-
is, so there is nothing to decide. Under the default name order that
172-
shows up as whether the name has a given name at all: one that starts
173-
with a listed particle keeps it, while one starting with an unlisted
174-
particle has no given name — the whole thing is the surname. (Which
175-
field each piece lands in is ``name_order``'s question, covered
176-
below.)
171+
is, so there is nothing to decide. That shows up as whether the name
172+
has a given name at all: one that starts with a listed particle keeps
173+
it, while one starting with an unlisted particle has no given name —
174+
the whole thing is the surname. Which field a *listed* particle lands
175+
in is ``name_order``'s question, covered below; an unlisted one is the
176+
surname under every order, because a word that can never be a given
177+
name leaves the order nothing to decide.
177178

178179
.. doctest::
179180

@@ -186,8 +187,8 @@ below.)
186187

187188
If your data never uses ``Van`` as a given name, take it out of the
188189
ambiguous set: a leading ``van`` is then no decision at all, so no
189-
ambiguity is recorded, and under the default order it becomes part of
190-
the surname:
190+
ambiguity is recorded and it becomes part of the surname — under any
191+
``name_order``, since that is what taking the word out asserted:
191192

192193
.. doctest::
193194

docs/release_log.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ Release Log
1010
editing one in place as a way to change a default and replaces it
1111
with configuring a ``Lexicon`` or a private ``Constants``.
1212

13-
Nothing moved between vocabularies and no parse changes: over the
14-
751 names of the differential corpora, every one of the seven
15-
fields is identical to 2.1 through both the 2.0 and the 1.x API.
13+
Nothing moved between vocabularies and no parse changes in the
14+
default name order: over the 751 names of the differential
15+
corpora, every one of the seven fields is identical to 2.1 through
16+
both the 2.0 and the 1.x API. One non-default order does change,
17+
below.
1618
What breaks is code that *writes* to a default word list, and code
1719
that imports one by its 1.x name has until 3.0.
1820

@@ -22,6 +24,8 @@ Release Log
2224

2325
**Behavior Changes**
2426

27+
- Fix a name opening with a particle that is *never* a given name being split at the particle under ``Policy(name_order=FAMILY_FIRST)``: ``"de Mesnil"`` read as family ``de``, given ``Mesnil``, and ``"de la Vega"`` as family ``de``, given ``la Vega``. Each is now the whole surname, as it has always been in the default order. The rule that folds a leading never-given particle into the family keyed on the ``GIVEN`` role, which under a family-first order belongs to the token *after* the particle, so the test read the wrong word and declined; it now keys on the position that opens the name and fires under every ``name_order``. The decision behind that: a word that can never be a given name leaves ``name_order`` nothing to decide, so declaring family-first is not a reason to make ``de`` a surname on its own. A leading particle that *may* be a given name is genuinely order-dependent and is untouched -- ``"van Gogh"`` still reads as family ``van``, given ``Gogh`` under ``FAMILY_FIRST``. This is also what gives ``Lexicon.particles_ambiguous`` an effect outside the default order: taking a word out of it now changes the parsed fields under a family-first order, where before it moved only the ambiguity report. One shape moves the other way, on the same re-key: a bare never-given particle in the *given* position rather than the leading one -- ``"Mesnil de"`` under ``FAMILY_FIRST`` -- was folded by the old role test and now reads as family ``Mesnil``, given ``de``. Default-order output is byte-identical over all 751 corpus names, at the 1.4.0, 2.0.0 and 2.1.0 differential baselines alike (closes #359)
28+
2529
- Change the ``detail`` text of a ``PARTICLE_OR_GIVEN`` ambiguity to name the role the leading particle was actually given. It said "read as a given name" under every ``name_order``, which is false under ``Policy(name_order=FAMILY_FIRST)`` -- there ``"Van Johnson"`` reads as family ``Van``, given ``Johnson``, and the report described the reading not taken. It now ends "read as a family name" in that case, reading the role off the assigned token the way ``SUFFIX_OR_NAME`` already did -- that kind names both parts (``read as a family name rather than a post-nominal``), while this one names only the part it took. The ``kind`` is unchanged and stays ``PARTICLE_OR_GIVEN``: the fork really is particle-or-given, and only the human-readable text moved. Default-order output is identical (#355)
2630

2731
**Deprecations**

docs/usage.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ names together as easily as two surnames:
117117

118118
Position matters in exactly one place: the start of a name. A particle
119119
there has no surname to attach to yet, so what decides the reading is
120-
whether it is one that can double as a given name. Where the pieces
121-
then land is ``name_order``'s question — see :doc:`customize` — and
122-
the destinations below are the default given-first order's: the
123-
particle either becomes the given name or turns the whole name into a
124-
surname:
120+
whether it is one that can double as a given name: the particle either
121+
becomes the given name or turns the whole name into a surname. Only
122+
the first of those is ``name_order``'s question — see
123+
:doc:`customize`, and read the given name below as the default
124+
given-first order's — since a particle that can never be a given name
125+
is the surname whatever order you declare:
125126

126127
.. doctest::
127128

nameparser/_lexicon.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,13 @@ class Lexicon:
340340
#: into two pieces exactly as "van Gogh" does. What membership
341341
#: decides is what becomes of that piece afterwards. Under EITHER
342342
#: ``name_order`` a member records a particle-or-given ambiguity
343-
#: and a non-member records none; under the default given-first
344-
#: order a non-member is additionally folded back into the family
345-
#: name once roles exist, so the whole name is the surname ("de
346-
#: Mesnil" -- a bare "de", with nothing to fold into, is left
347-
#: alone). Which field each piece lands in is ``name_order``'s
348-
#: question, not this set's.
343+
#: and a non-member records none, and a non-member is additionally
344+
#: folded back into the family name once roles exist, so the whole
345+
#: name is the surname ("de Mesnil" -- a bare "de", with nothing to
346+
#: fold into, is left alone). That fold is order-independent too
347+
#: (#359): a word that can never be a given name leaves
348+
#: ``name_order`` nothing to decide. Which field a MEMBER's piece
349+
#: lands in is ``name_order``'s question, not this set's.
349350
#: No constant of its own -- the default derives
350351
#: as particles minus
351352
#: :data:`~nameparser.config.particles.NON_GIVEN_NAME_PARTICLES`

nameparser/config/particles.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
from nameparser.config.bound_given_names import BOUND_GIVEN_NAMES
33

44
#: The sub-set of :py:data:`PARTICLES` that are *never* a standalone given
5-
#: name. Under the default given-first order that means a name *starting*
6-
#: with one of these has no given name -- the whole thing is a surname
7-
#: (e.g. "de Mesnil" -> family name "de Mesnil"). The reading is scoped to
8-
#: the order on purpose: ``Policy(name_order=FAMILY_FIRST)`` parses the
9-
#: same input as family "de", given "Mesnil", because which side of a
10-
#: leading particle the family name sits on is ``name_order``'s question,
11-
#: not this set's. What membership decides under either order is the
12-
#: ambiguity report -- see :py:data:`PARTICLES` below.
5+
#: name. A name *starting* with one of these has no given name -- the
6+
#: whole thing is a surname (e.g. "de Mesnil" -> family name "de Mesnil")
7+
#: -- and that reading holds under EVERY ``name_order`` (#359). It is not
8+
#: scoped to the default order the way the rest of the positional read is:
9+
#: ``name_order`` says which side of the name the family sits on, and a
10+
#: word that can never be a given name leaves it nothing to decide, so
11+
#: ``Policy(name_order=FAMILY_FIRST)`` reads "de Mesnil" as the family
12+
#: name too. Membership also decides the ambiguity report -- see
13+
#: :py:data:`PARTICLES` below.
1314
#: Curated to exclude anything that can be a given name in some culture
1415
#: (`al`, `van`, `von`, `della`, `di`, `del`, `da`, `vander`, ...) and
1516
#: anything that is also a bound given-name particle (`abu`). When unsure,
@@ -86,16 +87,17 @@
8687
#: particle is the exception and chains nothing: the chain skips the
8788
#: first piece unconditionally, membership in this set or any other
8889
#: never entering into it. Where the pieces then land is again a later
89-
#: question,
90-
#: and this one is ``name_order``'s: under the default given-first order
91-
#: a leading :py:data:`NON_GIVEN_NAME_PARTICLES` member makes the whole
92-
#: name a family name ("de la Vega"), while a leading particle outside
93-
#: that set is read as the given name ("Van Johnson") -- whereas
94-
#: ``Policy(name_order=FAMILY_FIRST)`` splits both at the leading
95-
#: particle alike ("de la Vega" -> family "de", given "la Vega"; "Van
96-
#: Johnson" -> family "Van", given "Johnson"), which is the same
97-
#: chains-nothing grouping read the other way round. What membership
98-
#: decides under EITHER order is the report: a leading particle outside
90+
#: question, and this is where membership decides something: a leading
91+
#: :py:data:`NON_GIVEN_NAME_PARTICLES` member makes the whole name a
92+
#: family name ("de la Vega") under every ``name_order``, because a word
93+
#: that is never a given name leaves the order nothing to place. A
94+
#: leading particle OUTSIDE that set could be either, so there
95+
#: ``name_order`` decides after all: the default given-first order reads
96+
#: it as the given name ("Van Johnson"), while
97+
#: ``Policy(name_order=FAMILY_FIRST)`` splits the same chains-nothing
98+
#: grouping the other way round ("Van Johnson" -> family "Van", given
99+
#: "Johnson"). What membership decides under EITHER order is also the
100+
#: report: a leading particle outside
99101
#: :py:data:`NON_GIVEN_NAME_PARTICLES` records a particle-or-given
100102
#: ambiguity for the reading not taken, and one inside it records none.
101103
#:

0 commit comments

Comments
 (0)