markdocpy

markdocpy public API.

  1"""markdocpy public API."""
  2
  3from __future__ import annotations
  4
  5from dataclasses import dataclass
  6from typing import Any, Dict, List
  7
  8from .ast.function import Function
  9from .ast.node import Node
 10from .ast.tag import Tag
 11from .ast.variable import Variable
 12from .version import __version__
 13from .parser.parser import parse as _parse_tokens
 14from .parser.tokenizer import Tokenizer
 15from .renderer.html import render as _render_html
 16from .schema.nodes import nodes
 17from .schema.tags import tags, truthy
 18from .transform.transformer import global_attributes, merge_config, transform as _transform
 19from .validator.validator import validate_tree
 20
 21
 22@dataclass
 23class _Renderers:
 24    html = staticmethod(_render_html)
 25
 26
 27renderers = _Renderers()
 28
 29
 30def parse(
 31    content: str, *, file: str | None = None, slots: bool = False, location: bool = False
 32) -> Node:
 33    _ = file, slots, location
 34    tokenizer = Tokenizer()
 35    tokens = tokenizer.tokenize(content)
 36    return _parse_tokens(tokens, slots=slots)
 37
 38
 39def resolve(content: Node | List[Node], config: Dict[str, Any]):
 40    if isinstance(content, list):
 41        return [child.resolve(config) for child in content]
 42    return content.resolve(config)
 43
 44
 45def transform(content: Node | List[Node], config: Dict[str, Any] | None = None):
 46    merged = merge_config(config)
 47    resolved = resolve(content, merged)
 48    return _transform(resolved, merged)
 49
 50
 51def validate(content: Node | List[Node], config: Dict[str, Any] | None = None):
 52    return validate_tree(content, config)
 53
 54
 55def create_element(
 56    name: str | Dict[str, Any], attributes: Dict[str, Any] | None = None, *children: Any
 57) -> Tag:
 58    if isinstance(name, dict):
 59        attributes = name
 60        name = attributes.get("name")
 61    return Tag(name, attributes or {}, list(children))
 62
 63
 64class Markdoc:
 65    Tokenizer = Tokenizer
 66    Tag = Tag
 67    renderers = renderers
 68    nodes = nodes
 69    tags = tags
 70    truthy = truthy
 71
 72    def __init__(self, config: Dict[str, Any]):
 73        """Create a Markdoc wrapper with a fixed config."""
 74        self.config = config
 75
 76    def parse(self, content: str) -> Node:
 77        """Parse Markdoc content into an AST."""
 78        return parse(content)
 79
 80    def resolve(self, content: Node | List[Node]):
 81        """Resolve variables/functions using the stored config."""
 82        return resolve(content, self.config)
 83
 84    def transform(self, content: Node | List[Node]):
 85        """Transform AST nodes into a renderable tree."""
 86        return transform(content, self.config)
 87
 88    def validate(self, content: Node | List[Node]):
 89        """Validate AST nodes against the schema."""
 90        return validate(content, self.config)
 91
 92
 93__all__ = [
 94    "Node",
 95    "Tag",
 96    "Tokenizer",
 97    "Variable",
 98    "Function",
 99    "parse",
100    "resolve",
101    "transform",
102    "validate",
103    "create_element",
104    "renderers",
105    "nodes",
106    "tags",
107    "truthy",
108    "global_attributes",
109    "Markdoc",
110    "__version__",
111]
@dataclass
class Node:
11@dataclass
12class Node:
13    """AST node for parsed Markdoc content."""
14
15    type: str
16    children: List["Node"] = field(default_factory=list)
17    attributes: Dict[str, Any] = field(default_factory=dict)
18    tag: Optional[str] = None
19    content: Optional[str] = None
20    slots: Dict[str, "Node"] = field(default_factory=dict)
21    inline: bool = False
22
23    def resolve(self, config: Any) -> "Node":
24        """Resolve variables/functions in this node and its children."""
25        self.attributes = _resolve_value(self.attributes, config)
26        resolved_children = []
27        for child in self.children:
28            if isinstance(child, Node):
29                resolved_children.append(child.resolve(config))
30            else:
31                resolved_children.append(_resolve_value(child, config))
32        self.children = resolved_children
33        return self
34
35    def transform(self, config: Any) -> Any:
36        """Transform the node into a renderable tree."""
37        from ..transform.transformer import transform
38
39        return transform(self, config)

