source: TI12-security/trunk/python/ndg_security_common/ndg/security/common/utils/etree.py @ 5712

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_common/ndg/security/common/utils/etree.py@5712
Revision 5712, 6.9 KB checked in by pjkersha, 10 years ago (diff)

Tidied up XACML package for nose tests.

Line 
1"""ElementTree Utilities package for NDG Security
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "02/04/09"
7__copyright__ = ""
8__license__ = "BSD - see LICENSE file in top-level directory"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = '$Id: $'
11try: # python 2.5
12    from xml.etree import cElementTree, ElementTree
13except ImportError:
14    # if you've installed it yourself it comes this way
15    import cElementTree, ElementTree
16
17import re
18
19# Fred Lundh's customisation for C14N functionality - egg available from
20# http://ndg.nerc.ac.uk/dist site
21c14nWarning = ("Custom ElementC14N package is not installed, canonicalize "
22               "function is disabled")
23try:
24    from elementtree import ElementC14N
25    elementC14nNotInstalled = False
26except ImportError:
27    elementC14nNotInstalled = True
28    import warnings
29    warnings.warn(c14nWarning)
30   
31from cStringIO import StringIO
32
33
34class QName(ElementTree.QName):
35    """Extend ElementTree implementation for improved attribute access support
36    """ 
37    # ElementTree tag is of the form {namespace}localPart.  getNs extracts the
38    # namespace from within the brackets but if not found returns ''
39    getNs = staticmethod(lambda tag: getattr(re.search('(?<=\{).+(?=\})', tag),
40                                             'group', 
41                                             str)())
42                                             
43    getLocalPart = staticmethod(lambda tag: tag.rsplit('}', 1)[-1])
44   
45    def __init__(self, namespaceURI, tag=None, prefix=None):
46        ElementTree.QName.__init__(self, namespaceURI, tag=tag)
47       
48        if tag:
49            self.namespaceURI = namespaceURI
50            self.localPart = tag
51        else:
52            self.namespaceURI = QName.getNs(namespaceURI)
53            self.localPart = QName.getLocalPart(namespaceURI)
54           
55        self.prefix = prefix
56
57    def __eq__(self, qname):
58        """Enable equality check for QName
59        @type qname: ndg.security.common.utils.etree.QName
60        @param qname: Qualified Name to compare with self
61        """
62        if not isinstance(qname, QName):
63            raise TypeError('Expecting %r; got %r' % (QName, type(qname)))
64                           
65        return (self.prefix, self.namespaceURI, self.localPart) == \
66               (qname.prefix, qname.namespaceURI, qname.localPart)
67
68    def __ne__(self, qname):
69        """Enable equality check for QName
70        @type qname: ndg.security.common.utils.etree.QName
71        @param qname: Qualified Name to compare with self
72        """
73        return not self.__eq__(qname)
74               
75    def _getPrefix(self):
76        return self.__prefix
77
78    def _setPrefix(self, value):
79        self.__prefix = value
80   
81    prefix = property(_getPrefix, _setPrefix, None, "Prefix")
82
83    def _getLocalPart(self):
84        return self.__localPart
85   
86    def _setLocalPart(self, value):
87        self.__localPart = value
88       
89    localPart = property(_getLocalPart, _setLocalPart, None, "LocalPart")
90
91    def _getNamespaceURI(self):
92        return self.__namespaceURI
93
94    def _setNamespaceURI(self, value):
95        self.__namespaceURI = value
96 
97    namespaceURI = property(_getNamespaceURI, _setNamespaceURI, None, 
98                            "Namespace URI'")
99
100
101def canonicalize(elem, **kw):
102    '''ElementTree based Canonicalization - See ElementC14N for keyword
103    info.  Also useful for pretty printing XML
104    @type elem: ElementTree.Element
105    @param elem: element to be canonicalized
106    @rtype: basestring
107    @return: canonicalised output
108    '''
109    if elementC14nNotInstalled:
110        raise NotImplementedError(c14nWarning)
111   
112    f = StringIO()
113    ElementC14N.write(ElementC14N.build_scoped_tree(elem), f, **kw)
114    return f.getvalue()
115
116
117def prettyPrint(*arg, **kw):
118    '''Lightweight pretty printing of ElementTree elements'''
119   
120    # Keep track of namespace declarations made so they're not repeated
121    declaredNss = []
122   
123    _prettyPrint = _PrettyPrint(declaredNss)
124    return _prettyPrint(*arg, **kw)
125
126
127class _PrettyPrint(object):
128    def __init__(self, declaredNss):
129        self.declaredNss = declaredNss
130   
131    @staticmethod
132    def estrip(elem):
133        ''' Just want to get rid of unwanted whitespace '''
134        if elem is None:
135            return ''
136        else:
137            # just in case the elem is another simple type - e.g. int -
138            # wrapper it as a string
139            return str(elem).strip()
140       
141    def __call__(self, elem, indent='', html=0, space=' '*4):
142        '''Most of the work done in this wrapped function - wrapped so that
143        state can be maintained for declared namespace declarations during
144        recursive calls using "declaredNss" above''' 
145        strAttribs = []
146        for attr, attrVal in elem.attrib.items():
147            nsDeclaration = ''
148           
149            attrNamespace = QName.getNs(attr)
150            if attrNamespace:
151                nsPrefix = ElementTree._namespace_map.get(attrNamespace)
152                if nsPrefix is None:
153                    raise KeyError('prettyPrint: missing namespace "%s" for ' 
154                                   'ElementTree._namespace_map'%attrNamespace)
155               
156                attr = "%s:%s" % (nsPrefix, QName.getLocalPart(attr))
157               
158                if attrNamespace not in self.declaredNss:
159                    nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix,attrNamespace)
160                    self.declaredNss.append(attrNamespace)
161               
162            strAttribs.append('%s %s="%s"' % (nsDeclaration, attr, attrVal))
163           
164        strAttrib = ''.join(strAttribs)
165       
166        namespace = QName.getNs(elem.tag)
167        nsPrefix = ElementTree._namespace_map.get(namespace)
168        if nsPrefix is None:
169            raise KeyError('prettyPrint: missing namespace "%s" for ' 
170                           'ElementTree._namespace_map' % namespace)
171           
172        tag = "%s:%s" % (nsPrefix, QName.getLocalPart(elem.tag))
173       
174        # Put in namespace declaration if one doesn't already exist
175        # FIXME: namespace declaration handling is wrong for handling child
176        # element scope
177        if namespace in self.declaredNss:
178            nsDeclaration = ''
179        else:
180            nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix, namespace)
181            self.declaredNss.append(namespace)
182           
183        result = '%s<%s%s%s>%s' % (indent, tag, nsDeclaration, strAttrib, 
184                                   _PrettyPrint.estrip(elem.text))
185       
186        children = len(elem)
187        if children:
188            for child in elem:
189                declaredNss = self.declaredNss[:]
190                _prettyPrint = _PrettyPrint(declaredNss)
191                result += '\n'+ _prettyPrint(child, indent=indent+space) 
192               
193            result += '\n%s%s</%s>' % (indent,
194                                     _PrettyPrint.estrip(child.tail),
195                                     tag)
196        else:
197            result += '</%s>' % tag
198           
199        return result
200
Note: See TracBrowser for help on using the repository browser.