all2md.ast

Abstract Syntax Tree (AST) module for document representation.

This module provides a complete Abstract Syntax Tree representation for markdown documents, enabling separation of document parsing from markdown rendering. The AST approach allows for:

  1. Multiple markdown flavor support (CommonMark, GFM, etc.)

  2. Document structure validation and manipulation

  3. Improved testability and maintainability

  4. Bidirectional conversion support

The module consists of several components:

  • nodes: AST node classes representing document structure

  • visitors: Visitor pattern implementation for AST traversal

  • serialization: JSON serialization and deserialization of AST structures

  • transforms: AST transformation utilities (cloning, filtering, rewriting)

  • builder: Helper classes for constructing complex AST structures

Examples

Basic usage:

>>> from all2md.ast import Document, Heading, Paragraph, Text
>>> from all2md.renderers.markdown import MarkdownRenderer
>>>
>>> # Create AST
>>> doc = Document(children=[
...     Heading(level=1, content=[Text(content="Title")]),
...     Paragraph(content=[Text(content="Hello world")])
... ])
>>>
>>> # Render to markdown
>>> renderer = MarkdownRenderer()
>>> markdown = renderer.render_to_string(doc)
class all2md.ast.Node

Bases: ABC

Base class for all AST nodes.

All document nodes inherit from this base class and support the visitor pattern for traversal and rendering.

Parameters:
  • metadata (dict, default = empty dict) – Arbitrary metadata associated with this node

  • source_location (SourceLocation or None, default = None) – Information about where this node came from in the source

metadata: dict[str, Any]
source_location: SourceLocation | None
abstractmethod accept(visitor: Any) Any

Accept a visitor for processing this node.

Parameters:

visitor (Any) – A visitor object with visit_* methods

Returns:

Result from the visitor’s processing

Return type:

Any

class all2md.ast.SourceLocation

Bases: object

Source location information for AST nodes.

Preserves information about where a node originated in the source document, useful for debugging, error messages, and round-trip conversions.

Parameters:
  • format (str) – Source format (e.g., ‘pdf’, ‘html’, ‘docx’)

  • page (int or None, default = None) – Page number in source document (for paginated formats)

  • line (int or None, default = None) – Line number in source document (for text formats)

  • column (int or None, default = None) – Column number in source document

  • element_id (str or None, default = None) – Source element identifier (e.g., HTML element ID)

  • metadata (dict, default = empty dict) – Additional format-specific location information

format: str
page: int | None = None
line: int | None = None
column: int | None = None
element_id: str | None = None
metadata: dict[str, Any]
__init__(format: str, page: int | None = None, line: int | None = None, column: int | None = None, element_id: str | None = None, metadata: dict[str, ~typing.Any]=<factory>) None
class all2md.ast.Document

Bases: Node

Root document node containing all other nodes.

The Document node represents the complete document and contains a list of block-level children.

Parameters:
  • children (list of Node, default = empty list) – Block-level nodes in the document

  • metadata (dict, default = empty dict) – Document-level metadata (title, author, etc.)

  • source_location (SourceLocation or None, default = None) – Source location information

children: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this document.

Parameters:

visitor (Any) – A visitor object with visit_document method

Returns:

Result from visitor.visit_document(self)

Return type:

Any

add_section_after(target: str | int, new_section: Section | Document, case_sensitive: bool = False) Document

Add a new section after the specified target section.

Parameters:
  • target (str or int) – Heading text or section index to insert after

  • new_section (Section or Document) – Section or document to insert

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string targets)

Returns:

New document with the section inserted

Return type:

Document

Raises:

ValueError – If target section is not found

Examples

>>> from all2md.ast.sections import Section
>>> from all2md.ast.nodes import Heading, Paragraph, Text
>>> new_section = Section(
...     heading=Heading(level=2, content=[Text("New Section")]),
...     content=[Paragraph(content=[Text("Content")])],
...     level=2, start_index=0, end_index=0
... )
>>> updated_doc = doc.add_section_after("Introduction", new_section)
add_section_before(target: str | int, new_section: Section | Document, case_sensitive: bool = False) Document

Add a new section before the specified target section.

Parameters:
  • target (str or int) – Heading text or section index to insert before

  • new_section (Section or Document) – Section or document to insert

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string targets)

Returns:

New document with the section inserted

Return type:

Document

Raises:

ValueError – If target section is not found

Examples

>>> from all2md.ast.sections import Section
>>> from all2md.ast.nodes import Heading, Paragraph, Text
>>> new_section = Section(
...     heading=Heading(level=2, content=[Text("Preface")]),
...     content=[Paragraph(content=[Text("Introduction text")])],
...     level=2, start_index=0, end_index=0
... )
>>> updated_doc = doc.add_section_before("Chapter 1", new_section)
remove_section(target: str | int, case_sensitive: bool = False) Document

Remove a section from the document.

Parameters:
  • target (str or int) – Heading text or section index to remove

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string targets)

Returns:

New document with the section removed

Return type:

Document

Raises:

ValueError – If target section is not found

Examples

>>> updated_doc = doc.remove_section("Obsolete Section")
>>> updated_doc = doc.remove_section(2)  # Remove third section
replace_section(target: str | int, new_content: Section | Document | list[Node], case_sensitive: bool = False) Document

Replace a section with new content.

Parameters:
  • target (str or int) – Heading text or section index to replace

  • new_content (Section, Document, or list of Node) – New content to replace the section with

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string targets)

Returns:

New document with the section replaced

Return type:

Document

Raises:

ValueError – If target section is not found

Examples

Replace with a new section:
>>> from all2md.ast.sections import Section
>>> from all2md.ast.nodes import Heading, Paragraph, Text
>>> new_section = Section(
...     heading=Heading(level=2, content=[Text("Updated")]),
...     content=[Paragraph(content=[Text("New content")])],
...     level=2, start_index=0, end_index=0
... )
>>> updated_doc = doc.replace_section("Old Section", new_section)
Replace with custom nodes:
>>> new_nodes = [
...     Heading(level=2, content=[Text("New Heading")]),
...     Paragraph(content=[Text("Content here")])
... ]
>>> updated_doc = doc.replace_section(0, new_nodes)
insert_into_section(target: str | int, content: Node | list[Node], position: Literal['start', 'end', 'after_heading'] = 'end', case_sensitive: bool = False) Document

Insert content into an existing section.

Parameters:
  • target (str or int) – Heading text or section index to insert into

  • content (Node or list of Node) – Content to insert

  • position ({"start", "end", "after_heading"}, default = "end") – Where to insert the content within the section

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string targets)

Returns:

New document with content inserted

Return type:

Document

Raises:

ValueError – If target section is not found

Notes

  • “start”: Insert at the beginning of the section content (before all content)

  • “after_heading”: Insert immediately after the heading (same as “start”)

  • “end”: Insert at the end of the section content (after all content)

Examples