AST node for parsed Markdoc content.

Node( type: str, children: List[Node] = <factory>, attributes: Dict[str, Any] = <factory>, tag: Optional[str] = None, content: Optional[str] = None, slots: Dict[str, Node] = <factory>, inline: bool = False)
type: str
children: List[Node]
attributes: Dict[str, Any]
tag: Optional[str] = None
content: Optional[str] = None
slots: Dict[str, Node]
inline: bool = False
def resolve(self, config: Any) -> Node:
23    def resolve(self, config: Any) -> "Node":
24        """Resolve variables/functions in this node and its children."""
25        self.attributes = _resolve_value(self.attributes, config)
26        resolved_children = []
27        for child in self.children:
28            if isinstance(child, Node):
29                resolved_children.append(child.resolve(config))
30            else:
31                resolved_children.append(_resolve_value(child, config))
32        self.children = resolved_children
33        return self

Resolve variables/functions in this node and its children.

def transform(self, config: Any) -> Any:
35    def transform(self, config: Any) -> Any:
36        """Transform the node into a renderable tree."""
37        from ..transform.transformer import transform
38
39        return transform(self, config)

Transform the node into a renderable tree.

@dataclass
class Tag:
 8@dataclass
 9class Tag:
10    """Renderable tag node produced by the transformer."""
11
12    name: Optional[str]
13    attributes: Mapping[str, Any] = field(default_factory=dict)
14    children: List[Any] = field(default_factory=list)
15    self_closing: bool = False
16
17    @staticmethod
18    def is_tag(value: Any) -> bool:
19        return isinstance(value, Tag)
20
21    def with_children(self, children: Iterable[Any]) -> "Tag":
22        return Tag(self.name, dict(self.attributes), list(children))

Renderable tag node produced by the transformer.

Tag( name: Optional[str], attributes: Mapping[str, Any] = <factory>, children: List[Any] = <factory>, self_closing: bool = False)
name: Optional[str]
attributes: Mapping[str, Any]
children: List[Any]
self_closing: bool = False
@staticmethod
def is_tag(value: Any) -> bool:
17    @staticmethod
18    def is_tag(value: Any) -> bool:
19        return isinstance(value, Tag)
def with_children(self, children: Iterable[Any]) -> Tag:
21    def with_children(self, children: Iterable[Any]) -> "Tag":
22        return Tag(self.name, dict(self.attributes), list(children))
class Tokenizer:
 9class Tokenizer:
10    def __init__(self, config: dict | None = None) -> None:
11        options = config or {}
12        self.parser = MarkdownIt("commonmark", options_update=options) if options else MarkdownIt()
13        self.parser.enable("table")
14        self.parser.disable(["lheading", "code"])
15
16    def tokenize(self, content: str):
17        normalized = _normalize_block_tags(content)
18        return self.parser.parse(normalized, {})
Tokenizer(config: dict | None = None)
10    def __init__(self, config: dict | None = None) -> None:
11        options = config or {}
12        self.parser = MarkdownIt("commonmark", options_update=options) if options else MarkdownIt()
13        self.parser.enable("table")
14        self.parser.disable(["lheading", "code"])
parser
def tokenize(self, content: str):
16    def tokenize(self, content: str):
17        normalized = _normalize_block_tags(content)
18        return self.parser.parse(normalized, {})
@dataclass
class Variable:
 8@dataclass
 9class Variable:
10    """Reference to a variable to be resolved at transform time."""
11
12    path: List[Any] = field(default_factory=list)
13
14    def __init__(self, path: str | Iterable[Any] | None = None):
15        if path is None:
16            self.path = []
17        elif isinstance(path, str):
18            self.path = [path]
19        else:
20            self.path = list(path)
21
22    @property
23    def name(self) -> str:
24        return _path_to_string(self.path)
25
26    def resolve(self, config: Dict[str, Any]) -> Any:
27        """Resolve the variable value from the config."""
28        variables = config.get("variables", {})
29        if callable(variables):
30            return variables(self.path)
31        current = variables
32        for segment in self.path:
33            if isinstance(current, dict):
34                if segment not in current:
35                    return MISSING
36                current = current[segment]
37                continue
38            if isinstance(current, (list, tuple)):
39                if not isinstance(segment, int) or segment < 0 or segment >= len(current):
40                    return MISSING
41                current = current[segment]
42                continue
43            return MISSING
44        return current

