Add dependency checking to hooke.plugin.load_graph
authorW. Trevor King <wking@drexel.edu>
Tue, 3 Aug 2010 12:09:18 +0000 (08:09 -0400)
committerW. Trevor King <wking@drexel.edu>
Tue, 3 Aug 2010 12:09:18 +0000 (08:09 -0400)
hooke/plugin/__init__.py
hooke/plugin/tutorial.py

index f589e3591fbc5dfc7ce9a892d9d8b0682a3b5442..052284d84317dd3c3c86c8b4ae7b2e319b6ae544 100644 (file)
@@ -23,6 +23,7 @@ All of the science happens in here.
 """
 
 import ConfigParser as configparser
+import logging
 
 from ..config import Setting
 from ..util.pluggable import IsSubclass, construct_graph
@@ -78,7 +79,9 @@ class Plugin (object):
     """A pluggable collection of Hooke commands.
 
     Fulfills the same role for Hooke that a software package does for
-    an operating system.
+    an operating system: contains a chunk of related commands and
+    routines.  Command configuration also happens on the `Plugin`
+    level, with per-plugin sections in the configuration file.
     """
     def __init__(self, name):
         self.name = name
@@ -105,6 +108,7 @@ class Plugin (object):
         """
         return list(self._commands)
 
+
 class Builtin (Plugin):
     """A required collection of Hooke commands.
 
@@ -113,6 +117,7 @@ class Builtin (Plugin):
     """
     pass
 
+
 # Plugin utility functions
 
 def argument_to_setting(section_name, argument):
@@ -126,6 +131,7 @@ def argument_to_setting(section_name, argument):
     return Setting(section_name, option=argument.name, value=argument.default,
                    help=argument._help)
 
+
 # Construct plugin dependency graph and load plugin instances.
 
 PLUGIN_GRAPH = construct_graph(
@@ -158,6 +164,7 @@ def default_settings():
     return settings
 
 def load_graph(graph, config, include_section):
+    enabled = {}
     items = []
     for node in graph:
         item = node.data
@@ -165,7 +172,17 @@ def load_graph(graph, config, include_section):
             include = config.getboolean(include_section, item.name)
         except configparser.NoOptionError:
             include = True # non-optional include (e.g. a Builtin)
+        enabled[item.name] = include
+        print item.name, include
         if include == True:
+            for dependency in node:
+                if enabled.get(dependency.data.name, None) != True:
+                    log = logging.getLogger('hooke')
+                    log.warn(
+                     'could not setup plugin %s.  unsatisfied dependency on %s'.
+                     % (item.name, dependency.data.name))
+                    enabled[item.name] = False
+                    continue
             try:
                 item.config = dict(config.items(item.setting_section))
             except configparser.NoSectionError:
index 9f0fb5181658651139c9fa0af2ebedecb13d3848..3fc9319930f821d0ea0ee501a409070bb03b43f9 100644 (file)
 Hooke plugin, including description of main Hooke internals.
 """
 
-import numpy as np
-
-from .. import curve as lhc
-
-'''
-SYNTAX OF DATA TYPE DECLARATION:
-    type = type of object
-    [ type ] = list containing objects of type
-    {typekey:typearg} = dictionary with keys of type typekey and args of type typearg
-    ( type ) = tuple containing objects of type
-'''
-
-
-class tutorialCommands(object):
-    '''
-    Here we define the class containing all the Hooke commands we want to define
-    in the plugin.
-
-    Notice the class name!!
-    The syntax is filenameCommands. That is, if your plugin is pluggy.py, your class
-    name is pluggyCommands.
-
-    Otherwise, the class will be ignored by Hooke.
-    '''
-
-    def _plug_init(self):
-        '''
-        This is the plugin initialization.
-        When Hooke starts and the plugin is loaded, this function is executed.
-        If there is something you need to do when Hooke starts, code it in this function.
-        '''
+from numpy import arange
+
+from ..command import Command, Argument, Failure
+from ..playlist import FilePlaylist
+from ..plugin import Plugin
+
+
+class TutorialPlugin (Plugin):
+    """An example plugin explaining how to code plugins.
+
+    Unlike previous versions of Hooke, the class name is no longer
+    important.  Plugins identify themselves to
+    :func:`hooke.util.pluggable.construct_graph` by being subclasses
+    of :class:`hooke.plugin.Plugin`.  However, for consistency we
+    suggest the following naming scheme, show here for the 'tutorial'
+    plugin:
+
+    ===========  ==============
+    module file  tutorial.py
+    class name   TutorialPlugin
+    .name        'tutorial'
+    ===========  ==============
+
+    To ensure filename sanity,
+    :func:`hooke.util.pluggable.construct_graph` requires that
+    :attr:`name` does match the submodule name, but don't worry,
+    you'll get a clear exception message if you make a mistake.
+    """
+    def __init__(self):
+        """TutorialPlugin initialization code.
+
+        We call our base class' :meth:`__init__` and setup
+        :attr:`_commands`.
+        """
+        # This is the plugin initialization.  When Hooke starts and
+        # the plugin is loaded, this function is executed.  If there
+        # is something you need to do when Hooke starts, code it in
+        # this function.
         print 'I am the Tutorial plugin initialization!'
 
+        # This super() call similar to the old-style
+        #   Plugin.__init__
+        # but super() is more robust under multiple inheritance.
+        # See Guido's introduction:
+        #   http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
+        # And the related PEPs:
+        #   http://www.python.org/dev/peps/pep-0253/
+        #   http://www.python.org/dev/peps/pep-3135/
+        super(TutorialPlugin, self).__init__(name='tutorial')
+
+        # We want :meth:`commands` to return a list of
+        # :class:`hooke.command.Command` instances.  Rather than
+        # instantiate the classes for each call to :meth:`commands`,
+        # we instantiate them in a list here, and rely on
+        # :meth:`hooke.plugin.Plugin.commands` to return copies of
+        # that list.
+        self._commands = [] #CommandInit]
+
+
+    def dependencies(self):
+        """Return a list  of names of :class:`hooke.plugin.Plugin`\s we
+        require.
+
+        Some plugins use features from other plugins.  Hooke makes sure that
+        plugins are configured in topological order and that no plugin is
+        enabled if it is missing dependencies.
+        """
+        return ['vclamp']
         #Here we initialize a local configuration variable; see plotmanip_absvalue() for explanation.
         self.config['tutorial_absvalue']=0
-        pass
 
     def do_nothing(self,args):
         '''
@@ -210,8 +244,8 @@ class tutorialCommands(object):
         '''
 
         #We generate some interesting data to plot for this example.
-        xdata1=np.arange(-5,5,0.1)
-        xdata2=np.arange(-5,5,0.1)
+        xdata1=arange(-5,5,0.1)
+        xdata2=arange(-5,5,0.1)
         ydata1=[item**2 for item in xdata1]
         ydata2=[item**3 for item in xdata2]
 
@@ -265,8 +299,8 @@ class tutorialCommands(object):
         How to draw a scatter plot.
         '''
         #We generate some interesting data to plot for this example.
-        xdata1=np.arange(-5,5,1)
-        xdata2=np.arange(-5,5,1)
+        xdata1=arange(-5,5,1)
+        xdata2=arange(-5,5,1)
         ydata1=[item**2 for item in xdata1]
         ydata2=[item**3 for item in xdata2]