diff --git a/AGENTS.md b/AGENTS.md index 5027712a..d6db4b0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -139,20 +139,34 @@ uv run sphinx-build -b html docs dist/docs # so a copy spelled some other way is not undeclared but unseen. What # 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. Enrol the new ledger in -# _CORPUS_CLAIMS always, and in the others where they apply: +# needs no notion of how a copy is spelled. +# THREE 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 three separate +# red runs if you add them one at a time. 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). +# - _CORPUS_CLAIMS: a ledger with no entry hard-fails. Add the +# filename mapped to {} while the ledger is empty, then one _Claim +# per rule as rules land. The test prints the values to record. A +# _Claim's digest moves whenever the corpus text does, so a pure +# RENAME of a corpus name lands here as names-unchanged, +# roles-unchanged, digest-moved; a count that GREW means the rule +# absorbed something. +# - _CROSS_RULE_WINNERS: add the filename mapped to {} while the +# ledger has no contested name. Equality since #452, which found +# that a ledger with no rows had been indistinguishable from one +# needing none -- the two 2.x ledgers whose shapes #452 moved +# between rules had no section at all. +# 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 # constant it copies -- but only if no existing key already matches # that issue. A retroactive ledger can repeat an older one's rule # verbatim (fix(#271/#272/#298) is in both today), and every rule # must match exactly one key. -# - _CORPUS_CLAIMS: REQUIRED, not conditional -- a ledger with no -# entry hard-fails. Add the filename mapped to {} while the ledger -# is empty, then one _Claim per rule as rules land. The test prints -# the values to record. # - _LATIN_ALTERNATION_SOURCES: same, for a rule copying a Latin # vocabulary (maiden markers, ambiguous acronyms). An alternation # matching no key fails as undeclared -- add it, or record it in diff --git a/docs/release_log.rst b/docs/release_log.rst index 05bb32d6..75fbf50e 100644 --- a/docs/release_log.rst +++ b/docs/release_log.rst @@ -1,5 +1,7 @@ Release Log =========== +* 2.3.0 - Unreleased + * 2.2.0 - August 31, 2026 nameparser 2.2 is a rename plus about thirty parsing fixes. diff --git a/nameparser/_version.py b/nameparser/_version.py index ff4a494f..a607b7e4 100644 --- a/nameparser/_version.py +++ b/nameparser/_version.py @@ -7,13 +7,13 @@ #: cannot carry the dev marker, so `VERSION >= (2, 2, 0)` is already #: true here while 2.2.0 is unreleased. Compare `__version__` instead #: where that distinction matters. -VERSION = (2, 2, 0) +VERSION = (2, 3, 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.2.0.dev0, which sorts above 2.1.0 and BELOW 2.2.0, so an install +#: 2.3.0.dev0, which sorts above 2.2.0 and BELOW 2.3.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 diff --git a/tests/v2/test_ledger_guards.py b/tests/v2/test_ledger_guards.py index 185145f0..8edf2ae9 100644 --- a/tests/v2/test_ledger_guards.py +++ b/tests/v2/test_ledger_guards.py @@ -352,7 +352,8 @@ def test_script_ranges_membership_is_decided() -> None: "fix(#271/#272/#298)", # the canonical class "fix(#298)", # the 间隔号 lookahead }), - "expected_since_2.1.0.toml": frozenset(), # open cycle, no rules yet + "expected_since_2.1.0.toml": frozenset(), # 2.2 cycle: no span-bearing rule + "expected_since_2.2.0.toml": frozenset(), # open cycle, no rules yet } #: The leading `fix(...)`/`feat(...)` tag of a rule's `issue`, which is @@ -1776,6 +1777,7 @@ def _claim(rule: dict) -> _Claim: "fix(#371) a suffix never begins a name: the Ph./D. merge declines at the head": _Claim(4, ('family', 'given', 'middle', 'suffix', 'title'), "1425d85a2d86"), }, + "expected_since_2.2.0.toml": {}, # open cycle, no rules yet "expected_since_2.1.0.toml": { "fix(#371) a suffix never begins a name: the Ph./D. merge declines at the head": _Claim(4, ('family', 'given', 'middle', 'suffix', 'title'), "1425d85a2d86"), @@ -1973,6 +1975,7 @@ def test_every_rule_claims_the_recorded_share_of_the_corpus() -> None: #: Re-measure rather than adjust them if a parser change moves one: #: a diff shape that shifted is a finding, not a number to update. _CROSS_RULE_WINNERS: dict[str, dict[tuple[str, tuple[str, ...]], str]] = { + "expected_since_2.2.0.toml": {}, # open cycle, no rules and so no contest "expected_since_1.4.0.toml": { ("Andrews, M.D.", ("given", "suffix")): "fix(comma-family)", ("田中, 太郎さん", ("given", "suffix")): "fix(cjk-comma-honorific-peel)", diff --git a/tools/differential/compare.py b/tools/differential/compare.py index 6f679310..a2a749c8 100644 --- a/tools/differential/compare.py +++ b/tools/differential/compare.py @@ -25,7 +25,7 @@ FIELDS = ("title", "first", "middle", "last", "suffix", "nickname", "maiden") -DEFAULT_BASELINE = "2.1.0" +DEFAULT_BASELINE = "2.2.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. diff --git a/tools/differential/expected_since_2.2.0.toml b/tools/differential/expected_since_2.2.0.toml new file mode 100644 index 00000000..0cade28b --- /dev/null +++ b/tools/differential/expected_since_2.2.0.toml @@ -0,0 +1,34 @@ +# Ledger for baseline 2.2.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.2.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.2.0 without this file. +# +# Do not copy rules across from expected_since_2.1.0.toml: that ledger +# classifies what 2.2 changed on top of 2.1, and a rule copied without +# checking either over-matches -- hiding a real 2.3 regression behind a +# 2.2-era label -- or never fires at all. Where a 2.3 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, for as long as the +# 2.3 cycle moves no corpus name at the default order. +# +# tests/v2/test_ledger_guards.py records this file in +# _SPAN_BEARING_RULES with an empty set and in _CORPUS_CLAIMS with an +# empty mapping. The first rule that hand-copies _SCRIPT_RANGES has to +# be recorded in the former 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.