1# -*- coding: utf-8 -*- 2# Cerealizer 3# Copyright (C) 2005-2008 Jean-Baptiste LAMY 4# Copyright (C) 2008 Peter Eckersley 5# 6# This program is free software. 7# It is available under the Python licence. 8 9"""Cerealizer -- A secure Pickle-like module 10 11The interface of the Cerealizer module is similar to Pickle, and it supports 12__getstate__, __setstate__, __getinitargs__ and __getnewargs__. 13 14Cerealizer supports int, long, float, bool, complex, string, unicode, tuple, list, set, frozenset, 15dict, old-style and new-style class instances. C-defined types are supported but saving the C-side 16data may require to write e.g. a specific Handler or a __getstate__ and __setstate__ pair. 17Objects with __slots__ are supported too. 18 19You have to register the class you want to serialize, by calling cerealizer.register(YourClass). 20Cerealizer can be considered as secure AS LONG AS the following methods of 'YourClass' are secure: 21 - __new__ 22 - __del__ 23 - __getstate__ 24 - __setstate__ 25 - __init__ (ONLY if __getinitargs__ is used for the class) 26 27These methods are the only one Cerealizer may call. For a higher security, Cerealizer maintains 28its own reference to these method (exepted __del__ that can only be called indirectly). 29 30Cerealizer doesn't aim at producing Human-readable files. About performances, Cerealizer is 31really fast and, when powered by Psyco, it may even beat cPickle! Although Cerealizer is 32implemented in less than 500 lines of pure-Python code (which is another reason for Cerealizer 33to be secure, since less code means less bugs :-). 34 35Compared to Pickle (cPickle): 36 - Cerealizer is secure 37 - Cerealizer achieves similar performances (using Psyco) 38 - Cerealizer requires you to declare the serializable classes 39 40Compared to Jelly (from TwistedMatrix): 41 - Cerealizer is faster 42 - Cerealizer does a better job with object cycles, C-defined types and tuples (*) 43 - Cerealizer files are not Human readable 44 45(*) Jelly handles them, but tuples and objects in a cycle are first created as _Tuple or 46_Dereference objects; this works for Python classes, but not with C-defined types which 47expects a precise type (e.g. tuple and not _Tuple). 48 49 50 51IMPLEMENTATION DETAILS 52 53GENERAL FILE FORMAT STRUCTURE 54 55Cerealizer format is simple but quite surprising. It uses a "double flat list" format. 56It looks like that : 57 58 <magic code (currently cereal1)>\\n 59 <number of objects>\\n 60 <classname of object #0>\\n 61 <optional data for creating object #0 (currently nothing except for tuples)> 62 <classname of object #1>\\n 63 <optional data for creating object #1 (currently nothing except for tuples)> 64 [...] 65 <data of object #0 (format depend of the type of object #0)> 66 <data of object #1 (format depend of the type of object #1)> 67 [...] 68 <reference to the 'root' object> 69 70As you can see, the information for a given object is splitted in two parts, the first one 71for object's class, and the second one for the object's data. 72 73To avoid problems, the order of the objects is the following: 74 75 <list, dict, set> 76 <object, instance> 77 <tuple, sorted by depth (=max number of folded tuples)> 78 79Objects are put after basic types (list,...), since object's __setstate__ might rely on 80a list, and thus the list must be fully loaded BEFORE calling the object's __setstate__. 81 82 83DATA (<data of object #n> above) 84 85The part <data of object #n> saves the data of object #n. It may contains reference to other data 86(see below, in Cerealizer references include reference to other objects but also raw data like int). 87 88 - an object is saved by : <reference to the object state (the value returned by object.__getstate__() or object.__dict__)> 89 e.g. 'r7\\n' (object #7 being e.g. the __dict__). 90 91 - a list or a set is saved by : <number of item>\\n 92 <reference to item #0> 93 <reference to item #1> 94 [...] 95 e.g. '3\\ni0\\ni1\\ni2\\n' for [0, 1, 2] 96 97 - a dict is saved by : <number of item>\\n 98 <reference to value #0> 99 <reference to key #0> 100 <reference to value #1> 101 <reference to key #1> 102 [...] 103 104 105REFERENCES (<reference to XXX> above) 106 107In Cerealizer a reference can be either a reference to another object being serialized in the 108same file, or a raw value (e.g. an integer). 109 - an int is saved by e.g. 'i187\\n' 110 - a long is saved by e.g. 'l10000000000\\n' 111 - a float is saved by e.g. 'f1.07\\n' 112 - a bool is saved by 'b0' or 'b1' 113 - a string is saved by e.g. 's5\\nascii' (where 5 is the number of characters) 114 - an unicode is saved by e.g. 'u4\\nutf8' (where 4 is the number of characters) 115 - an object reference is saved by e.g. 'r3\\n' (where 3 means reference to object #3) 116 - None is saved by 'n' 117""" 118 119__alls__ = ["load", "dump", "loads", "dumps", "freeze_configuration", "register"] 120VERSION = "0.6" 121 122import logging 123logger = logging.getLogger("cerealizer") 124#logging.basicConfig(level=logging.INFO) 125 126from cStringIO import StringIO 127from new import instance 128 129class EndOfFile(StandardError): pass 130class NotCerealizerFileError(StandardError): pass 131class NonCerealizableObjectError(StandardError): pass 132 133def _priority_sorter(a, b): return cmp(a[0], b[0]) 134 135class Dumper(object): 136 def __init__(self): self.init() 137 def init(self): 138 self.objs = [] 139 self.objs_id = set() 140 self.priorities_objs = [] # [(priority1, obj1), (priority2, obj2),...] 141 self.obj2state = {} 142 self.obj2newargs = {} 143 self.id2id = {} 144 self.id2obj = None 145 146 def dump(self, root_obj, s): 147 self.collect(root_obj) 148 self.priorities_objs.sort(_priority_sorter) 149 self.objs.extend([o for (priority, o) in self.priorities_objs]) 150 151 s.write("cereal1\n%s\n" % len(self.objs)) 152 153 i = 0 154 for obj in self.objs: 155 self.id2id[id(obj)] = i 156 i += 1 157 for obj in self.objs: _HANDLERS_[obj.__class__].dump_obj (obj, self, s) 158 for obj in self.objs: _HANDLERS_[obj.__class__].dump_data(obj, self, s) 159 160 _HANDLERS_[root_obj.__class__].dump_ref(root_obj, self, s) 161 self.init() 162 163 def undump(self, s): 164 txt = s.read(8) 165 if txt != "cereal1\n": 166 if txt == "": 167 raise EndOfFile("") 168 raise NotCerealizerFileError('Not a cerealizer file:\n"%s"' % txt) 169 170 nb = int(s.readline()) 171 self.id2obj = [ None ] * nb # DO NOT DO self.id2obj = [comprehension list], since undump_ref may access id2obj during its construction 172 for i in range(nb): 173 classname = s.readline() 174 handler = _HANDLERS.get(classname) 175 if not handler: raise NonCerealizableObjectError("Object of class/type '%s' cannot be de-cerealized! Use cerealizer.register to extend Cerealizer support to other classes." % classname[:-1]) 176 self.id2obj[i] = handler.undump_obj(self, s) 177 for obj in self.id2obj: _HANDLERS_[obj.__class__].undump_data(obj, self, s) 178 179 r = self.undump_ref(s) 180 self.init() 181 return r 182 183 def collect(self, obj): 184 """Dumper.collect(OBJ) -> bool 185 186Collects OBJ for serialization. Returns false is OBJ is already collected; else returns true.""" 187 handler = _HANDLERS_.get(obj.__class__) 188 if not handler: raise NonCerealizableObjectError("Object of class/type '%s' cannot be cerealized! Use cerealizer.register to extend Cerealizer support to other classes." % obj.__class__) 189 handler.collect(obj, self) 190 191 def dump_ref (self, obj, s): 192 """Dumper.dump_ref(OBJ, S) 193 194Writes a reference to OBJ in file S.""" 195 _HANDLERS_[obj.__class__].dump_ref(obj, self, s) 196 197 def undump_ref(self, s): 198 """Dumper.undump_ref(S) -> obj 199 200Reads a reference from file S.""" 201 c = s.read(1) 202 if c == "i": return int (s.readline()) 203 elif c == "f": return float(s.readline()) 204 elif c == "s": return s.read(int(s.readline())) 205 elif c == "u": return s.read(int(s.readline())).decode("utf8") 206 elif c == "r": return self.id2obj[int(s.readline())] 207 elif c == "n": return None 208 elif c == "b": return bool(int(s.read(1))) 209 elif c == "l": return long(s.readline()) 210 elif c == "c": return complex(s.readline()) 211 raise ValueError("Unknown ref code '%s'!" % c) 212 213 def immutable_depth(self, t): 214 depth = 0 215 for i in t: 216 i2 = self.obj2newargs.get(id(i)) 217 if not i2 is None: i = i2 218 if isinstance(i, tuple) or isinstance(i, frozenset): 219 x = self.immutable_depth(i) 220 if x > depth: depth = x 221 return depth + 1 222 223class Handler(object): 224 """Handler 225 226A customized handler for serialization and deserialization. 227You can subclass it to extend cerealization support to new object. 228See also ObjHandler.""" 229 230 def collect(self, obj, dumper): 231 """Handler.collect(obj, dumper) -> bool 232 233Collects all the objects referenced by OBJ. 234For each objects ROBJ referenced by OBJ, calls collect method of the Handler for ROBJ's class, 235i.e._HANDLERS_[ROBJ.__class__].collect(ROBJ, dumper). 236Returns false if OBJ is already referenced (and thus no collection should occur); else returns true. 237""" 238 i = id(obj) 239 if not i in dumper.objs_id: 240 dumper.objs.append(obj) 241 dumper.objs_id.add(i) 242 return 1 243 244 def dump_obj (self, obj, dumper, s): 245 """Handler.dump_obj(obj, dumper, s) 246 247Dumps OBJ classname in file S.""" 248 s.write(self.classname) 249 250 def dump_data(self, obj, dumper, s): 251 """Handler.dump_data(obj, dumper, s) 252 253Dumps OBJ data in file S.""" 254 255 def dump_ref (self, obj, dumper, s): 256 """Handler.dump_ref(obj, dumper, s) 257 258Write a reference to OBJ in file S. 259You should not override dump_ref, since they is no corresponding 'undump_ref' that you 260can override.""" 261 s.write("r%s\n" % dumper.id2id[id(obj)]) 262 263 def undump_obj(self, dumper, s): 264 """Handler.undump_obj(dumper, s) 265 266Returns a new uninitialized (=no __init__'ed) instance of the class. 267If you override undump_obj, DUMPER and file S can be used to read additional data 268saved by Handler.dump_obj().""" 269 270 def undump_data(self, obj, dumper, s): 271 """Handler.undump_data(obj, dumper, s) 272 273Reads the data for OBJ, from DUMPER and file S. 274If you override undump_data, you should use DUMPER.undump_ref(S) to 275read a reference or a basic type (=a string, an int,...).""" 276 277 278class RefHandler(object): 279 def collect (self, obj, dumper) : pass 280 def dump_obj (self, obj, dumper, s): pass 281 def dump_data(self, obj, dumper, s): pass 282 283class NoneHandler(RefHandler): 284 def dump_ref (self, obj, dumper, s): s.write("n") 285 286class StrHandler(RefHandler): 287 def dump_ref (self, obj, dumper, s): s.write("s%s\n%s" % (len(obj), obj)) 288 289class UnicodeHandler(RefHandler): 290 def dump_ref (self, obj, dumper, s): 291 obj = obj.encode("utf8") 292 s.write("u%s\n%s" % (len(obj), obj)) 293 294class BoolHandler(RefHandler): 295 def dump_ref (self, obj, dumper, s): s.write("b%r" % int(obj)) 296 297class IntHandler(RefHandler): 298 def dump_ref (self, obj, dumper, s): s.write("i%r\n" % obj) 299 300class LongHandler(RefHandler): 301 def dump_ref (self, obj, dumper, s): s.write("l%r\n" % obj) 302 303class FloatHandler(RefHandler): 304 def dump_ref (self, obj, dumper, s): s.write("f%r\n" % obj) 305 306class ComplexHandler(RefHandler): 307 def dump_ref (self, obj, dumper, s): 308 c = repr(obj) 309 if c.startswith("("): c = c[1:-1] # complex("(1+2j)") doesn't work 310 s.write("c%s\n" % c) 311 312 313class TupleHandler(Handler): 314 classname = "tuple\n" 315 def collect(self, obj, dumper): 316 if not id(obj) in dumper.objs_id: 317 dumper.priorities_objs.append((dumper.immutable_depth(obj), obj)) 318 dumper.objs_id.add(id(obj)) 319 320 for i in obj: dumper.collect(i) 321 return 1 322 323 def dump_obj(self, obj, dumper, s): 324 s.write("%s%s\n" % (self.classname, len(obj))) 325 for i in obj: _HANDLERS_[i.__class__].dump_ref(i, dumper, s) 326 327 def undump_obj(self, dumper, s): return tuple([dumper.undump_ref(s) for i in range(int(s.readline()))]) 328 329class FrozensetHandler(TupleHandler): 330 classname = "frozenset\n" 331 def undump_obj(self, dumper, s): return frozenset([dumper.undump_ref(s) for i in range(int(s.readline()))]) 332 333 334class ListHandler(Handler): 335 classname = "list\n" 336 def collect(self, obj, dumper): 337 if Handler.collect(self, obj, dumper): 338 for i in obj: dumper.collect(i) 339 return 1 340 341 def dump_data(self, obj, dumper, s): 342 s.write("%s\n" % len(obj)) 343 for i in obj: _HANDLERS_[i.__class__].dump_ref(i, dumper, s) 344 345 def undump_obj(self, dumper, s): return [] 346 347 def undump_data(self, obj, dumper, s): 348 for i in range(int(s.readline())): obj.append(dumper.undump_ref(s)) 349 350class SetHandler(ListHandler): 351 classname = "set\n" 352 def undump_obj(self, dumper, s): return set() 353 def undump_data(self, obj, dumper, s): 354 for i in range(int(s.readline())): obj.add(dumper.undump_ref(s)) 355 356class DictHandler(Handler): 357 classname = "dict\n" 358 def collect(self, obj, dumper): 359 if Handler.collect(self, obj, dumper): 360 for i in obj.iterkeys (): dumper.collect(i) # Collect is not ordered 361 for i in obj.itervalues(): dumper.collect(i) 362 return 1 363 364 def dump_data(self, obj, dumper, s): 365 s.write("%s\n" % len(obj)) 366 for k, v in obj.iteritems(): 367 _HANDLERS_[v.__class__].dump_ref(v, dumper, s) # Value is saved fist 368 _HANDLERS_[k.__class__].dump_ref(k, dumper, s) 369 370 def undump_obj(self, dumper, s): return {} 371 372 def undump_data(self, obj, dumper, s): 373 for i in range(int(s.readline())): 374 obj[dumper.undump_ref(s)] = dumper.undump_ref(s) # Value is read fist 375 376 377class ObjHandler(Handler): 378 """ObjHandler 379 380A Cerealizer Handler that can support any new-style class instances, old-style class instances 381as well as C-defined types (although it may not save the C-side data).""" 382 def __init__(self, Class, classname = ""): 383 self.Class = Class 384 self.Class_new = getattr(Class, "__new__" , instance) 385 self.Class_getstate = getattr(Class, "__getstate__", None) # Check for and store __getstate__ and __setstate__ now 386 self.Class_setstate = getattr(Class, "__setstate__", None) # so we are are they are not modified in the class or the object 387 if classname: self.classname = "%s\n" % classname 388 else: self.classname = "%s.%s\n" % (Class.__module__, Class.__name__) 389 390 def collect(self, obj, dumper): 391 i = id(obj) 392 if not i in dumper.objs_id: 393 dumper.priorities_objs.append((-1, obj)) 394 dumper.objs_id.add(i) 395 396 if self.Class_getstate: state = self.Class_getstate(obj) 397 else: state = obj.__dict__ 398 dumper.obj2state[i] = state 399 dumper.collect(state) 400 return 1 401 402 def dump_data(self, obj, dumper, s): 403 i = dumper.obj2state[id(obj)] 404 _HANDLERS_[i.__class__].dump_ref(i, dumper, s) 405 406 def undump_obj(self, dumper, s): return self.Class_new(self.Class) 407 408 def undump_data(self, obj, dumper, s): 409 if self.Class_setstate: self.Class_setstate(obj, dumper.undump_ref(s)) 410 else: obj.__dict__ = dumper.undump_ref(s) 411 412class SlotedObjHandler(ObjHandler): 413 """SlotedObjHandler 414 415A Cerealizer Handler that can support new-style class instances with __slot__.""" 416 def __init__(self, Class, classname = ""): 417 ObjHandler.__init__(self, Class, classname) 418 self.Class_slots = Class.__slots__ 419 420 def collect(self, obj, dumper): 421 i = id(obj) 422 if not i in dumper.objs_id: 423 dumper.priorities_objs.append((-1, obj)) 424 dumper.objs_id.add(i) 425 426 if self.Class_getstate: state = self.Class_getstate(obj) 427 else: state = dict([(slot, getattr(obj, slot, None)) for slot in self.Class_slots]) 428 dumper.obj2state[i] = state 429 dumper.collect(state) 430 return 1 431 432 def undump_data(self, obj, dumper, s): 433 if self.Class_setstate: self.Class_setstate(obj, dumper.undump_ref(s)) 434 else: 435 state = dumper.undump_ref(s) 436 for slot in self.Class_slots: setattr(obj, slot, state[slot]) 437 438class InitArgsObjHandler(ObjHandler): 439 """InitArgsObjHandler 440 441A Cerealizer Handler that can support class instances with __getinitargs__.""" 442 def __init__(self, Class, classname = ""): 443 ObjHandler.__init__(self, Class, classname) 444 self.Class_getinitargs = Class.__getinitargs__ 445 self.Class_init = Class.__init__ 446 447 def collect(self, obj, dumper): 448 i = id(obj) 449 if not i in dumper.objs_id: 450 dumper.priorities_objs.append((-1, obj)) 451 dumper.objs_id.add(i) 452 453 dumper.obj2state[i] = state = self.Class_getinitargs(obj) 454 dumper.collect(state) 455 return 1 456 457 def undump_data(self, obj, dumper, s): self.Class_init(obj, *dumper.undump_ref(s)) 458 459class NewArgsObjHandler(ObjHandler): 460 """NewArgsObjHandler 461 462A Cerealizer Handler that can support class instances with __getnewargs__.""" 463 def __init__(self, Class, classname = ""): 464 ObjHandler.__init__(self, Class, classname) 465 self.Class_getnewargs = Class.__getnewargs__ 466 467 def collect(self, obj, dumper): 468 i = id(obj) 469 if not i in dumper.objs_id: 470 dumper.obj2newargs[i] = newargs = self.Class_getnewargs(obj) 471 dumper.collect(newargs) 472 473 dumper.priorities_objs.append((dumper.immutable_depth(newargs), obj)) 474 dumper.objs_id.add(i) 475 476 if self.Class_getstate: state = self.Class_getstate(obj) 477 else: state = obj.__dict__ 478 dumper.obj2state[i] = state 479 dumper.collect(state) 480 return 1 481 482 def dump_obj (self, obj, dumper, s): 483 s.write(self.classname) 484 newargs = dumper.obj2newargs[id(obj)] 485 _HANDLERS_[newargs.__class__].dump_ref(newargs, dumper, s) 486 487 def undump_obj(self, dumper, s): return self.Class_new(self.Class, *dumper.undump_ref(s)) 488 489 490_configurable = 1 491_HANDLERS = {} 492_HANDLERS_ = {} 493def register(Class, handler = None, classname = ""): 494 """register(Class, handler = None, classname = "") 495 496Registers CLASS as a serializable and secure class. 497By calling register, YOU HAVE TO ASSUME THAT THE FOLLOWING METHODS ARE SECURE: 498 - CLASS.__new__ 499 - CLASS.__del__ 500 - CLASS.__getstate__ 501 - CLASS.__setstate__ 502 - CLASS.__getinitargs__ 503 - CLASS.__init__ (only if CLASS.__getinitargs__ exists) 504 505HANDLER is the Cerealizer Handler object that handles serialization and deserialization for Class. 506If not given, Cerealizer create an instance of ObjHandler, which is suitable for old-style and 507new_style Python class, and also C-defined types (although if it has some C-side data, you may 508have to write a custom Handler or a __getstate__ and __setstate__ pair). 509 510CLASSNAME is the classname used in Cerealizer files. It defaults to the full classname (module.class) 511but you may choose something shorter -- as long as there is no risk of name clash.""" 512 if not _configurable: raise StandardError("Cannot register new classes after freeze_configuration has been called!") 513 if "\n" in classname: raise ValueError("CLASSNAME cannot have \\n (Cerealizer automatically add a trailing \\n for performance reason)!") 514 if not handler: 515 if hasattr(Class, "__getnewargs__" ): handler = NewArgsObjHandler (Class, classname) 516 elif hasattr(Class, "__getinitargs__"): handler = InitArgsObjHandler(Class, classname) 517 elif hasattr(Class, "__slots__" ): handler = SlotedObjHandler (Class, classname) 518 else: handler = ObjHandler (Class, classname) 519 if _HANDLERS_.has_key(Class): raise ValueError("Class %s has already been registred!" % Class) 520 if not isinstance(handler, RefHandler): 521 if _HANDLERS .has_key(handler.classname): raise ValueError("A class has already been registred under the name %s!" % handler.classname[:-1]) 522 _HANDLERS [handler.classname] = handler 523 if handler.__class__ is ObjHandler: 524 logger.info("Registring class %s as '%s'" % (Class, handler.classname[:-1])) 525 else: 526 logger.info("Registring class %s as '%s' (using %s)" % (Class, handler.classname[:-1], handler.__class__.__name__)) 527 else: 528 logger.info("Registring reference '%s'" % Class) 529 530 _HANDLERS_[Class] = handler 531 532register_class = register # For backward compatibility 533 534def register_alias(Class, alias): 535 """register_alias(Class, alias) 536 537Registers ALIAS as an alias classname for CLASS. 538Usefull for keeping backward compatibility in files: e.g. if you have renamed OldClass to 539NewClass, just do: 540 541 cerealizer.register_alias(NewClass, "OldClass") 542 543and you'll be able to open old files containing OldClass serialized.""" 544 handler = _HANDLERS_.get(Class) 545 if not handler: 546 raise ValueError("Cannot register alias '%s' to Class %s: the class is not yet registred!" % (alias, Class)) 547 if _HANDLERS.has_key(alias): 548 raise ValueError("Cannot register alias '%s' to Class %s: another class is already registred under the alias name!" % (alias, Class)) 549 logger.info("Registring alias '%s' for %s" % (alias, Class)) 550 _HANDLERS[alias + "\n"] = handler 551 552 553def freeze_configuration(): 554 """freeze_configuration() 555 556Ends Cerealizer configuration. When freeze_configuration() is called, it is no longer possible 557to register classes, using register(). 558Calling freeze_configuration() is not mandatory, but it may enforce security, by forbidding 559unexpected calls to register().""" 560 global _configurable 561 _configurable = 0 562 logger.info("Configuration frozen") 563 564register(type(None), NoneHandler ()) 565register(str , StrHandler ()) 566register(unicode , UnicodeHandler ()) 567register(bool , BoolHandler ()) 568register(int , IntHandler ()) 569register(long , LongHandler ()) 570register(float , FloatHandler ()) 571register(complex , ComplexHandler ()) 572register(dict , DictHandler ()) 573register(list , ListHandler ()) 574register(set , SetHandler ()) 575register(tuple , TupleHandler ()) 576register(frozenset , FrozensetHandler()) 577 578 579def dump(obj, file, protocol = 0): 580 """dump(obj, file, protocol = 0) 581 582Serializes object OBJ in FILE. 583FILE should be an opened file in *** binary *** mode. 584PROTOCOL is unused, it exists only for compatibility with Pickle.""" 585 Dumper().dump(obj, file) 586 587def load(file): 588 """load(file) -> obj 589 590De-serializes an object from FILE. 591FILE should be an opened file in *** binary *** mode.""" 592 return Dumper().undump(file) 593 594def dumps(obj, protocol = 0): 595 """dumps(obj, protocol = 0) -> str 596 597Serializes object OBJ and returns the serialized string. 598PROTOCOL is unused, it exists only for compatibility with Pickle.""" 599 s = StringIO() 600 Dumper().dump(obj, s) 601 return s.getvalue() 602 603def loads(string): 604 """loads(file) -> obj 605 606De-serializes an object from STRING.""" 607 return Dumper().undump(StringIO(string)) 608 609 610def dump_class_of_module(*modules): 611 """dump_class_of_module(*modules) 612 613Utility function; for each classes found in the given module, print the needed call to register.""" 614 class D: pass 615 class O(object): pass 616 s = set([c for module in modules for c in module.__dict__.values() if isinstance(c, type(D)) or isinstance(c, type(O))]) 617 l = ['cerealizer.register(%s.%s)' % (c.__module__, c.__name__) for c in s] 618 l.sort() 619 for i in l: print i 620 621