# 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
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