Reference to a variable to be resolved at transform time.

Variable(path: Union[str, Iterable[Any], NoneType] = None)
14    def __init__(self, path: str | Iterable[Any] | None = None):
15        if path is None:
16            self.path = []
17        elif isinstance(path, str):
18            self.path = [path]
19        else:
20            self.path = list(path)
path: List[Any]
name: str
22    @property
23    def name(self) -> str:
24        return _path_to_string(self.path)
def resolve(self, config: Dict[str, Any]) -> Any:
26    def resolve(self, config: Dict[str, Any]) -> Any:
27        """Resolve the variable value from the config."""
28        variables = config.get("variables", {})
29        if callable(variables):
30            return variables(self.path)
31        current = variables
32        for segment in self.path:
33            if isinstance(current, dict):
34                if segment not in current:
35                    return MISSING
36                current = current[segment]
37                continue
38            if isinstance(current, (list, tuple)):
39                if not isinstance(segment, int) or segment < 0 or segment >= len(current):
40                    return MISSING
41                current = current[segment]
42                continue
43            return MISSING
44        return current

Resolve the variable value from the config.

@dataclass
class Function:
11@dataclass
12class Function:
13    """Reference to a function to be resolved at transform time."""
14
15    name: str
16    args: List[Any] = field(default_factory=list)
17    kwargs: Dict[str, Any] = field(default_factory=dict)
18
19    def resolve(self, config: Dict[str, Any]) -> Any:
20        """Resolve the function with args/kwargs using the config."""
21        fn = config.get("functions", {}).get(self.name)
22        if fn is None:
23            return None
24        resolved_args = [_resolve_value(arg, config) for arg in self.args]
25        resolved_kwargs = {key: _resolve_value(val, config) for key, val in self.kwargs.items()}
26        parameters = {index: value for index, value in enumerate(resolved_args)}
27        parameters.update(resolved_kwargs)
28        if callable(fn):
29            return fn(*resolved_args, **resolved_kwargs)
30        transform = fn.get("transform") if isinstance(fn, dict) else None
31        if callable(transform):
32            return _call_transform(transform, parameters, config)
33        return None

Reference to a function to be resolved at transform time.

Function( name: str, args: List[Any] = <factory>, kwargs: Dict[str, Any] = <factory>)
name: str
args: List[Any]
kwargs: Dict[str, Any]
def resolve(self, config: Dict[str, Any]) -> Any:
19    def resolve(self, config: Dict[str, Any]) -> Any:
20        """Resolve the function with args/kwargs using the config."""
21        fn = config.get("functions", {}).get(self.name)
22        if fn is None:
23            return None
24        resolved_args = [_resolve_value(arg, config) for arg in self.args]
25        resolved_kwargs = {key: _resolve_value(val, config) for key, val in self.kwargs.items()}
26        parameters = {index: value for index, value in enumerate(resolved_args)}
27        parameters.update(resolved_kwargs)
28        if callable(fn):
29            return fn(*resolved_args, **resolved_kwargs)
30        transform = fn.get("transform") if isinstance(fn, dict) else None
31        if callable(transform):
32            return _call_transform(transform, parameters, config)
33        return None

Resolve the function with args/kwargs using the config.

