Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,17 @@ uv run sphinx-build -b html docs dist/docs
# covers those is _CORPUS_CLAIMS, which records what every rule claims
# -- its regex's corpus reach, its roles, and which names -- and so
# needs no notion of how a copy is spelled.
# FOUR rosters are keyed by FILENAME and checked by EQUALITY, so a new
# SIX rosters are keyed by FILENAME and checked by EQUALITY, so a new
# ledger must be enrolled in every one of them on the day it lands, even
# empty -- each fails loudly and names itself, but that is four separate
# red runs if you add them one at a time. Three sit in the test module
# and one in tools/differential/compare.py; a fifth, compare.py's
# _RECORDED_DIFFS, is enrolled transitively, since the guard holds
# set(_RECORDED_DIFFS) == set(_CROSS_RULE_WINNERS). Two more are keyed
# by rule CONTENT and apply only where such a rule exists.
# empty -- each fails loudly and names itself, but that is six separate
# red runs if you add them one at a time. Four sit in the test module
# and two in tools/differential/compare.py. Faster than reading this
# list: create the ledger, move DEFAULT_BASELINE, and run
# tests/v2/test_ledger_guards.py once -- every roster still missing the
# file fails by name. (Opening the 2.4 cycle found _ORDER_EXEMPTION_EFFECT
# that way; it landed with f478fb0 after this list was last written.)
# Two more are keyed by rule CONTENT and apply only where such a rule
# exists.
# Required, by filename:
# - _SPAN_BEARING_RULES: add the filename, mapped to the set of issue
# tags whose rules carry a script-span class (empty set if none).
Expand All @@ -201,6 +204,11 @@ uv run sphinx-build -b html docs dist/docs
# run itself refuses, pre-worker, a ledger missing from either
# shape roster -- an empty section is a statement, a missing one is
# nobody having looked -- so the guard and the run agree on it.
# - _ORDER_EXEMPTION_EFFECT: add the filename mapped to [] while the
# ledger has no order-decided contest. It is the recorded negative
# control for precedes_narrower -- every contest measured with
# exemptions ignored -- so a row appears when two rules land that
# file order alone arbitrates, whether or not one declares it.
# Conditional, by rule content:
# - _HONORIFIC_SOURCES: if the ledger has a CJK honorific rule, add a
# substring of its issue (keyed that way, not by tag) mapped to the
Expand Down
4 changes: 4 additions & 0 deletions docs/release_log.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Release Log
===========
* 2.4.0 - Unreleased

nameparser 2.4 is under development.

* 2.3.0 - September 12, 2026

nameparser 2.3 is parsing fixes and new honorific vocabulary;
Expand Down
10 changes: 5 additions & 5 deletions nameparser/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
#:
#: Bumped when a cycle OPENS, not when it ships, so the tree says what
#: it is building. Note the cost of the tuple being purely numeric: it
#: cannot carry the dev marker, so `VERSION >= (2, 2, 0)` is already
#: true here while 2.2.0 is unreleased. Compare `__version__` instead
#: cannot carry the dev marker, so `VERSION >= (2, 4, 0)` is already
#: true here while 2.4.0 is unreleased. Compare `__version__` instead
#: where that distinction matters.
VERSION = (2, 3, 0)
VERSION = (2, 4, 0)
#: PEP 440 pre-release/dev segment appended to the numeric version, or
#: "" for a final release. Joined WITHOUT a dot ("2.0.0rc1", not
#: "2.0.0.rc1"); setuptools reads __version__ as the package version.
#:
#: "dev" through the cycle, cleared at release. It normalizes to
#: 2.3.0.dev0, which sorts above 2.2.0 and BELOW 2.3.0, so an install
#: 2.4.0.dev0, which sorts above 2.3.0 and BELOW 2.4.0, so an install
#: from master can never masquerade as the release it precedes.
PRE_RELEASE = ""
PRE_RELEASE = "dev"
__version__ = ".".join(map(str, VERSION)) + PRE_RELEASE
10 changes: 6 additions & 4 deletions tests/v2/test_ledger_guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,8 @@ def test_script_ranges_membership_is_decided() -> None:
"fix(#298)", # the 间隔号 lookahead
}),
"expected_since_2.1.0.toml": frozenset(), # 2.2 cycle: no span-bearing rule
# open cycle: its one rule, fix(#462), is a Latin letter shape and
# copies no script range
"expected_since_2.2.0.toml": frozenset(),
"expected_since_2.2.0.toml": frozenset(), # 2.3 cycle: no span-bearing rule
"expected_since_2.3.0.toml": frozenset(), # open cycle, no rules yet
}

