Package svnmailer :: Module typedstruct
[hide private]

Source Code for Module svnmailer.typedstruct

  1  # -*- coding: utf-8 -*- 
  2  # pylint: disable-msg = W0613 
  3  # 
  4  # Copyright 2004-2006 André Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 17  """ 
 18  Typed Data Structures 
 19  ===================== 
 20   
 21  This module provides helpers for creating typed data structures. 
 22   
 23  Basic Usage 
 24  ----------- 
 25  In order to create a new data structure, you inherit from C{Struct} and 
 26  define the members like so (the booleans are explained later):: 
 27   
 28      class MyStruct(typedstruct.Struct): 
 29          __slots__ = typedstruct.members(locals(), { 
 30              'name1': None, 
 31              # ... and/or ... 
 32              'name2': <type>, 
 33              # ... and/or ... 
 34              'name3': (<type>, <param>), 
 35          }) 
 36   
 37  If there are no fixed types at all (always C{None}, you can still benefit 
 38  from the features of the L{Struct} class and further write it a bit simpler:: 
 39   
 40      class MyStruct(typedstruct.Struct): 
 41          __slots__ = typedstruct.members(locals(), ( 
 42              'name1', 'name2', ... 
 43          )) 
 44   
 45  Well, the main reason for using the Struct class is to get some level of type 
 46  safety and automatic conversion without a complex written definition of 
 47  C{property} for each and every member (it uses some property like descriptors 
 48  internally, however). This encapsulates a lot of ugly logic and error handling 
 49  (more or less) into a single piece of code and makes the member definitions 
 50  I{much} easier to read and maintain. For example, you can create a struct 
 51  member of type C{regex}. Now you assign a string to this member and it is 
 52  automatically compiled to a regex, which you get, if you retrieve the 
 53  value later. As you'll see, the C{regex} type needs to be defined as a class 
 54  which should be inherited from the L{MemberDescriptor} class and assigned 
 55  to the C{regex} type name via a type mapping dict:: 
 56   
 57      class RegexMember(typedstruct.MemberDescriptor): 
 58          def transform(self, value, arg): 
 59              import re 
 60              return re.compile(value) 
 61      # ... 
 62      typemap = {'regex': RegexMember} 
 63      # ... 
 64      class MyStruct(typedstruct.Struct): 
 65          __slots__ = typedstruct.members(locals(), { 
 66              'checker': 'regex', 
 67          }, typemap = typemap) 
 68      # ... 
 69      store = MyStruct() 
 70      store.checker = r'[a-zA-Z]$' 
 71      # ... 
 72      if store.checker.match(stringtocheck): 
 73          # do something 
 74   
 75  Constraints 
 76  ----------- 
 77  Member names must be valid python identifiers. Further all names starting 
 78  I{and} ending with underscores are reserved for L{Struct}'s or python's 
 79  own purposes. 
 80  """ 
 81  __author__    = "André Malo" 
 82  __docformat__ = "epytext en" 
 83  __all__       = ['members', 'Struct', 'MemberDescriptor'] 
 84   
 85  # global imports 
 86  from svnmailer import util 
 87   
 88   
89 -class MemberDescriptor(object):
90 """ Base class for members descriptors 91 92 @ivar name: The name of the member 93 @type name: C{str} 94 95 @ivar param: The descriptor parameter 96 @type param: any 97 98 @ivar __private: The reference to the private container 99 @type __private: C{StructPrivate} 100 """
101 - def __init__(self, name, private, param = None):
102 """ Initialization """ 103 self.name = name 104 self.param = param 105 self.__private = private
106 107
108 - def __get__(self, instance, owner):
109 """ Gets the member value """ 110 if instance is None: 111 return None 112 113 priv = self.__private 114 arg = priv.getArg(instance) 115 mapper = priv.getMaps(instance).get(self.name) 116 117 value = self.substitute( 118 priv.getValues(instance).get(self.name), 119 util.ReadOnlyDict(priv.getSubst(instance)), 120 arg 121 ) 122 if mapper is not None: 123 value = self.postmap(value, mapper, arg) 124 125 return value
126 127
128 - def __set__(self, instance, value):
129 """ Sets the members value """ 130 priv = self.__private 131 arg = priv.getArg(instance) 132 mapper = priv.getMaps(instance).get(self.name) 133 134 if mapper is not None: 135 value = self.premap(value, mapper, arg) 136 137 priv.getValues(instance)[self.name] = self.transform(value, arg)
138 139
140 - def __delete__(self, instance):
141 """ Raises an AttributeError """ 142 raise AttributeError( 143 "member '%s' cannot be deleted" % self.name 144 )
145 146
147 - def premap(self, value, mapper, arg):
148 """ Premapper - passes through by default 149 150 The premapper is called if the value is set before doing 151 anything else. 152 153 @note: It is not called if no mapper function is defined (or it 154 is C{None}). 155 156 @param value: The value to premap 157 @type value: any 158 159 @param mapper: The mapping argument 160 @type mapper: any 161 162 @param arg: The argument used for struct initialization 163 @type arg: any 164 """ 165 return value
166 167
168 - def transform(self, value, arg):
169 """ Transformer - passes through by default 170 171 Override this method in order to do any value transformation, 172 e.g. compile the input string as regex or split it into a list. 173 174 The C{transform} method is called with the value returned from 175 the L{premap} method. The result is stored as final member value. 176 177 @param value: The value to tranform 178 @type value: any 179 180 @param arg: The argument used for struct initialization 181 @type arg: any 182 183 @return: The transformed value 184 @rtype: any 185 """ 186 return value
187 188
189 - def substitute(self, value, subst, arg):
190 """ Substituter - passes through by default 191 192 Use this method to do any dynamic processing on 193 the retrieved value before it's being L{postmap}ped. 194 195 @param value: The value to substitute 196 @type value: any 197 198 @param subst: The substitution dictionary 199 @type subst: C{dict} 200 201 @param arg: The argument used for struct initialization 202 @type arg: any 203 204 @return: The substituted value 205 @rtype: any 206 """ 207 return value
208 209
210 - def postmap(self, value, mapper, arg):
211 """ Postmapper - passes through by default 212 213 The postmapper is called before the value is finally returned 214 to the caller (after being substituted). 215 216 @note: The postmapper is not called if no mapper function 217 is defined (or it is C{None}). 218 219 @param value: The value to postmap 220 @type value: any 221 222 @param mapper: The mapping argument 223 @type mapper: any 224 225 @param arg: The argument used for struct initialization 226 @type arg: any 227 """ 228 return value
229 230
231 -class Struct(object):
232 """ General structure stub """ 233 _set_ = _members_ = None # satisfy pylint 234
235 - def __new__(cls, _maps_ = None, _arg_ = None, **kwargs):
236 """ Object creation 237 238 @param _maps_: The maps to use 239 @type _maps_: C{dict} 240 241 @param _arg_: The opaque argument for custom descriptors 242 @type _arg_: any 243 """ 244 self = object.__new__(cls) 245 self._set_.private.initInstance(self, _maps_, _arg_) 246 247 return self
248 249
250 - def __init__(self, _maps_ = None, _arg_ = None, **kwargs):
251 """ Stub Initialization 252 253 @param _maps_: unused, but consistent to L{__new__} 254 @type _maps_: C{dict} 255 256 @param _arg_: unused, but consistent to L{__new__} 257 @type _arg_: any 258 """ 259 for name, value in kwargs.items(): 260 self._set_(name, value)
261 262
263 - def __del__(self):
264 """ Removes all references from private container """ 265 self._set_.private.removeInstance(self)
266 267
268 - def __repr__(self):
269 """ Representation for debugging purposes 270 271 @return: A pythonic representation of the struct 272 @rtype: C{str} 273 """ 274 return "%s.%s(\n %s\n)" % ( 275 self.__class__.__module__, 276 self.__class__.__name__, 277 ',\n '.join([ 278 "_maps_ = %r" % self._set_.private.getMaps(self), 279 "_arg_ = %r" % self._set_.private.getArg(self), 280 ] + [ 281 "%s = %r" % (name, getattr(self, name)) 282 for name in self._members_ 283 if getattr(self, name) is not None 284 ]) 285 )
286 287
288 -def members(space, the_members, aliases = None, typemap = None):
289 """ supply the member and slot entries 290 291 @param space: The namespace to pollute 292 @type space: C{dict} 293 294 @param the_members: The member list / description 295 @type the_members: C{tuple} or C{dict} 296 297 @param aliases: The member name aliases 298 @type aliases: C{dict} 299 300 @param typemap: The type mapping table 301 @type typemap: C{dict} 302 303 @return: The list of __slots__ to use. 304 @rtype: C{list} 305 """ 306 if type(the_members) in [tuple, list]: 307 the_members = dict.fromkeys(the_members) 308 names = the_members.keys() 309 310 private = StructPrivate(names, aliases or {}) 311 typemap = dict(typemap or {}) 312 typemap[None] = MemberDescriptor 313 314 for name, param in the_members.items(): 315 if name[:1] == '_' and name[-1:] == '_': 316 raise AttributeError("%r is not allowed as member name" % name) 317 318 if type(param) in (tuple, list): 319 space[name] = typemap[param[0]](name, private, param[1]) 320 else: 321 space[name] = typemap[param](name, private) 322 323 space.update({ 324 '_set_' : StructSetDescriptor(private), 325 '_sub_' : StructSubDescriptor(private), 326 '_members_': StructMembersDescriptor(private), 327 '_dict_' : StructDictDescriptor(private), 328 }) 329 330 return names
331 332 # ==================================================================== 333 # PRIVATE STUFF 334 # ==================================================================== 335
336 -class StructPrivate(object):
337 """ Private container class for Struct internals """ 338
339 - def __init__(self, names, aliases):
340 """ Initialization 341 342 @param names: The member names to serve 343 @type names: C{tuple} 344 345 @param aliases: The name mappings 346 @type aliases: C{dict} 347 """ 348 self.members = tuple(names) 349 self.aliases = util.ReadOnlyDict(aliases) 350 self.values = {} 351 self.subst = {} 352 self.maps = {} 353 self.args = {}
354 355
356 - def initInstance(self, instance, maps, arg):
357 """ Initializes the class for a particular instance 358 359 @param instance: The instance in question 360 @type instance: C{object} 361 362 @param maps: The maps to use 363 @type maps: C{dict} 364 365 @param arg: The initialization argument 366 @type arg: any 367 """ 368 this = id(instance) 369 370 # merge alias maps to real 371 maps = dict(maps or {}) 372 for alias, real in self.aliases.items(): 373 if maps.has_key(alias): 374 maps[real] = maps[alias] 375 del maps[alias] 376 377 self.maps[this] = util.ReadOnlyDict(maps) 378 self.args[this] = arg 379 self.values[this] = {} 380 self.subst[this] = {}
381 382
383 - def removeInstance(self, instance):
384 """ Removes all data, referring to a particular instance 385 386 @param instance: The instance in question 387 @type instance: C{object} 388 """ 389 this = id(instance) 390 391 try: 392 del self.args[this] 393 del self.maps[this] 394 del self.values[this] 395 del self.subst[this] 396 except KeyError: 397 raise RuntimeError("%r was not properly initialized" % self)
398 399
400 - def getValues(self, instance):
401 """ Returns the value dict for the particular instance 402 403 @param instance: The instance in question 404 @type instance: C{object} 405 406 @return: The value dictionary 407 @rtype: C{dict} 408 """ 409 this = id(instance) 410 return self.values[this]
411 412
413 - def getSubst(self, instance):
414 """ Returns the subst dict for the particular instance 415 416 @param instance: The instance in question 417 @type instance: C{object} 418 419 @return: The substitution dict 420 @rtype: C{dict} 421 """ 422 this = id(instance) 423 return self.subst[this]
424 425
426 - def getMaps(self, instance):
427 """ Returns the map dict for the particular instance 428 429 @param instance: The instance in question 430 @type instance: C{object} 431 432 @return: The map dictionary 433 @rtype: C{dict} 434 """ 435 this = id(instance) 436 return self.maps[this]
437 438
439 - def getArg(self, instance):
440 """ Returns the arg for the particular instance 441 442 @param instance: The instance in question 443 @type instance: C{object} 444 445 @return: The arg used for initialization 446 @rtype: any 447 """ 448 this = id(instance) 449 return self.args[this]
450 451
452 -class StructDescriptor(object):
453 """ Base class for struct descriptors 454 455 @ivar private: The private data container 456 @type private: C{StructPrivate} 457 """
458 - def __init__(self, private):
459 """ Initialization 460 461 @param private: The private data container 462 @type private: C{StructPrivate} 463 """ 464 self.private = private
465 466
467 - def __set__(self, instance, value):
468 """ setting is not allowed """ 469 raise AttributeError("attribute is read-only")
470 471
472 - def __delete__(self, instance):
473 """ deleting is not allowed """ 474 raise AttributeError("attribute is read-only")
475 476
477 -class StructSetDescriptor(StructDescriptor):
478 """ _set_ descriptor """ 479
480 - def __get__(self, instance, owner):
481 """ Returns an aliasing setter function """ 482 def aliassetter(name, value): 483 """ Set the self.name = value 484 485 @param name: Name of the struct member or an alias 486 @type name: C{str} 487 488 @param value: Value of the struct member 489 @type value: any 490 491 @exception AttributeError: The specified struct member doesn't 492 exist (nor is it an alias) 493 """ 494 name = self.private.aliases.get(name, name) 495 if name[:1] == '_' and name[-1:] == '_': 496 raise AttributeError("%r is not a struct member" % name) 497 498 setattr(instance or owner, name, value)
499 500 # starting point for the Struct class 501 aliassetter.private = self.private 502 503 return aliassetter
504 505
506 -class StructSubDescriptor(StructDescriptor):
507 """ _sub_ descriptor """ 508
509 - def __get__(self, instance, owner):
510 """ Returns an aliasing setter function """ 511 512 def subsetter(name, value, default = False): 513 """ Sets a key-value-pair for substitutions 514 515 If C{default} is true and the name already exists, 516 it will not be overidden. 517 518 @param name: The key 519 @type name: C{str} 520 521 @param value: The value 522 @type value: C{str} 523 524 @param default: Is the supplied value a default? 525 @type default: C{bool} 526 """ 527 if instance is None: 528 raise AttributeError( 529 "%s._sub_ only works on instances" % owner.__name__ 530 ) 531 532 subst = self.private.getSubst(instance) 533 if not default or not subst.has_key(name): 534 subst[name] = value
535 536 def getdict(): 537 """ Returns the current dict (read-only) """ 538 return util.ReadOnlyDict(self.private.getSubst(instance))
539 540 subsetter.dict = getdict 541 return subsetter 542 543
544 -class StructMembersDescriptor(StructDescriptor):
545 """ _members_ descriptor """
546 - def __get__(self, instance, owner):
547 """ Returns the struct members as tuple """ 548 return self.private.members
549 550
551 -class StructDictDescriptor(StructDescriptor):
552 """ _dict_ descriptor """
553 - def __get__(self, instance, owner):
554 """ Returns the values dict read-only """ 555 if instance is None: 556 return util.ReadOnlyDict.fromkeys(self.private.members) 557 558 return util.ReadOnlyDict(self.private.getValues(instance))
559