1 | """Utilities package for NDG XACML |
---|
2 | |
---|
3 | NERC DataGrid |
---|
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$' |
---|
11 | import UserDict |
---|
12 | |
---|
13 | # Interpret a string as a boolean |
---|
14 | str2Bool = lambda str: str.lower() in ("yes", "true", "t", "1") |
---|
15 | |
---|
16 | class UniqList(list): |
---|
17 | """Extended version of list type to enable a list with unique items. |
---|
18 | If an item is added that is already present then it is silently omitted |
---|
19 | from the list |
---|
20 | """ |
---|
21 | def extend(self, iter): |
---|
22 | return super(UniqList, self).extend([i for i in iter if i not in self]) |
---|
23 | |
---|
24 | def __iadd__(self, iter): |
---|
25 | return super(UniqList, self).__iadd__([i for i in iter |
---|
26 | if i not in self]) |
---|
27 | |
---|
28 | def append(self, item): |
---|
29 | for i in self: |
---|
30 | if i == item: |
---|
31 | return None |
---|
32 | |
---|
33 | return super(UniqList, self).append(item) |
---|
34 | |
---|
35 | |
---|
36 | class TypedList(list): |
---|
37 | """Extend list type to enabled only items of a given type. Supports |
---|
38 | any type where the array type in the Standard Library is restricted to |
---|
39 | only limited set of primitive types |
---|
40 | """ |
---|
41 | |
---|
42 | def __init__(self, elementType, *arg, **kw): |
---|
43 | """ |
---|
44 | @type elementType: type/tuple |
---|
45 | @param elementType: object type or types which the list is allowed to |
---|
46 | contain. If more than one type, pass as a tuple |
---|
47 | """ |
---|
48 | self.__elementType = elementType |
---|
49 | super(TypedList, self).__init__(*arg, **kw) |
---|
50 | |
---|
51 | def __repr__(self): |
---|
52 | return "%r type: %s" % (self.__elementType, |
---|
53 | super(TypedList, self).__repr__()) |
---|
54 | |
---|
55 | def _getElementType(self): |
---|
56 | return self.__elementType |
---|
57 | |
---|
58 | elementType = property(fget=_getElementType, |
---|
59 | doc="The allowed type or types for list elements") |
---|
60 | |
---|
61 | def extend(self, iter): |
---|
62 | for i in iter: |
---|
63 | if not isinstance(i, self.__elementType): |
---|
64 | raise TypeError("List items must be of type %s" % |
---|
65 | (self.__elementType,)) |
---|
66 | |
---|
67 | return super(TypedList, self).extend(iter) |
---|
68 | |
---|
69 | def __iadd__(self, iter): |
---|
70 | for i in iter: |
---|
71 | if not isinstance(i, self.__elementType): |
---|
72 | raise TypeError("List items must be of type %s" % |
---|
73 | (self.__elementType,)) |
---|
74 | |
---|
75 | return super(TypedList, self).__iadd__(iter) |
---|
76 | |
---|
77 | def append(self, item): |
---|
78 | if not isinstance(item, self.__elementType): |
---|
79 | raise TypeError("List items must be of type %s" % |
---|
80 | (self.__elementType,)) |
---|
81 | |
---|
82 | return super(TypedList, self).append(item) |
---|
83 | |
---|
84 | |
---|
85 | class RestrictedKeyNamesDict(dict): |
---|
86 | """Utility class for holding a constrained list of key names |
---|
87 | """ |
---|
88 | |
---|
89 | def __init__(self, *arg, **kw): |
---|
90 | """Alter standard dict() initialisation to fix key names set at |
---|
91 | initialisation |
---|
92 | """ |
---|
93 | super(RestrictedKeyNamesDict, self).__init__(*arg, **kw) |
---|
94 | self.__keyNames = self.keys() |
---|
95 | |
---|
96 | def __setitem__(self, key, val): |
---|
97 | if key not in self.__keyNames: |
---|
98 | raise KeyError('Key name %r not recognised. Valid key names ' |
---|
99 | 'are: %r' % (key, self.__keyNames)) |
---|
100 | |
---|
101 | dict.__setitem__(self, key, val) |
---|
102 | |
---|
103 | def update(self, d, **kw): |
---|
104 | for dictArg in (d, kw): |
---|
105 | for k in dictArg: |
---|
106 | if k not in self.__keyNames: |
---|
107 | raise KeyError('Key name "%s" not recognised. Valid ' |
---|
108 | 'key names are: %s' % |
---|
109 | self.__keyNames) |
---|
110 | |
---|
111 | dict.update(self, d, **kw) |
---|
112 | |
---|
113 | |
---|
114 | _isIterable = lambda obj: getattr(obj, '__iter__', False) |
---|
115 | |
---|
116 | |
---|
117 | class VettedDict(UserDict.DictMixin): |
---|
118 | """Enforce custom checking on keys and items before addition to a |
---|
119 | dictionary""" |
---|
120 | |
---|
121 | def __init__(self, *args): |
---|
122 | """Initialise setting the allowed type or types for keys and items |
---|
123 | |
---|
124 | @param args: two arguments: the first is a callable which filters for |
---|
125 | permissable keys in this dict, the second sets the type or list of |
---|
126 | types permissable for items in this dict |
---|
127 | @type args: tuple |
---|
128 | """ |
---|
129 | if len(args) != 2: |
---|
130 | raise TypeError('__init__() takes 2 arguments, KeyFilter and ' |
---|
131 | 'valueFilter (%d given)' % len(args)) |
---|
132 | |
---|
133 | # Validation of inputs |
---|
134 | for arg, argName in zip(args, ('KeyFilter', 'valueFilter')): |
---|
135 | if not callable(arg): |
---|
136 | raise TypeError('Expecting callable for %r input; got %r' % |
---|
137 | (argName, type(arg))) |
---|
138 | |
---|
139 | self.__KeyFilter, self.__valueFilter = args |
---|
140 | |
---|
141 | self.__map = {} |
---|
142 | |
---|
143 | def _verifyKeyValPair(self, key, val): |
---|
144 | """Check given key value pair and return False if they should be |
---|
145 | filtered out. Filter functions may also raise an exception if they |
---|
146 | wish to abort completely |
---|
147 | """ |
---|
148 | if not self.__KeyFilter(key): |
---|
149 | return False |
---|
150 | |
---|
151 | elif not self.__valueFilter(val): |
---|
152 | return False |
---|
153 | |
---|
154 | else: |
---|
155 | return True |
---|
156 | |
---|
157 | def __setitem__(self, key, val): |
---|
158 | """Enforce type checking when setting new items""" |
---|
159 | if self._verifyKeyValPair(key, val): |
---|
160 | self.__map[key] = val |
---|
161 | |
---|
162 | def __getitem__(self, key): |
---|
163 | """Provde implementation for getting items""" |
---|
164 | if key not in self.__map: |
---|
165 | raise KeyError('%r key not found in dict' % key) |
---|
166 | |
---|
167 | return self.__map[key] |
---|
168 | |
---|
169 | def get(self, key, *arg): |
---|
170 | """Provide implementation of get item with default""" |
---|
171 | if key in self.__map: |
---|
172 | return self.__map[key] |
---|
173 | |
---|
174 | elif len(arg) > 1: |
---|
175 | # Default value set |
---|
176 | return arg[1] |
---|
177 | else: |
---|
178 | return None |
---|
179 | |
---|
180 | |
---|
181 | |
---|