1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
3 # This file is part of Hooke.
5 # Hooke is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 # Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke. If not, see
17 # <http://www.gnu.org/licenses/>.
19 """The `plugin` module provides optional submodules that add new Hooke
22 All of the science happens in here.
25 import ConfigParser as configparser
28 from ..config import Setting
29 from ..util.pluggable import IsSubclass, construct_graph
37 # ('flatfilts-rolf', True),
40 # ('massanalysis', True),
41 # ('multidistance', True),
44 ('polymer_fit', True),
45 # ('procplots', True),
47 # ('showconvoluted', True),
48 # ('superimpose', True),
53 """List of plugin modules and whether they should be included by
54 default. TODO: autodiscovery
69 """List of builtin modules. TODO: autodiscovery
72 PLUGIN_SETTING_SECTION = 'plugins'
73 """Name of the config section which controls plugin selection.
77 # Plugins and settings
79 class Plugin (object):
80 """A pluggable collection of Hooke commands.
82 Fulfills the same role for Hooke that a software package does for
83 an operating system: contains a chunk of related commands and
84 routines. Command configuration also happens on the `Plugin`
85 level, with per-plugin sections in the configuration file.
87 def __init__(self, name):
89 self.setting_section = '%s plugin' % self.name
93 def dependencies(self):
94 """Return a list of names of :class:`Plugin`\s we require."""
97 def default_settings(self):
98 """Return a list of :class:`hooke.config.Setting`\s for any
99 configurable plugin settings.
101 The suggested section setting is::
103 Setting(section=self.setting_section, help=self.__doc__)
108 """Return a list of :class:`hooke.command.Command`\s provided.
110 return list(self._commands)
113 class Builtin (Plugin):
114 """A required collection of Hooke commands.
116 These "core" plugins provide essential administrative commands
117 (playlist handling, etc.).
122 # Plugin utility functions
124 def argument_to_setting(section_name, argument):
125 """Convert an :class:`~hooke.command.Argument` to a
126 `~hooke.conf.Setting`.
128 This is useful if, for example, you want to define arguments with
129 configurable default values.
131 Conversion is lossy transition, because
132 :class:`~hooke.command.Argument`\s store more information than
133 `~hooke.conf.Setting`\s.
135 return Setting(section_name, option=argument.name, value=argument.default,
136 type=argument.type, count=argument.count,
140 # Construct plugin dependency graph and load plugin instances.
142 PLUGIN_GRAPH = construct_graph(
143 this_modname=__name__,
144 submodnames=[name for name,include in PLUGIN_MODULES] + BUILTIN_MODULES,
145 class_selector=IsSubclass(Plugin, blacklist=[Plugin, Builtin]))
146 """Topologically sorted list of all possible :class:`Plugin`\s and
150 def default_settings():
151 settings = [Setting(PLUGIN_SETTING_SECTION,
152 help='Enable/disable default plugins.')]
153 for pnode in PLUGIN_GRAPH:
154 if pnode.data.name in BUILTIN_MODULES:
155 continue # builtin inclusion is not optional
157 default_include = [di for mod_name,di in PLUGIN_MODULES
158 if mod_name == plugin.name][0]
159 help = 'Commands: ' + ', '.join([c.name for c in plugin.commands()])
160 settings.append(Setting(
161 section=PLUGIN_SETTING_SECTION,
163 value=str(default_include),
166 for pnode in PLUGIN_GRAPH:
168 settings.extend(plugin.default_settings())
171 def load_graph(graph, config, include_section):
177 include = config.getboolean(include_section, item.name)
178 except configparser.NoOptionError:
179 include = True # non-optional include (e.g. a Builtin)
180 enabled[item.name] = include
182 for dependency in node:
183 if enabled.get(dependency.data.name, None) != True:
184 log = logging.getLogger('hooke')
186 'could not setup plugin %s. unsatisfied dependency on %s.'
187 % (item.name, dependency.data.name))
188 enabled[item.name] = False