source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/utils/__init__.py @ 5554

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/utils/__init__.py@5554
Revision 5554, 6.9 KB checked in by pjkersha, 11 years ago (diff)
  • Started adding ElementTree based parsers for SAML classes in ndg.security.common.saml.xml.etree.
  • ndg.security.common.utils.prettyPrint needs a bug fix for namespace declarations
Line 
1"""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
17# Fred Lundh's customisation for C14N functionality - egg available from
18# http://ndg.nerc.ac.uk/dist site
19c14nWarning = ("Custom ElementC14N package is not installed, canonicalize "
20               "function is disabled")
21try:
22    from elementtree import ElementC14N
23    elementC14nNotInstalled = False
24except ImportError:
25    elementC14nNotInstalled = True
26    import warnings
27    warnings.warn(c14nWarning)
28   
29from cStringIO import StringIO
30
31# ElementTree helpers 
32getNs = lambda elem: elem.tag.split('}')[0][1:]
33getLocalName = lambda elem: elem.tag.rsplit('}',1)[-1]
34
35class QName(ElementTree.QName):
36    """Extend ElementTree implementation for improved attribute access support
37    """
38   
39    def __init__(self, namespaceURI, tag=None, prefix=None):
40        ElementTree.QName.__init__(self, namespaceURI, tag=tag)
41       
42        if tag:
43            self.namespaceURI = namespaceURI
44            self.localPart = tag
45        else:
46            self.namespaceURI = getNs(self.text)
47            self.localPart = getLocalName(self.text)
48           
49        self.prefix = prefix
50
51    def _getPrefix(self):
52        return self.__prefix
53
54    def _setPrefix(self, value):
55        self.__prefix = value
56   
57    prefix = property(_getPrefix, _setPrefix, None, "Prefix")
58
59    def _getLocalPart(self):
60        return self.__localPart
61   
62    def _setLocalPart(self, value):
63        self.__localPart = value
64       
65    localPart = property(_getLocalPart, _setLocalPart, None, "LocalPart")
66
67    def _getNamespaceURI(self):
68        return self.__namespaceURI
69
70    def _setNamespaceURI(self, value):
71        self.__namespaceURI = value
72 
73    namespaceURI = property(_getNamespaceURI, _setNamespaceURI, None, 
74                            "Namespace URI'")
75
76def canonicalize(elem, **kw):
77    '''ElementTree based Canonicalization - See ElementC14N for keyword
78    info.  Also useful for pretty printing XML
79    @type elem: ElementTree.Element
80    @param elem: element to be canonicalized
81    @rtype: basestring
82    @return: canonicalised output
83    '''
84    if elementC14nNotInstalled:
85        raise NotImplementedError(c14nWarning)
86   
87    f = StringIO()
88    ElementC14N.write(ElementC14N.build_scoped_tree(elem), f, **kw)
89    return f.getvalue()
90
91def prettyPrint(*arg, **kw):
92    '''Lightweight pretty printing of ElementTree elements'''
93   
94    # Keep track of namespace declarations made so they're not repeated
95    declaredNss = []
96   
97    def estrip(elem):
98        ''' Just want to get rid of unwanted whitespace '''
99        if elem is None:
100            return ''
101        else:
102            # just in case the elem is another simple type - e.g. int -
103            # wrapper it as a string
104            return str(elem).strip()
105
106    def _prettyPrint(elem, indent='', html=0, space=' '*4):
107        '''Most of the work done in this wrapped function - wrapped so that
108        state can be maintained for declared namespace declarations during
109        recursive calls using "declaredNss" above''' 
110        strAttrib = ''.join([' %s="%s"' % (att, attVal) 
111                             for att, attVal in elem.attrib.items()])
112       
113        namespace = getNs(elem)
114        nsPrefix = ElementTree._namespace_map.get(namespace)
115        if nsPrefix is None:
116            raise KeyError('prettyPrint: missing namespace "%s" for ' 
117                           'ElementTree._namespace_map' % namespace)
118           
119        tag = "%s:%s" % (nsPrefix, getLocalName(elem))
120       
121        # Put in namespace declaration if one doesn't already exist
122        # FIXME: namespace declaration handling is wrong for handling child
123        # element scope
124        if namespace in declaredNss:
125            nsDeclaration = ''
126        else:
127            nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix, namespace)
128            declaredNss.append(namespace)
129           
130        result = '%s<%s%s%s>%s' % (indent, tag, nsDeclaration, strAttrib, 
131                                   estrip(elem.text))
132       
133        children = len(elem)
134        if children:
135            result += ''.join(['\n'+ _prettyPrint(item, indent=indent+space) 
136                               for item in elem]) + \
137                    '\n%s%s</%s>' % (indent, estrip(item.tail), tag)
138        else:
139            result += '</%s>' % tag
140           
141        return result
142   
143    return _prettyPrint(*arg, **kw)
144
145
146class UniqList(list):
147    """Extended version of list type to enable a list with unique items.
148    If an item is added that is already present then it is silently omitted
149    from the list
150    """
151    def extend(self, iter):
152        return super(UniqList, self).extend([i for i in iter if i not in self])
153       
154    def __iadd__(self, iter):
155        return super(UniqList, self).__iadd__([i for i in iter 
156                                               if i not in self])
157         
158    def append(self, item):
159        for i in self:
160            if i == item:
161                return None
162           
163        return super(UniqList, self).append(item)
164
165
166class TypedList(list):
167    """Extend list type to enabled only items of a given type.  Supports
168    any type where the array type in the Standard Library is restricted to
169    only limited set of primitive types
170    """
171   
172    def __init__(self, elementType, *arg, **kw):
173        """
174        @type elementType: type/tuple
175        @param elementType: object type or types which the list is allowed to
176        contain.  If more than one type, pass as a tuple
177        """
178        self.__elementType = elementType
179        super(TypedList, self).__init__(*arg, **kw)
180   
181    def _getElementType(self):
182        return self.__elementType
183   
184    elementType = property(fget=_getElementType, 
185                           doc="The allowed type or types for list elements")
186     
187    def extend(self, iter):
188        for i in iter:
189            if not isinstance(i, self.__elementType):
190                raise TypeError("List items must be of type %s" % 
191                                (self.__elementType,))
192               
193        return super(TypedList, self).extend(iter)
194       
195    def __iadd__(self, iter):
196        for i in iter:
197            if not isinstance(i, self.__elementType):
198                raise TypeError("List items must be of type %s" % 
199                                (self.__elementType,))
200                   
201        return super(TypedList, self).__iadd__(iter)
202         
203    def append(self, item):
204        if not isinstance(item, self.__elementType):
205                raise TypeError("List items must be of type %s" % 
206                                (self.__elementType,))
207   
208        return super(TypedList, self).append(item)
Note: See TracBrowser for help on using the repository browser.