DefTree API

Element

class deftree.Element(name)

Element class. This class defines the Element interface

add_attribute(name, value)

Creates an Attribute instance with name and value as a child to self.

add_element(name)

Creates an Element instance with name as a child to self.

append(item)

Inserts the item at the end of this element’s internal list of children. Raises TypeError if item is not a Element or Attribute

attributes([name, value])

Iterates over the current element and returns all attributes. Only Attributes. Name and value are optional and used for filters.

clear()

Resets an element. This function removes all children, clears all attributes

copy()

Returns a deep copy of the current Element.

elements([name])

Iterates over the current element and returns all elements. If the optional argument name is not None only Element with a name equal to name is returned.

get_attribute(name[, value])

Returns the first Attribute instance whose name matches name and if value is not None whose value equal value. If no matching attribute is found it returns None.

get_element(name)

Returns the first Element whose name matches name, if none is found returns None.

get_parent()

Returns the parent of the current Element

index(item)

Returns the index of the item in this element, raises ValueError if not found.

insert(index, item)

Inserts the item at the given position in this element. Raises TypeError if item is not a Element or Attribute

iter()

Creates a tree iterator with the current element as the root. The iterator iterates over this element and all elements below it in document (depth first) order. Both Element and Attribute are returned from the iterator.

iter_attributes([name])

Creates a tree iterator with the current element as the root. The iterator iterates over this element and all elements below it, in document (depth first) order. If the optional argument name is not None only Attribute with a name equal to name is returned.

iter_elements([name])

Creates a tree iterator with the current element as the root. The iterator iterates over this element and all elements below it, in document (depth first) order. If the optional argument name is not None only Element with a name equal to name is returned.

remove(child)

Removes child from the element. Compares on instance identity not name. Raises TypeError if child is not a Element or Attribute

set_attribute(name, value)

Sets the first Attribute with name to value.

Attribute

class deftree.Attribute(parent, name, value)

Attribute class. This class defines the Attribute interface.

get_parent()

Returns the parent element of the attribute.

value

A property of the attribute, used to set and get the attributes value

DefTree

class deftree.DefTree

DefTree class. This class represents an entire element hierarchy.

dump()

Writes the the DefTree structure to sys.stdout. This function should be used for debugging only.

from_string(text[, parser])

Parses a Defold document section from a string constant which it returns. parser is an optional parser instance. If not given the standard parser is used. Returns the root of DefTree.

get_document_path()

Returns the path to the parsed document.

get_root()

Returns the root Element

parse(source[, parser])

Parses a Defold document into a DefTree which it returns. source is a file_path. parser is an optional parser instance. If not given the standard parser is used.

write(file_path)

Writes the element tree to a file, as plain text. file_path needs to be a path.

Helpers

deftree.parse(source)

Parses a Defold document into a DefTree which it returns. source is a file_path. parser is an optional parser instance. If not given the standard parser is used.

deftree.from_string(text[, parser])

Parses a Defold document section from a string constant which it returns. parser is an optional parser instance. If not given the standard parser is used. Returns the root of DefTree.

deftree.to_string(element[, parser])

Generates a string representation of the Element, including all children. element is a Element instance.

deftree.dump(element[, parser])

Writes the element tree or element structure to sys.stdout. This function should be used for debugging only. element is either an DefTree, or Element.

deftree.validate(string, path_or_string[, verbose])

Verifies that a document in string format equals a document in path_or_string. If Verbose is True it echoes the result. This function should be used for debugging only. Returns a bool representing the result

Using DefTree

If you are not familiar with Defold files this is how the syntax looks, it is in a Google Protobuf format.

elementname {
  attributename: attributevalue
  position {
    x: 0.0
    y: 0.0
    z: 0.0
  }
  type: TYPE_BOX
  blend_mode: BLEND_MODE_ALPHA
  texture: "atlas/logo"
  id: "logo"
}

Parsing Defold Documents

Parsing from a file is done by calling the parse method

import deftree
tree = deftree.parse(path)  # parse the document into a DefTree
root = tree.get_root()      # returns the root from the tree

Or alternatively we can first create a DefTree instance

tree = deftree.Deftree()
root = tree.parse(path)

We can also parse a document from a string

tree = deftree.from_string(document_as_string)  # parse the document into a DefTree
root = tree.get_root()      # returns the root from the tree

Finding interesting elements

Element has some useful methods that help iterate recursively over all the sub-tree below it (its children, their children, and so on). For example, Element.iter():

for child in root.iter():
    print(child.name)

We can also iterate only elements by calling Element.iter_elements()

for child in root.iter_elements():
    print(child.name)

for child in root.iter_elements("nodes"):  # iter_elements also supports filtering on name
    print(child.name)

Element.get_attribute() finds the first attributes with the given name in that element.

attribute = element.get_attribute("id")

Modifying existing scenes

DefTree provides a simple way to edit Defold documents and write them to files. The DefTree.write() method serves this purpose. Once created, an Element object may be manipulated by directly changing its fields, as well as adding new children (for example with Element.insert()).

E.g. if we want to find all box-nodes in a gui and change its layers.

for element in root.iter_elements("nodes")
    if element.get_attribute("type") == "TYPE_BOX":
        element.set_attribute("layer", 'new_layer')

We can also add new attributes and elements all together.

new_element = root.add_element("layers")
new_element.add_attribute("name", 'new_layer')

DefTree Attributes that are of number types support basic math functions directly

new_element = root.get_element("position")
attribute = new_element.get_attribute("x")
attribute += 10

You can either use the set value if you are getting, or you can use its .value property

