Run update-copyright.py.
[hooke.git] / hooke / util / pluggable.py
index 4f595c04224d2d9ce5034ee98084e5c78dec2dca..a3c2d5b8a16427b13d8491114c9829bd349d0c73 100644 (file)
@@ -1,7 +1,27 @@
-"""`pluggable`
+# Copyright (C) 2010-2012 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of Hooke.
+#
+# Hooke is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# Hooke is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Hooke.  If not, see <http://www.gnu.org/licenses/>.
+
+"""``pluggable`` provides utility functions for extensible plugin modules.
 """
 
-from ..util.graph import Node, Graph
+import logging
+
+from ..compat.odict import odict
+from .graph import Node, Graph
 
 
 class IsSubclass (object):
@@ -40,6 +60,53 @@ class IsSubclass (object):
             return False
         return subclass
 
+
+def submods(this_modname, submodnames):
+    """Iterate through (submodname, submod) pairs.
+    """
+    for submodname in submodnames:
+        count = len([s for s in submodnames if s == submodname])
+        assert count > 0, 'No %s entries: %s' % (submodname, submodnames)
+        assert count == 1, 'Multiple (%d) %s entries: %s' \
+            % (count, submodname, submodnames)
+        try:
+            this_mod = __import__(this_modname, fromlist=[submodname])
+        except ImportError, e:
+            # Use the root logger because the 'hooke' logger is
+            # configured by a Hooke instance after module imports.
+            logging.warn('could not import %s from %s: %s'
+                         % (submodname, this_modname, e))
+            continue
+        submod = getattr(this_mod, submodname)
+        yield (submodname, submod)
+
+
+def construct_odict(this_modname, submodnames, class_selector,
+                    instantiate=True):
+    """Search the submodules `submodnames` of a module `this_modname`
+    for class objects for which `class_selector(class)` returns
+    `True`.  If `instantiate == True` these classes are instantiated
+    and stored in the returned :class:`hooke.compat.odict.odict` in
+    the order in which they were discovered.  Otherwise, the class
+    itself is stored.
+    """
+    objs = odict()
+    for submodname,submod in submods(this_modname, submodnames):
+        for objname in dir(submod):
+            obj = getattr(submod, objname)
+            if class_selector(obj):
+                if instantiate == True:
+                    try:
+                        obj = obj()
+                    except Exception, e:
+                        logging.error('could not instantiate %s from %s: %s'
+                                      % (obj, submodname, e))
+                        raise
+                name = getattr(obj, 'name', submodname)
+                objs[name] = obj
+    return objs
+
+
 def construct_graph(this_modname, submodnames, class_selector,
                     assert_name_match=True):
     """Search the submodules `submodnames` of a module `this_modname`
@@ -54,17 +121,16 @@ def construct_graph(this_modname, submodnames, class_selector,
     is returned.
     """
     instances = {}
-    for submodname in submodnames:
-        count = len([s for s in submodnames if s == submodname])
-        assert count > 0, 'No %s entries: %s' % (submodname, submodnames)
-        assert count == 1, 'Multiple (%d) %s entries: %s' \
-            % (count, submodname, submodnames)
-        this_mod = __import__(this_modname, fromlist=[submodname])
-        submod = getattr(this_mod, submodname)
+    for submodname,submod in submods(this_modname, submodnames):
         for objname in dir(submod):
             obj = getattr(submod, objname)
             if class_selector(obj):
-                instance = obj()
+                try:
+                    instance = obj()
+                except Exception, e:
+                    logging.error('could not instantiate %s from %s: %s'
+                                  % (obj, submodname, e))
+                    raise
                 if assert_name_match == True and instance.name != submodname:
                     raise Exception(
                         'Instance name %s does not match module name %s'