graphtage.plist

A graphtage.Filetype for parsing, diffing, and rendering Apple plist files.

plist classes

PLIST

class graphtage.plist.PLIST

Bases: Filetype

The Apple PLIST filetype.

__init__()

Initializes the PLIST file type.

By default, PLIST associates itself with the “plist” and “application/x-plist” MIME types.

build_tree(path: str, options: BuildOptions | None = None) TreeNode

Builds an intermediate representation tree from a file of this Filetype.

Parameters:
  • path – Path to the file to parse

  • options – An optional set of options for building the tree

Returns:

The root tree node of the provided file

Return type:

TreeNode

build_tree_handling_errors(path: str, options: BuildOptions | None = None) str | TreeNode

Same as Filetype.build_tree(), but it should return a human-readable error string on failure.

This function should never throw an exception.

Parameters:
  • path – Path to the file to parse

  • options – An optional set of options for building the tree

Returns:

On success, the root tree node, or a string containing the error message on failure.

Return type:

Union[str, TreeNode]

default_instance: Filetype = <graphtage.plist.PLIST object>
get_default_formatter() PLISTFormatter

Returns the default formatter for printing files of this type.

PLISTFormatter

class graphtage.plist.PLISTFormatter(*args, **kwargs)

Bases: GraphtageFormatter

DEFAULT_INSTANCE: Formatter[T] = <graphtage.plist.PLISTFormatter object>

A default instance of this formatter, automatically instantiated by the FormatterChecker metaclass.

__init__()
static __new__(cls, *args, **kwargs) Formatter[T]

Instantiates a new formatter.

This automatically instantiates and populates Formatter.sub_formatters and sets their parent to this new formatter.

get_formatter(item: T) Callable[[Printer, T], Any] | None

Looks up a formatter for the given item using this formatter as a base.

Equivalent to:

get_formatter(item.__class__, base_formatter=self)
is_partial: bool = False
parent: Formatter[T] | None = None

The parent formatter for this formatter instance.

This is automatically populated by Formatter.__new__() and should never be manually modified.

print(printer: Printer, *args, **kwargs)

Prints the given node or edit.

Parameters:
  • printer – The printer to which to write.

  • node_or_edit – The node or edit to print.

  • with_edits – If :keyword:True, print any edits associated with the node.

Note

The protocol for determining how a node or edit should be printed is very complex due to its extensibility. See the Printing Protocol for a detailed description.

print_BoolNode(printer, node: BoolNode)
print_FloatNode(printer: Printer, node: FloatNode)
print_IntegerNode(printer: Printer, node: IntegerNode)
print_LeafNode(printer: Printer, node: LeafNode)
print_PLISTNode(printer: Printer, node: PLISTNode)
print_StringNode(printer: Printer, node: StringNode)
property root: Formatter[T]

Returns the root formatter.

sub_format_types: Sequence[Type[Formatter[T]]] = [<class 'graphtage.plist.PLISTSequenceFormatter'>]

A list of formatter types that should be used as sub-formatters in the Formatting Protocol.

sub_formatters: List[Formatter[T]] = []

The list of instantiated formatters corresponding to Formatter.sub_format_types.

This list is automatically populated by Formatter.__new__() and should never be manually modified.

static write_obj(printer: Printer, obj)

PLISTNode

class graphtage.plist.PLISTNode(root: TreeNode)

Bases: ContainerNode

__init__(root: TreeNode)
add_edit_modifier(modifier: Callable[[TreeNode, TreeNode], Edit | None])
all_children_are_leaves() bool

Tests whether all of the children of this container are leaves.

Equivalent to:

all(c.is_leaf for c in self)
Returns:

True if all children are leaves.

Return type:

bool

calculate_total_size() int

Calculates the size of this node. This is an arbitrary, immutable value that is used to calculate the bounded costs of edits on this node.

Returns:

An arbitrary integer representing the size of this node.

Return type:

int