>>> from all2md.ast.nodes import Paragraph, Text
>>> new_para = Paragraph(content=[Text("Additional info")])
>>> updated_doc = doc.insert_into_section("Methods", new_para, position="end")
__init__(children: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Heading

Bases: Node

Heading node (h1-h6).

Represents a document heading with a level from 1 to 6 and inline content.

Parameters:
  • level (int) – Heading level (1-6, where 1 is most important)

  • content (list of Node, default = empty list) – Inline nodes representing heading text

  • metadata (dict, default = empty dict) – Heading metadata

  • source_location (SourceLocation or None, default = None) – Source location information

level: int
content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this heading.

Parameters:

visitor (Any) – A visitor object with visit_heading method

Returns:

Result from visitor.visit_heading(self)

Return type:

Any

__init__(level: int, content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Paragraph

Bases: Node

Paragraph node containing inline content.

Represents a paragraph of text with inline formatting.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes representing paragraph content

  • metadata (dict, default = empty dict) – Paragraph metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this paragraph.

Parameters:

visitor (Any) – A visitor object with visit_paragraph method

Returns:

Result from visitor.visit_paragraph(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.CodeBlock

Bases: Node

Code block node with optional language specification.

Represents a fenced or indented code block.

Parameters:
  • content (str) – Code content (not parsed as markdown)

  • language (str or None, default = None) – Programming language for syntax highlighting

  • fence_char (str, default = '`') – Character used for fencing (` or ~)

  • fence_length (int, default = 3) – Number of fence characters

  • metadata (dict, default = empty dict) – Code block metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: str
language: str | None = None
fence_char: str = '`'
fence_length: int = 3
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this code block.

Parameters:

visitor (Any) – A visitor object with visit_code_block method

Returns:

Result from visitor.visit_code_block(self)

Return type:

Any

__init__(content: str, language: str | None = None, fence_char: str = '`', fence_length: int = 3, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.BlockQuote

Bases: Node

Block quote node containing other block elements.

Represents a quoted section of content.

Parameters:
  • children (list of Node, default = empty list) – Block-level nodes in the quote

  • metadata (dict, default = empty dict) – Block quote metadata

  • source_location (SourceLocation or None, default = None) – Source location information

children: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this block quote.

Parameters:

visitor (Any) – A visitor object with visit_block_quote method

Returns:

Result from visitor.visit_block_quote(self)

Return type:

Any

__init__(children: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.List

Bases: Node

List node (ordered or unordered).

Represents an ordered (numbered) or unordered (bulleted) list.

Parameters:
  • ordered (bool) – True for ordered lists, False for unordered

  • items (list of ListItem, default = empty list) – List items

  • start (int, default = 1) – Starting number for ordered lists

  • tight (bool, default = True) – Whether list is tight (no blank lines between items)

  • metadata (dict, default = empty dict) – List metadata

  • source_location (SourceLocation or None, default = None) – Source location information

ordered: bool
items: list[ListItem]
start: int = 1
tight: bool = True
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this list.

Parameters:

visitor (Any) – A visitor object with visit_list method

Returns:

Result from visitor.visit_list(self)

Return type:

Any

__init__(ordered: bool, items: list[ListItem] = <factory>, start: int = 1, tight: bool = True, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.ListItem

Bases: Node

List item node containing block content.

Represents a single item in a list, which can contain paragraphs, nested lists, or other block elements.

Parameters:
  • children (list of Node, default = empty list) – Block-level nodes in the list item

  • task_status ({'checked', 'unchecked'} or None, default = None) – For task lists (GFM extension)

  • metadata (dict, default = empty dict) – List item metadata

  • source_location (SourceLocation or None, default = None) – Source location information

children: list[Node]
task_status: Literal['checked', 'unchecked'] | None = None
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this list item.

Parameters:

visitor (Any) – A visitor object with visit_list_item method

Returns:

Result from visitor.visit_list_item(self)

Return type:

Any

__init__(children: list[Node] = <factory>, task_status: Literal['checked', 'unchecked'] | None=None, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Table

Bases: Node

Table node with optional header and alignment.

Represents a table structure (GFM extension).

Parameters:
  • rows (list of TableRow, default = empty list) – Table rows (excluding header)

  • header (TableRow or None, default = None) – Optional header row

  • alignments (list, default = empty list) – Column alignments (‘left’, ‘center’, ‘right’, or None)

  • caption (str or None, default = None) – Optional table caption

  • metadata (dict, default = empty dict) – Table metadata

  • source_location (SourceLocation or None, default = None) – Source location information

rows: list[TableRow]
header: TableRow | None = None
alignments: list[Literal['left', 'center', 'right'] | None]
caption: str | None = None
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this table.

Parameters:

visitor (Any) – A visitor object with visit_table method

Returns:

Result from visitor.visit_table(self)

Return type:

Any

__init__(rows: list[TableRow] = <factory>, header: TableRow | None = None, alignments: list[~typing.Literal['left', 'center', 'right'] | None]=<factory>, caption: str | None = None, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.TableRow

Bases: Node

Table row node containing cells.

Represents a single row in a table.

Parameters:
  • cells (list of TableCell, default = empty list) – Cells in this row

  • is_header (bool, default = False) – Whether this is a header row

  • metadata (dict, default = empty dict) – Row metadata

  • source_location (SourceLocation or None, default = None) – Source location information

cells: list[TableCell]
is_header: bool = False
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this table row.

Parameters:

visitor (Any) – A visitor object with visit_table_row method

Returns:

Result from visitor.visit_table_row(self)

Return type:

Any

__init__(cells: list[TableCell] = <factory>, is_header: bool = False, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.TableCell

Bases: Node

Table cell node with optional span and alignment.

Represents a single cell in a table.

Parameters:
  • content (list of Node, default = empty list) – Inline content of the cell

  • colspan (int, default = 1) – Number of columns this cell spans

  • rowspan (int, default = 1) – Number of rows this cell spans

  • alignment ({'left', 'center', 'right'} or None, default = None) – Cell alignment

  • metadata (dict, default = empty dict) – Cell metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
colspan: int = 1
rowspan: int = 1
alignment: Literal['left', 'center', 'right'] | None = None
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this table cell.

Parameters:

visitor (Any) – A visitor object with visit_table_cell method

Returns:

Result from visitor.visit_table_cell(self)

Return type:

Any

__init__(content: list[Node] = <factory>, colspan: int = 1, rowspan: int = 1, alignment: Literal['left', 'center', 'right'] | None=None, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.ThematicBreak

Bases: Node

Thematic break node (horizontal rule).

Represents a thematic break/horizontal rule.

Parameters:
  • metadata (dict, default = empty dict) – Break metadata

  • source_location (SourceLocation or None, default = None) – Source location information

metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this thematic break.

Parameters:

visitor (Any) – A visitor object with visit_thematic_break method

Returns:

Result from visitor.visit_thematic_break(self)

Return type:

Any

__init__(metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.HTMLBlock

Bases: Node

Raw HTML block node.

Represents a block of raw HTML content. This node preserves HTML as-is without modification or sanitization.

Parameters:
  • content (str) – Raw HTML content

  • metadata (dict, default = empty dict) – HTML block metadata

  • source_location (SourceLocation or None, default = None) – Source location information

Warning

Security: Raw HTML content is preserved without sanitization. Renderers MUST sanitize user-provided HTML content before rendering to prevent XSS attacks and other security vulnerabilities. For strict security contexts, consider using ValidationVisitor with allow_raw_html=False to reject documents containing raw HTML.

See also

ValidationVisitor

AST validator with optional HTML rejection

content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this HTML block.

Parameters:

visitor (Any) – A visitor object with visit_html_block method

Returns:

Result from visitor.visit_html_block(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Comment

Bases: Node

Block-level comment node.

Represents a standalone comment block that appears at the block level. Comments can originate from HTML comments, LaTeX comments, code comments, or other format-specific comment mechanisms.

The node uses metadata to store variant information such as comment type, author, date, identifier, and other format-specific attributes.

Parameters:
  • content (str) – Comment text content

  • metadata (dict, default = empty dict) – Comment metadata. Common metadata keys: - ‘comment_type’: str - Type of comment (‘html’, ‘docx_review’, ‘latex’, ‘code’, ‘generic’) - ‘author’: str - Comment author name - ‘date’: str - Comment timestamp - ‘identifier’: str - Comment ID for linking/referencing - ‘label’: str - Comment label/number - ‘range_start’: str - Start of commented text range - ‘range_end’: str - End of commented text range

  • source_location (SourceLocation or None, default = None) – Source location information

Examples

HTML block comment:
>>> Comment(
...     content="This section needs review",
...     metadata={'comment_type': 'html'}
... )
DOCX reviewer comment at document end:
>>> Comment(
...     content="Please verify these numbers",
...     metadata={
...         'comment_type': 'docx_review',
...         'author': 'Jane Smith',
...         'date': '2025-01-20',
...         'identifier': 'comment2',
...         'label': '2'
...     }
... )
LaTeX comment:
>>> Comment(
...     content="TODO: Add more examples here",
...     metadata={'comment_type': 'latex'}
... )
content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this comment block.

Parameters:

visitor (Any) – A visitor object with visit_comment method

Returns:

Result from visitor.visit_comment(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.CommentInline

Bases: Node

Inline comment node.

Represents an inline comment that appears within the text flow. Comments can originate from HTML comments, DOCX reviewer comments, or other format-specific comment mechanisms.

The node uses metadata to store variant information such as comment type, author, date, identifier, and other format-specific attributes.

Parameters:
  • content (str) – Comment text content

  • metadata (dict, default = empty dict) – Comment metadata. Common metadata keys: - ‘comment_type’: str - Type of comment (‘html’, ‘docx_review’, ‘latex’, ‘code’, ‘generic’) - ‘author’: str - Comment author name - ‘date’: str - Comment timestamp - ‘identifier’: str - Comment ID for linking/referencing - ‘label’: str - Comment label/number - ‘range_start’: str - Start of commented text range - ‘range_end’: str - End of commented text range

  • source_location (SourceLocation or None, default = None) – Source location information

Examples

HTML comment:
>>> CommentInline(
...     content="TODO: Fix this logic",
...     metadata={'comment_type': 'html'}
... )
DOCX reviewer comment:
>>> CommentInline(
...     content="This needs clarification",
...     metadata={
...         'comment_type': 'docx_review',
...         'author': 'John Doe',
...         'date': '2025-01-20',
...         'identifier': 'comment1'
...     }
... )
content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this inline comment.

Parameters:

visitor (Any) – A visitor object with visit_comment_inline method

Returns:

Result from visitor.visit_comment_inline(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Text

Bases: Node

Plain text node.

Represents plain text content without formatting.

Parameters:
  • content (str) – Text content

  • metadata (dict, default = empty dict) – Text metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this text.

Parameters:

visitor (Any) – A visitor object with visit_text method

Returns:

Result from visitor.visit_text(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Emphasis

Bases: Node

Emphasis (italic) node.

Represents emphasized (typically italic) text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes with emphasis

  • metadata (dict, default = empty dict) – Emphasis metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this emphasis.

Parameters:

visitor (Any) – A visitor object with visit_emphasis method

Returns:

Result from visitor.visit_emphasis(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Strong

Bases: Node

Strong (bold) node.

Represents strong (typically bold) text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes with strong emphasis

  • metadata (dict, default = empty dict) – Strong metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this strong emphasis.

Parameters:

visitor (Any) – A visitor object with visit_strong method

Returns:

Result from visitor.visit_strong(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Code

Bases: Node

Inline code node.

Represents inline code (monospace text).

Parameters:
  • content (str) – Code content

  • metadata (dict, default = empty dict) – Code metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this inline code.

Parameters:

visitor (Any) – A visitor object with visit_code method

Returns:

Result from visitor.visit_code(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Link

Bases: Node

Link node.

Represents a hyperlink with optional title.

Parameters:
  • url (str) – Link destination URL

  • content (list of Node, default = empty list) – Inline nodes representing link text

  • title (str or None, default = None) – Optional link title (tooltip)

  • metadata (dict, default = empty dict) – Link metadata

  • source_location (SourceLocation or None, default = None) – Source location information

url: str
content: list[Node]
title: str | None = None
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this link.

Parameters:

visitor (Any) – A visitor object with visit_link method

Returns:

Result from visitor.visit_link(self)

Return type:

Any

__init__(url: str, content: list[Node] = <factory>, title: str | None = None, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Image

Bases: Node

Image node.

Represents an embedded image.

Parameters:
  • url (str) – Image source URL or data URI

  • alt_text (str, default = '') – Alternative text description

  • title (str or None, default = None) – Optional image title

  • width (int or None, default = None) – Optional width in pixels

  • height (int or None, default = None) – Optional height in pixels

  • metadata (dict, default = empty dict) – Image metadata

  • source_location (SourceLocation or None, default = None) – Source location information

url: str
alt_text: str = ''
title: str | None = None
width: int | None = None
height: int | None = None
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this image.

Parameters:

visitor (Any) – A visitor object with visit_image method

Returns:

Result from visitor.visit_image(self)

Return type:

Any

__init__(url: str, alt_text: str = '', title: str | None = None, width: int | None = None, height: int | None = None, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.LineBreak

Bases: Node

Line break node.

Represents a line break (hard or soft).

Parameters:
  • soft (bool, default = False) – True for soft breaks (newline in source), False for hard breaks

  • metadata (dict, default = empty dict) – Line break metadata

  • source_location (SourceLocation or None, default = None) – Source location information

soft: bool = False
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this line break.

Parameters:

visitor (Any) – A visitor object with visit_line_break method

Returns:

Result from visitor.visit_line_break(self)

Return type:

Any

__init__(soft: bool = False, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Strikethrough

Bases: Node

Strikethrough node (GFM extension).

Represents strikethrough text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes with strikethrough

  • metadata (dict, default = empty dict) – Strikethrough metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this strikethrough.

Parameters:

visitor (Any) – A visitor object with visit_strikethrough method

Returns:

Result from visitor.visit_strikethrough(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Mark

Bases: Node

Mark (highlight) node (non-standard extension).

Represents highlighted/marked text, as produced by the ==text== syntax of pymdownx.mark (Material for MkDocs). Renders to <mark> in HTML and to ==text== in flavors that support it.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes that are highlighted

  • metadata (dict, default = empty dict) – Mark metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this mark.

Parameters:

visitor (Any) – A visitor object with visit_mark method

Returns:

Result from visitor.visit_mark(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Underline

Bases: Node

Underline node (non-standard extension).

Represents underlined text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes with underline

  • metadata (dict, default = empty dict) – Underline metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this underline.

Parameters:

visitor (Any) – A visitor object with visit_underline method

Returns:

Result from visitor.visit_underline(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Superscript

Bases: Node

Superscript node (non-standard extension).

Represents superscript text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes in superscript

  • metadata (dict, default = empty dict) – Superscript metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this superscript.

Parameters:

visitor (Any) – A visitor object with visit_superscript method

Returns:

Result from visitor.visit_superscript(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.Subscript

Bases: Node

Subscript node (non-standard extension).

Represents subscript text.

Parameters:
  • content (list of Node, default = empty list) – Inline nodes in subscript

  • metadata (dict, default = empty dict) – Subscript metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this subscript.

Parameters:

visitor (Any) – A visitor object with visit_subscript method

Returns:

Result from visitor.visit_subscript(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.HTMLInline

Bases: Node

Inline HTML node.

Represents inline HTML content. This node preserves HTML as-is without modification or sanitization.

Parameters:
  • content (str) – Raw HTML content

  • metadata (dict, default = empty dict) – HTML metadata

  • source_location (SourceLocation or None, default = None) – Source location information

Warning

Security: Raw HTML content is preserved without sanitization. Renderers MUST sanitize user-provided HTML content before rendering to prevent XSS attacks and other security vulnerabilities. For strict security contexts, consider using ValidationVisitor with allow_raw_html=False to reject documents containing raw HTML.

See also

ValidationVisitor

AST validator with optional HTML rejection

content: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this inline HTML.

Parameters:

visitor (Any) – A visitor object with visit_html_inline method

Returns:

Result from visitor.visit_html_inline(self)

Return type:

Any

__init__(content: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.FootnoteReference

Bases: Node

Footnote reference node (inline).

Represents an inline reference to a footnote, typically rendered as [^id]. This is used with flavors that support footnotes (e.g., MultiMarkdown, Pandoc).

Parameters:
  • identifier (str) – Footnote identifier (e.g., “1”, “note1”)

  • metadata (dict, default = empty dict) – Footnote reference metadata

  • source_location (SourceLocation or None, default = None) – Source location information

identifier: str
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this footnote reference.

Parameters:

visitor (Any) – A visitor object with visit_footnote_reference method

Returns:

Result from visitor.visit_footnote_reference(self)

Return type:

Any

__init__(identifier: str, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.FootnoteDefinition

Bases: Node

Footnote definition node (block).

Represents a footnote definition, typically rendered as [^id]: content. Used with MultiMarkdown, Pandoc, Kramdown, and MarkdownPlus flavors.

Parameters:
  • identifier (str) – Footnote identifier matching a FootnoteReference

  • content (list of Node, default = empty list) – Block-level content of the footnote

  • metadata (dict, default = empty dict) – Footnote definition metadata

  • source_location (SourceLocation or None, default = None) – Source location information

identifier: str
content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this footnote definition.

Parameters:

visitor (Any) – A visitor object with visit_footnote_definition method

Returns:

Result from visitor.visit_footnote_definition(self)

Return type:

Any

__init__(identifier: str, content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.MathInline

Bases: Node

Inline math node.

Represents inline mathematical content, typically rendered with $ delimiters. Supported by GFM, Pandoc, Kramdown, and MarkdownPlus flavors.

Parameters:
  • content (str) – LaTeX math content (without delimiters)

  • notation ({"latex", "mathml", "html"}, default "latex") – Format of the primary math representation stored in content.

  • representations (dict, default = empty dict) – Additional representations keyed by notation. The primary notation is automatically registered when missing.

  • metadata (dict, default = empty dict) – Math metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: str
notation: Literal['latex', 'mathml', 'html'] = 'latex'
representations: dict[Literal['latex', 'mathml', 'html'], str]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this inline math.

Parameters:

visitor (Any) – A visitor object with visit_math_inline method

Returns:

Result from visitor.visit_math_inline(self)

Return type:

Any

get_preferred_representation(preferred: Literal['latex', 'mathml', 'html']) tuple[str, Literal['latex', 'mathml', 'html']]

Return math content for the requested representation with fallback.

Parameters:

preferred ({"latex", "mathml", "html"}) – Requested representation for rendering

Returns:

Math content and notation actually provided

Return type:

tuple[str, MathNotation]

__init__(content: str, notation: ~typing.Literal['latex', 'mathml', 'html'] = 'latex', representations: dict[~typing.Literal['latex', 'mathml', 'html'], str] = <factory>, metadata: dict[str, ~typing.Any] = <factory>, source_location: ~all2md.ast.nodes.SourceLocation | None = None) None
class all2md.ast.MathBlock

Bases: Node

Math block node.

Represents a block of mathematical content, typically rendered with $$ delimiters. Supported by GFM, MultiMarkdown, Pandoc, Kramdown, and MarkdownPlus flavors.

Parameters:
  • content (str) – LaTeX math content (without delimiters)

  • notation ({"latex", "mathml", "html"}, default "latex") – Format of the primary math representation stored in content.

  • representations (dict, default = empty dict) – Additional representations keyed by notation. The primary notation is automatically registered when missing.

  • metadata (dict, default = empty dict) – Math block metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: str
notation: Literal['latex', 'mathml', 'html'] = 'latex'
representations: dict[Literal['latex', 'mathml', 'html'], str]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this math block.

Parameters:

visitor (Any) – A visitor object with visit_math_block method

Returns:

Result from visitor.visit_math_block(self)

Return type:

Any

get_preferred_representation(preferred: Literal['latex', 'mathml', 'html']) tuple[str, Literal['latex', 'mathml', 'html']]

Return math content for the requested representation with fallback.

__init__(content: str, notation: ~typing.Literal['latex', 'mathml', 'html'] = 'latex', representations: dict[~typing.Literal['latex', 'mathml', 'html'], str] = <factory>, metadata: dict[str, ~typing.Any] = <factory>, source_location: ~all2md.ast.nodes.SourceLocation | None = None) None
class all2md.ast.DefinitionList

Bases: Node

Definition list node (block).

Represents a definition list containing terms and their descriptions. Supported by MultiMarkdown, Pandoc, Kramdown, and MarkdownPlus flavors.

Parameters:
  • items (list of tuple, default = empty list) – List of (DefinitionTerm, list[DefinitionDescription]) tuples

  • metadata (dict, default = empty dict) – Definition list metadata

  • source_location (SourceLocation or None, default = None) – Source location information

items: list[tuple[DefinitionTerm, list[DefinitionDescription]]]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this definition list.

Parameters:

visitor (Any) – A visitor object with visit_definition_list method

Returns:

Result from visitor.visit_definition_list(self)

Return type:

Any

__init__(items: list[tuple[~all2md.ast.nodes.DefinitionTerm, list[~all2md.ast.nodes.DefinitionDescription]]]=<factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.DefinitionTerm

Bases: Node

Definition term node (block).

Represents a term in a definition list.

Parameters:
  • content (list of Node, default = empty list) – Inline content of the term

  • metadata (dict, default = empty dict) – Term metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this definition term.

Parameters:

visitor (Any) – A visitor object with visit_definition_term method

Returns:

Result from visitor.visit_definition_term(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
class all2md.ast.DefinitionDescription

Bases: Node

Definition description node (block).

Represents a description/definition in a definition list.

Parameters:
  • content (list of Node, default = empty list) – Block-level content of the description

  • metadata (dict, default = empty dict) – Description metadata

  • source_location (SourceLocation or None, default = None) – Source location information

content: list[Node]
metadata: dict[str, Any]
source_location: SourceLocation | None = None
accept(visitor: Any) Any

Accept a visitor for processing this definition description.

Parameters:

visitor (Any) – A visitor object with visit_definition_description method

Returns:

Result from visitor.visit_definition_description(self)

Return type:

Any

__init__(content: list[Node] = <factory>, metadata: dict[str, ~typing.Any]=<factory>, source_location: SourceLocation | None = None) None
all2md.ast.get_node_children(node: Node) list[Node]

Get all child nodes from a node.

This is a helper function for visitor pattern implementations that need to traverse the AST. It returns a list of all child nodes regardless of the node type.

Parameters:

node (Node) – The node to get children from

Returns:

List of child nodes (empty list if node has no children)

Return type:

list of Node

Examples

>>> heading = Heading(level=1, content=[Text("Hello"), Strong(content=[Text("world")])])
>>> children = get_node_children(heading)
>>> len(children)
2
all2md.ast.replace_node_children(node: Node, new_children: list[Node]) Node

Create a copy of a node with replaced children.

This is a helper function for transformer pattern implementations. It creates a new node of the same type with the children replaced.

Parameters:
  • node (Node) – The node to copy and modify

  • new_children (list of Node) – New children to use in the copy

Returns:

New node with replaced children

Return type:

Node

Raises:

ValueError – If the node type doesn’t support children, if children are of the wrong type, or if the structure is invalid

Notes

Special handling for Table nodes:

The function inspects new_children to determine header vs body rows. The first TableRow with is_header=True becomes the table header. All other rows become body rows. This allows: - Adding a header: Include a TableRow with is_header=True - Removing a header: Omit rows with is_header=True - Preserving structure: Set is_header appropriately on rows

All new_children must be TableRow instances, or ValueError is raised.

Examples

Replace heading content:
>>> heading = Heading(level=1, content=[Text("Hello")])
>>> new_heading = replace_node_children(heading, [Text("Goodbye")])
>>> new_heading.content[0].content
'Goodbye'
Add header to table:
>>> table = Table(rows=[TableRow(cells=[...], is_header=False)])
>>> header = TableRow(cells=[...], is_header=True)
>>> new_table = replace_node_children(table, [header] + table.rows)
>>> new_table.header is not None
True
class all2md.ast.NodeVisitor

Bases: ABC

Abstract base class for AST node visitors.

Subclasses should implement visit_* methods for each node type they want to process. The visitor pattern allows algorithms to be separated from the node structure.

All visit methods should accept a node and return Any (typically None for side-effect visitors, or accumulated results for transforming visitors).

Examples

Simple visitor that counts nodes:

>>> class NodeCounter(NodeVisitor):
...     def __init__(self):
...         self.count = 0
...
...     def generic_visit(self, node):
...         self.count += 1
...         # Visit children if they exist
...         if hasattr(node, 'children'):
...             for child in node.children:
...                 child.accept(self)
...
>>> counter = NodeCounter()
>>> document.accept(counter)
>>> print(counter.count)
abstractmethod visit_document(node: Document) Any

Visit a Document node.

Parameters:

node (Document) – The document node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_heading(node: Heading) Any

Visit a Heading node.

Parameters:

node (Heading) – The heading node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_paragraph(node: Paragraph) Any

Visit a Paragraph node.

Parameters:

node (Paragraph) – The paragraph node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_code_block(node: CodeBlock) Any

Visit a CodeBlock node.

Parameters:

node (CodeBlock) – The code block node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_block_quote(node: BlockQuote) Any

Visit a BlockQuote node.

Parameters:

node (BlockQuote) – The block quote node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_list(node: List) Any

Visit a List node.

Parameters:

node (List) – The list node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_list_item(node: ListItem) Any

Visit a ListItem node.

Parameters:

node (ListItem) – The list item node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_table(node: Table) Any

Visit a Table node.

Parameters:

node (Table) – The table node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_table_row(node: TableRow) Any

Visit a TableRow node.

Parameters:

node (TableRow) – The table row node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_table_cell(node: TableCell) Any

Visit a TableCell node.

Parameters:

node (TableCell) – The table cell node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_thematic_break(node: ThematicBreak) Any

Visit a ThematicBreak node.

Parameters:

node (ThematicBreak) – The thematic break node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_html_block(node: HTMLBlock) Any

Visit an HTMLBlock node.

Parameters:

node (HTMLBlock) – The HTML block node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_comment(node: Comment) Any

Visit a Comment node (block-level).

Parameters:

node (Comment) – The comment block node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_text(node: Text) Any

Visit a Text node.

Parameters:

node (Text) – The text node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_emphasis(node: Emphasis) Any

Visit an Emphasis node.

Parameters:

node (Emphasis) – The emphasis node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_strong(node: Strong) Any

Visit a Strong node.

Parameters:

node (Strong) – The strong node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_code(node: Code) Any

Visit a Code node.

Parameters:

node (Code) – The code node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_link(node: Link) Any

Visit a Link node.

Parameters:

node (Link) – The link node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_image(node: Image) Any

Visit an Image node.

Parameters:

node (Image) – The image node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_line_break(node: LineBreak) Any

Visit a LineBreak node.

Parameters:

node (LineBreak) – The line break node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_strikethrough(node: Strikethrough) Any

Visit a Strikethrough node.

Parameters:

node (Strikethrough) – The strikethrough node to visit

Returns:

Result of processing this node

Return type:

Any

visit_mark(node: Mark) Any

Visit a Mark (highlight) node.

Unlike most visit methods this is concrete rather than abstract so that visitors predating the Mark node (e.g. format renderers that do not have a dedicated highlight syntax) degrade gracefully: the default simply visits the node’s children, emitting their content unwrapped rather than dropping it. Visitors that support highlighting should override this method.

Parameters:

node (Mark) – The mark node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_underline(node: Underline) Any

Visit an Underline node.

Parameters:

node (Underline) – The underline node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_superscript(node: Superscript) Any

Visit a Superscript node.

Parameters:

node (Superscript) – The superscript node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_subscript(node: Subscript) Any

Visit a Subscript node.

Parameters:

node (Subscript) – The subscript node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_html_inline(node: HTMLInline) Any

Visit an HTMLInline node.

Parameters:

node (HTMLInline) – The inline HTML node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_comment_inline(node: CommentInline) Any

Visit a CommentInline node (inline).

Parameters:

node (CommentInline) – The inline comment node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_footnote_reference(node: FootnoteReference) Any

Visit a FootnoteReference node.

Parameters:

node (FootnoteReference) – The footnote reference node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_math_inline(node: MathInline) Any

Visit a MathInline node.

Parameters:

node (MathInline) – The inline math node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_footnote_definition(node: FootnoteDefinition) Any

Visit a FootnoteDefinition node.

Parameters:

node (FootnoteDefinition) – The footnote definition node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_definition_list(node: DefinitionList) Any

Visit a DefinitionList node.

Parameters:

node (DefinitionList) – The definition list node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_definition_term(node: DefinitionTerm) Any

Visit a DefinitionTerm node.

Parameters:

node (DefinitionTerm) – The definition term node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_definition_description(node: DefinitionDescription) Any

Visit a DefinitionDescription node.

Parameters:

node (DefinitionDescription) – The definition description node to visit

Returns:

Result of processing this node

Return type:

Any

abstractmethod visit_math_block(node: MathBlock) Any

Visit a MathBlock node.

Parameters:

node (MathBlock) – The math block node to visit

Returns:

Result of processing this node

Return type:

Any

generic_visit(node: Node) Any

Fallback visitor for unhandled node types.

This method is called when no specific visit_* method exists. The default implementation does nothing but can be overridden.

Parameters:

node (Node) – The node to visit

Returns:

Result of processing (default: None)

Return type:

Any

class all2md.ast.ValidationVisitor

Bases: NodeVisitor

Visitor that validates AST structure.

This visitor checks for structural issues in the AST, such as: - Invalid nesting (e.g., block nodes inside inline nodes) - Missing required fields - Invalid field values - Presence of raw HTML content (when disallowed) - Block/inline containment rules (in strict mode) - URL scheme validation (in strict mode)

Parameters:
  • strict (bool, default = True) – Whether to raise errors on validation failures

  • allow_raw_html (bool, default = False) – Whether to allow HTMLBlock and HTMLInline nodes. By default (False), any raw HTML content will trigger a validation error for security. Set to True only when you trust the HTML source and need to preserve raw HTML content. This follows security best practices of default-deny for potentially unsafe content.

Notes

Security Considerations:

Raw HTML in documents can pose security risks (XSS attacks, code injection). This validator defaults to rejecting HTML to enforce security best practices. Only set allow_raw_html=True when: - You trust the document source - HTML will be sanitized before rendering - You’re working in a trusted/non-web context

Examples

Strict validation (rejects HTML by default):
>>> validator = ValidationVisitor(strict=True)
>>> doc.accept(validator)  # Raises ValueError if HTML present
Allow HTML from trusted sources:
>>> validator = ValidationVisitor(strict=True, allow_raw_html=True)
>>> doc.accept(validator)  # Permits HTMLBlock/HTMLInline nodes

Initialize the validator with strictness and HTML policy.

Parameters:
  • strict (bool, default = True) – Whether to raise errors immediately on validation failures

  • allow_raw_html (bool, default = False) – Whether to allow raw HTML nodes (security: default deny)

INLINE_NODES = frozenset({<class 'all2md.ast.nodes.Code'>, <class 'all2md.ast.nodes.Emphasis'>, <class 'all2md.ast.nodes.FootnoteReference'>, <class 'all2md.ast.nodes.HTMLInline'>, <class 'all2md.ast.nodes.Image'>, <class 'all2md.ast.nodes.LineBreak'>, <class 'all2md.ast.nodes.Link'>, <class 'all2md.ast.nodes.Mark'>, <class 'all2md.ast.nodes.MathInline'>, <class 'all2md.ast.nodes.Strikethrough'>, <class 'all2md.ast.nodes.Strong'>, <class 'all2md.ast.nodes.Subscript'>, <class 'all2md.ast.nodes.Superscript'>, <class 'all2md.ast.nodes.Text'>, <class 'all2md.ast.nodes.Underline'>})
BLOCK_NODES = frozenset({<class 'all2md.ast.nodes.BlockQuote'>, <class 'all2md.ast.nodes.CodeBlock'>, <class 'all2md.ast.nodes.DefinitionDescription'>, <class 'all2md.ast.nodes.DefinitionList'>, <class 'all2md.ast.nodes.DefinitionTerm'>, <class 'all2md.ast.nodes.Document'>, <class 'all2md.ast.nodes.FootnoteDefinition'>, <class 'all2md.ast.nodes.HTMLBlock'>, <class 'all2md.ast.nodes.Heading'>, <class 'all2md.ast.nodes.List'>, <class 'all2md.ast.nodes.ListItem'>, <class 'all2md.ast.nodes.MathBlock'>, <class 'all2md.ast.nodes.Paragraph'>, <class 'all2md.ast.nodes.Table'>, <class 'all2md.ast.nodes.TableCell'>, <class 'all2md.ast.nodes.TableRow'>, <class 'all2md.ast.nodes.ThematicBreak'>})
__init__(strict: bool = True, allow_raw_html: bool = False)

Initialize the validator with strictness and HTML policy.

Parameters:
  • strict (bool, default = True) – Whether to raise errors immediately on validation failures

  • allow_raw_html (bool, default = False) – Whether to allow raw HTML nodes (security: default deny)

visit_document(node: Document) None

Validate a Document node.

visit_heading(node: Heading) None

Validate a Heading node.

visit_paragraph(node: Paragraph) None

Validate a Paragraph node.

visit_code_block(node: CodeBlock) None

Validate a CodeBlock node.

visit_block_quote(node: BlockQuote) None

Validate a BlockQuote node.

visit_list(node: List) None

Validate a List node.

visit_list_item(node: ListItem) None

Validate a ListItem node.

visit_table(node: Table) None

Validate a Table node.

visit_table_row(node: TableRow) None

Validate a TableRow node.

visit_table_cell(node: TableCell) None

Validate a TableCell node.

visit_thematic_break(node: ThematicBreak) None

Validate a ThematicBreak node.

visit_html_block(node: HTMLBlock) None

Validate an HTMLBlock node.

Checks if raw HTML is allowed based on the allow_raw_html setting.

visit_comment(node: Comment) None

Validate a Comment node (block-level).

Comments are informational and generally safe, but we validate that they have content.

visit_text(node: Text) None

Validate a Text node.

visit_emphasis(node: Emphasis) None

Validate an Emphasis node.

visit_strong(node: Strong) None

Validate a Strong node.

visit_code(node: Code) None

Validate a Code node.

visit_link(node: Link) None

Validate a Link node.

visit_image(node: Image) None

Validate an Image node.

visit_line_break(node: LineBreak) None

Validate a LineBreak node.

visit_strikethrough(node: Strikethrough) None

Validate a Strikethrough node.

visit_mark(node: Mark) None

Validate a Mark (highlight) node.

visit_underline(node: Underline) None

Validate an Underline node.

visit_superscript(node: Superscript) None

Validate a Superscript node.

visit_subscript(node: Subscript) None

Validate a Subscript node.

visit_html_inline(node: HTMLInline) None

Validate an HTMLInline node.

Checks if raw HTML is allowed based on the allow_raw_html setting.

visit_comment_inline(node: CommentInline) None

Validate a CommentInline node (inline).

Comments are informational and generally safe, but we validate that they have content.

visit_footnote_reference(node: FootnoteReference) None

Validate a FootnoteReference node.

visit_math_inline(node: MathInline) None

Validate a MathInline node.

visit_footnote_definition(node: FootnoteDefinition) None

Validate a FootnoteDefinition node.

visit_definition_list(node: DefinitionList) None

Validate a DefinitionList node.

visit_definition_term(node: DefinitionTerm) None

Validate a DefinitionTerm node.

visit_definition_description(node: DefinitionDescription) None

Validate a DefinitionDescription node.

visit_math_block(node: MathBlock) None

Validate a MathBlock node.

class all2md.ast.ListBuilder

Bases: object

Helper for building nested list structures.

This class manages the complexity of creating properly nested lists, handling level transitions and list type changes automatically.

Parameters:
  • root (Document or None, default = None) – Root document to append lists to. If None, creates a new Document.

  • allow_placeholders (bool, default = False) – If True, allows creation of placeholder list items when nesting without a parent item. Placeholder items will be marked with metadata {“placeholder”: True}. If False, raises ValueError on invalid nesting.

  • default_start (int, default = 1) – Default starting number for ordered lists

  • default_tight (bool, default = True) – Default tight spacing for lists (True means no blank lines between items)

Examples

>>> builder = ListBuilder()
>>> builder.add_item(level=1, ordered=False, content=[Text("Item 1")])
>>> builder.add_item(level=2, ordered=False, content=[Text("Nested")])
>>> builder.add_item(level=1, ordered=False, content=[Text("Item 2")])
>>> doc = builder.get_document()

Initialize the list builder with an optional root document.

__init__(root: Document | None = None, allow_placeholders: bool = False, default_start: int = 1, default_tight: bool = True)

Initialize the list builder with an optional root document.

add_item(level: int, ordered: bool, content: list[Node], task_status: Literal['checked', 'unchecked'] | None = None, start: int | None = None, tight: bool | None = None) None

Add a list item at the specified nesting level.

Parameters:
  • level (int) – Nesting level (1 is top-level, 2 is nested once, etc.)

  • ordered (bool) – True for ordered lists, False for unordered

  • content (list of Node) – Block content for the list item

  • task_status ({'checked', 'unchecked'} or None, default = None) – Optional task list status

  • start (int or None, default = None) – Starting number for ordered lists. If None, uses default_start. Only applies when a new list is created.

  • tight (bool or None, default = None) – Tight spacing for lists. If None, uses default_tight. Only applies when a new list is created.

Raises:

ValueError – If level is less than 1, or if allow_placeholders=False and nesting is attempted without a parent item

get_document() Document

Get the constructed document.

Returns:

Document containing all built lists

Return type:

Document

class all2md.ast.TableBuilder

Bases: object

Helper for building table structures.

This class simplifies table construction by handling row and cell management, alignment tracking, and header designation.

Parameters:

has_header (bool, default = False) – Whether the first row should be designated as a header

Examples

>>> builder = TableBuilder(has_header=True)
>>> builder.add_row([Text("Name"), Text("Age")], is_header=True)
>>> builder.add_row([Text("Alice"), Text("30")])
>>> builder.add_row([Text("Bob"), Text("25")])
>>> table = builder.get_table()

Initialize the table builder with optional header flag.

__init__(has_header: bool = False)

Initialize the table builder with optional header flag.

add_row(cells: Sequence[str | Node | Sequence[Node]], is_header: bool = False, alignments: list[Literal['left', 'center', 'right'] | None] | None = None) None

Add a row to the table.

This method accepts a flexible cell specification allowing mixed types per cell. Each cell can be a plain string, a single Node, or a sequence of inline Node objects.

Parameters:
  • cells (Sequence of str, Node, or Sequence of Node) –

    Cell contents where each cell can be: - A plain string (e.g., “Hello”) - A single Node (e.g., Text(“Hello”)) - A sequence of inline nodes (e.g., [Text(“Hello”), Strong(content=[Text(“world”)])])

    Mixed types are supported per-cell. An empty sequence creates an empty row.

  • is_header (bool, default False) – Whether this is a header row. If has_header=True and no header exists yet, the first row added will automatically be treated as a header regardless of this parameter.

  • alignments (list of Alignment or None, optional) – Column alignments (only used for header rows). Length should match the number of cells. If not specified and this is a header row, all alignments will be set to None (default alignment).

Examples

[“Name”, “Age”] # All strings [Text(“Name”), Text(“Age”)] # All single nodes [[Text(“Name”)], [Text(“Age”)]] # All node sequences [“Name”, [Text(“Age: “), Strong(content=[Text(“30”)])]] # Mixed [Text(“Name”), [Text(“Age: “), Strong(content=[Text(“30”)])]] # Mixed

Notes

The flexible typing allows for ergonomic table building while maintaining type safety. The implementation normalizes all inputs to TableCell objects internally.

set_caption(caption: str) None

Set the table caption.

Parameters:

caption (str) – Table caption text

set_column_alignment(column_index: int, alignment: Literal['left', 'center', 'right'] | None) None

Set alignment for a specific column.

Parameters:
  • column_index (int) – Zero-based column index

  • alignment ({'left', 'center', 'right'} or None) – Column alignment

get_table() Table

Get the constructed table.

Returns:

Completed table node

Return type:

Table

class all2md.ast.DocumentBuilder

Bases: object

Helper for building complete documents.

This class provides a fluent interface for constructing documents with multiple block-level elements. It supports method chaining for ergonomic document construction.

Examples

Basic usage with headings and paragraphs:

>>> builder = DocumentBuilder()
>>> builder.add_heading(1, [Text("Title")])
>>> builder.add_paragraph([Text("Content")])
>>> doc = builder.get_document()

Using method chaining:

>>> doc = (DocumentBuilder()
...     .add_heading(1, [Text("My Document")])
...     .add_paragraph([Text("Introduction paragraph.")])
...     .add_code_block("print('Hello, world!')", language="python")
...     .add_thematic_break()
...     .get_document())

Adding complex structures:

>>> builder = DocumentBuilder()
>>> builder.add_block_quote([Paragraph(content=[Text("Quote text")])])
>>> builder.add_list([ListItem(children=[Paragraph(content=[Text("Item 1")])])], ordered=False)
>>> builder.add_table(
...     rows=[TableRow(cells=[TableCell(content=[Text("Cell")])])],
...     header=TableRow(cells=[TableCell(content=[Text("Header")])])
... )
>>> doc = builder.get_document()

Adding multiple nodes at once:

>>> nodes = [
...     Heading(level=1, content=[Text("Title")]),
...     Paragraph(content=[Text("Paragraph 1")]),
...     Paragraph(content=[Text("Paragraph 2")])
... ]
>>> builder = DocumentBuilder()
>>> builder.add_nodes(nodes)
>>> doc = builder.get_document()

Initialize the document builder with an empty children list.

__init__() None

Initialize the document builder with an empty children list.

add_node(node: Node) DocumentBuilder

Add a node to the document.

Parameters:

node (Node) – Block-level node to add

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_heading(level: int, content: list[Node]) DocumentBuilder

Add a heading to the document.

Parameters:
  • level (int) – Heading level (1-6)

  • content (list of Node) – Inline content

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_paragraph(content: list[Node]) DocumentBuilder

Add a paragraph to the document.

Parameters:

content (list of Node) – Inline content

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_code_block(content: str, language: str | None = None) DocumentBuilder

Add a code block to the document.

Parameters:
  • content (str) – Code content

  • language (str or None, default = None) – Programming language

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_thematic_break() DocumentBuilder

Add a thematic break to the document.

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_block_quote(children: list[Node]) DocumentBuilder

Add a block quote to the document.

Parameters:

children (list of Node) – Block-level content for the quote

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_list(items: list[ListItem], ordered: bool = False, start: int = 1, tight: bool = True) DocumentBuilder

Add a list to the document.

Parameters:
  • items (list of ListItem) – List items

  • ordered (bool, default = False) – True for ordered list, False for unordered

  • start (int, default = 1) – Starting number for ordered lists

  • tight (bool, default = True) – Tight spacing (no blank lines between items)

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_table(rows: list[TableRow], header: TableRow | None = None, alignments: list[Literal['left', 'center', 'right'] | None] | None = None, caption: str | None = None) DocumentBuilder

Add a table to the document.

Parameters:
  • rows (list of TableRow) – Table body rows

  • header (TableRow or None, default = None) – Optional header row

  • alignments (list of Alignment or None, optional) – Column alignments

  • caption (str or None, default = None) – Optional table caption

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_html_block(content: str) DocumentBuilder

Add an HTML block to the document.

Parameters:

content (str) – Raw HTML content

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_footnote_definition(identifier: str, content: list[Node]) DocumentBuilder

Add a footnote definition to the document.

Parameters:
  • identifier (str) – Footnote identifier/label

  • content (list of Node) – Block-level content for the footnote

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_definition_list(items: list[tuple[DefinitionTerm, list[DefinitionDescription]]]) DocumentBuilder

Add a definition list to the document.

Parameters:

items (list of tuple) – List of (term, descriptions) tuples

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_math_block(content: str, notation: Literal['latex', 'mathml', 'html'] = 'latex') DocumentBuilder

Add a math block to the document.

Parameters:
  • content (str) – Math expression content

  • notation ({"latex", "mathml", "html"}, default = "latex") – Math notation system

Returns:

Self for method chaining

Return type:

DocumentBuilder

add_nodes(nodes: list[Node]) DocumentBuilder

Add multiple nodes to the document at once.

Parameters:

nodes (list of Node) – Block-level nodes to add

Returns:

Self for method chaining

Return type:

DocumentBuilder

Examples

>>> builder = DocumentBuilder()
>>> builder.add_nodes([
...     Heading(level=1, content=[Text("Title")]),
...     Paragraph(content=[Text("Content")])
... ])
get_document() Document

Get the constructed document.

Returns:

Completed document

Return type:

Document

all2md.ast.ast_to_dict(node: Node | SourceLocation) dict[str, Any]

Convert an AST node to a dictionary representation.

Parameters:

node (Node or SourceLocation) – The AST node to convert

Returns:

Dictionary representation of the node

Return type:

dict

Examples

>>> from all2md.ast import Text
>>> text = Text(content="Hello")
>>> ast_to_dict(text)
{'node_type': 'Text', 'content': 'Hello', 'metadata': {}, 'source_location': None}
all2md.ast.dict_to_ast(data: dict[str, Any], strict_mode: bool = True) Node | SourceLocation

Convert a dictionary representation back to an AST node.

Parameters:
  • data (dict) – Dictionary representation of a node

  • strict_mode (bool, default True) – If True, raise ValueError on unknown node types. If False, skip unknown nodes (useful for forward compatibility).

Returns:

Reconstructed AST node

Return type:

Node or SourceLocation

Raises:

ValueError – If the dictionary contains an unknown node type and strict_mode is True

Examples

>>> data = {'node_type': 'Text', 'content': 'Hello', 'metadata': {}, 'source_location': None}
>>> node = dict_to_ast(data)
>>> print(node.content)
Hello
all2md.ast.ast_to_json(node: Node, indent: int | None = None) str

Serialize an AST node to JSON string with schema versioning.

The serialized JSON includes a schema_version field to enable migration paths for future AST evolution. Unicode characters are preserved without escape sequences for improved readability.

Parameters:
  • node (Node) – The AST node to serialize

  • indent (int or None, default = None) – Number of spaces for indentation (None for compact format)

Returns:

JSON string representation with schema version

Return type:

str

Notes

The output format includes a schema_version field:

{“schema_version”: 1, “node_type”: “…”, …}

Examples

>>> from all2md.ast import Document, Paragraph, Text
>>> doc = Document(children=[
...     Paragraph(content=[Text(content="Hello")])
... ])
>>> json_str = ast_to_json(doc, indent=2)
>>> print(json_str)
all2md.ast.json_to_ast(json_str: str, validate_schema: bool = True, strict_mode: bool = True) Node

Deserialize a JSON string to an AST node.

This function handles schema versioning to support future AST evolution. If no schema_version is present in the JSON (for backward compatibility), it assumes version 1.

Parameters:
  • json_str (str) – JSON string representation

  • validate_schema (bool, default True) – If True, validate schema version and raise errors on unsupported versions. If False, skip schema validation (useful for forward compatibility).

  • strict_mode (bool, default True) – If True, raise ValueError on unknown node types or attributes. If False, log warnings and skip unknown elements (useful for forward compatibility).

Returns:

Reconstructed AST node

Return type:

Node

Raises:
  • ValueError – If JSON is invalid, contains unknown node types, or has an unsupported schema version (when validate_schema=True or strict_mode=True)

  • json.JSONDecodeError – If JSON string is malformed

Notes

Backward compatibility is maintained for JSON without schema_version field.

Examples

>>> json_str = '{"schema_version": 1, "node_type": "Text", "content": "Hello"}'
>>> node = json_to_ast(json_str)
>>> print(node.content)
Hello

Optional validation allows forward compatibility:

>>> # With validation disabled, can load newer schema versions
>>> node = json_to_ast(future_json, validate_schema=False, strict_mode=False)
class all2md.ast.NodeTransformer

Bases: NodeVisitor

Base class for transforming AST nodes.

Subclasses should implement visit_* methods that return modified nodes or None to remove nodes. The transformer creates a new AST with the transformations applied.

Examples

>>> class UppercaseTransformer(NodeTransformer):
...     def visit_text(self, node):
...         return Text(content=node.content.upper())
>>>
>>> transformer = UppercaseTransformer()
>>> new_doc = transformer.transform(doc)
transform(node: Node) Node | None

Transform an AST node.

Parameters:

node (Node) – Node to transform

Returns:

Transformed node or None to remove

Return type:

Node or None

visit_document(node: Document) Document

Transform a Document node.

visit_heading(node: Heading) Heading

Transform a Heading node.

visit_paragraph(node: Paragraph) Paragraph

Transform a Paragraph node.

visit_code_block(node: CodeBlock) CodeBlock

Transform a CodeBlock node.

visit_block_quote(node: BlockQuote) BlockQuote

Transform a BlockQuote node.

visit_list(node: List) List

Transform a List node.

visit_list_item(node: ListItem) ListItem

Transform a ListItem node.

visit_table(node: Table) Table

Transform a Table node.

visit_table_row(node: TableRow) TableRow

Transform a TableRow node.

visit_table_cell(node: TableCell) TableCell

Transform a TableCell node.

visit_thematic_break(node: ThematicBreak) ThematicBreak

Transform a ThematicBreak node.

visit_html_block(node: HTMLBlock) HTMLBlock

Transform an HTMLBlock node.

visit_comment(node: Comment) Comment

Transform a Comment node (block-level).

visit_text(node: Text) Text

Transform a Text node.

visit_emphasis(node: Emphasis) Emphasis

Transform an Emphasis node.

visit_strong(node: Strong) Strong

Transform a Strong node.

visit_code(node: Code) Code

Transform a Code node.

visit_link(node: Link) Link

Transform a Link node.

visit_image(node: Image) Image

Transform an Image node.

visit_line_break(node: LineBreak) LineBreak

Transform a LineBreak node.

visit_strikethrough(node: Strikethrough) Strikethrough

Transform a Strikethrough node.

visit_mark(node: Mark) Mark

Transform a Mark (highlight) node.

visit_underline(node: Underline) Underline

Transform an Underline node.

visit_superscript(node: Superscript) Superscript

Transform a Superscript node.

visit_subscript(node: Subscript) Subscript

Transform a Subscript node.

visit_html_inline(node: HTMLInline) HTMLInline

Transform an HTMLInline node.

visit_comment_inline(node: CommentInline) CommentInline

Transform a CommentInline node (inline).

visit_footnote_reference(node: FootnoteReference) FootnoteReference

Transform a FootnoteReference node.

visit_math_inline(node: MathInline) MathInline

Transform a MathInline node.

visit_footnote_definition(node: FootnoteDefinition) FootnoteDefinition

Transform a FootnoteDefinition node.

visit_definition_list(node: DefinitionList) DefinitionList

Transform a DefinitionList node.

visit_definition_term(node: DefinitionTerm) DefinitionTerm

Transform a DefinitionTerm node.

visit_definition_description(node: DefinitionDescription) DefinitionDescription

Transform a DefinitionDescription node.

visit_math_block(node: MathBlock) MathBlock

Transform a MathBlock node.

class all2md.ast.NodeCollector

Bases: NodeVisitor

Visitor that collects nodes matching a condition.

Parameters:

predicate (callable or None, default = None) – Function that takes a node and returns True to collect it

Initialize the collector with an optional predicate function.

__init__(predicate: Callable[[Node], bool] | None = None)

Initialize the collector with an optional predicate function.

visit_document(node: Document) None

Visit a Document node.

visit_heading(node: Heading) None

Visit a Heading node.

visit_paragraph(node: Paragraph) None

Visit a Paragraph node.

visit_code_block(node: CodeBlock) None

Visit a CodeBlock node.

visit_block_quote(node: BlockQuote) None

Visit a BlockQuote node.

visit_list(node: List) None

Visit a List node.

visit_list_item(node: ListItem) None

Visit a ListItem node.

visit_table(node: Table) None

Visit a Table node.

visit_table_row(node: TableRow) None

Visit a TableRow node.

visit_table_cell(node: TableCell) None

Visit a TableCell node.

visit_thematic_break(node: ThematicBreak) None

Visit a ThematicBreak node.

visit_html_block(node: HTMLBlock) None

Visit an HTMLBlock node.

visit_comment(node: Comment) None

Visit a Comment node (block-level).

visit_text(node: Text) None

Visit a Text node.

visit_emphasis(node: Emphasis) None

Visit an Emphasis node.

visit_strong(node: Strong) None

Visit a Strong node.

visit_code(node: Code) None

Visit a Code node.

visit_link(node: Link) None

Visit a Link node.

visit_image(node: Image) None

Visit an Image node.

visit_line_break(node: LineBreak) None

Visit a LineBreak node.

visit_strikethrough(node: Strikethrough) None

Visit a Strikethrough node.

visit_mark(node: Mark) None

Visit a Mark (highlight) node.

visit_underline(node: Underline) None

Visit an Underline node.

visit_superscript(node: Superscript) None

Visit a Superscript node.

visit_subscript(node: Subscript) None

Visit a Subscript node.

visit_html_inline(node: HTMLInline) None

Visit an HTMLInline node.

visit_comment_inline(node: CommentInline) None

Visit a CommentInline node (inline).

visit_footnote_reference(node: FootnoteReference) None

Visit a FootnoteReference node.

visit_math_inline(node: MathInline) None

Visit a MathInline node.

visit_footnote_definition(node: FootnoteDefinition) None

Visit a FootnoteDefinition node.

visit_definition_list(node: DefinitionList) None

Visit a DefinitionList node.

visit_definition_term(node: DefinitionTerm) None

Visit a DefinitionTerm node.

visit_definition_description(node: DefinitionDescription) None

Visit a DefinitionDescription node.

visit_math_block(node: MathBlock) None

Visit a MathBlock node.

all2md.ast.clone_node(node: Node) Node

Create a deep copy of an AST node.

Parameters:

node (Node) – Node to clone

Returns:

Deep copy of the node

Return type:

Node

Examples

>>> cloned_doc = clone_node(doc)
>>> cloned_doc is doc  # False
False
all2md.ast.extract_nodes(doc: Document, node_type: Type[Node] | None = None) list[Node]

Extract all nodes of a specific type from a document.

Parameters:
  • doc (Document) – Document to extract from

  • node_type (type or None, default = None) – Node type to extract (None for all nodes)

Returns:

All matching nodes

Return type:

list of Node

Examples

>>> headings = extract_nodes(doc, Heading)
>>> images = extract_nodes(doc, Image)
all2md.ast.filter_nodes(doc: Document, predicate: Callable[[Node], bool]) Document

Filter nodes from a document based on a condition.

Parameters:
  • doc (Document) – Document to filter

  • predicate (callable) – Function that takes a node and returns True to keep it

Returns:

New document with filtered nodes

Return type:

Document

Notes

The root Document node is always preserved, regardless of the predicate. Only the children of the Document are filtered according to the predicate.

Examples

Remove all images:
>>> filtered_doc = filter_nodes(doc, lambda n: not isinstance(n, Image))
Keep only headings and paragraphs:
>>> filtered_doc = filter_nodes(doc, lambda n: isinstance(n, (Heading, Paragraph)))
all2md.ast.transform_nodes(doc: Document, transformer: NodeTransformer) Document

Apply a transformation visitor to a document.

Parameters:
Returns:

Transformed document

Return type:

Document

Examples

>>> transformer = HeadingLevelTransformer(offset=1)
>>> new_doc = transform_nodes(doc, transformer)
all2md.ast.merge_documents(docs: list[Document], metadata_merger: Callable[[dict[str, Any], dict[str, Any]], dict[str, Any]] | None = None) Document

Merge multiple documents into a single document.

Parameters:
  • docs (list of Document) – Documents to merge

  • metadata_merger (callable or None, default = None) –

    Optional function to customize metadata merging. Takes (existing_metadata, new_metadata) and returns merged metadata dict. If None, uses last-write-wins strategy where later documents overwrite earlier ones for duplicate keys.

    Common strategies provided: - last_write_wins_merger: Later values overwrite (default behavior) - first_write_wins_merger: Earlier values are preserved - merge_lists_merger: Concatenate list values, last-wins for others

Returns:

Merged document with all children and combined metadata

Return type:

Document

Examples

Basic merge with default last-write-wins:

>>> merged = merge_documents([doc1, doc2, doc3])

Preserve first document’s metadata values:

>>> merged = merge_documents([doc1, doc2], metadata_merger=first_write_wins_merger)

Concatenate list-valued metadata:

>>> merged = merge_documents([doc1, doc2], metadata_merger=merge_lists_merger)

Custom merger:

>>> def custom_merger(existing, new):
...     # Custom logic here
...     return merged_dict
>>> merged = merge_documents([doc1, doc2], metadata_merger=custom_merger)

Notes

The default behavior (when metadata_merger=None) uses last-write-wins strategy, meaning later documents’ metadata values overwrite earlier ones for duplicate keys.

all2md.ast.last_write_wins_merger(existing: dict[str, Any], new: dict[str, Any]) dict[str, Any]

Merge metadata with last-write-wins strategy (default).

Later document’s metadata values overwrite earlier ones for duplicate keys.

Parameters:
  • existing (dict) – Existing accumulated metadata

  • new (dict) – New metadata to merge in

Returns:

Merged metadata dictionary

Return type:

dict

Examples

>>> existing = {"author": "Alice", "version": "1.0"}
>>> new = {"version": "2.0", "date": "2025-01-01"}
>>> last_write_wins_merger(existing, new)
{"author": "Alice", "version": "2.0", "date": "2025-01-01"}
all2md.ast.first_write_wins_merger(existing: dict[str, Any], new: dict[str, Any]) dict[str, Any]

Merge metadata with first-write-wins strategy.

Earlier document’s metadata values are preserved for duplicate keys.

Parameters:
  • existing (dict) – Existing accumulated metadata

  • new (dict) – New metadata to merge in

Returns:

Merged metadata dictionary

Return type:

dict

Examples

>>> existing = {"author": "Alice", "version": "1.0"}
>>> new = {"version": "2.0", "date": "2025-01-01"}
>>> first_write_wins_merger(existing, new)
{"author": "Alice", "version": "1.0", "date": "2025-01-01"}
all2md.ast.merge_lists_merger(existing: dict[str, Any], new: dict[str, Any]) dict[str, Any]

Merge metadata with list concatenation for list values.

When both existing and new have list values for the same key, concatenate them. For non-list values, uses last-write-wins strategy.

Parameters:
  • existing (dict) – Existing accumulated metadata

  • new (dict) – New metadata to merge in

Returns:

Merged metadata dictionary

Return type:

dict

Examples

>>> existing = {"tags": ["python", "ast"], "version": "1.0"}
>>> new = {"tags": ["markdown"], "version": "2.0"}
>>> merge_lists_merger(existing, new)
{"tags": ["python", "ast", "markdown"], "version": "2.0"}
class all2md.ast.HeadingLevelTransformer

Bases: NodeTransformer

Transformer that adjusts heading levels by an offset.

Parameters:
  • offset (int) – Amount to shift heading levels (can be negative)

  • min_level (int, default = 1) – Minimum allowed heading level

  • max_level (int, default = 6) – Maximum allowed heading level

Examples

>>> # Increase all heading levels by 1
>>> transformer = HeadingLevelTransformer(offset=1)
>>> new_doc = transformer.transform(doc)

Initialize the transform with offset and level constraints.

__init__(offset: int, min_level: int = 1, max_level: int = 6)

Initialize the transform with offset and level constraints.

visit_heading(node: Heading) Heading

Transform heading level.

class all2md.ast.LinkRewriter

Bases: NodeTransformer

Transformer that rewrites link URLs.

Parameters:
  • url_mapper (callable) – Function that takes a URL string and returns a new URL string

  • validate_urls (bool, default = True) – Whether to validate URLs after rewriting for dangerous schemes. When True, raises ValueError if the url_mapper produces a URL with a dangerous scheme (javascript:, vbscript:, etc.). This provides defense-in-depth against accidentally creating unsafe URLs.

Raises:

ValueError – If validate_urls=True and url_mapper produces a URL with a dangerous or unrecognized scheme

Examples

>>> # Convert relative links to absolute
>>> def make_absolute(url):
...     if url.startswith('/'):
...         return f'https://example.com{url}'
...     return url
>>> transformer = LinkRewriter(make_absolute)
>>> new_doc = transformer.transform(doc)
>>> # Disable validation if you need to generate non-standard URLs
>>> transformer = LinkRewriter(my_mapper, validate_urls=False)

Notes

Security Considerations:

By default (validate_urls=True), this transformer validates that the url_mapper does not produce dangerous URLs. This prevents accidental creation of XSS vectors like javascript: or data:text/html URLs. Only disable validation if you have a specific need and understand the security implications.

Initialize the transform with a URL mapping function.

Parameters:
  • url_mapper (callable) – Function that takes a URL string and returns a new URL string

  • validate_urls (bool, default = True) – Whether to validate URLs after rewriting for dangerous schemes

__init__(url_mapper: Callable[[str], str], validate_urls: bool = True)

Initialize the transform with a URL mapping function.

Parameters:
  • url_mapper (callable) – Function that takes a URL string and returns a new URL string

  • validate_urls (bool, default = True) – Whether to validate URLs after rewriting for dangerous schemes

visit_link(node: Link) Link

Rewrite link URL.

visit_image(node: Image) Image

Rewrite image URL.

class all2md.ast.TextReplacer

Bases: NodeTransformer

Transformer that replaces text patterns.

Parameters:
  • pattern (str) – Pattern to search for

  • replacement (str) – Replacement string

  • use_regex (bool, default = False) – Whether to use regex matching

Raises:
  • ValueError – If use_regex=True and pattern is not a valid regular expression

  • SecurityError – If use_regex=True and pattern contains dangerous constructs that could lead to ReDoS (Regular Expression Denial of Service) attacks

Examples

>>> # Replace all occurrences of "foo" with "bar"
>>> transformer = TextReplacer("foo", "bar")
>>> new_doc = transformer.transform(doc)
>>> # Use regex for pattern matching
>>> transformer = TextReplacer(r"\\d+", "NUMBER", use_regex=True)
>>> new_doc = transformer.transform(doc)

Notes

For security reasons, when use_regex=True, this transform validates user-supplied regex patterns to prevent ReDoS attacks. Patterns with nested quantifiers or excessive backtracking potential are rejected. See validate_user_regex_pattern() for details on what patterns are considered safe.

Initialize the transform with pattern and replacement.

Parameters:
  • pattern (str) – Pattern to search for (literal string or regex)

  • replacement (str) – Replacement string

  • use_regex (bool, default = False) – Whether to use regex matching

Raises:
  • ValueError – If use_regex=True and pattern is not a valid regular expression

  • SecurityError – If use_regex=True and pattern contains dangerous constructs

__init__(pattern: str, replacement: str, use_regex: bool = False)

Initialize the transform with pattern and replacement.

Parameters:
  • pattern (str) – Pattern to search for (literal string or regex)

  • replacement (str) – Replacement string

  • use_regex (bool, default = False) – Whether to use regex matching

Raises:
  • ValueError – If use_regex=True and pattern is not a valid regular expression

  • SecurityError – If use_regex=True and pattern contains dangerous constructs

visit_text(node: Text) Text

Replace text content.

all2md.ast.extract_text(node_or_nodes: Node | list[Node], joiner: str = ' ') str

Extract plain text from a node or list of nodes.

This function recursively traverses the AST and concatenates all Text node content, joining text parts with the specified joiner string. It uses get_node_children() to properly handle all node types including List, Table, and other complex structures.

Parameters:
  • node_or_nodes (Node or list of Node) – A single node or list of nodes to extract text from

  • joiner (str, default = " ") – String to use for joining text parts. Use “” for no separation (useful for ID generation) or “ “ for natural text with spaces (useful for word counting).

Returns:

Concatenated text content from all Text nodes

Return type:

str

Notes

Spacing Behavior:

The joiner is applied at each level of the AST hierarchy when combining child nodes. This can result in extra spaces when Text nodes already contain whitespace at their boundaries. For example:

>>> para = Paragraph(content=[
...     Text(content="This is "),  # trailing space
...     Strong(content=[Text(content="bold")]),
...     Text(content=" text.")     # leading space
... ])
>>> extract_text(para)
'This is  bold  text.'  # Note the double spaces

This occurs because: 1. Text content is preserved exactly (including trailing/leading spaces) 2. The joiner adds spacing between nodes at each nesting level 3. These can combine to create multiple consecutive spaces

Workarounds: - Use joiner=”” and rely on spaces within Text nodes: extract_text(node, joiner=””) - Post-process with regex: re.sub(r’\s+’, ‘ ‘, extract_text(node)).strip() - Normalize Text nodes before extraction to trim whitespace

This behavior is intentional to preserve the exact text content and provide consistent separation at structural boundaries.

Examples

Extract text with space joiner (default):

>>> from all2md.ast import Paragraph, Text, Strong
>>> para = Paragraph(content=[
...     Text(content="This is "),
...     Strong(content=[Text(content="bold")]),
...     Text(content=" text.")
... ])
>>> extract_text(para)
'This is  bold  text.'  # Note: double spaces preserved

Extract text with no joiner (preserves only Text content):

>>> from all2md.ast import Heading, Text
>>> heading = Heading(level=2, content=[Text(content="My Heading")])
>>> extract_text(heading.content, joiner="")
'My Heading'

Extract text from a list of nodes:

>>> from all2md.ast import Text, Emphasis
>>> nodes = [Text(content="Hello"), Emphasis(content=[Text(content="world")])]
>>> extract_text(nodes)
'Hello world'

Extract text from complex structures (List, Table):

>>> from all2md.ast import List, ListItem, Text, Paragraph
>>> lst = List(ordered=False, items=[
...     ListItem(children=[Paragraph(content=[Text(content="Item 1")])]),
...     ListItem(children=[Paragraph(content=[Text(content="Item 2")])])
... ])
>>> extract_text(lst)
'Item 1 Item 2'

Normalize whitespace in extracted text:

>>> import re
>>> result = extract_text(para)
>>> normalized = re.sub(r'\\s+', ' ', result).strip()
>>> normalized
'This is bold text.'
class all2md.ast.Section

Bases: object

Represents a document section with heading and content.

A section consists of a heading node and all content nodes that follow it until the next heading of the same or higher level.

Parameters:
  • heading (Heading) – The heading node for this section

  • content (list of Node) – All nodes between this heading and the next same-or-higher level heading

  • level (int) – Heading level (1-6)

  • start_index (int) – Index of the heading in the parent document’s children list

  • end_index (int) – Exclusive end index (one past the last content node)

Examples

>>> section = Section(
...     heading=Heading(level=1, content=[Text("Title")]),
...     content=[Paragraph(content=[Text("Content")])],
...     level=1,
...     start_index=0,
...     end_index=2
... )
heading: Heading
content: list[Node]
level: int = 1
start_index: int = 0
end_index: int = 0
to_document() Document

Convert this section to a standalone document.

Returns:

New document containing the heading and content

Return type:

Document

Examples

>>> doc = section.to_document()
>>> len(doc.children)
2
get_heading_text() str

Extract plain text from the heading.

Returns:

Plain text content of the heading

Return type:

str

Examples

>>> section.get_heading_text()
'Introduction'
__init__(heading: Heading, content: list[Node] = <factory>, level: int = 1, start_index: int = 0, end_index: int = 0) None
all2md.ast.get_all_sections(doc: Document, min_level: int = 1, max_level: int = 6) list[Section]

Extract all sections from a document.

Parameters:
  • doc (Document) – Document to extract sections from

  • min_level (int, default = 1) – Minimum heading level to include (1 is highest)

  • max_level (int, default = 6) – Maximum heading level to include (6 is lowest)

Returns:

All sections in document order

Return type:

list of Section

Notes

A section is defined as a heading plus all content until the next same-or-higher level heading. Content before the first heading is not included in any section (see get_preamble).

Examples

>>> sections = get_all_sections(doc)
>>> for section in sections:
...     print(f"Level {section.level}: {section.get_heading_text()}")
Get only top-level sections:
>>> top_sections = get_all_sections(doc, min_level=1, max_level=1)
all2md.ast.get_preamble(doc: Document) list[Node]

Get all content before the first heading.

Parameters:

doc (Document) – Document to extract preamble from

Returns:

All nodes before the first heading (empty if document starts with heading)

Return type:

list of Node

Examples

>>> preamble = get_preamble(doc)
>>> if preamble:
...     print(f"Found {len(preamble)} preamble nodes")
all2md.ast.parse_section_ranges(section_spec: str, total_sections: int) list[int]

Parse section range specification into list of 0-based section indices.

Supports various formats (all 1-indexed input): - “1-3” -> [0, 1, 2] - “5” -> [4] - “10-” -> [9, 10, …, total_sections-1] - “1-3,5,10-” -> combined ranges - “5-3” -> [2, 3, 4] (automatically swaps to “3-5”)

Reversed ranges (where start > end) are automatically corrected by swapping the values. For example, “10-5” is treated as “5-10”.

Parameters:
  • section_spec (str) – Section range specification (1-based section numbers)

  • total_sections (int) – Total number of sections in document

Returns:

Sorted list of 0-based section indices

Return type:

list of int

Examples

>>> parse_section_ranges("1-3,5", 10)
[0, 1, 2, 4]
>>> parse_section_ranges("8-", 10)
[7, 8, 9]
>>> parse_section_ranges("10-5", 10)
[4, 5, 6, 7, 8, 9]
all2md.ast.query_sections(doc: Document, spec: str | int | list[int] | Callable[[Section], bool] | None = None, *, level: int | None = None, min_level: int = 1, max_level: int = 6, case_sensitive: bool = False) list[Section]

Universal section query function with flexible parameters.

This function consolidates multiple section-finding functions into one powerful interface that handles querying by name, index, pattern, predicate, or level filtering.

Parameters:
  • doc (Document) – Document to search

  • spec (str, int, list[int], callable, or None) – Query specification: - None: return all sections (respects level filters) - str: heading text or pattern (“Introduction”, “Chapter*”) - int: single section index (0-based) - list[int]: multiple section indices (0-based) - callable: predicate function(section) -> bool

  • level (int or None, default = None) – If specified, only return sections at this exact level (1-6)

  • min_level (int, default = 1) – Minimum heading level to include

  • max_level (int, default = 6) – Maximum heading level to include

  • case_sensitive (bool, default = False) – Whether text matching is case-sensitive (for string specs)

Returns:

Matching sections in document order

Return type:

list of Section

Raises:
  • ValueError – If spec is invalid or indices are out of range

  • IndexError – If a specified index is out of range

Examples

Find all sections:
>>> sections = query_sections(doc)
Find by exact heading text:
>>> sections = query_sections(doc, "Introduction")
Find by pattern (wildcards):
>>> sections = query_sections(doc, "Chapter*")
Find by single index:
>>> sections = query_sections(doc, 0)  # First section
Find by multiple indices:
>>> sections = query_sections(doc, [0, 2, 4])
Find by predicate:
>>> sections = query_sections(doc, lambda s: len(s.content) > 5)
Find by level:
>>> sections = query_sections(doc, level=2)
Find by level range:
>>> sections = query_sections(doc, min_level=2, max_level=3)
Combined filters:
>>> sections = query_sections(doc, "Chapter*", level=1)
all2md.ast.count_sections(doc: Document, level: int | None = None) int

Count the number of sections in a document.

Parameters:
  • doc (Document) – Document to count sections in

  • level (int or None, default = None) – If specified, only count sections at this level

Returns:

Number of sections

Return type:

int

Examples

>>> total_sections = count_sections(doc)
>>> top_level = count_sections(doc, level=1)
all2md.ast.find_heading(doc: Document, text: str, level: int | None = None, case_sensitive: bool = False) tuple[int, Heading] | None

Find a heading node in the document.

This function finds heading nodes directly, not sections. Use query_sections() if you want to find sections (heading + content).

Parameters:
  • doc (Document) – Document to search

  • text (str) – Text to search for

  • level (int or None, default = None) – If specified, only match headings at this level

  • case_sensitive (bool, default = False) – Whether to perform case-sensitive matching

Returns:

Tuple of (index, heading_node) if found, None otherwise

Return type:

tuple of (int, Heading) or None

Examples

>>> result = find_heading(doc, "Introduction")
>>> if result:
...     idx, heading = result
...     print(f"Found at index {idx}, level {heading.level}")
all2md.ast.extract_sections(doc: Document, spec: str | int | list[int], *, case_sensitive: bool = False, combine: bool = True, separator: Node | None = None) Document

Extract sections from document using flexible specification.

This function provides powerful section extraction with support for ranges, wildcards, multiple sections, and fuzzy matching with helpful suggestions.

Parameters:
  • doc (Document) – Source document

  • spec (str or int or list of int) – Extraction specification: - Name: “Introduction” (exact match) - Pattern: “Intro*”, “Results” (uses fnmatch) - Single index: 0 or “#:1” (0-based or 1-based with #:) - Range: “#:1-3”, “#:3-” (1-based, inclusive) - Multiple: “#:1,3,5” or [0, 2, 4] (1-based or 0-based list)

  • case_sensitive (bool, default False) – Whether name matching is case-sensitive

  • combine (bool, default True) – If True, combine sections into single document with separators. If False, return document with just the first match.

  • separator (Node or None, default None) – Node to insert between sections (default: ThematicBreak)

Returns:

Document with extracted sections

Return type:

Document

Raises:

ValueError – If spec is invalid or no sections match

Examples

Extract by name:
>>> extracted = extract_sections(doc, "Introduction")
Extract with wildcards:
>>> extracted = extract_sections(doc, "Chapter*")
Extract by range:
>>> extracted = extract_sections(doc, "#:1-3")
Extract multiple specific sections:
>>> extracted = extract_sections(doc, "#:1,3,5")
>>> extracted = extract_sections(doc, [0, 2, 4])
Extract without separators:
>>> extracted = extract_sections(doc, "#:1-3", separator=None)
all2md.ast.generate_toc(doc: Document, max_level: int = 3, style: Literal['markdown', 'list', 'nested'] = 'markdown') str | List

Generate a table of contents from document headings.

Parameters:
  • doc (Document) – Document to generate TOC from

  • max_level (int, default = 3) – Maximum heading level to include (1-6)

  • style ({"markdown", "list", "nested"}, default = "markdown") – Output style for the TOC

Returns:

Table of contents as markdown string (for “markdown” style) or List node (for “list” and “nested” styles)

Return type:

str or List

Notes

  • “markdown”: Returns markdown-formatted TOC as a string

  • “list”: Returns a flat List AST node with all headings

  • “nested”: Returns nested List AST nodes respecting heading hierarchy

Examples

>>> toc = generate_toc(doc, max_level=3)
>>> print(toc)
# Table of Contents
  • [Introduction](#introduction) - [Background](#background) - [Motivation](#motivation)

  • [Methods](#methods)

all2md.ast.insert_toc(doc: Document, position: Literal['start', 'after_first_heading'] = 'start', max_level: int = 3, style: Literal['markdown', 'list', 'nested'] = 'markdown') Document

Insert a table of contents into the document.

Parameters:
  • doc (Document) – Document to modify

  • position ({"start", "after_first_heading"}, default = "start") – Where to insert the TOC

  • max_level (int, default = 3) – Maximum heading level to include

  • style ({"markdown", "list", "nested"}, default = "markdown") – Style of the TOC

Returns:

New document with TOC inserted

Return type:

Document

Examples

>>> doc_with_toc = insert_toc(doc, position="start", max_level=3)
class all2md.ast.DocumentSplitter

Bases: object

Handles various document splitting strategies.

This class provides methods to split documents at semantic boundaries based on different criteria: heading levels, word counts, number of parts, or automatic detection.

static split_by_heading_level(doc: Document, level: int, include_preamble: bool = True) list[SplitResult]

Split document at every heading of specified level.

Parameters:
  • doc (Document) – Document to split

  • level (int) – Heading level to split on (1-6)

  • include_preamble (bool) – Whether to include content before first heading as separate split

Returns:

Split documents, one per section at specified level

Return type:

list of SplitResult

Raises:

ValueError – If level is not between 1 and 6

Examples

>>> DocumentSplitter.split_by_heading_level(doc, level=1)
static split_by_word_count(doc: Document, target_words: int) list[SplitResult]

Split document by word count, maintaining section boundaries.

Accumulates sections until target word count is reached, then creates a split. Ensures splits occur at section boundaries for semantic coherence.

Parameters:
  • doc (Document) – Document to split

  • target_words (int) – Target word count per split (approximate)

Returns:

Split documents with roughly equal word counts

Return type:

list of SplitResult

Raises:

ValueError – If target_words is less than 1

Examples

>>> DocumentSplitter.split_by_word_count(doc, target_words=500)
static split_by_parts(doc: Document, num_parts: int) list[SplitResult]

Split document into N roughly equal parts at section boundaries.

Calculates total word count and divides by num_parts to determine target words per part. Then uses word count splitting to create approximately equal splits.

Parameters:
  • doc (Document) – Document to split

  • num_parts (int) – Number of parts to create

Returns:

Split documents with roughly equal word counts

Return type:

list of SplitResult

Raises:

ValueError – If num_parts is less than 1

Examples

>>> DocumentSplitter.split_by_parts(doc, num_parts=5)
static split_into_slices(doc: Document, num_slices: int) list[SplitResult]

Divide a document into exactly num_slices balanced, contiguous slices.

Unlike split_by_parts() (which targets a word count and may yield a different number of parts), this guarantees exactly num_slices slices whenever the document has at least that many semantic “atoms” (sections, or word-count blocks for heading-light documents). When the document has fewer atoms than requested, it returns one slice per atom.

This powers the --slice X/Y paging flag, where a deterministic Y matters so callers can page 1/Y, 2/Y, ... Y/Y.

Parameters:
  • doc (Document) – Document to slice.

  • num_slices (int) – Desired number of slices (Y). Must be >= 1.

Returns:

min(num_slices, num_atoms) contiguous slices in document order.

Return type:

list of SplitResult

Raises:

ValueError – If num_slices is less than 1.

static split_by_break(doc: Document) list[SplitResult]

Split document at thematic breaks (horizontal rules).

Splits the document at any ThematicBreak nodes, which represent horizontal rules (---, ***, ___) in Markdown and similar separators in other formats.

Parameters:

doc (Document) – Document to split

Returns:

Split documents at thematic break boundaries

Return type:

list of SplitResult

Examples

>>> DocumentSplitter.split_by_break(doc)
static split_by_delimiter(doc: Document, delimiter: str) list[SplitResult]

Split document at custom text delimiters.

Searches for paragraphs or text nodes that contain only the delimiter text (allowing for whitespace) and splits the document at those points.

Parameters:
  • doc (Document) – Document to split

  • delimiter (str) – Text delimiter to split on (e.g., "-----", "***", "<!-- split -->")

Returns:

Split documents at delimiter boundaries

Return type:

list of SplitResult

Examples

>>> DocumentSplitter.split_by_delimiter(doc, delimiter="-----")
static split_auto(doc: Document, target_words: int = 1500) list[SplitResult]

Automatically determine best split strategy based on document structure.

Analyzes document to find natural split points: 1. Try h1 boundaries if sections are reasonable size 2. Otherwise try h2 boundaries 3. Fall back to word count splitting if sections too large

Parameters:
  • doc (Document) – Document to split

  • target_words (int) – Target word count per split for fallback strategy

Returns:

Split documents using the best detected strategy

Return type:

list of SplitResult

Examples

>>> DocumentSplitter.split_auto(doc)  # Target ~1500 words per split
static split_by_sections(doc: Document, include_preamble: bool = True) list[SplitResult]

Split document into separate documents by sections.

This method was moved from document_utils.py and adapted to return list[SplitResult] for consistency with other DocumentSplitter methods.

Parameters:
  • doc (Document) – Document to split

  • include_preamble (bool, default = True) – If True and there is content before the first heading, include it as a separate split at the beginning

Returns:

List of split results, one per section (plus preamble if present)

Return type:

list of SplitResult

Examples

>>> splits = DocumentSplitter.split_by_sections(doc)
>>> for i, split_result in enumerate(splits):
...     print(f"Section {i}: {len(split_result.document.children)} nodes")
class all2md.ast.SplitResult

Bases: object

Represents a split portion of a document.

Variables:
  • document (Document) – The split document AST

  • index (int) – 1-based index of this split (001, 002, etc.)

  • title (Optional[str]) – Title/heading text for this split (if available)

  • word_count (int) – Approximate word count for this split

  • metadata (dict) – Additional metadata for this split

document: Document
index: int
title: str | None = None
word_count: int = 0
metadata: dict
get_filename_slug() str

Generate filesystem-safe slug from title.

Returns:

Sanitized slug suitable for filenames

Return type:

str

Examples

>>> split = SplitResult(doc, 1, title="Chapter 1: Introduction")
>>> split.get_filename_slug()
'chapter-1-introduction'
__init__(document: Document, index: int, title: str | None = None, word_count: int = 0, metadata: dict = <factory>) None
all2md.ast.parse_split_spec(spec: str) tuple[str, Any]

Parse –split-by CLI argument into strategy and parameters.

Parameters:

spec (str) – Split specification string

Returns:

(strategy_name, parameter) where: - (“heading”, 1) for “h1” - (“heading”, 2) for “h2” - (“length”, 400) for “length=400” - (“parts”, 4) for “parts=4” - (“delimiter”, “—–”) for “delimiter=—–” - (“break”, None) for “break” - (“page”, None) for “page” - (“chapter”, None) for “chapter” - (“auto”, None) for “auto”

Return type:

tuple of (str, Any)

Raises:

ValueError – If spec format is invalid

Examples

>>> parse_split_spec("h1")
('heading', 1)
>>> parse_split_spec("length=500")
('length', 500)
>>> parse_split_spec("parts=3")
('parts', 3)
>>> parse_split_spec("delimiter=-----")
('delimiter', '-----')
>>> parse_split_spec("auto")
('auto', None)

For AST documentation, see Advanced Modules and the user guide at Working with the AST.