Repository navigation
Read the vocabulary sets through Lexicon._sets (#754, 4i-a) - #989
Merged
Merged
Conversation
The pipeline, _render, _parser and the three tools that read a lexicon's sets now read them through one internal object, _lexicon._LexiconSets, held in a slot inherited from a plain base so that dataclasses.fields(Lexicon), equality, reprs and pickles stay as they were. A membership test stays a C-level attribute read: the frame counts are identical on the reference row, a family comma and two more rows. tests/v2/test_layering.py refuses a vocabulary field read off a lexicon, per field, with synthetic and recorded negative controls. Groundwork for 4i-b, which fills the object from readings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build _LexiconSets by keyword and hold its field names to _VOCAB_FIELDS. The guard now follows aliases (getattr through one, a tool's `L = Parser().lexicon`), exempts `sets` only where every binding assigns it from a `_sets`, and scans every file under tools/; its recorded control moves to 114 library reads. The checklist grep stops printing a bare `_sets`, and the curated sub-set paragraph names the three rosters _VOCAB_FIELDS feeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sub-phase 4i-a of #754's
Lexiconsurface, as decided in #988. This is internal plumbing only: the parse reads the vocabulary sets through one internal object instead ofLexicon's public fields, and the three tools that read a lexicon's sets move to the same object. Nothing public changes and no parse moves.What changed
_lexicon._LexiconSetsis a frozen, slotted object with one frozenset per_VOCAB_FIELDSentry:surnamesandgiven_names, which stay public. They are included because they sit in the same roster loops (_vocab.in_any_wordlist,claimed_as_non_name,_assemble._listed_whole), so each loop reads one object._setsslot. EachLexiconbuilds a_LexiconSetsin__post_init__, from its normalized fields. It lives in a slot_setsinherited from a plain base class (_HoldsSets):init=False, would changedataclasses.fields(Lexicon), the pickled state and the 2.8 pickles' layout check.dataclasses.replaceand|rebuild it._lexicon(the pipeline stages,_render,_parser) now readslexicon._sets.titleswhere it readlexicon.titles. So dotools/name_usage.py,tools/es_given_names.pyandtools/differential/local_agreement.py. None of the tools moved to the index:local_agreementreads a pack parser's lexicon, which the shipped index does not describe.name_usagereadssuffix_acronyms_ambiguous, a marker the compile derives and no index row states._lexiconitself and stay;TailRead.titlesreads, not reads of a lexicon;surnamesandgiven_names, and 5 rostergetattrs.local_corpora's counter, which is not a lexicon.Reads:lines. They still name a set asLexicon.<field>. AGENTS.md now says the stages read it through_sets, and the release checklist's grep becomes\b(policy|lexicon|lex)(\._sets)?\.[a-z][a-z_]*. It keeps printing the field name and no longer prints a bare_sets. A note says that_classify's blanketReads:line covers twenty fields: the 17 the grep lists in the module, and the rest read through_vocab._LexiconSetsis built by keyword.tests/v2/test_lexicon.pyholds its field names to_VOCAB_FIELDS, in order, and holds each attribute to be the field's own set.The guard
tests/v2/test_layering.pywalks every library module but_lexicon, plus every file undertools/(40 files), and refuses a vocabulary field read off a lexicon. It runs one test per_VOCAB_FIELDSentry.<x>._sets, or through a namesetsthat every binding in the module assigns from a_sets.setsbound any other way fails: an annotated assignment, a walrus, unpacking, afortarget, a parameter or awithtarget.TailRead'sread.titlesis named as not a lexicon.getattrfails if its object is a lexicon or an alias of one, or if it names a field as a constant off anything but a_sets.lexicon,lex,<x>.lexicon,Lexicon.default(), or a name some binding gives one of those (an assignment, a walrus, aLexiconannotation). This includes the AGENTS.md recipe'sL = Parser().lexicon..lexicon;setsand of an alias has its own row, in the library and in the tools._UNGUARDED_READSrecords what the check refuses on today's sources with._setsstripped out:getattrs (3 of them through thesetsthat stripping makes a lexicon) and 3sets =bindings;lex._sets.titles_ambiguousin_classifyback tolex.titles_ambiguousfailed exactly[titles_ambiguous].Frames
Measured on Python 3.11.17 with the benchmark's counter (50 names, four-digit counter),
b55d4d33against this branch:Dr. Juan{i:04d} de la Vega III(tools/perf/call_count.py --against b55d4d33)Smith{i:04d}, Johnde la Vega{i:04d}, Juan Q., Jr.John Smith{i:04d}There is no new frame per token and no new frame per parse. Wall clock over 6,000 names stayed within the run-to-run spread (96 to 98 µs per name on both trees).
_CALL_BASELINEis unchanged.How 4i-b plugs in
4i-b changes how
_setsis filled, and nothing that reads it:_setsis built from the lexicon's readings, compiled bycompile_readings, instead of from its public fields._setscomes fromnameparser/config/_compiled.py, as_default_lexiconreads it today, soparse("John Smith")still loads no index._LexiconSetsas attributes._vocab.suffix_kindand classify's particle kind then read them there instead of the index.dataclasses.fields(Lexicon)changes in 4i-b anyway,_setsmay become a private field then, and the base class can go._parser._apply_choicereads_setsbut still writes the public fields throughdataclasses.replace. 4i-b has to give it the readings to write until 4i-c retires it.L.titles - L.suffix_acronyms - L.suffix_wordsonL = Parser().lexicon. 4i-b rewrites it overreadings_ofor_sets.The same plan is recorded in the new
decisions/vocabulary-modelentry (2026-11-02 #754, sub-phase 4i-a), and the RFC's 4i-a row is marked landed.A design-docs review re-measured every number in the decisions entry and reproduced them. It raised three low findings, all fixed before the first push: a roster wording, the AGENTS.md scope sentence, and a missing tool-side
lexcontrol row.An independent review of the PR found no blocking defect. Its five small items are fixed in a second commit:
_VOCAB_FIELDSis read by two rosters. There are three,_assemble._listed_wholeamong them, and_LISTED_FIELDSleaves four fields out;Verification
uv run pytest: 34,457 passed, 29 skipped, without namedivider. The run includestests/v2/test_snapshot.py, unchanged, andtest_cldr_agreement.py, no move.uv run pytest -n0 -m serial: 42 passed.uv run mypy,uv run ruff checkandtools/vocab/build.py --checkare clean.namedivider-pythoninstalled.dataclasses.fields(Lexicon)is unchanged (24 fields), reprs are untouched, andtests/v2/data/lexicon_v28.picklestill loads.ALLOWEDwas not extended.🤖 Generated with Claude Code