children() Sequence[TreeNode]

The children of this node.

Equivalent to:

list(self)
copy() T

Creates a deep copy of this node

copy_from(children: Iterable[TreeNode]) T

Constructs a new instance of this tree node from a list of its children

dfs() Iterator[TreeNode]

Performs a depth-first traversal over all of this node’s descendants.

self is always included and yielded first.

This implementation is equivalent to:

stack = [self]
while stack:
    node = stack.pop()
    yield node
    stack.extend(reversed(node.children()))
diff(node: TreeNode) EditedTreeNode | T

Performs a diff against the provided node.

Parameters:

node – The node against which to perform the diff.

Returns:

An edited version of this node with all edits being completed.

Return type:

Union[EditedTreeNode, T]

editable_dict() Dict[str, Any]

Copies self.__dict__, calling TreeNode.editable_dict() on any TreeNode objects therein.

This is equivalent to:

ret = dict(self.__dict__)
if not self.is_leaf:
    for key, value in ret.items():
        if isinstance(value, TreeNode):
            ret[key] = value.make_edited()
return ret

This is used by TreeNode.make_edited().

property edited: bool

Returns whether this node has been edited.

The default implementation returns False, whereas EditedTreeNode.edited() returns True.

edits(node: TreeNode) Edit

Calculates the best edit to transform this node into the provided node.

Parameters:

node – The node to which to transform.

Returns:

The best possible edit.

Return type:

Edit

get_all_edit_contexts(node: TreeNode) Iterator[Tuple[Tuple[TreeNode, ...], Edit]]

Returns an iterator over all edit contexts that will transform this node into the provided node.

Parameters:

node – The node to which to transform this one.

Returns:

An iterator over pairs of paths from node to the edited node, as well as its edit. Note that this iterator will automatically explode any CompoundEdit in the sequence.

Return type:

