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