17db96d56Sopenharmony_ci#
27db96d56Sopenharmony_ci# ElementTree
37db96d56Sopenharmony_ci# $Id: ElementInclude.py 3375 2008-02-13 08:05:08Z fredrik $
47db96d56Sopenharmony_ci#
57db96d56Sopenharmony_ci# limited xinclude support for element trees
67db96d56Sopenharmony_ci#
77db96d56Sopenharmony_ci# history:
87db96d56Sopenharmony_ci# 2003-08-15 fl   created
97db96d56Sopenharmony_ci# 2003-11-14 fl   fixed default loader
107db96d56Sopenharmony_ci#
117db96d56Sopenharmony_ci# Copyright (c) 2003-2004 by Fredrik Lundh.  All rights reserved.
127db96d56Sopenharmony_ci#
137db96d56Sopenharmony_ci# fredrik@pythonware.com
147db96d56Sopenharmony_ci# http://www.pythonware.com
157db96d56Sopenharmony_ci#
167db96d56Sopenharmony_ci# --------------------------------------------------------------------
177db96d56Sopenharmony_ci# The ElementTree toolkit is
187db96d56Sopenharmony_ci#
197db96d56Sopenharmony_ci# Copyright (c) 1999-2008 by Fredrik Lundh
207db96d56Sopenharmony_ci#
217db96d56Sopenharmony_ci# By obtaining, using, and/or copying this software and/or its
227db96d56Sopenharmony_ci# associated documentation, you agree that you have read, understood,
237db96d56Sopenharmony_ci# and will comply with the following terms and conditions:
247db96d56Sopenharmony_ci#
257db96d56Sopenharmony_ci# Permission to use, copy, modify, and distribute this software and
267db96d56Sopenharmony_ci# its associated documentation for any purpose and without fee is
277db96d56Sopenharmony_ci# hereby granted, provided that the above copyright notice appears in
287db96d56Sopenharmony_ci# all copies, and that both that copyright notice and this permission
297db96d56Sopenharmony_ci# notice appear in supporting documentation, and that the name of
307db96d56Sopenharmony_ci# Secret Labs AB or the author not be used in advertising or publicity
317db96d56Sopenharmony_ci# pertaining to distribution of the software without specific, written
327db96d56Sopenharmony_ci# prior permission.
337db96d56Sopenharmony_ci#
347db96d56Sopenharmony_ci# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
357db96d56Sopenharmony_ci# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
367db96d56Sopenharmony_ci# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
377db96d56Sopenharmony_ci# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
387db96d56Sopenharmony_ci# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
397db96d56Sopenharmony_ci# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
407db96d56Sopenharmony_ci# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
417db96d56Sopenharmony_ci# OF THIS SOFTWARE.
427db96d56Sopenharmony_ci# --------------------------------------------------------------------
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
457db96d56Sopenharmony_ci# See https://www.python.org/psf/license for licensing details.
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci##
487db96d56Sopenharmony_ci# Limited XInclude support for the ElementTree package.
497db96d56Sopenharmony_ci##
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ciimport copy
527db96d56Sopenharmony_cifrom . import ElementTree
537db96d56Sopenharmony_cifrom urllib.parse import urljoin
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ciXINCLUDE = "{http://www.w3.org/2001/XInclude}"
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ciXINCLUDE_INCLUDE = XINCLUDE + "include"
587db96d56Sopenharmony_ciXINCLUDE_FALLBACK = XINCLUDE + "fallback"
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_ci# For security reasons, the inclusion depth is limited to this read-only value by default.
617db96d56Sopenharmony_ciDEFAULT_MAX_INCLUSION_DEPTH = 6
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ci##
657db96d56Sopenharmony_ci# Fatal include error.
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ciclass FatalIncludeError(SyntaxError):
687db96d56Sopenharmony_ci    pass
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_ciclass LimitedRecursiveIncludeError(FatalIncludeError):
727db96d56Sopenharmony_ci    pass
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ci##
767db96d56Sopenharmony_ci# Default loader.  This loader reads an included resource from disk.
777db96d56Sopenharmony_ci#
787db96d56Sopenharmony_ci# @param href Resource reference.
797db96d56Sopenharmony_ci# @param parse Parse mode.  Either "xml" or "text".
807db96d56Sopenharmony_ci# @param encoding Optional text encoding (UTF-8 by default for "text").
817db96d56Sopenharmony_ci# @return The expanded resource.  If the parse mode is "xml", this
827db96d56Sopenharmony_ci#    is an ElementTree instance.  If the parse mode is "text", this
837db96d56Sopenharmony_ci#    is a Unicode string.  If the loader fails, it can return None
847db96d56Sopenharmony_ci#    or raise an OSError exception.
857db96d56Sopenharmony_ci# @throws OSError If the loader fails to load the resource.
867db96d56Sopenharmony_ci
877db96d56Sopenharmony_cidef default_loader(href, parse, encoding=None):
887db96d56Sopenharmony_ci    if parse == "xml":
897db96d56Sopenharmony_ci        with open(href, 'rb') as file:
907db96d56Sopenharmony_ci            data = ElementTree.parse(file).getroot()
917db96d56Sopenharmony_ci    else:
927db96d56Sopenharmony_ci        if not encoding:
937db96d56Sopenharmony_ci            encoding = 'UTF-8'
947db96d56Sopenharmony_ci        with open(href, 'r', encoding=encoding) as file:
957db96d56Sopenharmony_ci            data = file.read()
967db96d56Sopenharmony_ci    return data
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_ci##
997db96d56Sopenharmony_ci# Expand XInclude directives.
1007db96d56Sopenharmony_ci#
1017db96d56Sopenharmony_ci# @param elem Root element.
1027db96d56Sopenharmony_ci# @param loader Optional resource loader.  If omitted, it defaults
1037db96d56Sopenharmony_ci#     to {@link default_loader}.  If given, it should be a callable
1047db96d56Sopenharmony_ci#     that implements the same interface as <b>default_loader</b>.
1057db96d56Sopenharmony_ci# @param base_url The base URL of the original file, to resolve
1067db96d56Sopenharmony_ci#     relative include file references.
1077db96d56Sopenharmony_ci# @param max_depth The maximum number of recursive inclusions.
1087db96d56Sopenharmony_ci#     Limited to reduce the risk of malicious content explosion.
1097db96d56Sopenharmony_ci#     Pass a negative value to disable the limitation.
1107db96d56Sopenharmony_ci# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded.
1117db96d56Sopenharmony_ci# @throws FatalIncludeError If the function fails to include a given
1127db96d56Sopenharmony_ci#     resource, or if the tree contains malformed XInclude elements.
1137db96d56Sopenharmony_ci# @throws IOError If the function fails to load a given resource.
1147db96d56Sopenharmony_ci# @returns the node or its replacement if it was an XInclude node
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_cidef include(elem, loader=None, base_url=None,
1177db96d56Sopenharmony_ci            max_depth=DEFAULT_MAX_INCLUSION_DEPTH):
1187db96d56Sopenharmony_ci    if max_depth is None:
1197db96d56Sopenharmony_ci        max_depth = -1
1207db96d56Sopenharmony_ci    elif max_depth < 0:
1217db96d56Sopenharmony_ci        raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth)
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    if hasattr(elem, 'getroot'):
1247db96d56Sopenharmony_ci        elem = elem.getroot()
1257db96d56Sopenharmony_ci    if loader is None:
1267db96d56Sopenharmony_ci        loader = default_loader
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci    _include(elem, loader, base_url, max_depth, set())
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci
1317db96d56Sopenharmony_cidef _include(elem, loader, base_url, max_depth, _parent_hrefs):
1327db96d56Sopenharmony_ci    # look for xinclude elements
1337db96d56Sopenharmony_ci    i = 0
1347db96d56Sopenharmony_ci    while i < len(elem):
1357db96d56Sopenharmony_ci        e = elem[i]
1367db96d56Sopenharmony_ci        if e.tag == XINCLUDE_INCLUDE:
1377db96d56Sopenharmony_ci            # process xinclude directive
1387db96d56Sopenharmony_ci            href = e.get("href")
1397db96d56Sopenharmony_ci            if base_url:
1407db96d56Sopenharmony_ci                href = urljoin(base_url, href)
1417db96d56Sopenharmony_ci            parse = e.get("parse", "xml")
1427db96d56Sopenharmony_ci            if parse == "xml":
1437db96d56Sopenharmony_ci                if href in _parent_hrefs:
1447db96d56Sopenharmony_ci                    raise FatalIncludeError("recursive include of %s" % href)
1457db96d56Sopenharmony_ci                if max_depth == 0:
1467db96d56Sopenharmony_ci                    raise LimitedRecursiveIncludeError(
1477db96d56Sopenharmony_ci                        "maximum xinclude depth reached when including file %s" % href)
1487db96d56Sopenharmony_ci                _parent_hrefs.add(href)
1497db96d56Sopenharmony_ci                node = loader(href, parse)
1507db96d56Sopenharmony_ci                if node is None:
1517db96d56Sopenharmony_ci                    raise FatalIncludeError(
1527db96d56Sopenharmony_ci                        "cannot load %r as %r" % (href, parse)
1537db96d56Sopenharmony_ci                        )
1547db96d56Sopenharmony_ci                node = copy.copy(node)  # FIXME: this makes little sense with recursive includes
1557db96d56Sopenharmony_ci                _include(node, loader, href, max_depth - 1, _parent_hrefs)
1567db96d56Sopenharmony_ci                _parent_hrefs.remove(href)
1577db96d56Sopenharmony_ci                if e.tail:
1587db96d56Sopenharmony_ci                    node.tail = (node.tail or "") + e.tail
1597db96d56Sopenharmony_ci                elem[i] = node
1607db96d56Sopenharmony_ci            elif parse == "text":
1617db96d56Sopenharmony_ci                text = loader(href, parse, e.get("encoding"))
1627db96d56Sopenharmony_ci                if text is None:
1637db96d56Sopenharmony_ci                    raise FatalIncludeError(
1647db96d56Sopenharmony_ci                        "cannot load %r as %r" % (href, parse)
1657db96d56Sopenharmony_ci                        )
1667db96d56Sopenharmony_ci                if e.tail:
1677db96d56Sopenharmony_ci                    text += e.tail
1687db96d56Sopenharmony_ci                if i:
1697db96d56Sopenharmony_ci                    node = elem[i-1]
1707db96d56Sopenharmony_ci                    node.tail = (node.tail or "") + text
1717db96d56Sopenharmony_ci                else:
1727db96d56Sopenharmony_ci                    elem.text = (elem.text or "") + text
1737db96d56Sopenharmony_ci                del elem[i]
1747db96d56Sopenharmony_ci                continue
1757db96d56Sopenharmony_ci            else:
1767db96d56Sopenharmony_ci                raise FatalIncludeError(
1777db96d56Sopenharmony_ci                    "unknown parse type in xi:include tag (%r)" % parse
1787db96d56Sopenharmony_ci                )
1797db96d56Sopenharmony_ci        elif e.tag == XINCLUDE_FALLBACK:
1807db96d56Sopenharmony_ci            raise FatalIncludeError(
1817db96d56Sopenharmony_ci                "xi:fallback tag must be child of xi:include (%r)" % e.tag
1827db96d56Sopenharmony_ci                )
1837db96d56Sopenharmony_ci        else:
1847db96d56Sopenharmony_ci            _include(e, loader, base_url, max_depth, _parent_hrefs)
1857db96d56Sopenharmony_ci        i += 1
186