Ran update_copyright.py, updating all the copyright blurbs and adding AUTHORS.
[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     'debug',
61     'note',
62     'playlist',
63     'system',
64     ]
65 """List of builtin modules.  TODO: autodiscovery
66 """
67
68 PLUGIN_SETTING_SECTION = 'plugins'
69 """Name of the config section which controls plugin selection.
70 """
71
72
73 # Plugins and settings
74
75 class Plugin (object):
76     """A pluggable collection of Hooke commands.
77
78     Fulfills the same role for Hooke that a software package does for
79     an operating system.
80     """
81     def __init__(self, name):
82         self.name = name
83         self.setting_section = '%s plugin' % self.name
84         self.config = {}
85
86     def dependencies(self):
87         """Return a list of :class:`Plugin`\s we require."""
88         return []
89
90     def default_settings(self):
91         """Return a list of :class:`hooke.config.Setting`\s for any
92         configurable plugin settings.
93
94         The suggested section setting is::
95
96             Setting(section=self.setting_section, help=self.__doc__)
97         """
98         return []
99
100     def commands(self):
101         """Return a list of :class:`hooke.command.Command`\s provided.
102         """
103         return []
104
105 class Builtin (Plugin):
106     """A required collection of Hooke commands.
107
108     These "core" plugins provide essential administrative commands
109     (playlist handling, etc.).
110     """
111     pass
112
113 # Construct plugin dependency graph and load plugin instances.
114
115 PLUGIN_GRAPH = construct_graph(
116     this_modname=__name__,
117     submodnames=[name for name,include in PLUGIN_MODULES] + BUILTIN_MODULES,
118     class_selector=IsSubclass(Plugin, blacklist=[Plugin, Builtin]))
119 """Topologically sorted list of all possible :class:`Plugin`\s and
120 :class:`Builtin`\s.
121 """
122
123 def default_settings():
124     settings = [Setting(PLUGIN_SETTING_SECTION,
125                         help='Enable/disable default plugins.')]
126     for pnode in PLUGIN_GRAPH:
127         if pnode.data.name in BUILTIN_MODULES:
128             continue # builtin inclusion is not optional
129         plugin = pnode.data
130         default_include = [di for mod_name,di in PLUGIN_MODULES
131                            if mod_name == plugin.name][0]
132         help = 'Commands: ' + ', '.join([c.name for c in plugin.commands()])
133         settings.append(Setting(
134                 section=PLUGIN_SETTING_SECTION,
135                 option=plugin.name,
136                 value=str(default_include),
137                 help=help,
138                 ))
139     for pnode in PLUGIN_GRAPH:
140         plugin = pnode.data
141         settings.extend(plugin.default_settings())
142     return settings
143
144 def load_graph(graph, config, include_section):
145     items = []
146     for node in graph:
147         item = node.data
148         try:
149             include = config.getboolean(include_section, item.name)
150         except configparser.NoOptionError:
151             include = True # non-optional include (e.g. a Builtin)
152         if include == True:
153             try:
154                 item.config = dict(config.items(item.setting_section))
155             except configparser.NoSectionError:
156                 pass
157             items.append(item)
158     return items