def parse( content: str, *, file: str | None = None, slots: bool = False, location: bool = False) -> Node:
31def parse(
32    content: str, *, file: str | None = None, slots: bool = False, location: bool = False
33) -> Node:
34    _ = file, slots, location
35    tokenizer = Tokenizer()
36    tokens = tokenizer.tokenize(content)
37    return _parse_tokens(tokens, slots=slots)
def resolve( content: Union[Node, List[Node]], config: Dict[str, Any]):
40def resolve(content: Node | List[Node], config: Dict[str, Any]):
41    if isinstance(content, list):
42        return [child.resolve(config) for child in content]
43    return content.resolve(config)
def transform( content: Union[Node, List[Node]], config: Optional[Dict[str, Any]] = None):
46def transform(content: Node | List[Node], config: Dict[str, Any] | None = None):
47    merged = merge_config(config)
48    resolved = resolve(content, merged)
49    return _transform(resolved, merged)
def validate( content: Union[Node, List[Node]], config: Optional[Dict[str, Any]] = None):
52def validate(content: Node | List[Node], config: Dict[str, Any] | None = None):
53    return validate_tree(content, config)
def create_element( name: Union[str, Dict[str, Any]], attributes: Optional[Dict[str, Any]] = None, *children: Any) -> Tag:
56def create_element(
57    name: str | Dict[str, Any], attributes: Dict[str, Any] | None = None, *children: Any
58) -> Tag:
59    if isinstance(name, dict):
60        attributes = name
61        name = attributes.get("name")
62    return Tag(name, attributes or {}, list(children))
renderers = _Renderers()
nodes = {'document': {'render': 'article', 'children': ['heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'comment', 'list', 'hr'], 'attributes': {'frontmatter': {'render': False}}}, 'heading': {'render': 'h{level}', 'children': ['inline'], 'attributes': {'level': {'type': <class 'int'>, 'render': False, 'required': True}}}, 'paragraph': {'render': 'p', 'children': ['inline']}, 'image': {'render': 'img', 'self_closing': True, 'attributes': {'src': {'type': <class 'str'>, 'required': True}, 'alt': {'type': <class 'str'>}, 'title': {'type': <class 'str'>}}}, 'fence': {'render': 'pre', 'attributes': {'content': {'type': <class 'str'>, 'render': False, 'required': True}, 'language': {'type': <class 'str'>, 'render': 'data-language'}, 'process': {'type': <class 'bool'>, 'render': False, 'default': True}}}, 'blockquote': {'render': 'blockquote', 'children': ['heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr']}, 'item': {'render': 'li', 'children': ['inline', 'heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr']}, 'list': {'render': None, 'children': ['item'], 'attributes': {'ordered': {'type': <class 'bool'>, 'render': False, 'required': True}, 'start': {'type': <class 'int'>}, 'marker': {'type': <class 'str'>, 'render': False}}}, 'hr': {'render': 'hr', 'self_closing': True}, 'table': {'render': 'table'}, 'td': {'render': 'td', 'children': ['inline', 'heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr'], 'attributes': {'align': {'type': <class 'str'>}, 'colspan': {'type': <class 'int'>, 'render': 'colSpan'}, 'rowspan': {'type': <class 'int'>, 'render': 'rowSpan'}}}, 'th': {'render': 'th', 'attributes': {'width': {'type': <class 'str'>}, 'align': {'type': <class 'str'>}, 'colspan': {'type': <class 'int'>, 'render': 'colSpan'}, 'rowspan': {'type': <class 'int'>, 'render': 'rowSpan'}}}, 'tr': {'render': 'tr', 'children': ['th', 'td']}, 'tbody': {'render': 'tbody', 'children': ['tr', 'tag']}, 'thead': {'render': 'thead', 'children': ['tr']}, 'strong': {'render': 'strong', 'children': ['em', 's', 'link', 'code_inline', 'text', 'tag'], 'attributes': {'marker': {'type': <class 'str'>, 'render': False}}}, 'em': {'render': 'em', 'children': ['strong', 's', 'link', 'code_inline', 'text', 'tag'], 'attributes': {'marker': {'type': <class 'str'>, 'render': False}}}, 's': {'render': 's', 'children': ['strong', 'em', 'link', 'code_inline', 'text', 'tag']}, 'inline': {'children': ['strong', 'em', 's', 'code_inline', 'text', 'tag', 'link', 'image', 'hardbreak', 'softbreak', 'comment']}, 'link': {'render': 'a', 'children': ['strong', 'em', 's', 'code_inline', 'text', 'tag'], 'attributes': {'href': {'type': <class 'str'>, 'required': True}, 'title': {'type': <class 'str'>}}}, 'code_inline': {'render': 'code', 'attributes': {'content': {'type': <class 'str'>, 'render': False, 'required': True}}}, 'text': {'attributes': {'content': {'type': <class 'str'>, 'required': True}}}, 'hardbreak': {'render': 'br', 'self_closing': True}, 'softbreak': {'render': None}, 'comment': {'attributes': {'content': {'type': <class 'str'>, 'required': True}}}, 'error': {}, 'node': {}, 'code': {'render': 'pre'}}
tags = {'if': {'attributes': {'primary': {'render': False}}, 'transform': <function _transform_if>}, 'else': {'self_closing': True, 'attributes': {'primary': {'render': False}}}, 'table': {'transform': <function _transform_tag>}, 'partial': {'inline': False, 'self_closing': True, 'attributes': {'file': {'type': <class 'markdocpy.schema.tags.PartialFile'>, 'render': False, 'required': True}, 'variables': {'type': <class 'dict'>, 'render': False}}, 'transform': <function _transform_partial>}, 'slot': {'render': False}}
def truthy(value: Any) -> bool:
 9def truthy(value: Any) -> bool:
10    return value is not False and value is not None
global_attributes = {'class': {'type': <class 'markdocpy.schema_types.class_type.ClassType'>, 'render': True}, 'id': {'type': <class 'markdocpy.schema_types.id_type.IdType'>, 'render': True}}
class Markdoc:
65class Markdoc:
66    Tokenizer = Tokenizer
67    Tag = Tag
68    renderers = renderers
69    nodes = nodes
70    tags = tags
71    truthy = truthy
72
73    def __init__(self, config: Dict[str, Any]):
74        """Create a Markdoc wrapper with a fixed config."""
75        self.config = config
76
77    def parse(self, content: str) -> Node:
78        """Parse Markdoc content into an AST."""
79        return parse(content)
80
81    def resolve(self, content: Node | List[Node]):
82        """Resolve variables/functions using the stored config."""
83        return resolve(content, self.config)
84
85    def transform(self, content: Node | List[Node]):
86        """Transform AST nodes into a renderable tree."""
87        return transform(content, self.config)
88
89    def validate(self, content: Node | List[Node]):
90        """Validate AST nodes against the schema."""
91        return validate(content, self.config)
Markdoc(config: Dict[str, Any])
73    def __init__(self, config: Dict[str, Any]):
74        """Create a Markdoc wrapper with a fixed config."""
75        self.config = config

Create a Markdoc wrapper with a fixed config.

renderers = _Renderers()
nodes = {'document': {'render': 'article', 'children': ['heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'comment', 'list', 'hr'], 'attributes': {'frontmatter': {'render': False}}}, 'heading': {'render': 'h{level}', 'children': ['inline'], 'attributes': {'level': {'type': <class 'int'>, 'render': False, 'required': True}}}, 'paragraph': {'render': 'p', 'children': ['inline']}, 'image': {'render': 'img', 'self_closing': True, 'attributes': {'src': {'type': <class 'str'>, 'required': True}, 'alt': {'type': <class 'str'>}, 'title': {'type': <class 'str'>}}}, 'fence': {'render': 'pre', 'attributes': {'content': {'type': <class 'str'>, 'render': False, 'required': True}, 'language': {'type': <class 'str'>, 'render': 'data-language'}, 'process': {'type': <class 'bool'>, 'render': False, 'default': True}}}, 'blockquote': {'render': 'blockquote', 'children': ['heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr']}, 'item': {'render': 'li', 'children': ['inline', 'heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr']}, 'list': {'render': None, 'children': ['item'], 'attributes': {'ordered': {'type': <class 'bool'>, 'render': False, 'required': True}, 'start': {'type': <class 'int'>}, 'marker': {'type': <class 'str'>, 'render': False}}}, 'hr': {'render': 'hr', 'self_closing': True}, 'table': {'render': 'table'}, 'td': {'render': 'td', 'children': ['inline', 'heading', 'paragraph', 'image', 'table', 'tag', 'fence', 'blockquote', 'list', 'hr'], 'attributes': {'align': {'type': <class 'str'>}, 'colspan': {'type': <class 'int'>, 'render': 'colSpan'}, 'rowspan': {'type': <class 'int'>, 'render': 'rowSpan'}}}, 'th': {'render': 'th', 'attributes': {'width': {'type': <class 'str'>}, 'align': {'type': <class 'str'>}, 'colspan': {'type': <class 'int'>, 'render': 'colSpan'}, 'rowspan': {'type': <class 'int'>, 'render': 'rowSpan'}}}, 'tr': {'render': 'tr', 'children': ['th', 'td']}, 'tbody': {'render': 'tbody', 'children': ['tr', 'tag']}, 'thead': {'render': 'thead', 'children': ['tr']}, 'strong': {'render': 'strong', 'children': ['em', 's', 'link', 'code_inline', 'text', 'tag'], 'attributes': {'marker': {'type': <class 'str'>, 'render': False}}}, 'em': {'render': 'em', 'children': ['strong', 's', 'link', 'code_inline', 'text', 'tag'], 'attributes': {'marker': {'type': <class 'str'>, 'render': False}}}, 's': {'render': 's', 'children': ['strong', 'em', 'link', 'code_inline', 'text', 'tag']}, 'inline': {'children': ['strong', 'em', 's', 'code_inline', 'text', 'tag', 'link', 'image', 'hardbreak', 'softbreak', 'comment']}, 'link': {'render': 'a', 'children': ['strong', 'em', 's', 'code_inline', 'text', 'tag'], 'attributes': {'href': {'type': <class 'str'>, 'required': True}, 'title': {'type': <class 'str'>}}}, 'code_inline': {'render': 'code', 'attributes': {'content': {'type': <class 'str'>, 'render': False, 'required': True}}}, 'text': {'attributes': {'content': {'type': <class 'str'>, 'required': True}}}, 'hardbreak': {'render': 'br', 'self_closing': True}, 'softbreak': {'render': None}, 'comment': {'attributes': {'content': {'type': <class 'str'>, 'required': True}}}, 'error': {}, 'node': {}, 'code': {'render': 'pre'}}
tags = {'if': {'attributes': {'primary': {'render': False}}, 'transform': <function _transform_if>}, 'else': {'self_closing': True, 'attributes': {'primary': {'render': False}}}, 'table': {'transform': <function _transform_tag>}, 'partial': {'inline': False, 'self_closing': True, 'attributes': {'file': {'type': <class 'markdocpy.schema.tags.PartialFile'>, 'render': False, 'required': True}, 'variables': {'type': <class 'dict'>, 'render': False}}, 'transform': <function _transform_partial>}, 'slot': {'render': False}}
def truthy(value: Any) -> bool:
 9def truthy(value: Any) -> bool:
10    return value is not False and value is not None
config
def parse(self, content: str) -> Node:
77    def parse(self, content: str) -> Node:
78        """Parse Markdoc content into an AST."""
79        return parse(content)

Parse Markdoc content into an AST.

def resolve( self, content: Union[Node, List[Node]]):
81    def resolve(self, content: Node | List[Node]):
82        """Resolve variables/functions using the stored config."""
83        return resolve(content, self.config)

Resolve variables/functions using the stored config.

def transform( self, content: Union[Node, List[Node]]):
85    def transform(self, content: Node | List[Node]):
86        """Transform AST nodes into a renderable tree."""
87        return transform(content, self.config)

Transform AST nodes into a renderable tree.

def validate( self, content: Union[Node, List[Node]]):
89    def validate(self, content: Node | List[Node]):
90        """Validate AST nodes against the schema."""
91        return validate(content, self.config)

Validate AST nodes against the schema.

class Markdoc.Tokenizer:
 9class Tokenizer:
10    def __init__(self, config: dict | None = None) -> None:
11        options = config or {}
12        self.parser = MarkdownIt("commonmark", options_update=options) if options else MarkdownIt()
13        self.parser.enable("table")
14        self.parser.disable(["lheading", "code"])
15
16    def tokenize(self, content: str):
17        normalized = _normalize_block_tags(content)
18        return self.parser.parse(normalized, {})
@dataclass
class Markdoc.Tag:
 8@dataclass
 9class Tag:
10    """Renderable tag node produced by the transformer."""
11
12    name: Optional[str]
13    attributes: Mapping[str, Any] = field(default_factory=dict)
14    children: List[Any] = field(default_factory=list)
15    self_closing: bool = False
16
17    @staticmethod
18    def is_tag(value: Any) -> bool:
19        return isinstance(value, Tag)
20
21    def with_children(self, children: Iterable[Any]) -> "Tag":
22        return Tag(self.name, dict(self.attributes), list(children))

Renderable tag node produced by the transformer.

__version__ = '0.3.0.dev1'