#: The leading `fix(...)`/`feat(...)` tag of a rule's `issue`, which is
Expand Down Expand Up @@ -3367,6 +3366,7 @@ def _claim(rule: dict) -> _Claim:
_Claim(17, ('_ambiguities', 'family', 'given', 'middle',
'suffix', 'title'), "ef4a7afe791a", None),
},
"expected_since_2.3.0.toml": {}, # open cycle, no rules yet
}


Expand Down Expand Up @@ -3467,8 +3467,9 @@ def test_every_rule_claims_the_recorded_share_of_the_corpus() -> None:
#: default-order only, the blind spot `orders` (#468) opened. One copy
#: of each fact, since two means one of them goes quietly stale.
_CROSS_RULE_WINNERS: dict[str, dict[str, str]] = {
# open cycle: one rule, so nothing for a second one to contest
"expected_since_2.2.0.toml": {},
# open cycle: no rules, so no contest
"expected_since_2.3.0.toml": {},
"expected_since_1.4.0.toml": {
# Spelled out since #508: the bare `fix(comma-family)` this row
# carried is a prefix of THREE rules in this ledger (measured
Expand Down Expand Up @@ -4978,6 +4979,7 @@ def test_a_rule_reaching_no_corpus_name_says_why_it_is_kept() -> None:
],
"expected_since_2.1.0.toml": [],
"expected_since_2.2.0.toml": [],
"expected_since_2.3.0.toml": [], # open cycle, no rules yet
}


Expand Down
6 changes: 4 additions & 2 deletions tools/differential/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
FIELDS = ("title", "first", "middle", "last", "suffix", "nickname",
"maiden")

DEFAULT_BASELINE = "2.2.0"
DEFAULT_BASELINE = "2.3.0"
REPO_ROOT = HERE.parents[1]
#: The v2 API's names for the same seven roles FIELDS names in v1
#: vocabulary. Both are compared from baseline 2.0 on.
Expand Down Expand Up @@ -1746,8 +1746,9 @@ class _ShapeMismatch(NamedTuple):
#: every run. 'Nguyen, Van' is classified by nothing only because it
#: diffs from nothing.
_RECORDED_DIFFS: dict[str, dict[str, tuple[str, ...]]] = {
# open cycle: one rule, so nothing for a second one to contest
"expected_since_2.2.0.toml": {},
# open cycle: no rules, so nothing for a second one to contest
"expected_since_2.3.0.toml": {},
"expected_since_1.4.0.toml": {
"Andrews, M.D.": ("given", "suffix"),
"田中, 太郎さん": ("given", "suffix"),
Expand Down Expand Up @@ -2151,6 +2152,7 @@ class _ShapeMismatch(NamedTuple):
"Lala Lajpat Rai": ("family", "middle", "suffix"),
"Smith, John E, III, Jr": ("_initials",),
},
"expected_since_2.3.0.toml": {}, # open cycle, no watched name yet
}


Expand Down
34 changes: 34 additions & 0 deletions tools/differential/expected_since_2.3.0.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Ledger for baseline 2.3.0 -- what changes for a user upgrading from
# the previous minor. Same rule grammar as the other ledgers: `issue`,
# `name_regex` and `fields` are every one of them required. Neither
# narrowing key may stand on its own -- #451 banned a rule with
# `fields` and no regex, #456 banned the reverse -- so a rule here
# narrows by name AND by role, never by just one.
#
# Opened empty the day 2.3.0 shipped (AGENTS.md release step 8), and it
# is opened before it has anything to say because DEFAULT_BASELINE and
# the ledger are coupled: _allowlist_for treats a missing file as a
# hard error, on the reasoning that an absent ledger classifies nothing
# and would make every diff report as unexplained. So the baseline
# could not advance to 2.3.0 without this file.
#
# Do not copy rules across from expected_since_2.2.0.toml: that ledger
# classifies what 2.3 changed on top of 2.2, and a rule copied without
# checking either over-matches -- hiding a real 2.4 regression behind a
# 2.3-era label -- or never fires at all. Where a 2.4 change is visible
# from several baselines the rule may legitimately appear in more than
# one file, but each copy is checked against its own run rather than
# assumed.
#
# tests/v2/test_differential.py permits exactly one empty ledger, the
# one DEFAULT_BASELINE names -- which is this file.
#
# tests/v2/test_ledger_guards.py and tools/differential/compare.py
# enroll this file, empty, in every roster keyed by ledger filename
# (AGENTS.md release step 8 lists them). The first rule that copies a
# script range, a CJK honorific or a Latin vocabulary has to be
# recorded in its roster or the sweep fails.
#
# There is deliberately no `change = []` line. TOML forbids appending a
# [[change]] table to a statically defined array, so that line would
# block the first entry.