Source code for k1lib.cli.kxml

# AUTOGENERATED FILE! PLEASE DON'T EDIT HERE. EDIT THE SOURCE NOTEBOOKS INSTEAD
"""All tools related to xml file format. Expected to use behind the "kxml"
module name, like this::

    from k1lib.imports import *
    cat("abc.xml") | kxml.node() | kxml.display()
"""
from k1lib import cli; from typing import Iterator
import xml.etree.ElementTree as ET; import copy, xml, k1lib
from typing import List
from k1lib.cli.typehint import *
__all__ = ["node", "maxDepth", "tags", "pretty", "display"]
[docs]class node(cli.BaseCli): # node """Turns lines into a single node. Example:: s = \"\"\" <html> <head> <style></style> </head> <body> <div></div> </body> </html>\"\"\" # returns root node s | kxml.node() # same thing as above, demonstrating you can pipe in list of strings s.split(\"\\n\") | kxml.node() """ # node def _typehint(self, inp): return ET.Element # node
[docs] def __ror__(self, it:Iterator[str]) -> ET.Element: # node return ET.fromstring("".join(it)) # node
def oCatNode(cs, ts, _): # oCatNode c, n = cs # oCatNode if c.text: # oCatNode def inner(fn): # oCatNode with open(fn) as f: return ET.fromstring(f.read()) # oCatNode return [cli.aS(inner).hint(ET.Element)] # oCatNode tOpt.addPass(oCatNode, [cli.cat().__class__, node]) # oCatNode def _maxDepth(node, maxDepth:int, depth:int=0): # _maxDepth if depth >= maxDepth: # _maxDepth while len(node) > 0: del node[0] # _maxDepth for n in node: _maxDepth(n, maxDepth, depth+1) # _maxDepth return node # _maxDepth
[docs]class maxDepth(cli.BaseCli): # maxDepth
[docs] def __init__(self, depth:int=None, copy:bool=True): # maxDepth """Filters out too deep nodes. Example:: # returns root node, but prunes children deeper than the specified depth s | kxml.node() | kxml.maxDepth() :param depth: max depth to include in :param copy: whether to limit the nodes itself, or limit a copy""" # maxDepth self.depth = depth if depth != None else float("inf") # maxDepth self.copy = copy # maxDepth
[docs] def __ror__(self, node:ET.Element) -> ET.Element: # maxDepth if self.copy: node = copy.deepcopy(node) # maxDepth return _maxDepth(node, self.depth) # maxDepth
def _tags(node, tag:str, nested): # _tags if node.tag == tag: yield node # _tags if node.tag != tag or nested: # _tags for n in node: yield from _tags(n, tag, nested) # _tags
[docs]class tags(cli.BaseCli): # tags
[docs] def __init__(self, *tags:List[str], nested=False): # tags """Finds all tags that have a particular name.. Example:: s = \"\"\" <EXPERIMENT_PACKAGE_SET> <EXPERIMENT_PACKAGE> <EXPERIMENT_PACKAGE/> <Pool/> <RUN_SET/> </EXPERIMENT_PACKAGE> <EXPERIMENT_PACKAGE> <Pool/> <RUN_SET/> </EXPERIMENT_PACKAGE> </EXPERIMENT_PACKAGE_SET>\"\"\" # returns a list of "Pool" tags (with 2 elements) that are 2 levels deep s | kxml.node() | kxml.tags("Pool") | toList() # returns list with 2 tags s | kxml.node() | kxml.tags("EXPERIMENT_PACKAGE") # returns list with 3 tags s | kxml.node() | kxml.tags("EXPERIMENT_PACKAGE", nested=True) :param nested: whether to search for "div" tag inside of another "div" tag""" # tags self.tags = tags; self.nested = nested # tags
[docs] def __ror__(self, node:ET.Element) -> Iterator[ET.Element]: # tags return [_tags(node, tag, self.nested) for tag in self.tags] | cli.joinStreams() # tags
def _pretty(node, depth:int=0, indents=[]): # _pretty attr = "".join([f" {k}=\"{v}\"" for k, v in node.attrib.items()]) # _pretty text = (node.text or "").strip("\t \n\r") # _pretty if len(node) == 0: # _pretty if text == "": yield indents[depth] + f"<{node.tag}{attr}/>" # _pretty else: yield indents[depth] + f"<{node.tag}{attr}>{text}</{node.tag}>" # _pretty else: # _pretty yield indents[depth] + f"<{node.tag}{attr}>" # _pretty for n in node: yield from _pretty(n, depth+1, indents) # _pretty yield indents[depth] + f"</{node.tag}>" # _pretty
[docs]class pretty(cli.BaseCli): # pretty
[docs] def __init__(self, indent:str=None): # pretty """Converts the element into a list of xml strings, and make them pretty. Example:: # prints out the element s | kxml.node() | kxml.pretty() | stdout()""" # pretty self.indent = cli.init.patchDefaultIndent(indent) # pretty
[docs] def __ror__(self, it:ET.Element) -> Iterator[str]: # pretty indents = [i*self.indent for i in range(100)] # pretty return _pretty(it, indents=indents) | cli.filt(cli.op().strip() != "") # pretty
[docs]class display(cli.BaseCli): # display
[docs] def __init__(self, depth:int=3, lines:int=20): # display """Convenience method for getting head, make it pretty and print it out. Example:: # prints out the element s | kxml.node() | kxml.display() :param depth: prune tags deeper than the specified depth. Put "None" to not prune at all :param lines: max number of lines to print out. Put "None" if you want to display everything""" # display self.depth = depth; self.lines = lines # display
[docs] def __ror__(self, it:ET.Element, lines=10): # display if self.depth is not None: it = it | maxDepth(self.depth) # display it | pretty() | cli.head(self.lines) | cli.stdout() # display