#
# Support for XML representation
#
# Copyright (C) 2019 Red Hat, Inc. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA
#
# For more info about DBus specification see:
# https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
#
from xml.etree import ElementTree
from xml.dom import minidom
__all__ = [
"XMLParser",
"XMLGenerator"
]
[docs]class XMLParser(object):
"""Class for parsing XML."""
[docs] @staticmethod
def xml_to_element(xml):
return ElementTree.fromstring(xml)
[docs] @staticmethod
def is_member(member_node):
return member_node.tag in ("method", "signal", "property")
[docs] @staticmethod
def is_interface(member_node):
return member_node.tag == "interface"
[docs] @staticmethod
def is_signal(member_node):
return member_node.tag == "signal"
[docs] @staticmethod
def is_method(member_node):
return member_node.tag == "method"
[docs] @staticmethod
def is_property(member_node):
return member_node.tag == "property"
[docs] @staticmethod
def is_parameter(member_node):
return member_node.tag == "arg"
[docs] @staticmethod
def has_name(node, node_name):
return node.attrib.get("name", "") == node_name
[docs] @staticmethod
def get_name(node):
return node.attrib["name"]
[docs] @staticmethod
def get_type(node):
return node.attrib["type"]
[docs] @staticmethod
def get_access(node):
return node.attrib["access"]
[docs] @staticmethod
def get_direction(node):
return node.attrib["direction"]
[docs] @staticmethod
def get_interfaces_from_node(node_element):
"""Return a dictionary of interfaces defined in a node element."""
interfaces = {}
for element in node_element.iterfind("interface"):
interfaces[element.attrib["name"]] = element
return interfaces
[docs]class XMLGenerator(XMLParser):
"""Class for generating XML."""
[docs] @staticmethod
def element_to_xml(element):
"""Return XML of the element."""
return ElementTree.tostring(
element,
method="xml",
encoding="unicode"
)
[docs] @staticmethod
def prettify_xml(xml):
"""Return pretty printed normalized XML.
Python 3.8 changed the order of the attributes and introduced
the function canonicalize that should be used to normalize XML.
"""
# Remove newlines and extra whitespaces,
xml_line = "".join([line.strip() for line in xml.splitlines()])
# Generate pretty xml.
xml = minidom.parseString(xml_line).toprettyxml(indent=" ")
# Normalize attributes.
canonicalize = getattr(
ElementTree, "canonicalize", lambda xml, *args, **kwargs: xml
)
return canonicalize(xml, with_comments=True)
[docs] @staticmethod
def add_child(parent_element, child_element):
"""Append the child element to the parent element."""
parent_element.append(child_element)
[docs] @staticmethod
def create_node():
"""Create a node element called node."""
return ElementTree.Element("node")
[docs] @staticmethod
def create_interface(name):
"""Create an interface element."""
return ElementTree.Element("interface", {"name": name})
[docs] @staticmethod
def create_signal(name):
"""Create a signal element."""
return ElementTree.Element("signal", {"name": name})
[docs] @staticmethod
def create_method(name):
"""Create a method element."""
return ElementTree.Element("method", {"name": name})
[docs] @staticmethod
def create_parameter(name, param_type, direction):
"""Create a parameter element."""
tag = "arg"
attr = {
"name": name,
"type": param_type,
"direction": direction
}
return ElementTree.Element(tag, attr)
[docs] @staticmethod
def create_property(name, property_type, access):
"""Create a property element."""
tag = "property"
attr = {
"name": name,
"type": property_type,
"access": access
}
return ElementTree.Element(tag, attr)