The dom.element module

This module defines the Element class.

An Element describes an object and can have child objects. An Element can display a head and optionally a tail. The head is text that is printed before the children (if any). The tail is displayed after the children, and will in most cases be used as a closing delimiter.

An Element can be constructed in two ways: either using the from_origin() class method from tokens (this is done by the LilyPondTransform class), or manually using the normal constructor.

You can specify all child elements in the constructor, so theoretically you can build a whole document in one expression.

To get the textual output of an element and all its child elements, use the write() method. Indented output is created by the write_indented() method.

Whitespace is handled in a smart way: Element subclasses can specify the preferred whitespace before, after and between elements, and elements that have head and tail texts can also specify the preperred whitespace after the head and before the tail. When outputting the text, the whitespace between elements is combined to fulfil all requirements but to prevent double spaces.

When an Element is constructed from tokens using the with_origin() constructor, it is able to write ifself back in the document if modified, using the edit() method.

Element inherits from Node, and thus from list, to build a reliable and easy to navigate tree structure.

class Point(pos, end, text, modified, space_before, space_after)

Bases: tuple

A Point describes a piece of text at a certain position. See Element.points().

end

The end position in the original text. None for newly added nodes.

modified

True if the text has been modified.

pos

The position in the original text. None for newly added nodes.

space_after

The desired whitespace after this text fragment.

space_before

The desired whitespace before this text fragment.

text

A callable returning the text (the write_head or write_tail method of the respective element).

class ElementType(name, bases, namespace)[source]

Bases: type

Metaclass for Element.

This meta class automatically adds an empty __slots__ attribute if it is not defined in the class body, and replaces space defaults with dynamic properties that are settable on a per-instance basis.

class Element(*children, **attrs)[source]

Bases: Node

Base class for all element types.

The Element has no head or tail value.

Child elements can be specified directly as arguments to the constructor. Using keyword arguments you can give other spacing preferences than the default values for space_before, space_after, space_between, space_after_head and space_before_tail.

space_before = ''

whitespace before this element

space_after_head = ''

whitespace before first child

space_between = ''

whitespace between children

space_before_tail = ''

whitespace before tail

space_after = ''

whitespace after this element

copy(with_children=True)[source]

Copy the node, without the origin.

If with_children is True (the default), child nodes are also copied.

copy_with_origin(with_children=True)[source]

Copy the node, with origin if available.

If with_children is True (the default), child nodes are also copied.

copy_origin_from(other, modified=None)[source]

Copy the origin from another element node to ourself.

If modified is True, sets ourself as “modified”, i.e. we will write back changes when requested via edits(). If modified is False, our “modified” flag will be set to to unmodified state. If modified is None (the default); the modified flag will be copied from the other.

Note

The modified flag makes only sense for TextElement types, that have a writable head value. Using this method on other node types can lead to changes going unnoticed.

py_dump(file=None, indent_width=4)[source]

Print out the node to the console in Python syntax.

This can be used to speed up developing code that creates DOM documents. If the head value of every TextElement has a proper repr() value, the code can be directly executed in Python.

For example:

>>> from quickly.lang.latex import Latex
>>> from parce.transform import transform_text
>>> transform_text(Latex.root, r"\begin[opts]{lilypond}music = { c }\end{lilypond}").py_dump()
tex.Document(
    tex.Environment(
        tex.Command('begin',
            tex.Option(
                tex.Text('opts')),
            tex.EnvironmentName('lilypond')),
        lily.Document(
            lily.Assignment(
                lily.Identifier(
                    lily.Symbol('music')),
                lily.EqualSign(),
                lily.MusicList(
                    lily.Note('c')))),
        tex.Command('end',
            tex.EnvironmentName('lilypond'))))
property pos

Return the position of this element.

Only makes sense for elements that have an origin, or one of the descendants has an origin. Possibly an expensive call, when a node tree has been heavily modified already. Returns None if this node and no single descendant of it has an origin.

property end

Return the end position of this element.

Only makes sense for elements that have an origin, or one of the descendants has an origin. Possibly an expensive call, when a node tree has been heavily modified already. Returns None if this node and no single descendant of it has an origin.

find_child(position)[source]

Return the child node touching the position.

