Package pytilities :: Package delegation :: Module profile
[hide private]
[frames] | no frames]

Source Code for Module pytilities.delegation.profile

  1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net> 
  2  #  
  3  # This file is part of pytilities. 
  4  #  
  5  # pytilities is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  #  
 10  # pytilities is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  #  
 15  # You should have received a copy of the GNU General Public License 
 16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>. 
 17  # 
 18   
 19  __docformat__ = 'reStructuredText' 
 20   
 21  import copy 
 22   
23 -class Profile(object):
24 25 """ 26 States what attributes to delegate, and to which target attributes. 27 28 Instance methods: 29 30 - `has_readable`: Check for read-access mapping of attribute 31 - `has_writable`: Check for read-access mapping of attribute 32 - `has_deletable`: Check for read-access mapping of attribute 33 - `get_readable`: Get read-access mapping for attribute 34 - `get_writable`: Get read-access mapping for attribute 35 - `get_deletable`: Get read-access mapping for attribute 36 - `remove_mappings`: Remove attribute mappings 37 - `add_mappings`: Add attribute mappings 38 39 Operators: 40 41 self |= b::Profile 42 union of this profile and the other 43 44 self | b::Profile 45 union of this profile and the other 46 47 self &= b::Profile 48 intersection of this profile and the other 49 50 self & b::Profile 51 intersection of this profile and the other 52 53 self - b::Profile 54 difference of this profile and the other 55 56 self -= b::Profile 57 difference of this profile and the other 58 59 self ^ b::Profile 60 symetric difference of this profile and the other 61 62 self ^= b::Profile 63 symetric difference of this profile and the other 64 """ 65
66 - def __init__(self):
67 self.__readables = {} 68 self.__writables = {} 69 self.__deletables = {}
70
71 - def has_readable(self, attribute):
72 """ 73 Check for read-access mapping of attribute 74 75 Returns True if the mapping exists 76 """ 77 return attribute in self.__readables
78
79 - def has_writable(self, attribute):
80 """ 81 Check for write-access mapping of attribute 82 83 Returns True if the mapping exists 84 """ 85 return attribute in self.__writables
86
87 - def has_deletable(self, attribute):
88 """ 89 Check for delete-access mapping of attribute 90 91 Returns True if the mapping exists 92 """ 93 return attribute in self.__deletables
94
95 - def get_readable(self, attribute):
96 """ 97 Get the mapped value of a readable attribute 98 99 Returns ::string 100 """ 101 return self.__readables[attribute]
102
103 - def get_writable(self, attribute):
104 """ 105 Get the mapped value of a writable attribute 106 107 Returns ::string 108 """ 109 return self.__writables[attribute]
110
111 - def get_deletable(self, attribute):
112 """ 113 Get the mapped value of a deletable attribute 114 115 Returns ::string 116 """ 117 return self.__deletables[attribute]
118
119 - def __are_conflict_free(self, other):
120 ''' 121 Returns True if mappings of self don't conflict with those of other 122 ''' 123 # check for conflicts: intersect keys, then see whether intersections 124 # map to the same values 125 # If you get an assertion failure about this, then you should check 126 # your bases to see if any of them map an attribute differently 127 # e.g. with profiles a, b and attribute x: a.x should map to the same 128 # value as b.x 129 return reduce(lambda x, y: x and y, 130 (reduce(lambda x,y: x and y, 131 (a[key] == b[key] 132 for key 133 in set(a.iterkeys()) & set(b.iterkeys())), 134 True) 135 for a, b 136 in zip((self.__readables, self.__writables, 137 self.__deletables), 138 (other.__readables, other.__writables, 139 other.__deletables))), 140 True)
141 142 # the | operator
143 - def __or__(self, other):
144 '''See __ior__''' 145 p = self.copy() 146 p |= other 147 return p
148
149 - def __ior__(self, other):
150 """ 151 union of this profile and the other 152 153 Parameters: 154 155 - `other` :: Profile 156 the other profile 157 158 Preconditions: 159 1. The mappings of the two profiles musn't conflict. Two profiles 160 conflict when they have at least one mapping with the same source 161 name, but a different target name. 162 """ 163 assert self.__are_conflict_free(other), \ 164 "Profiles have conflicting mappings, can't take union" 165 166 # no conflicts, go ahead and update 167 self.__readables.update(other.__readables) 168 self.__writables.update(other.__writables) 169 self.__deletables.update(other.__deletables) 170 return self
171
172 - def __and__(self, other):
173 p = self.copy() 174 p &= other 175 return p
176
177 - def __iand__(self, other):
178 """ 179 intersection of this profile and the other 180 181 Parameters: 182 183 - `other` :: Profile 184 the other profile 185 186 Preconditions: 187 1. The mappings of the two profiles musn't conflict. Two profiles 188 conflict when they have at least one mapping with the same source 189 name, but a different target name. 190 """ 191 192 assert self.__are_conflict_free(other), \ 193 "Profiles have conflicting mappings, can't take union" 194 195 # no conflicts, go ahead and update 196 for a, b in zip( 197 (self.__readables, self.__writables, self.__deletables), 198 (other.__readables, other.__writables, other.__deletables)): 199 for key in a.keys(): 200 if key not in b: 201 del a[key] 202 203 return self
204 205
206 - def __sub__(self, other):
207 p = self.copy() 208 p -= other 209 return p
210
211 - def __isub__(self, other):
212 """ 213 difference of this profile and the other 214 215 Parameters: 216 217 - `other` :: Profile 218 the other profile 219 """ 220 221 for a, b in zip( 222 (self.__readables, self.__writables, self.__deletables), 223 (other.__readables, other.__writables, other.__deletables)): 224 for key in a.keys(): 225 if key in b: 226 del a[key] 227 228 return self
229 230
231 - def __xor__(self, other):
232 p = self.copy() 233 p ^= other 234 return p
235
236 - def __ixor__(self, other):
237 """ 238 symetric difference of this profile and the other 239 240 Parameters: 241 242 - `other` :: Profile 243 the other profile 244 """ 245 246 # get the intersection of keys 247 inter_readables = set(self.__readables.iterkeys()) \ 248 & set(other.__readables.iterkeys()) 249 inter_writables = set(self.__writables.iterkeys()) \ 250 & set(other.__writables.iterkeys()) 251 inter_deletables = set(self.__deletables.iterkeys()) \ 252 & set(other.__deletables.iterkeys()) 253 254 # or the two together 255 self.__readables.update(other.__readables) 256 self.__writables.update(other.__writables) 257 self.__deletables.update(other.__deletables) 258 259 # remove the intersection 260 for a, b in zip( 261 (self.__readables, self.__writables, self.__deletables), 262 (inter_readables, inter_writables, inter_deletables)): 263 for key in a.keys(): 264 if key in b: 265 del a[key] 266 267 return self
268
269 - def remove_mappings(self, modifiers, *names):
270 """ 271 Remove attribute mappings 272 273 Parameters: 274 275 `modifiers` :: string 276 the types of delegation access to the target to remove. Valid 277 modifiers are combinations of: 278 279 - `r`: read 280 - `w`: write 281 - `d`: delete 282 283 `names` :: (source_name::string...) 284 names of source_name parts of, the mappings to remove 285 """ 286 287 for name in names: 288 assert isinstance(name, basestring), \ 289 "Invalid arguments: names should be list of strings: %s" \ 290 % (names) 291 292 for name in names: 293 if "r" in modifiers: 294 self.__readables.pop(name) 295 296 if "w" in modifiers: 297 self.__writables.pop(name) 298 299 if "d" in modifiers: 300 self.__deletables.pop(name)
301 302
303 - def add_mappings(self, modifiers, *names, **mapped_names):
304 """ 305 Map source to target attribute names, for delegation. 306 307 Parameters: 308 309 `modifiers` :: string 310 the types of delegation access to the target. Valid modifiers 311 are combinations of: 312 313 - `r`: read 314 - `w`: write 315 - `d`: delete 316 317 `names` :: (source_name::string...) 318 names of the attribute names to delegate. The target attribute 319 name is the same as `source_name`. This is the same as adding a 320 {source_name : source_name} mapping, using the mapped_names 321 argument. 322 323 `mapped_names` :: {source_name::string : target_name::string ...} 324 names of source and target attributes to map and delegate. 325 326 `target_name`s will be mangled if they have a __ prefix. 327 Mangling will occur every time it is delegated to, so you can 328 change the target object any time. 329 """ 330 for name in names: 331 assert isinstance(name, basestring), ( 332 "Invalid arguments: names should be list of strings. Got: %s" % 333 names) 334 335 for key, name in mapped_names.iteritems(): 336 assert isinstance(key, basestring) and \ 337 isinstance(name, basestring), \ 338 "Invalid arguments: mapped_names should be dict of " + \ 339 "string:string. Got: %s" % mapped_names 340 341 if "r" in modifiers: 342 self.__readables.update(zip(names, names), **mapped_names) 343 344 if "w" in modifiers: 345 self.__writables.update(zip(names, names), **mapped_names) 346 347 if "d" in modifiers: 348 self.__deletables.update(zip(names, names), **mapped_names)
349
350 - def copy(self):
351 return copy.deepcopy(self)
352