From 7581d6568dc58de48c847c8b702d060101a903f3 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 3 Aug 2010 08:09:18 -0400 Subject: [PATCH] Add dependency checking to hooke.plugin.load_graph --- hooke/plugin/__init__.py | 19 ++++++- hooke/plugin/tutorial.py | 106 ++++++++++++++++++++++++++------------- 2 files changed, 88 insertions(+), 37 deletions(-) diff --git a/hooke/plugin/__init__.py b/hooke/plugin/__init__.py index f589e35..052284d 100644 --- a/hooke/plugin/__init__.py +++ b/hooke/plugin/__init__.py @@ -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: diff --git a/hooke/plugin/tutorial.py b/hooke/plugin/tutorial.py index 9f0fb51..3fc9319 100644 --- a/hooke/plugin/tutorial.py +++ b/hooke/plugin/tutorial.py @@ -21,42 +21,76 @@ 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] -- 2.26.2