Home | History | Annotate | Download | only in etree
      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     file = open(href)
     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     file.close()
     86     return data
     87 
     88 ##
     89 # Expand XInclude directives.
     90 #
     91 # @param elem Root element.
     92 # @param loader Optional resource loader.  If omitted, it defaults
     93 #     to {@link default_loader}.  If given, it should be a callable
     94 #     that implements the same interface as <b>default_loader</b>.
     95 # @throws FatalIncludeError If the function fails to include a given
     96 #     resource, or if the tree contains malformed XInclude elements.
     97 # @throws IOError If the function fails to load a given resource.
     98 
     99 def include(elem, loader=None):
    100     if loader is None:
    101         loader = default_loader
    102     # look for xinclude elements
    103     i = 0
    104     while i < len(elem):
    105         e = elem[i]
    106         if e.tag == XINCLUDE_INCLUDE:
    107             # process xinclude directive
    108             href = e.get("href")
    109             parse = e.get("parse", "xml")
    110             if parse == "xml":
    111                 node = loader(href, parse)
    112                 if node is None:
    113                     raise FatalIncludeError(
    114                         "cannot load %r as %r" % (href, parse)
    115                         )
    116                 node = copy.copy(node)
    117                 if e.tail:
    118                     node.tail = (node.tail or "") + e.tail
    119                 elem[i] = node
    120             elif parse == "text":
    121                 text = loader(href, parse, e.get("encoding"))
    122                 if text is None:
    123                     raise FatalIncludeError(
    124                         "cannot load %r as %r" % (href, parse)
    125                         )
    126                 if i:
    127                     node = elem[i-1]
    128                     node.tail = (node.tail or "") + text + (e.tail or "")
    129                 else:
    130                     elem.text = (elem.text or "") + text + (e.tail or "")
    131                 del elem[i]
    132                 continue
    133             else:
    134                 raise FatalIncludeError(
    135                     "unknown parse type in xi:include tag (%r)" % parse
    136                 )
    137         elif e.tag == XINCLUDE_FALLBACK:
    138             raise FatalIncludeError(
    139                 "xi:fallback tag must be child of xi:include (%r)" % e.tag
    140                 )
    141         else:
    142             include(e, loader)
    143         i = i + 1
    144