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)