4f595c04224d2d9ce5034ee98084e5c78dec2dca
[hooke.git] / hooke / util / pluggable.py
1 """`pluggable`
2 """
3
4 from ..util.graph import Node, Graph
5
6
7 class IsSubclass (object):
8     """A safe subclass comparator.
9     
10     Examples
11     --------
12
13     >>> class A (object):
14     ...     pass
15     >>> class B (A):
16     ...     pass
17     >>> C = 5
18     >>> is_subclass = IsSubclass(A)
19     >>> is_subclass(A)
20     True
21     >>> is_subclass = IsSubclass(A, blacklist=[A])
22     >>> is_subclass(A)
23     False
24     >>> is_subclass(B)
25     True
26     >>> is_subclass(C)
27     False
28     """
29     def __init__(self, base_class, blacklist=None):
30         self.base_class = base_class
31         if blacklist == None:
32             blacklist = []
33         self.blacklist = blacklist
34     def __call__(self, other):
35         try:
36             subclass = issubclass(other, self.base_class)
37         except TypeError:
38             return False
39         if other in self.blacklist:
40             return False
41         return subclass
42
43 def construct_graph(this_modname, submodnames, class_selector,
44                     assert_name_match=True):
45     """Search the submodules `submodnames` of a module `this_modname`
46     for class objects for which `class_selector(class)` returns
47     `True`.  These classes are instantiated, and the `instance.name`
48     is compared to the `submodname` (if `assert_name_match` is
49     `True`).
50
51     The instances are further arranged into a dependency
52     :class:`hooke.util.graph.Graph` according to their
53     `instance.dependencies()` values.  The topologically sorted graph
54     is returned.
55     """
56     instances = {}
57     for submodname in submodnames:
58         count = len([s for s in submodnames if s == submodname])
59         assert count > 0, 'No %s entries: %s' % (submodname, submodnames)
60         assert count == 1, 'Multiple (%d) %s entries: %s' \
61             % (count, submodname, submodnames)
62         this_mod = __import__(this_modname, fromlist=[submodname])
63         submod = getattr(this_mod, submodname)
64         for objname in dir(submod):
65             obj = getattr(submod, objname)
66             if class_selector(obj):
67                 instance = obj()
68                 if assert_name_match == True and instance.name != submodname:
69                     raise Exception(
70                         'Instance name %s does not match module name %s'
71                         % (instance.name, submodname))
72                 instances[instance.name] = instance
73     nodes = {}
74     for i in instances.values():     # make nodes for each instance
75         nodes[i.name] = Node(data=i)
76     for n in nodes.values():         # fill in dependencies for each node
77         n.extend([nodes[name] for name in n.data.dependencies()])
78     graph = Graph(nodes.values())
79     graph.topological_sort()
80     return graph