If two child nodes touch the position, the one to the right is chosen. Only returns a node that has a pos attribute, i.e. at least one of its descendants has an origin.

find_descendant(position, end=None)[source]

Return the youngest descendant node that contains position.

If two descendant nodes touch the position, the one to the right is chosen. Only returns a node that has a pos attribute, i.e. at least one of its descendants has an origin. If end is specified, stops with the last node that contains the range positionend. Returns None if there is no such node that contains this position.

find_descendants(position, end=None)[source]

Yield the child at position, then the grandchild, etc.

Stops with the last node that really contains the position. Only yields nodes that have a pos attribute, i.e. at least one of its descendants has an origin. If end is specified, stops with the last node that contains the range positionend.

find_descendant_right(position)[source]

Return the first descendant that starts at or to the right of position.

Only returns a node that has a pos attribute, i.e. at least one of its descendants has an origin. Returns None if no node with a pos value is found at or to the right of the position.

find_descendant_left(position)[source]

Return the last descendant that ends at or to the left of position.

Only returns a node that has a pos attribute, i.e. at least one of its descendants has an origin. Returns None if no node with a pos value is found at or to the left of the position.

property head

The head contents.

property tail

The tail contents.

classmethod read_head(head_origin)[source]

Return the value as computed from the specified origin Tokens.

The default implementation concatenates the text from all tokens.

classmethod read_tail(tail_origin)[source]

Return the value as computed from the specified origin Tokens.

The default implementation concatenates the text from all tokens.

write_head()[source]

Return the textual output that represents our head value.

The default implementation just returns the head attribute, assuming it is text.

write_tail()[source]

Return the textual output that represents our tail value.

The default implementation just returns the tail attribute, assuming it is text.

repr_head()[source]

Return a representation for the head.

The default implementation returns None.

repr_tail()[source]

Return a representation for the tail.

The default implementation returns None.

head_point()[source]

Return the Point describing the head text.

Returns None for elements that don’t have a head text.

tail_point()[source]

Return the Point describing the tail text.

Returns None for elements that can’t have a tail text.

points()[source]

Yield Points for this element and all its descendants.

Each point is a Point describing a text piece and the desired whitespace before and after it.

concat_space(node, next_node)[source]

Return the minimum whitespace to apply between these child nodes.

This method is called in the points() method, when calculating whitespace between two adjacent child nodes. By default, the value of the space_between attribute is returned. Reimplement this method to differentiate whitespacing based on the (type or contents of the) nodes.

write()[source]

Return the combined output of this node and its children.

To get indented output, use write_indented() and/or the indent module.

edits(context, start=None, end=None)[source]

Yield three-tuples (pos, end, text) denoting text changes.

The context is a parce Context. If start and/or end are not specified, the edits encompass the full context’s range. All added or modified text fragments will still be written to the document, but no text outside the specified range will be deleted.

edit(document, context=None, start=None, end=None)[source]

Write back the modifications to the original parce document.

Returns the number of changes that are made. If you don’t specify the parce Context context, the document’s root context will be used.

If start and/or end are not specified, the edits encompass the full context’s range. All added or modified text fragments will still be written to the document, but no text outside the specified range will be deleted.

After writing back the modifications to the original document, you should transform a new dom.Document, because some parts need to be rebuilt.

signatures()[source]

Return an iterable of signature tuples.

A signature is a tuple. Every item in the tuple is an Element type, or a tuple of Element types; and is used with build_tree() to see whether an element can be a child of this element.

By default an empty iterable is returned.

add_argument(node)[source]

Called by build_tree() to add a child node as argument.

node is the node to be appended. You can reimplement this method to perform some manipulation before appending it.

child_order()[source]

Return an iterable of tuples with element types.

This is almost the same as signatures() but used when a child node is inserted using add().

By default an empty iterable is returned.

add(node)[source]

Add a node, calling child_order() to get the proper place to insert it.

When the node type matches with one of the types in a child order tuple, it is inserted in that position between the other children. Not all node types need to be present, but at least the order is always respected.

If the proper place can’t be found, the node is appended at the end.

indent_children()[source]

Return True if the children should indent a level, if they appear on a new line.

indent_align_indices()[source]

Yield zero or more child indices that new lines could align with.

