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