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""" 18Typed Data Structures 19===================== 20 21This module provides helpers for creating typed data structures. 22 23Basic Usage 24----------- 25In order to create a new data structure, you inherit from C{Struct} and 26define 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 37If there are no fixed types at all (always C{None}, you can still benefit 38from 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 45Well, the main reason for using the Struct class is to get some level of type 46safety and automatic conversion without a complex written definition of 47C{property} for each and every member (it uses some property like descriptors 48internally, 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 50I{much} easier to read and maintain. For example, you can create a struct 51member of type C{regex}. Now you assign a string to this member and it is 52automatically compiled to a regex, which you get, if you retrieve the 53value later. As you'll see, the C{regex} type needs to be defined as a class 54which should be inherited from the L{MemberDescriptor} class and assigned 55to 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 75Constraints 76----------- 77Member names must be valid python identifiers. Further all names starting 78I{and} ending with underscores are reserved for L{Struct}'s or python's 79own purposes. 80""" 81__author__="André Malo" 82__docformat__="epytext en" 83__all__=['members','Struct','MemberDescriptor'] 84 85# global imports 86fromsvnmailerimportutil 87 88

109""" Gets the member value """110ifinstanceisNone:111returnNone112113priv=self.__private114arg=priv.getArg(instance)115mapper=priv.getMaps(instance).get(self.name)116117value=self.substitute(118priv.getValues(instance).get(self.name),119util.ReadOnlyDict(priv.getSubst(instance)),120arg121)122ifmapperisnotNone:123value=self.postmap(value,mapper,arg)124125returnvalue

129""" Sets the members value """130priv=self.__private131arg=priv.getArg(instance)132mapper=priv.getMaps(instance).get(self.name)133134ifmapperisnotNone:135value=self.premap(value,mapper,arg)136137priv.getValues(instance)[self.name]=self.transform(value,arg)

148""" Premapper - passes through by default149150 The premapper is called if the value is set before doing151 anything else.152153 @note: It is not called if no mapper function is defined (or it154 is C{None}).155156 @param value: The value to premap157 @type value: any158159 @param mapper: The mapping argument160 @type mapper: any161162 @param arg: The argument used for struct initialization163 @type arg: any164 """165returnvalue

169""" Transformer - passes through by default170171 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.173174 The C{transform} method is called with the value returned from175 the L{premap} method. The result is stored as final member value.176177 @param value: The value to tranform178 @type value: any179180 @param arg: The argument used for struct initialization181 @type arg: any182183 @return: The transformed value184 @rtype: any185 """186returnvalue

211""" Postmapper - passes through by default212213 The postmapper is called before the value is finally returned214 to the caller (after being substituted).215216 @note: The postmapper is not called if no mapper function217 is defined (or it is C{None}).218219 @param value: The value to postmap220 @type value: any221222 @param mapper: The mapping argument223 @type mapper: any224225 @param arg: The argument used for struct initialization226 @type arg: any227 """228returnvalue

481""" Returns an aliasing setter function """482defaliassetter(name,value):483""" Set the self.name = value484485 @param name: Name of the struct member or an alias486 @type name: C{str}487488 @param value: Value of the struct member489 @type value: any490491 @exception AttributeError: The specified struct member doesn't492 exist (nor is it an alias)493 """494name=self.private.aliases.get(name,name)495ifname[:1]=='_'andname[-1:]=='_':496raiseAttributeError("%r is not a struct member"%name)497498setattr(instanceorowner,name,value)

499500# starting point for the Struct class501aliassetter.private=self.private502503returnaliassetter

510""" Returns an aliasing setter function """511512defsubsetter(name,value,default=False):513""" Sets a key-value-pair for substitutions514515 If C{default} is true and the name already exists,516 it will not be overidden.517518 @param name: The key519 @type name: C{str}520521 @param value: The value522 @type value: C{str}523524 @param default: Is the supplied value a default?525 @type default: C{bool}526 """527ifinstanceisNone:528raiseAttributeError(529"%s._sub_ only works on instances"%owner.__name__530)531532subst=self.private.getSubst(instance)533ifnotdefaultornotsubst.has_key(name):534subst[name]=value