Node#
Qualified name: rsm.nodes.Node
- class rsm.nodes.Node(label='', types=None, number=None, nonum=False, reftext_template='')[source]#
Bases:
None
A node in the manuscript tree.
A node represents a semantically meaningful element of the manuscript.
- Parameters:
label (str) – Unique identifier for this node.
types (Optional[list[str]]) – Types of this node.
number (Optional[int]) – Node number.
nonum (bool) – Whether this node should be automatically given a number.
reftext_template (str) – If not empty, replaces
classreftext
.
See also
Manuscript
The class of the root node of a manuscript tree.
NodeWithChildren
Subclass that implements methods to handle children.
Notes
An instance of Node cannot have children. Only instances of (subclasses) of
NodeWithChildren
may have them. However, for the sake of having a uniform API, Node implements the propertychildren
, which always returns an empty tuple.Methods
First ancestor of the specified type.
First child of the specified type.
ingest_dict_as_meta
Last child of the specified type.
The valid meta keys of the given class.
The next sibling, optionally of a specified type.
The previous sibling, optionally of a specified type.
Remove this Node from its parent's children.
Replace this node in its parent's children.
Represent this node as an S expression.
Generate the descendents of this Node in depth-first order.
Attributes
Whether to automatically assign a number to this node during transform step.
Tuple with this Node's children.
classreftext
full_number
Meta keys to add to those of the parent class.
number_as
number_within
The parent of this Node, or None.
Allowed types of parent Nodes.
reftext
Unique identifier.
Types of this node.
Node number.
Whether this node should be automatically given a number.
Reftext template, or "" to use
classreftext
.- autonumber: ClassVar[bool] = False[source]#
Whether to automatically assign a number to this node during transform step.
Examples
>>> msc, thm1, thm2 = nodes.Manuscript(), nodes.Theorem(), nodes.Theorem() >>> (thm1.number, thm2.number) == (None, None) True >>> tform = rsm.transformer.Transformer() >>> tform.transform(msc.append([thm1, thm2])) >>> thm1.number, thm2.number (1, 2)
- first_ancestor_of_type(cls)[source]#
First ancestor of the specified type.
- Parameters:
cls (Union[Type[Node], tuple[Type[rsm.nodes.Node]]]) – Desired class of the ancestor.
- Returns:
The first ancestor of the specified type, or None.
- Return type:
Optional[Node]
See also
Examples
Given the tree
>>> t = nodes.Text("Hello") >>> p = nodes.Paragraph().append(nodes.Span().append(t)) >>> print(p.sexp()) (Paragraph (Span (Text)))
Find an ancestor of a desired type.
>>> t.first_ancestor_of_type(nodes.Paragraph) Paragraph(parent=None, [Span])
Always check the return value against
None
.>>> t.first_ancestor_of_type(nodes.Manuscript) is None True
- first_of_type(cls, return_idx=False)[source]#
First child of the specified type.
- Parameters:
cls (Union[Type[Node], tuple[Type[rsm.nodes.Node]]]) – Desired class of the child.
return_idx (bool) – Whether to return the index of the child among this node’s children.
- Returns:
Node – The first child of the specified type, or None.
Node, int – If return_idx is True, return (child, index), or (None, None).
- Return type:
Union[Node, None, tuple[Optional[rsm.nodes.Node], Optional[int]]]
See also
Examples
>>> p = nodes.Paragraph().append([nodes.Text("one"), nodes.Text("two")]) >>> p.first_of_type(nodes.Text) Text("one") >>> p.first_of_type(nodes.Text, return_idx=True) (Text("one"), 0)
The index counts all existing children.
>>> p.prepend(nodes.Span()) Paragraph(parent=None, [Span, Text, Text]) >>> p.first_of_type(nodes.Text, return_idx=True) (Text("one"), 1)
- last_of_type(cls, return_idx=False)[source]#
Last child of the specified type.
For details, see
first_of_type()
.Examples
>>> p = nodes.Paragraph().append([nodes.Span(), nodes.Text("one"), nodes.Text("two")]) >>> p.last_of_type(nodes.Text, return_idx=True) (Text("two"), 2)
- classmethod metakeys()[source]#
The valid meta keys of the given class.
- Returns:
The valid meta keys.
- Return type:
set[str]
Examples
>>> all_nodes_meta = {"label", "types", "nonum", "reftext"} >>> nodes.Node.metakeys() == all_nodes_meta True >>> nodes.Span.metakeys() - all_nodes_meta == {"strong", "emphas", "little", "insert", "delete"} True >>> nodes.Author.metakeys() - all_nodes_meta == {"name", "affiliation", "email"} True
- newmetakeys: ClassVar[set] = {'label', 'nonum', 'reftext', 'types'}[source]#
Meta keys to add to those of the parent class.
Important
Only use this when defining a new Node subclass. When dealing with Node isntances, do not access this attribute directly neither for reading nor writing. Always use
metakeys()
in that case.See also
Examples
The keys in newmetakeys are added to the meta keys of the parent class.
>>> nodes.Heading.newmetakeys {'title'} >>> nodes.Heading.metakeys() == nodes.Node.metakeys() | {"title"} True
The intended use, and only supported use, of newmetakeys is at the time of class definition.
>>> class NewNode(nodes.Node): ... newmetakeys = {"newkey"} >>> NewNode.metakeys() == nodes.Node.metakeys() | {"newkey"} True
- next_sibling(cls=None)[source]#
The next sibling, optionally of a specified type.
For details, see
prev_sibling()
.See also
- property parent: Optional[NodeWithChildren][source]#
The parent of this Node, or None.
- possible_parents: ClassVar[set[Type[rsm.nodes.NodeWithChildren]]] = {}[source]#
Allowed types of parent Nodes.
When setting the parent of a Node, this attribute is checked to see whether the intended parent has an admissible type.
Examples
This is a class variable
>>> nodes.Item.possible_parents == {nodes.Itemize, nodes.Enumerate, nodes.Contents} True
This variable is checked when setting the parent directly.
>>> it = nodes.Item() >>> it.parent = nodes.Paragraph() Traceback (most recent call last): rsm.nodes.RSMNodeError: Node of type <class 'rsm.nodes.Item'> cannot have parent of type <class 'rsm.nodes.Paragraph'>
This is also used when setting the parent in some other indirect way, for example when calling
append()
on the desired parent.>>> nodes.Paragraph().append(it) Traceback (most recent call last): rsm.nodes.RSMNodeError: Node of type <class 'rsm.nodes.Item'> cannot have parent of type <class 'rsm.nodes.Paragraph'>
Allowed parents proceed without raising.
>>> nodes.Itemize().append(it) Itemize(parent=None, [Item])
- prev_sibling(cls=None)[source]#
The previous sibling, optionally of a specified type.
- Parameters:
cls (Optional[Union[Type[Node], str]]) – The type of the desired sibling. If
"self"
, search for the previous sibling with the same type as this node. IfNone
, return the immediately preceding sibling, regardless of its type.- Returns:
The desired sibling, or None.
- Return type:
Optional[Node]
See also
Examples
>>> p, s, t1, t2 = nodes.Paragraph(), nodes.Span(), nodes.Text("one"), nodes.Text("two") >>> p.append([t1, s, t2]) >>> t2.prev_sibling() Span(parent=Paragraph) >>> t2.prev_sibling(nodes.Text) Text("one") >>> t1.prev_sibling() is None True
Use
"self"
to find nodes of the same type.>>> s2 = nodes.Span() >>> p.append(s2) Paragraph(parent=None, [Text, Span, Text, Span]) >>> s2.prev_sibling() is t2 True >>> s2.prev_sibling("self") is s True
- remove_self()[source]#
Remove this Node from its parent’s children.
See also
Examples
>>> t = nodes.Text("remove me") >>> p = nodes.Paragraph().append(t) >>> p.children (Text("remove me"),) >>> t.remove_self() >>> p.children ()
- replace_self(replacement)[source]#
Replace this node in its parent’s children.
This is mostly useful during the transform step.
- Parameters:
replacement (Union[Node, Iterable[Node]]) – The Node or Nodes to replace this with.
- Raises:
RSMNodeError – If this Node’s parent is None.
See also
Examples
Wrap a Text in a strong Span to render it in bold face.
>>> p, t = nodes.Paragraph(), nodes.Text("one") >>> p.append(t) >>> print(p.sexp()) (Paragraph (Text)) >>> s = nodes.Span(strong=True) >>> t.replace_self(s) >>> s.append(t) >>> print(p.sexp()) (Paragraph (Span (Text)))
Note the call to
replace_self()
must happen before the Text is added to the Span.May also replace with a list of Nodes.
>>> t.replace_self([nodes.Text("new one"), nodes.Text("two")]) >>> print(p.sexp()) (Paragraph (Span (Text) (Text)))
The following recipe uses the above example to wrap every Text within
root
in a strong Span. Note this is done to each Text descendent ofroot
, regardless of depth, and without any reference to their original immediate parents.>>> root = nodes.Manuscript().append(...) >>> for t in root.traverse(nodeclass=Text): ... s = nodes.Span(strong=True) ... t.replace_self(s) ... s.append(t)
- sexp(tab_width=2, meta=False, ignore_meta_keys=None)[source]#
Represent this node as an S expression.
- Parameters:
tab_width (int) – How many spaces of indentation at each depth level.
meta (bool) – Whether to include meta in the output.
ignore_meta_keys (Optional[set]) – When meta is
True
, this controls which meta keys to include.
- Returns:
A string representation of the Node and its descendents.
- Return type:
str
See also
Examples
>>> p1, p2, p3, p4 = [nodes.Paragraph().append(nodes.Text()) for _ in range(4)] >>> msc = nodes.Manuscript().append( ... [ ... nodes.Section().append(p1), ... nodes.Section().append([nodes.Subsection().append([p2, p3])]), ... nodes.Section().append(p4), ... ] ... )
By default, meta keys are not included in the output.
>>> print(msc.sexp()) (Manuscript (Section (Paragraph (Text))) (Section (Subsection (Paragraph (Text)) (Paragraph (Text)))) (Section (Paragraph (Text))))
sexp()
is useful when transforming the tree.>>> p3.replace_self(nodes.Paragraph().append(nodes.Span(strong=True).append(nodes.Text()))) >>> print(msc.sexp()) (Manuscript (Section (Paragraph (Text))) (Section (Subsection (Paragraph (Text)) (Paragraph (Span (Text))))) (Section (Paragraph (Text))))
Output meta for even more debugging information.
>>> print(msc.sexp(meta=True)) (Manuscript { :reftext: Manuscript } (Section { :reftext: Section } (Paragraph { :reftext: Paragraph } (Text { :reftext: Text }))) (Section { :reftext: Section } (Subsection { :reftext: Section } (Paragraph { :reftext: Paragraph } (Text { :reftext: Text })) (Paragraph { :reftext: Paragraph } (Span { :reftext: Span , :strong: True } (Text { :reftext: Text }))))) (Section { :reftext: Section } (Paragraph { :reftext: Paragraph } (Text { :reftext: Text }))))
Use ignore_meta_keys for a less verbose output.
>>> print(msc.sexp(meta=True, ignore_meta_keys={"reftext"})) (Manuscript { } (Section { } (Paragraph { } (Text { }))) (Section { } (Subsection { } (Paragraph { } (Text { })) (Paragraph { } (Span { :strong: True } (Text { }))))) (Section { } (Paragraph { } (Text { }))))
- traverse(*, condition=None, nodeclass=None)[source]#
Generate the descendents of this Node in depth-first order.
By default, yield this node and then every descendent in depth-first order. If condition is given, yield only those Nodes that satisfy the condition. If nodeclass is given, it overrides condition and only those descendents of the specified type are yielded.
- Parameters:
condition (Optional[Callable[[Node], bool]]) – Callable that receives a single argument, a descendent Node, and returns whether it should be yielded.
nodeclass (Optional[NodeSubType]) – A Node subclass of the desired yielded descendent Nodes. If not None, condition is ignored.
- Yields:
See also
Notes
Passing
nodeclass=<NodeSubType>
is equivalent to passingcondition=lambda n: isinstance(n, <NodeSubType>)
.Examples
>>> p1, p2, p3, p4 = [nodes.Paragraph().append(nodes.Text()) for _ in range(4)] >>> msc = nodes.Manuscript().append( ... [ ... nodes.Section().append(p1), ... nodes.Section().append([nodes.Subsection().append([p2, p3])]), ... nodes.Section().append(p4), ... ] ... )
Visit every descendent, including self.
>>> for n in msc.traverse(): print(n) Manuscript(parent=None, [Section, Section, Section]) Section(parent=Manuscript, [Paragraph]) Paragraph(parent=Section, [Text]) Text("") Section(parent=Manuscript, [Subsection]) Subsection(parent=Section, [Paragraph, Paragraph]) Paragraph(parent=Subsection, [Text]) Text("") Paragraph(parent=Subsection, [Text]) Text("") Section(parent=Manuscript, [Paragraph]) Paragraph(parent=Section, [Text]) Text("")
Use nodeclass to yield only nodes of a specified type. Note that subclasses are also yielded.
>>> for n in msc.traverse(nodeclass=nodes.Section): print(n) Section(parent=Manuscript, [Paragraph]) Section(parent=Manuscript, [Subsection]) Subsection(parent=Section, [Paragraph, Paragraph]) Section(parent=Manuscript, [Paragraph])
Yield only nodes satisfying an arbitrary condition
>>> msc.children[1].nonum = True >>> for n in msc.traverse(condition=lambda n: n.nonum): print(n) Section(nonum=True, parent=Manuscript, [Subsection])