0.4.0 Tooling API: legaldown.grammar, legaldown.syntax, and the answers tools re-derive today - #98
Merged
Merged
Conversation
Two new public modules that re-export, as the same objects, what downstream tools imported from private modules: the language's constants and rules that need no document (grammar), and the lexer, markers, fragments and Markdown helpers for reading source (syntax). Nothing moves and no diagnostic changes. New constants, defined where they are used: PARTY_TYPES and MAX_SECTION_LEVEL (validator.patterns, read by core and units), DRAFTING_MARKER (validator.templates, formerly private) and FINAL_CHECK_RULES (validator.core, read by the final check). slugify_identifier takes a keyword-only fallback= for text that yields no identifier. choose_problem(directive, questions) gives the first choose-invalid message for one directive; check_choose is built on the same logic. find_markers(document) defaults to the package lexer. Guard test tests/test_tooling_surface.py pins both __all__ lists, the identity of every re-export, and the public home of each deep import PactTrack and legaldown-render use today. README gains a Tooling API section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
DocumentIndex.blanks (public Blank) and SectionIndexEntry.alternative hand over what validate already decides; Directive.positional_span/param_spans and Lexed.literals say where values and literal regions are; iter_document_directives yields every directive with its location, and collect_source_directives is built on it. The validator's internal accumulator Blank is now _BlankState. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
…tent, SourceLayout, Document.layout()/line_of() (#88, #93, #27) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
legaldown.syntax now also re-exports iter_document_directives and DirectiveLocation (#33), quote_blocks and drafting_note_blocks (#88, #93), code_content and CodeContent (#93), and the source-layout spans behind Document.layout() (#93, #27). The guard test covers them, and maps parser.quote_content and parser._layout to their public homes. README: result.index.blanks and SectionIndexEntry.alternative (#31) in the result table; the renderer helper table is replaced by a pointer to the Tooling API section, which gains the new names, a "where things are in the file" part, and migration rows. legaldown.validator names are no longer listed as private. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
- iter_document_directives / collect_source_directives: a ref, term or
definition block built in code whose value holds a line break no longer
raises; its directive is built directly (main's results again).
- iter_document_directives also yields the {{def:}} lifted into a
definition block (fragment None), as its docstring promised.
- Blank: type is None when no occurrence has a usable type (invalid
type, decision question's id); new `consistent` is False for mixed
types or two currencies/units, and `fixed` is "" then.
- syntax.body_layout(text): the layout of bare body text (a leading ---
is a rule, not frontmatter), for tools that laid out editor fields
with the private parser._layout.
- syntax re-exports is_drafting_note; README mentions is_template and
is_drafting_note again.
- choose_problem raises ValueError for a directive that is not a
well-formed {{choose:}}.
- Docstrings: layout spans (end exclusive, blank lines between parts
belong to none), quote helpers past MAX_QUOTE_DEPTH and depth=,
SectionIndexEntry.alternative (its preceding sibling).
- FINAL_CHECK_RULES moves to validator.patterns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
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.
Downstream tools (PactTrack, legaldown-render) import about 45 names from private modules, and re-derive what the validator already decides. This gives them a documented public surface for 0.4.0 that is covered by semver. Where the library already knows the answer, it now hands it over, so tools need fewer helpers.
No public behaviour changes. Every deep import path PactTrack and legaldown-render use keeps working. The new modules re-export the validator's own objects (
grammar.X is validator.patterns.X), and the top-levellegaldown.__all__does not grow. Three internal names were renamed or dropped. Neither downstream repo uses them, and none of them was ever exported:validator.templates.Blankaccumulator is now_BlankState;templates._DRAFTING_MARKERis now the publicDRAFTING_MARKER;parser.iter_directivesandparser.text_fragmentswere incidental imports and are gone.Closes #31, closes #32, closes #33, closes #34, closes #88, closes #93. Covers the block and item lines of #27;
Diagnostic.linealready shipped in #85.New public modules
legaldown.grammar: the language's constants and rules that need no document.PARTY_TYPES,MAX_SECTION_LEVEL,DRAFTING_MARKER,FINAL_CHECK_RULES. The validator now uses these constants itself, and its messages are unchanged.slugify_identifier(text, *, fallback="section")(Small public API: identifier fallback, final-check rules, one-directive choose check, quote body, grammar constants #34):fallback=""tells "nothing usable" apart from real text.choose_problem(directive, questions)(Small public API: identifier fallback, final-check rules, one-directive choose check, quote body, grammar constants #34): the first choose-invalid message, sharing one implementation withcheck_choose. It raisesValueErrorfor a directive that is not a well-formed{{choose:}}.parse_condition,condition_problem,exclusive,Presence,ALWAYS, and theis_valid_*helpers.legaldown.syntax: reading source the way the validator does.lex,Directive,Lexed,format_value.Directive.positional_spanandparam_spans(Source spans for directive arguments, and the regionslex()blanks #32) give where each value is written, quotes included.Lexed.literals(Source spans for directive arguments, and the regionslex()blanks #32) lists the comments and code spans the lexer skipped.iter_document_directives(document)→DirectiveLocation(section, block, fragment, directive)(Iterate a document's directives with their locations #33).{{ref:}},{{term:}}and{{def:}}the parser lifts into block fields; for those,fragmentisNone.collect_source_directivesis now built on it.find_markers(document)(the lexer argument is now optional),FoundMarker,parse_marker,format_marker,MARKER_RE.block_fragments,list_fragments,text_fragments.Quote,block_quotes,is_drafting_note.quote_blocks(block)anddrafting_note_blocks(block)(Expose a drafting note's content blocks without the [!DRAFTING] marker #88, Public API follow-up: quote content, fence helpers, answers reader, block lines #93): a quote's blocks as copies, and a drafting note's blocks without its[!DRAFTING]marker.code_content(block)→CodeContent(info, text, fenced)(Public API follow-up: quote content, fence helpers, answers reader, block lines #93): a code block read the CommonMark way.SourceLayout,SectionSpan,HeadingSpan,BlockSpan,ItemSpan.body_layout(text)lays out bare body text, such as an editor field, where a leading---is a thematic break.dedent,indent_width,FRONTMATTER_RE,LINE_ENDING_RE,HTML_COMMENT_RE.Answers in the result and the model
result.index.blanks(Expose whatvalidate_documentcomputes and discards: section alternatives and blanks #31):Blank(id, type, fixed, in_frontmatter, consistent)for each placeholder id, for every document.typeisNonewhen no occurrence has a usable type.consistentis false when the occurrences have different types or fix two currencies or units, andfixedis only set when it is true.SectionIndexEntry.alternative(Expose whatvalidate_documentcomputes and discards: section alternatives and blanks #31): true when a section is an alternative to its preceding sibling with the same identifier, and so shares its number.Document.layout()(Public API follow-up: quote content, fence helpers, answers reader, block lines #93, Source positions on sections, blocks, and diagnostics #27): file lines of the frontmatter, headings, blocks and list items.Document.line_of(section, block=None, item=None): one of those lines. These are the lines diagnostics report. Both returnNonefor a document built in code or changed since it was parsed.Docs
The README gains a Tooling API section with both modules, examples, and a migration table from each private path to its public home. The old renderer-helpers table now points to that section. The result table documents
blanksandalternative.Verification
2961 tests pass with the spec fixtures corpus, 2210 without it.
ruffis clean.tests/test_tooling_surface.pypins both__all__lists, checks that every name is the implementation's own object, and maps each private import PactTrack and legaldown-render use today to its public home.Differential check against
main: all 160 spec fixture documents, validated in normal andfinalmode, give byte-identical results. That covers every diagnostic (rule, level, message, line), the section index, the placed markers andcollect_source_directives.An independent review fuzzed the new code. All of these held:
Lexed.literals;code_contentagainst a CommonMark parser;line_ofon 3000 random nested-list documents;drafting_note_blocksagainst legaldown-render's current behaviour.Its should-fix findings are addressed in d42dc01.
🤖 Generated with Claude Code
https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz