""" Examples of several different things mutators can do. --fpm """ import gnosis.xml.pickle as xml_pickle import gnosis.xml.pickle.ext as mutate import gnosis.xml.pickle.util as util from gnosis.xml.pickle.ext import XMLP_Mutator, XMLP_Mutated from types import * import sys from UserList import UserList import funcs funcs.set_parser() class mystring(XMLP_Mutator): def mutate(self,obj): print "** mystring.mutate()" return XMLP_Mutated(obj) def unmutate(self,mobj): print "** mystring.unmutate()" return mobj.obj # test1 -- use our custom handler to pickle & unpickle # (here we fold two types to a single tagname) print "*** TEST 1 ***" my1 = mystring(StringType,"MyString",in_body=1) my2 = mystring(UnicodeType,"MyString",in_body=1) mutate.add_mutator(my1) mutate.add_mutator(my2) u = UserList(['aaa','bbb','ccc']) print u x = xml_pickle.dumps(u) print x del u z = xml_pickle.loads(x) print z # remove custom mutators mutate.remove_mutator(my1) mutate.remove_mutator(my2) # # test 2 -- custom pickler, but builtin unpickler # # use same tagname as builtin so builtin can unpickle it # (put text in body, even though builtin puts it in # the value= ... just to show that builtin can find it either way) print "*** TEST 2 ***" my1 = mystring(StringType,"string",in_body=1) my2 = mystring(UnicodeType,"string",in_body=1) mutate.add_mutator(my1) mutate.add_mutator(my2) u = UserList(['aaa','bbb','ccc']) print u x = xml_pickle.dumps(u) print x del u # remove custom mutators before unpickling mutate.remove_mutator(my1) mutate.remove_mutator(my2) # now the builtin is doing the unpickling z = xml_pickle.loads(x) print z # test 3 # # show how mutators can be chained together for neat purposes # (though this example is pretty useless :-) # # mynumlist handles lists of integers and pickles them as "n,n,n,n" # mycharlist does the same for single-char strings # # otherwise, the ListType builtin handles the list class mynumlist(XMLP_Mutator): def wants_obj(self,obj): # I only want lists of integers for i in obj: if type(i) is not IntType: return 0 return 1 def mutate(self,obj): t = '%d'%obj[0] for i in obj[1:]: t = t + ',%d'%i return XMLP_Mutated(t) def unmutate(self,mobj): l = map(int,mobj.obj.split(',')) return l class mycharlist(XMLP_Mutator): def wants_obj(self,obj): # I only want lists of single chars for i in obj: if type(i) is not StringType or \ len(i) != 1: return 0 return 1 def mutate(self,obj): t = '%s'%obj[0] for i in obj[1:]: t = t + ',%s'%i return XMLP_Mutated(t) def unmutate(self,mobj): l = mobj.obj.split(',') return l # unlike test#1 (folding multiple types -> one tag), here # we "explode" one type to multiple tags, so we can # make different XML representations based on object content print "*** TEST 3 ***" my1 = mynumlist(ListType,"NumList",in_body=1) my2 = mycharlist(ListType,"CharList",in_body=1) mutate.add_mutator(my1) mutate.add_mutator(my2) u = UserList([[1,2,3],['mmm','nnn','ooo'],['a','b','c'],[4.1,5.2,6.3]]) print u x = xml_pickle.dumps(u) print x del u g = xml_pickle.loads(x) print g # remove custom mutators mutate.remove_mutator(my1) mutate.remove_mutator(my2) # # test 4 # # A mutator that takes UserList-derived classes and saves them # as 'seq' types instead of 'PyObject' (the default) (i.e. a parser # that understands only 'seq' could now read an XML file containing # UserList-derived classes) # # note: being a simple example, this doesn't take into account # things like initargs that would be needed to make this # a complete implementation # note 2: this is basically how we handle "newstyle" objects in # Python 2.2+, coincidentally enough :-) class mutate_userlist(XMLP_Mutator): def __init__(self): XMLP_Mutator.__init__(self,type(UserList()),'userlist') # type(UserList()) should be InstanceType, which is # pretty generic, so we have to be careful and check # that the obj is a UserList (or derived) def wants_obj(self,obj): return isinstance(obj,UserList) # OTOH, we want all mutated objects that match our tag # ('userlist'), so do nothing for 'wants_mutated()' def mutate(self,obj): return XMLP_Mutated(obj.data, "%s.%s"%(util._module(obj),util._klass(obj))) def unmutate(self,mobj): p = mobj.extra.split('.') klass = util.get_class_from_name(p[1],p[0], xml_pickle.getParanoia()) return klass(mobj.obj) print "*** TEST 4 ***" xml_pickle.setParanoia(0) # let xml_pickle use our namespace my1 = mutate_userlist() mutate.add_mutator(my1) class mylist(UserList):pass class zlist(mylist): pass class foo:pass f = foo() f.u = UserList([1,2,3,4]) f.m = mylist([5,6,7,8]) f.z = zlist([9,10,11,12]) print f.u.__class__, f.u print f.m.__class__, f.m print f.z.__class__, f.z x = xml_pickle.dumps(f) print x del f g = xml_pickle.loads(x) print g.u.__class__, g.u print g.m.__class__, g.m print g.z.__class__, g.z mutate.remove_mutator(my1) # skip test 5 if Numeric not installed try: import Numeric except: sys.exit(0) # # test 5 -- this mutator takes a multidimensional Numerical.array and # pretty-prints it as a matrix in the XML body # # (this could be a whole lot fancier, but I kept it simple & dumb for # demonstration purposes) # class mutate_multidim(XMLP_Mutator): def __init__(self): XMLP_Mutator.__init__(self,Numeric.ArrayType,'numpy_matrix',in_body=1) def mutate(self,obj): s = '\n' # start on new line in XML file for arr in obj: for elem in arr: s += '%f ' %elem s += '\n' return XMLP_Mutated(s) def unmutate(self,mobj): text = mobj.obj lines = text.split('\n') list = [] for line in lines: a = map(float,line.split()) if len(a): list.append(a) a = Numeric.array(list) return a my1 = mutate_multidim() mutate.add_mutator(my1) f = foo() f.a = Numeric.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]],'f') print "ORIG: ",f.a x = xml_pickle.dumps(f) print x del f g = xml_pickle.loads(x) print "COPY: ",g.a mutate.remove_mutator(my1) # # test 6 -- override the default mxDateTime handlers # the default handlers prints the date/time as a single # string. this mutator makes it a 2-element dict instead, # one for date, one for time. # try: import mx.DateTime, re mxDateTime_type = type(mx.DateTime.localtime()) except: # skip test if mxDateTime not installed sys.exit(0) class mutate_mxdatetime(XMLP_Mutator): def __init__(self): XMLP_Mutator.__init__(self,mxDateTime_type,'mxDateTime', paranoia=0) def mutate(self,obj): # save as a dict -- # obj['YMD'] = "year/month/day" # obj['HMS'] = hour/min/second # (I avoid using strftime(), for portability reasons.) # Pickle seconds as a float to save full precision. d = {} d['YMD'] = "%d/%d/%d" % (obj.year,obj.month,obj.day) d['HMS'] = "%d:%d:%f" % (obj.hour,obj.minute,obj.second) return XMLP_Mutated(d) def unmutate(self,mobj): obj = mobj.obj # is this forgiving enough? :-) fmt = '\s*([0-9]+)\s*/\s*([0-9]+)\s*/\s*([0-9]+)' m1 = re.match(fmt,obj['YMD']) fmt = '\s*([0-9]+)\s*:\s*([0-9]+)\s*:\s*([0-9\.]+)' m2 = re.match(fmt,obj['HMS']) return apply(mx.DateTime.DateTime,map(float,m1.groups())+ map(float,m2.groups())) my1 = mutate_mxdatetime() mutate.add_mutator(my1) f = foo() f.a = mx.DateTime.now() print "ORIG: ",f.a x = xml_pickle.dumps(f) print x del f g = xml_pickle.loads(x) print "COPY: ",g.a mutate.remove_mutator(my1)