This only makes sense for nodes that trigger a new indent level when pretty-printing their contents, in most cases this will be a BlockElement node type.

When, within a BlockElement node, a new line is started, it will by default be indented with, say, two spaces. But when there are already child nodes on the current line, the next line’s indent could be aligned to one of them. This method yields the indices of the nodes, in priority, that may be used to align the indent with. The first one that matches will be used.

indent_override()[source]

Return an indent position that could be used if this node is the first on a new line.

By default None is returned, causing the normal indenting rules to apply.

write_indented(indent_width=2, start_indent=0, max_align_indent=16)[source]

Return the output of this node and its children with indentation added.

See for all the arguments the Indenter class from the indent module.

class HeadElement(*children, **attrs)[source]

Bases: Element

Element that has a fixed head value.

classmethod from_origin(head_origin=(), tail_origin=(), *children, **attrs)[source]

Instantiate an Element from the origin tokens, but don’t keep the tokens.

classmethod with_origin(head_origin=(), tail_origin=(), *children, **attrs)[source]

Instantiate an Element from the origin tokens, and keep the tokens.

This way, this element knows its position in the text source, even if the parce tree changes, or this element changes.

head_point()[source]

Return the Point describing the head text.

head_origin
class BlockElement(*children, **attrs)[source]

Bases: HeadElement

Element that has a fixed head and tail value.

classmethod with_origin(head_origin=(), tail_origin=(), *children, **attrs)[source]

Instantiate an Element from the origin tokens, and keep the tokens.

This way, this element knows its position in the text source, even if the parce tree changes, or this element changes.

indent_children()[source]

Reimplemented to indent children of a BlockElement type by default.

tail_point()[source]

Return the Point describing the tail text.

tail_origin
class TextElement(head, *children, **attrs)[source]

Bases: HeadElement

Element that has a variable/writable head value.

This value must be given to the constructor, and can be modified later.

If you want to, you can implement the check_head() method, which by default returns True, to perform some checking on the head value of this element. This prevents forgetting to set the head value on manual construction, which can lead to unexpected and difficult to debug bugs. This method is not called when an element is copied, constructed from or with an origin, or when the head attribute is modified manually later.

repr_head()[source]

Return a repr value for our head value.

classmethod check_head(head)[source]

Returns whether the proposed head value is valid.

body_equals(other)[source]

Compares the head values, called by Node.equals().

classmethod from_origin(head_origin=(), tail_origin=(), *children, **attrs)[source]

Instantiate an Element from the origin tokens, but don’t keep the tokens.

copy(with_children=True)[source]

Copy the node, without the origin.

If with_children is True (the default), child nodes are also copied.

copy_with_origin(with_children=True)[source]

Copy the node, with origin if available.

If with_children is True (the default), child nodes are also copied.

class MappingElement(head, *children, **attrs)[source]

Bases: TextElement

A TextElement with a fixed set of possible head values.

mapping = {}

The mapping class attribute is a dictionay mapping unique head values to unique output values. Other head values can’t be used, they result in a TypeError.

classmethod check_head(head)[source]

Returns whether the proposed head value is valid.

classmethod from_mapping(text, *children, **attrs)[source]

Convenience constructor to create this element from a text key that’s available in the mapping.

classmethod read_head(origin)[source]

Get the head value from our mapping.

write_head()[source]

Return the text value.

class ToggleElement(head, *children, **attrs)[source]

Bases: MappingElement

A TextElement for a toggled item that has two possible values.

E.g. \break or \noBreak, or \sustainOn and \sustainOff.

The on-value is represented by head value True, the off value by False.

toggle_on = '<on>'
toggle_off = '<off>'
build_tree(nodes, ignore_type=None)[source]

Build a tree of a stream of elements, based on their Element.signatures().

nodes is an iterable of nodes. Consumes all nodes, and make some nodes a child of a preceding node. Yields the resulting nodes. When a node specifies what child element types it can have, and those element types follow indeed, they are added as child element.

If ignore_type is given, it should be an Element type, or a tuple of Element types that are ignored, and added anyway as argument. This can be used to interperse Comment nodes.

Existing children are taken into account.

head_mapping(*element_types)[source]

Return a dictionary mapping (written out) head text to element type.

Makes only sense for HeadElement or MappingElement descendants.