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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/utils/__init__.py@5550
Revision 5550, 6.8 KB checked in by pjkersha, 11 years ago (diff)

ndg.security.common.utils: fixes to TypedList? and prettyPrint classes. prettyPrint now correctly substitutes namespace prefixes from ElementTree._namespace_map

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        if namespace in declaredNss:
123            nsDeclaration = ''
124        else:
125            nsDeclaration = ' xmlns:%s="%s"' % (nsPrefix, namespace)
126            declaredNss.append(namespace)
127           
128        result = '%s<%s%s%s>%s' % (indent, tag, nsDeclaration, strAttrib, 
129                                   estrip(elem.text))
130       
131        children = len(elem)
132        if children:
133            result += ''.join(['\n'+ _prettyPrint(item, indent=indent+space) 
134                               for item in elem]) + \
135                    '\n%s%s</%s>' % (indent, estrip(item.tail), tag)
136        else:
137            result += '</%s>' % tag
138           
139        return result
140   
141    return _prettyPrint(*arg, **kw)
142
143
144class UniqList(list):
145    """Extended version of list type to enable a list with unique items.
146    If an item is added that is already present then it is silently omitted
147    from the list
148    """
149    def extend(self, iter):
150        return super(UniqList, self).extend([i for i in iter if i not in self])
151       
152    def __iadd__(self, iter):
153        return super(UniqList, self).__iadd__([i for i in iter 
154                                               if i not in self])
155         
156    def append(self, item):
157        for i in self:
158            if i == item:
159                return None
160           
161        return super(UniqList, self).append(item)
162
163
164class TypedList(list):
165    """Extend list type to enabled only items of a given type.  Supports
166    any type where the array type in the Standard Library is restricted to
167    only limited set of primitive types
168    """
169   
170    def __init__(self, elementType, *arg, **kw):
171        """
172        @type elementType: type/tuple
173        @param elementType: object type or types which the list is allowed to
174        contain.  If more than one type, pass as a tuple
175        """
176        self.__elementType = elementType
177        super(TypedList, self).__init__(*arg, **kw)
178   
179    def _getElementType(self):
180        return self.__elementType
181   
182    elementType = property(fget=_getElementType, 
183                           doc="The allowed type or types for list elements")
184     
185    def extend(self, iter):
186        for i in iter:
187            if not isinstance(i, self.__elementType):
188                raise TypeError("List items must be of type %s" % 
189                                (self.__elementType,))
190               
191        return super(TypedList, self).extend(iter)
192       
193    def __iadd__(self, iter):
194        for i in iter:
195            if not isinstance(i, self.__elementType):
196                raise TypeError("List items must be of type %s" % 
197                                (self.__elementType,))
198                   
199        return super(TypedList, self).__iadd__(iter)
200         
201    def append(self, item):
202        if not isinstance(item, self.__elementType):
203                raise TypeError("List items must be of type %s" % 
204                                (self.__elementType,))
205   
206        return super(TypedList, self).append(item)
Note: See TracBrowser for help on using the repository browser.