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)