mirror of
https://github.com/openSUSE/osc.git
synced 2024-11-10 22:56:15 +01:00
_private.api: Rewrite find_node() and find_nodes() to use a simplified xpath notation
This commit is contained in:
parent
e4723f7f74
commit
e15c530fb2
@ -64,7 +64,40 @@ def post(apiurl, path, query=None):
|
||||
return root
|
||||
|
||||
|
||||
def find_nodes(root, root_name, node_name):
|
||||
def _to_xpath(*args):
|
||||
"""
|
||||
Convert strings and dictionaries to xpath:
|
||||
string gets translated to a node name
|
||||
dictionary gets translated to [@key='value'] predicate
|
||||
|
||||
All values are properly escaped.
|
||||
|
||||
Examples:
|
||||
args: ["directory", "entry", {"name": "osc"}]
|
||||
result: "directory/entry[@name='osc']"
|
||||
|
||||
args: ["attributes", "attribute", {"namespace": "OBS", "name": "BranchSkipRepositories"}, "value"]
|
||||
result: "attributes/attribute[@namespace='OBS'][@name='BranchSkipRepositories']/value"
|
||||
"""
|
||||
xpath = ""
|
||||
for arg in args:
|
||||
if isinstance(arg, str):
|
||||
arg = xml.sax.saxutils.escape(arg)
|
||||
xpath += f"/{arg}"
|
||||
elif isinstance(arg, dict):
|
||||
for key, value in arg.items():
|
||||
key = xml.sax.saxutils.escape(key)
|
||||
value = xml.sax.saxutils.escape(value)
|
||||
xpath += f"[@{key}='{value}']"
|
||||
else:
|
||||
raise TypeError(f"Argument '{arg}' has invalid type '{type(arg).__name__}'. Expected types: str, dict")
|
||||
|
||||
# strip the leading slash because we're making a relative search
|
||||
xpath = xpath.lstrip("/")
|
||||
return xpath
|
||||
|
||||
|
||||
def find_nodes(root, root_name, *args):
|
||||
"""
|
||||
Find nodes with given `node_name`.
|
||||
Also, verify that the root tag matches the `root_name`.
|
||||
@ -73,16 +106,16 @@ def find_nodes(root, root_name, node_name):
|
||||
:type root: xml.etree.ElementTree.Element
|
||||
:param root_name: Expected (tag) name of the root node.
|
||||
:type root_name: str
|
||||
:param node_name: Name of the nodes we're looking for.
|
||||
:type node_name: str
|
||||
:returns: List of nodes that match the given `node_name`.
|
||||
:param *args: Simplified xpath notation: strings are node names, dictionaries translate to [@key='value'] predicates.
|
||||
:type *args: list[str, dict]
|
||||
:returns: List of nodes that match xpath based on the given `args`.
|
||||
:rtype: list(xml.etree.ElementTree.Element)
|
||||
"""
|
||||
assert root.tag == root_name
|
||||
return root.findall(node_name)
|
||||
return root.findall(_to_xpath(*args))
|
||||
|
||||
|
||||
def find_node(root, root_name, node_name=None):
|
||||
def find_node(root, root_name, *args):
|
||||
"""
|
||||
Find a single node with given `node_name`.
|
||||
If `node_name` is not specified, the root node is returned.
|
||||
@ -92,17 +125,18 @@ def find_node(root, root_name, node_name=None):
|
||||
:type root: xml.etree.ElementTree.Element
|
||||
:param root_name: Expected (tag) name of the root node.
|
||||
:type root_name: str
|
||||
:param node_name: Name of the nodes we're looking for.
|
||||
:type node_name: str
|
||||
:returns: The node that matches the given `node_name`
|
||||
or the root node if `node_name` is not specified.
|
||||
:param *args: Simplified xpath notation: strings are node names, dictionaries translate to [@key='value'] predicates.
|
||||
:type *args: list[str, dict]
|
||||
:returns: The node that matches xpath based on the given `args`
|
||||
or the root node if `args` are not specified.
|
||||
:rtype: xml.etree.ElementTree.Element
|
||||
"""
|
||||
|
||||
assert root.tag == root_name
|
||||
if node_name:
|
||||
return root.find(node_name)
|
||||
return root
|
||||
if not args:
|
||||
# only verify the root tag
|
||||
return root
|
||||
return root.find(_to_xpath(*args))
|
||||
|
||||
|
||||
def write_xml_node_to_file(node, path, indent=True):
|
||||
|
Loading…
Reference in New Issue
Block a user