1*cda5da8dSAndroid Build Coastguard Worker# 2*cda5da8dSAndroid Build Coastguard Worker# ElementTree 3*cda5da8dSAndroid Build Coastguard Worker# $Id: ElementInclude.py 3375 2008-02-13 08:05:08Z fredrik $ 4*cda5da8dSAndroid Build Coastguard Worker# 5*cda5da8dSAndroid Build Coastguard Worker# limited xinclude support for element trees 6*cda5da8dSAndroid Build Coastguard Worker# 7*cda5da8dSAndroid Build Coastguard Worker# history: 8*cda5da8dSAndroid Build Coastguard Worker# 2003-08-15 fl created 9*cda5da8dSAndroid Build Coastguard Worker# 2003-11-14 fl fixed default loader 10*cda5da8dSAndroid Build Coastguard Worker# 11*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. 12*cda5da8dSAndroid Build Coastguard Worker# 13*cda5da8dSAndroid Build Coastguard Worker# [email protected] 14*cda5da8dSAndroid Build Coastguard Worker# http://www.pythonware.com 15*cda5da8dSAndroid Build Coastguard Worker# 16*cda5da8dSAndroid Build Coastguard Worker# -------------------------------------------------------------------- 17*cda5da8dSAndroid Build Coastguard Worker# The ElementTree toolkit is 18*cda5da8dSAndroid Build Coastguard Worker# 19*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 1999-2008 by Fredrik Lundh 20*cda5da8dSAndroid Build Coastguard Worker# 21*cda5da8dSAndroid Build Coastguard Worker# By obtaining, using, and/or copying this software and/or its 22*cda5da8dSAndroid Build Coastguard Worker# associated documentation, you agree that you have read, understood, 23*cda5da8dSAndroid Build Coastguard Worker# and will comply with the following terms and conditions: 24*cda5da8dSAndroid Build Coastguard Worker# 25*cda5da8dSAndroid Build Coastguard Worker# Permission to use, copy, modify, and distribute this software and 26*cda5da8dSAndroid Build Coastguard Worker# its associated documentation for any purpose and without fee is 27*cda5da8dSAndroid Build Coastguard Worker# hereby granted, provided that the above copyright notice appears in 28*cda5da8dSAndroid Build Coastguard Worker# all copies, and that both that copyright notice and this permission 29*cda5da8dSAndroid Build Coastguard Worker# notice appear in supporting documentation, and that the name of 30*cda5da8dSAndroid Build Coastguard Worker# Secret Labs AB or the author not be used in advertising or publicity 31*cda5da8dSAndroid Build Coastguard Worker# pertaining to distribution of the software without specific, written 32*cda5da8dSAndroid Build Coastguard Worker# prior permission. 33*cda5da8dSAndroid Build Coastguard Worker# 34*cda5da8dSAndroid Build Coastguard Worker# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 35*cda5da8dSAndroid Build Coastguard Worker# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- 36*cda5da8dSAndroid Build Coastguard Worker# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR 37*cda5da8dSAndroid Build Coastguard Worker# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 38*cda5da8dSAndroid Build Coastguard Worker# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 39*cda5da8dSAndroid Build Coastguard Worker# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 40*cda5da8dSAndroid Build Coastguard Worker# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 41*cda5da8dSAndroid Build Coastguard Worker# OF THIS SOFTWARE. 42*cda5da8dSAndroid Build Coastguard Worker# -------------------------------------------------------------------- 43*cda5da8dSAndroid Build Coastguard Worker 44*cda5da8dSAndroid Build Coastguard Worker# Licensed to PSF under a Contributor Agreement. 45*cda5da8dSAndroid Build Coastguard Worker# See https://www.python.org/psf/license for licensing details. 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker## 48*cda5da8dSAndroid Build Coastguard Worker# Limited XInclude support for the ElementTree package. 49*cda5da8dSAndroid Build Coastguard Worker## 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Workerimport copy 52*cda5da8dSAndroid Build Coastguard Workerfrom . import ElementTree 53*cda5da8dSAndroid Build Coastguard Workerfrom urllib.parse import urljoin 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard WorkerXINCLUDE = "{http://www.w3.org/2001/XInclude}" 56*cda5da8dSAndroid Build Coastguard Worker 57*cda5da8dSAndroid Build Coastguard WorkerXINCLUDE_INCLUDE = XINCLUDE + "include" 58*cda5da8dSAndroid Build Coastguard WorkerXINCLUDE_FALLBACK = XINCLUDE + "fallback" 59*cda5da8dSAndroid Build Coastguard Worker 60*cda5da8dSAndroid Build Coastguard Worker# For security reasons, the inclusion depth is limited to this read-only value by default. 61*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_MAX_INCLUSION_DEPTH = 6 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker## 65*cda5da8dSAndroid Build Coastguard Worker# Fatal include error. 66*cda5da8dSAndroid Build Coastguard Worker 67*cda5da8dSAndroid Build Coastguard Workerclass FatalIncludeError(SyntaxError): 68*cda5da8dSAndroid Build Coastguard Worker pass 69*cda5da8dSAndroid Build Coastguard Worker 70*cda5da8dSAndroid Build Coastguard Worker 71*cda5da8dSAndroid Build Coastguard Workerclass LimitedRecursiveIncludeError(FatalIncludeError): 72*cda5da8dSAndroid Build Coastguard Worker pass 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Worker## 76*cda5da8dSAndroid Build Coastguard Worker# Default loader. This loader reads an included resource from disk. 77*cda5da8dSAndroid Build Coastguard Worker# 78*cda5da8dSAndroid Build Coastguard Worker# @param href Resource reference. 79*cda5da8dSAndroid Build Coastguard Worker# @param parse Parse mode. Either "xml" or "text". 80*cda5da8dSAndroid Build Coastguard Worker# @param encoding Optional text encoding (UTF-8 by default for "text"). 81*cda5da8dSAndroid Build Coastguard Worker# @return The expanded resource. If the parse mode is "xml", this 82*cda5da8dSAndroid Build Coastguard Worker# is an ElementTree instance. If the parse mode is "text", this 83*cda5da8dSAndroid Build Coastguard Worker# is a Unicode string. If the loader fails, it can return None 84*cda5da8dSAndroid Build Coastguard Worker# or raise an OSError exception. 85*cda5da8dSAndroid Build Coastguard Worker# @throws OSError If the loader fails to load the resource. 86*cda5da8dSAndroid Build Coastguard Worker 87*cda5da8dSAndroid Build Coastguard Workerdef default_loader(href, parse, encoding=None): 88*cda5da8dSAndroid Build Coastguard Worker if parse == "xml": 89*cda5da8dSAndroid Build Coastguard Worker with open(href, 'rb') as file: 90*cda5da8dSAndroid Build Coastguard Worker data = ElementTree.parse(file).getroot() 91*cda5da8dSAndroid Build Coastguard Worker else: 92*cda5da8dSAndroid Build Coastguard Worker if not encoding: 93*cda5da8dSAndroid Build Coastguard Worker encoding = 'UTF-8' 94*cda5da8dSAndroid Build Coastguard Worker with open(href, 'r', encoding=encoding) as file: 95*cda5da8dSAndroid Build Coastguard Worker data = file.read() 96*cda5da8dSAndroid Build Coastguard Worker return data 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker## 99*cda5da8dSAndroid Build Coastguard Worker# Expand XInclude directives. 100*cda5da8dSAndroid Build Coastguard Worker# 101*cda5da8dSAndroid Build Coastguard Worker# @param elem Root element. 102*cda5da8dSAndroid Build Coastguard Worker# @param loader Optional resource loader. If omitted, it defaults 103*cda5da8dSAndroid Build Coastguard Worker# to {@link default_loader}. If given, it should be a callable 104*cda5da8dSAndroid Build Coastguard Worker# that implements the same interface as <b>default_loader</b>. 105*cda5da8dSAndroid Build Coastguard Worker# @param base_url The base URL of the original file, to resolve 106*cda5da8dSAndroid Build Coastguard Worker# relative include file references. 107*cda5da8dSAndroid Build Coastguard Worker# @param max_depth The maximum number of recursive inclusions. 108*cda5da8dSAndroid Build Coastguard Worker# Limited to reduce the risk of malicious content explosion. 109*cda5da8dSAndroid Build Coastguard Worker# Pass a negative value to disable the limitation. 110*cda5da8dSAndroid Build Coastguard Worker# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded. 111*cda5da8dSAndroid Build Coastguard Worker# @throws FatalIncludeError If the function fails to include a given 112*cda5da8dSAndroid Build Coastguard Worker# resource, or if the tree contains malformed XInclude elements. 113*cda5da8dSAndroid Build Coastguard Worker# @throws IOError If the function fails to load a given resource. 114*cda5da8dSAndroid Build Coastguard Worker# @returns the node or its replacement if it was an XInclude node 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Workerdef include(elem, loader=None, base_url=None, 117*cda5da8dSAndroid Build Coastguard Worker max_depth=DEFAULT_MAX_INCLUSION_DEPTH): 118*cda5da8dSAndroid Build Coastguard Worker if max_depth is None: 119*cda5da8dSAndroid Build Coastguard Worker max_depth = -1 120*cda5da8dSAndroid Build Coastguard Worker elif max_depth < 0: 121*cda5da8dSAndroid Build Coastguard Worker raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth) 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Worker if hasattr(elem, 'getroot'): 124*cda5da8dSAndroid Build Coastguard Worker elem = elem.getroot() 125*cda5da8dSAndroid Build Coastguard Worker if loader is None: 126*cda5da8dSAndroid Build Coastguard Worker loader = default_loader 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker _include(elem, loader, base_url, max_depth, set()) 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Workerdef _include(elem, loader, base_url, max_depth, _parent_hrefs): 132*cda5da8dSAndroid Build Coastguard Worker # look for xinclude elements 133*cda5da8dSAndroid Build Coastguard Worker i = 0 134*cda5da8dSAndroid Build Coastguard Worker while i < len(elem): 135*cda5da8dSAndroid Build Coastguard Worker e = elem[i] 136*cda5da8dSAndroid Build Coastguard Worker if e.tag == XINCLUDE_INCLUDE: 137*cda5da8dSAndroid Build Coastguard Worker # process xinclude directive 138*cda5da8dSAndroid Build Coastguard Worker href = e.get("href") 139*cda5da8dSAndroid Build Coastguard Worker if base_url: 140*cda5da8dSAndroid Build Coastguard Worker href = urljoin(base_url, href) 141*cda5da8dSAndroid Build Coastguard Worker parse = e.get("parse", "xml") 142*cda5da8dSAndroid Build Coastguard Worker if parse == "xml": 143*cda5da8dSAndroid Build Coastguard Worker if href in _parent_hrefs: 144*cda5da8dSAndroid Build Coastguard Worker raise FatalIncludeError("recursive include of %s" % href) 145*cda5da8dSAndroid Build Coastguard Worker if max_depth == 0: 146*cda5da8dSAndroid Build Coastguard Worker raise LimitedRecursiveIncludeError( 147*cda5da8dSAndroid Build Coastguard Worker "maximum xinclude depth reached when including file %s" % href) 148*cda5da8dSAndroid Build Coastguard Worker _parent_hrefs.add(href) 149*cda5da8dSAndroid Build Coastguard Worker node = loader(href, parse) 150*cda5da8dSAndroid Build Coastguard Worker if node is None: 151*cda5da8dSAndroid Build Coastguard Worker raise FatalIncludeError( 152*cda5da8dSAndroid Build Coastguard Worker "cannot load %r as %r" % (href, parse) 153*cda5da8dSAndroid Build Coastguard Worker ) 154*cda5da8dSAndroid Build Coastguard Worker node = copy.copy(node) # FIXME: this makes little sense with recursive includes 155*cda5da8dSAndroid Build Coastguard Worker _include(node, loader, href, max_depth - 1, _parent_hrefs) 156*cda5da8dSAndroid Build Coastguard Worker _parent_hrefs.remove(href) 157*cda5da8dSAndroid Build Coastguard Worker if e.tail: 158*cda5da8dSAndroid Build Coastguard Worker node.tail = (node.tail or "") + e.tail 159*cda5da8dSAndroid Build Coastguard Worker elem[i] = node 160*cda5da8dSAndroid Build Coastguard Worker elif parse == "text": 161*cda5da8dSAndroid Build Coastguard Worker text = loader(href, parse, e.get("encoding")) 162*cda5da8dSAndroid Build Coastguard Worker if text is None: 163*cda5da8dSAndroid Build Coastguard Worker raise FatalIncludeError( 164*cda5da8dSAndroid Build Coastguard Worker "cannot load %r as %r" % (href, parse) 165*cda5da8dSAndroid Build Coastguard Worker ) 166*cda5da8dSAndroid Build Coastguard Worker if e.tail: 167*cda5da8dSAndroid Build Coastguard Worker text += e.tail 168*cda5da8dSAndroid Build Coastguard Worker if i: 169*cda5da8dSAndroid Build Coastguard Worker node = elem[i-1] 170*cda5da8dSAndroid Build Coastguard Worker node.tail = (node.tail or "") + text 171*cda5da8dSAndroid Build Coastguard Worker else: 172*cda5da8dSAndroid Build Coastguard Worker elem.text = (elem.text or "") + text 173*cda5da8dSAndroid Build Coastguard Worker del elem[i] 174*cda5da8dSAndroid Build Coastguard Worker continue 175*cda5da8dSAndroid Build Coastguard Worker else: 176*cda5da8dSAndroid Build Coastguard Worker raise FatalIncludeError( 177*cda5da8dSAndroid Build Coastguard Worker "unknown parse type in xi:include tag (%r)" % parse 178*cda5da8dSAndroid Build Coastguard Worker ) 179*cda5da8dSAndroid Build Coastguard Worker elif e.tag == XINCLUDE_FALLBACK: 180*cda5da8dSAndroid Build Coastguard Worker raise FatalIncludeError( 181*cda5da8dSAndroid Build Coastguard Worker "xi:fallback tag must be child of xi:include (%r)" % e.tag 182*cda5da8dSAndroid Build Coastguard Worker ) 183*cda5da8dSAndroid Build Coastguard Worker else: 184*cda5da8dSAndroid Build Coastguard Worker _include(e, loader, base_url, max_depth, _parent_hrefs) 185*cda5da8dSAndroid Build Coastguard Worker i += 1 186