Add hooke.plugin.curve (moved from hooke.plugin.curvetools)
[hooke.git] / hooke / plugin / __init__.py
1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of Hooke.
4 #
5 # Hooke is free software: you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation, either
8 # version 3 of the License, or (at your option) any later version.
9 #
10 # Hooke is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
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/>.
18
19 """The `plugin` module provides optional submodules that add new Hooke
20 commands.
21
22 All of the science happens in here.
23 """
24
25 import ConfigParser as configparser
26
27 from ..config import Setting
28 from ..util.pluggable import IsSubclass, construct_graph
29
30
31 PLUGIN_MODULES = [
32 #    ('autopeak', True),
33 #    ('curvetools', True),
34     ('cut', True),
35 #    ('fit', True),
36 #    ('flatfilts-rolf', True),
37 #    ('flatfilts', True),
38 #    ('generalclamp', True),
39 #    ('generaltccd', True),
40 #    ('generalvclamp', True),
41 #    ('jumpstat', True),
42 #    ('macro', True),
43 #    ('massanalysis', True),
44 #    ('multidistance', True),
45 #    ('multifit', True),
46 #    ('pcluster', True),
47 #    ('peakspot', True),
48 #    ('procplots', True),
49 #    ('review', True),
50 #    ('showconvoluted', True),
51 #    ('superimpose', True),
52 #    ('tutorial', True),
53     ]
54 """List of plugin modules and whether they should be included by
55 default.  TODO: autodiscovery
56 """
57
58 BUILTIN_MODULES = [
59     'config',
60     'curve',
61     'debug',
62     'note',
63     'playlist',
64     'system',
65     ]
66 """List of builtin modules.  TODO: autodiscovery
67 """
68
69 PLUGIN_SETTING_SECTION = 'plugins'
70 """Name of the config section which controls plugin selection.
71 """
72
73
74 # Plugins and settings
75
76 class Plugin (object):
77     """A pluggable collection of Hooke commands.
78
79     Fulfills the same role for Hooke that a software package does for
80     an operating system.
81     """
82     def __init__(self, name):
83         self.name = name
84         self.setting_section = '%s plugin' % self.name
85         self.config = {}
86
87     def dependencies(self):
88         """Return a list of :class:`Plugin`\s we require."""
89         return []
90
91     def default_settings(self):
92         """Return a list of :class:`hooke.config.Setting`\s for any
93         configurable plugin settings.
94
95         The suggested section setting is::
96
97             Setting(section=self.setting_section, help=self.__doc__)
98         """
99         return []
100
101     def commands(self):
102         """Return a list of :class:`hooke.command.Command`\s provided.
103         """
104         return []
105
106 class Builtin (Plugin):
107     """A required collection of Hooke commands.
108
109     These "core" plugins provide essential administrative commands
110     (playlist handling, etc.).
111     """
112     pass
113
114 # Construct plugin dependency graph and load plugin instances.
115
116 PLUGIN_GRAPH = construct_graph(
117     this_modname=__name__,
118     submodnames=[name for name,include in PLUGIN_MODULES] + BUILTIN_MODULES,
119     class_selector=IsSubclass(Plugin, blacklist=[Plugin, Builtin]))
120 """Topologically sorted list of all possible :class:`Plugin`\s and
121 :class:`Builtin`\s.
122 """
123
124 def default_settings():
125     settings = [Setting(PLUGIN_SETTING_SECTION,
126                         help='Enable/disable default plugins.')]
127     for pnode in PLUGIN_GRAPH:
128         if pnode.data.name in BUILTIN_MODULES:
129             continue # builtin inclusion is not optional
130         plugin = pnode.data
131         default_include = [di for mod_name,di in PLUGIN_MODULES
132                            if mod_name == plugin.name][0]
133         help = 'Commands: ' + ', '.join([c.name for c in plugin.commands()])
134         settings.append(Setting(
135                 section=PLUGIN_SETTING_SECTION,
136                 option=plugin.name,
137                 value=str(default_include),
138                 help=help,
139                 ))
140     for pnode in PLUGIN_GRAPH:
141         plugin = pnode.data
142         settings.extend(plugin.default_settings())
143     return settings
144
145 def load_graph(graph, config, include_section):
146     items = []
147     for node in graph:
148         item = node.data
149         try:
150             include = config.getboolean(include_section, item.name)
151         except configparser.NoOptionError:
152             include = True # non-optional include (e.g. a Builtin)
153         if include == True:
154             try:
155                 item.config = dict(config.items(item.setting_section))
156             except configparser.NoSectionError:
157                 pass
158             items.append(item)
159     return items