Iterator[Tuple[Tuple[“TreeNode”, …], Edit]

get_all_edits(node: TreeNode) Iterator[Edit]

Returns an iterator over all edits that will transform this node into the provided node.

Parameters:

node – The node to which to transform this one.

Returns:

An iterator over edits. Note that this iterator will automatically explode any CompoundEdit in the sequence.

Return type:

Iterator[Edit]

property is_leaf: bool

Container nodes are never leaves, even if they have no children.

Returns:

False

Return type:

bool

make_edited() EditedTreeNode | T

Returns a new, copied instance of this node that is also an instance of EditedTreeNode.

This is equivalent to:

return self.__class__.edited_type()(self)
Returns:

A copied version of this node that is also an instance of EditedTreeNode and thereby mutable.

Return type:

Union[EditedTreeNode, T]

property parent: TreeNode | None

The parent node of this node, or None if it has no parent.

The setter for this property should only be called by the parent node setting itself as the parent of its child.

ContainerNode subclasses automatically set this property for all of their children. However, if you define a subclass of TreeNode does not extend off of ContainerNode and for which len(self.children()) > 0, then each child’s parent must be set.

print(printer: Printer)

Prints this node.

print_parent_context(printer: Printer, for_child: TreeNode)

Prints the context for the given child node.

For example, if this node represents a list and the child is the element at index 3, then “[3]” might be printed.

The child is expected to be one of this node’s children, but this is not validated.

The default implementation prints nothing.

to_obj()

Returns a pure Python representation of this node.

For example, a node representing a list, like graphtage.ListNode, should return a Python list. A node representing a mapping, like graphtage.MappingNode, should return a Python dict. Container nodes should recursively call TreeNode.to_obj() on all of their children.

This is used solely for the providing objects to operate on in the commandline expressions evaluation, for options like –match-if and –match-unless.

property total_size: int

The size of this node.

This is an arbitrary, immutable value that is used to calculate the bounded costs of edits on this node.

The first time this property is called, its value will be set and memoized by calling TreeNode.calculate_total_size().

Returns:

An arbitrary integer representing the size of this node.

Return type:

int

PLISTSequenceFormatter

class graphtage.plist.PLISTSequenceFormatter(*args, **kwargs)

Bases: SequenceFormatter

DEFAULT_INSTANCE: Formatter[T] = <graphtage.plist.PLISTSequenceFormatter object>

A default instance of this formatter, automatically instantiated by the FormatterChecker metaclass.

__init__()

Initializes a sequence formatter.

Parameters:
  • start_symbol – The symbol to print at the start of the sequence.

  • end_symbol – The symbol to print at the end of the sequence.

  • delimiter – A delimiter to print between items.

  • delimiter_callback

    A callback for when a delimiter is to be printed. If omitted, this defaults to:

    lambda p: p.write(delimiter)
    

static __new__(cls, *args, **kwargs) Formatter[T]

Instantiates a new formatter.

This automatically instantiates and populates Formatter.sub_formatters and sets their parent to this new formatter.

edit_print(printer: Printer, edit: Edit)

Called when the edit for an item is to be printed.

If the SequenceNode being printed either is not edited or has no edits, then the edit passed to this function will be a Match(child, child, 0).

This implementation simply delegates the print to the Formatting Protocol:

self.print(printer, edit)
get_formatter(item: T) Callable[[Printer, T], Any] | None

Looks up a formatter for the given item using this formatter as a base.

Equivalent to:

get_formatter(item.__class__, base_formatter=self)
is_partial: bool = True

This is a partial formatter; it will not be automatically used in the Formatting Protocol.

item_newline(printer: Printer, is_first: bool = False, is_last: bool = False)

Called before each node is printed.

This is also called one extra time after the last node, if there is at least one node printed.

The default implementation is simply:

printer.newline()
items_indent(printer: Printer) Printer

Returns a Printer context with an indentation.

This is called as:

with self.items_indent(printer) as p:

immediately after the self.start_symbol is printed, but before any of the items have been printed.

This default implementation is equivalent to:

return printer.indent()
parent: Formatter[T] | None = None

The parent formatter for this formatter instance.

This is automatically populated by Formatter.__new__() and should never be manually modified.

print(printer: Printer, node_or_edit: TreeNode | Edit, with_edits: bool = True)

Prints the given node or edit.

Parameters:
  • printer – The printer to which to write.

  • node_or_edit – The node or edit to print.

  • with_edits – If :keyword:True, print any edits associated with the node.

Note

The protocol for determining how a node or edit should be printed is very complex due to its extensibility. See the Printing Protocol for a detailed description.

print_KeyValuePairNode(printer: Printer, node: KeyValuePairNode)
print_ListNode(printer: Printer, *args, **kwargs)
print_MappingNode(printer: Printer, *args, **kwargs)
print_MultiSetNode(printer: Printer, *args, **kwargs)
print_SequenceNode(printer: Printer, node: SequenceNode)

Formats a sequence node.

The protocol for this function is as follows:

  • Print self.start_symbol

  • With the printer returned by self.items_indent:
    • For each edit in the sequence (or just a sequence of graphtage.Match for each child, if the node is not edited):
      • Call self.item_newline(printer, is_first=index == 0)

      • Call self.edit_print(printer, edit)

  • If at least one edit was printed, then call self.item_newline(printer, is_last=True)

  • Print self.start_symbol

property root: Formatter[T]

Returns the root formatter.

sub_format_types: Sequence[Type[Formatter[T]]] = ()

A list of formatter types that should be used as sub-formatters in the Formatting Protocol.

sub_formatters: List[Formatter[T]] = []

The list of instantiated formatters corresponding to Formatter.sub_format_types.

This list is automatically populated by Formatter.__new__() and should never be manually modified.

plist functions

build_tree

graphtage.plist.build_tree(path: str, options: BuildOptions | None = None, *args, **kwargs) PLISTNode

Constructs a PLIST tree from an PLIST file.