attribute = element.get_attribute("layer")
attribute.value = "new_layer"

We will probably then overwrite the file

tree.write(tree.get_document_path())

Design

Here I would like to go over some important details concerning implementation that may help when working with DefTree.

Defold Value vs Python Value

To simplify working with attributes I decided to split how the value looks for Defold and how it looks for python. Not only does this simplify working with attributes it also enables us to do some sanity checking to ensure that we do not set a value that was an int to a float because this would make the file corrupt for the Defold editor.

Defold will always enclose a string within two quotes like “/main/defold.png”. To make it easier for us to work with it DefTree reports this as /main/defold.png, i.e. without the quotes. As an example, let us assume we have a file that looks as follows:

nodes {
  id: "sprite"
  blend_mode: BLEND_MODE_ALPHA
  inherit_alpha: true
}

This enables the user to do this:

tree = root.parse(my_atlas)
root.get_root()

for ele in root.get_element("nodes"):
    node_id = ele.get_attribute("id")
    alpha = ele.get_attribute("inherit_alpha")
    if node_id == "sprite" and alpha:
        ...

in contrast to:

tree = root.parse(my_atlas)
root.get_root()

for ele in root.get_element("nodes"):
    node_id= ele.get_attribute("id")
    alpha = ele.get_attribute("inherit_alpha")
    if node_id== '"sprite"' and alpha == "true":
        ...

The former is a lot more readable and not as error prone, as I see it.

Attribute types

The attribute’s type is decided on creation and follow the logic below:

If the value is of type(bool) or a string equal to “true” or “false” it is considered a bool.

If the value consists of only capital letters and underscore (regex’d against [A-Z_]+) it is considered an enum.

If the value is of type(float) or it looks like a float (regex’d against \d+\.\d+[eE-]+\d+|\d+\.\d+) it is considered a float.

If the value is of type(int) or can be converted with int() it is considered an int.

Else it is considered a string.

Contributing

Bug fixes, feature additions, tests, documentation and more can be contributed via issues and/or pull requests. All contributions are welcome.

Bug fixes, feature additions, etc.

Please send a pull request to the master branch. Please include documentation and tests for new or changed features. Tests or documentation without bug fixes or feature additions are welcome too.

  • Fork the DefTree repository.
  • Create a branch from master.
  • Develop bug fixes, features, tests, etc.
  • Run the test suite.
  • Create a pull request to pull the changes from your branch to the DefTree master.

Guidelines

  • Separate code commits from reformatting commits.
  • Provide tests for any newly added code.
  • Follow PEP8.

Reporting Issues

When reporting issues, please include code that will reproduce the issue. The best reproductions are self-contained scripts with minimal dependencies.

Changelog

1.1.1

Changed

  • Fixed a bug where a negative number would be evaluated as a string

1.1.0

Added

  • Added Element.iter_attributes to iterate over the elements and its children’s elements attributes

Changed

  • Only imports re.compile from re instead of the whole of re
  • The string value of an attribute can now be get with Attribute.string
  • The Attribute.value and the value Attribute() returns should be the same
  • Now reports the python value when calling the __str__ method instead of the defold value
  • is_element and is_attribute are no longer flagged as internal
  • improved type checking when setting attribute values

1.0.2

Changed

  • How DefTree determines if a string is a string, int or float. Fix for bigger numbers with science annotation

1.0.1

Added

  • Added Element.add_element(name)
  • Added Element.add_attribute(name, value)
  • Added Element.set_attribute(name, value)
  • Added Element.elements() - for getting top level elements of Element
  • Added Element.attribute() - for getting top level attribute of Element
  • Exposed deftree.dump and deftree.validate in the documentation
  • Added DefTree.get_document_path() to get the path of the document that was parsed
  • Attribute are now sub classed into different types this to make it easier when editing values as Defold is picky

Changed

  • Element.iter_all() is now Element.iter()
  • Element.iter_find_elements(name) is now Element.iter_elements(name)
  • Changed how attributes reports their value. They should now be easier to work with, without any need add quotationmarks and such.

Removed

  • Removed SubElement() factory, now use element.add_element()
  • Removed Element.iter_attributes()
  • Removed Element.iter_find_attributes()
  • Removed NaiveDefParser as it was obsolete and inferior
  • Removed Example folder

0.2.0

Added

  • Raises ParseError when reading invalid documents

Changed

  • Updated docstrings to be easier to read.
  • Refactored internal usage of a level variable to track how deep the item were in the tree

Removed

  • Removed Element.add(), use Element.append() Element.insert()
  • Removed Element.items(), use Element.iter_all()

0.1.1

Added

  • Licence to github repository
  • Setup files for PyPi to github repository
  • Example usage
  • Unittesting with unittest
  • Coverage exclusion for usage with Coverage.py
  • Using __all__ to define public api, in case of wild import

Changed

  • Elements __setitem__ raises exception on invalid types
  • Elements __next__ implementation was broken
  • serialize() is now a class method

0.1.0

Added

  • First release of DefTree

DefTree 1.1.1

DefTree is a python module for modifying Defold documents. The first implementation was inspired by the xml.ElementTree library.

DefTree reads any Defold document into an object tree hierarchy and follow the these three main concepts

  1. DefTree represents the complete Defold document as a tree.
  2. Element represents a single node or block in this tree.
  3. Attribute represent a name value pair.

Installation

Note

DefTree is only supported by python >= 3.3.0

DefTree is a native python implementation and thus should work under the most common platforms that supports python. The package is distributed in the wheel format and is easily installed with pip.

pip install deftree

Old Versions

Old distributions may be accessed via PyPI.