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