0001"""
0002classresolver.py
0003 2 February 2004, Ian Bicking <ianb@colorstudy.com>
0004
0005Resolves strings to classes, and runs callbacks when referenced
0006classes are created.
0007
0008Classes are referred to only by name, not by module. So that
0009identically-named classes can coexist, classes are put into individual
0010registries, which are keyed on strings (names). These registries are
0011created on demand.
0012
0013Use like::
0014
0015 >>> import classregistry
0016 >>> registry = classregistry.registry('MyModules')
0017 >>> def afterMyClassExists(cls):
0018 ... print 'Class finally exists:', cls
0019 >>> registry.addClassCallback('MyClass', afterMyClassExists)
0020 >>> class MyClass:
0021 ... pass
0022 >>> registry.addClass(MyClass)
0023 Class finally exists: MyClass
0024
0025"""
0026
0027class ClassRegistry(object):
0028 """
0029 We'll be dealing with classes that reference each other, so
0030 class C1 may reference C2 (in a join), while C2 references
0031 C1 right back. Since classes are created in an order, there
0032 will be a point when C1 exists but C2 doesn't. So we deal
0033 with classes by name, and after each class is created we
0034 try to fix up any references by replacing the names with
0035 actual classes.
0036
0037 Here we keep a dictionaries of class names to classes -- note
0038 that the classes might be spread among different modules, so
0039 since we pile them together names need to be globally unique,
0040 to just module unique.
0041 Like needSet below, the container dictionary is keyed by the
0042 class registry.
0043 """
0044
0045 def __init__(self, name):
0046 self.name = name
0047 self.classes = {}
0048 self.callbacks = {}
0049 self.genericCallbacks = []
0050
0051 def addClassCallback(self, className, callback, *args, **kw):
0052 """
0053 Whenever a name is substituted for the class, you can register
0054 a callback that will be called when the needed class is
0055 created. If it's already been created, the callback will be
0056 called immediately.
0057 """
0058 if className in self.classes:
0059 callback(self.classes[className], *args, **kw)
0060 else:
0061 self.callbacks.setdefault(className, []).append((callback, args, kw))
0062
0063 def addCallback(self, callback, *args, **kw):
0064 """
0065 This callback is called for all classes, not just specific
0066 ones (like addClassCallback).
0067 """
0068 self.genericCallbacks.append((callback, args, kw))
0069 for cls in self.classes.values():
0070 callback(cls, *args, **kw)
0071
0072 def addClass(self, cls):
0073 """
0074 Everytime a class is created, we add it to the registry, so
0075 that other classes can find it by name. We also call any
0076 callbacks that are waiting for the class.
0077 """
0078 if cls.__name__ in self.classes:
0079 import sys
0080 other = self.classes[cls.__name__]
0081 raise ValueError(
0082 "class %s is already in the registry (other class is "
0083 "%r, from the module %s in %s; attempted new class is "
0084 "%r, from the module %s in %s)"
0085 % (cls.__name__,
0086 other, other.__module__,
0087 getattr(sys.modules.get(other.__module__),
0088 '__file__', '(unknown)'),
0089 cls, cls.__module__,
0090 getattr(sys.modules.get(cls.__module__),
0091 '__file__', '(unknown)')))
0092 self.classes[cls.__name__] = cls
0093 if cls.__name__ in self.callbacks:
0094 for callback, args, kw in self.callbacks[cls.__name__]:
0095 callback(cls, *args, **kw)
0096 del self.callbacks[cls.__name__]
0097 for callback, args, kw in self.genericCallbacks:
0098 callback(cls, *args, **kw)
0099
0100 def getClass(self, className):
0101 try:
0102 return self.classes[className]
0103 except KeyError:
0104 all = self.classes.keys()
0105 all.sort()
0106 raise KeyError(
0107 "No class %s found in the registry %s (these classes "
0108 "exist: %s)"
0109 % (className, self.name or '[default]', ', '.join(all)))
0110
0111 def allClasses(self):
0112 return self.classes.values()
0113
0114class _MasterRegistry(object):
0115 """
0116 This singleton holds all the class registries. There can be
0117 multiple registries to hold different unrelated sets of classes
0118 that reside in the same process. These registries are named with
0119 strings, and are created on demand. The MasterRegistry module
0120 global holds the singleton.
0121 """
0122
0123 def __init__(self):
0124 self.registries = {}
0125
0126 def registry(self, item):
0127 if item not in self.registries:
0128 self.registries[item] = ClassRegistry(item)
0129 return self.registries[item]
0130
0131MasterRegistry = _MasterRegistry()
0132registry = MasterRegistry.registry
0133
0134def findClass(name, class_registry=None):
0135 return registry(class_registry).getClass(name)