1 # 2 # ElementTree 3 # $Id: ElementInclude.py 3375 2008-02-13 08:05:08Z fredrik $ 4 # 5 # limited xinclude support for element trees 6 # 7 # history: 8 # 2003-08-15 fl created 9 # 2003-11-14 fl fixed default loader 10 # 11 # Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. 12 # 13 # fredrik (at] pythonware.com 14 # http://www.pythonware.com 15 # 16 # -------------------------------------------------------------------- 17 # The ElementTree toolkit is 18 # 19 # Copyright (c) 1999-2008 by Fredrik Lundh 20 # 21 # By obtaining, using, and/or copying this software and/or its 22 # associated documentation, you agree that you have read, understood, 23 # and will comply with the following terms and conditions: 24 # 25 # Permission to use, copy, modify, and distribute this software and 26 # its associated documentation for any purpose and without fee is 27 # hereby granted, provided that the above copyright notice appears in 28 # all copies, and that both that copyright notice and this permission 29 # notice appear in supporting documentation, and that the name of 30 # Secret Labs AB or the author not be used in advertising or publicity 31 # pertaining to distribution of the software without specific, written 32 # prior permission. 33 # 34 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 35 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- 36 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR 37 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 38 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 39 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 40 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 41 # OF THIS SOFTWARE. 42 # -------------------------------------------------------------------- 43 44 # Licensed to PSF under a Contributor Agreement. 45 # See http://www.python.org/psf/license for licensing details. 46 47 ## 48 # Limited XInclude support for the ElementTree package. 49 ## 50 51 import copy 52 from . import ElementTree 53 54 XINCLUDE = "{http://www.w3.org/2001/XInclude}" 55 56 XINCLUDE_INCLUDE = XINCLUDE + "include" 57 XINCLUDE_FALLBACK = XINCLUDE + "fallback" 58 59 ## 60 # Fatal include error. 61 62 class FatalIncludeError(SyntaxError): 63 pass 64 65 ## 66 # Default loader. This loader reads an included resource from disk. 67 # 68 # @param href Resource reference. 69 # @param parse Parse mode. Either "xml" or "text". 70 # @param encoding Optional text encoding. 71 # @return The expanded resource. If the parse mode is "xml", this 72 # is an ElementTree instance. If the parse mode is "text", this 73 # is a Unicode string. If the loader fails, it can return None 74 # or raise an IOError exception. 75 # @throws IOError If the loader fails to load the resource. 76 77 def default_loader(href, parse, encoding=None): 78 with open(href) as file: 79 if parse == "xml": 80 data = ElementTree.parse(file).getroot() 81 else: 82 data = file.read() 83 if encoding: 84 data = data.decode(encoding) 85 return data 86 87 ## 88 # Expand XInclude directives. 89 # 90 # @param elem Root element. 91 # @param loader Optional resource loader. If omitted, it defaults 92 # to {@link default_loader}. If given, it should be a callable 93 # that implements the same interface as <b>default_loader</b>. 94 # @throws FatalIncludeError If the function fails to include a given 95 # resource, or if the tree contains malformed XInclude elements. 96 # @throws IOError If the function fails to load a given resource. 97 98 def include(elem, loader=None): 99 if loader is None: 100 loader = default_loader 101 # look for xinclude elements 102 i = 0 103 while i < len(elem): 104 e = elem[i] 105 if e.tag == XINCLUDE_INCLUDE: 106 # process xinclude directive 107 href = e.get("href") 108 parse = e.get("parse", "xml") 109 if parse == "xml": 110 node = loader(href, parse) 111 if node is None: 112 raise FatalIncludeError( 113 "cannot load %r as %r" % (href, parse) 114 ) 115 node = copy.copy(node) 116 if e.tail: 117 node.tail = (node.tail or "") + e.tail 118 elem[i] = node 119 elif parse == "text": 120 text = loader(href, parse, e.get("encoding")) 121 if text is None: 122 raise FatalIncludeError( 123 "cannot load %r as %r" % (href, parse) 124 ) 125 if i: 126 node = elem[i-1] 127 node.tail = (node.tail or "") + text + (e.tail or "") 128 else: 129 elem.text = (elem.text or "") + text + (e.tail or "") 130 del elem[i] 131 continue 132 else: 133 raise FatalIncludeError( 134 "unknown parse type in xi:include tag (%r)" % parse 135 ) 136 elif e.tag == XINCLUDE_FALLBACK: 137 raise FatalIncludeError( 138 "xi:fallback tag must be child of xi:include (%r)" % e.tag 139 ) 140 else: 141 include(e, loader) 142 i = i + 1 143