all2md.ast.utils
Utility functions for working with AST nodes.
This module provides helper functions for common operations on AST nodes, including text extraction, node traversal, and node manipulation.
Functions
extract_text : Extract plain text from a node or list of nodes
Examples
Extract text from a heading:
>>> from all2md.ast import Heading, Text, Emphasis
>>> from all2md.ast.utils import extract_text
>>>
>>> heading = Heading(level=1, content=[
... Text(content="Hello "),
... Emphasis(content=[Text(content="world")])
... ])
>>> extract_text(heading)
'Hello world'
Extract text without spaces (for ID generation):
>>> extract_text(heading, joiner="")
'Helloworld'
- all2md.ast.utils.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:
- 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.'