Add hooke.plugin.argument_to_setting to convert Arguments -> Settings.
[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     ('convfilt', True),
35     ('cut', True),
36 #    ('fit', True),
37 #    ('flatfilts-rolf', True),
38     ('flatfilt', True),
39 #    ('generalclamp', True),
40 #    ('generaltccd', True),
41 #    ('generalvclamp', True),
42 #    ('jumpstat', True),
43 #    ('macro', True),
44 #    ('massanalysis', True),
45 #    ('multidistance', True),
46 #    ('multifit', True),
47 #    ('pcluster', True),
48 #    ('peakspot', True),
49 #    ('procplots', True),
50 #    ('review', True),
51 #    ('showconvoluted', True),
52 #    ('superimpose', True),
53 #    ('tutorial', True),
54     ]
55 """List of plugin modules and whether they should be included by
56 default.  TODO: autodiscovery
57 """
58
59 BUILTIN_MODULES = [
60     'config',
61     'curve',
62     'debug',
63     'note',
64     'playlist',
65     'system',
66     ]
67 """List of builtin modules.  TODO: autodiscovery
68 """
69
70 PLUGIN_SETTING_SECTION = 'plugins'
71 """Name of the config section which controls plugin selection.
72 """
73
74
75 # Plugins and settings
76
77 class Plugin (object):
78     """A pluggable collection of Hooke commands.
79
80     Fulfills the same role for Hooke that a software package does for
81     an operating system.
82     """
83     def __init__(self, name):
84         self.name = name
85         self.setting_section = '%s plugin' % self.name
86         self.config = {}
87         self._commands = []
88
89     def dependencies(self):
90         """Return a list of names of :class:`Plugin`\s we require."""
91         return []
92
93     def default_settings(self):
94         """Return a list of :class:`hooke.config.Setting`\s for any
95         configurable plugin settings.
96
97         The suggested section setting is::
98
99             Setting(section=self.setting_section, help=self.__doc__)
100         """
101         return []
102
103     def commands(self):
104         """Return a list of :class:`hooke.command.Command`\s provided.
105         """
106         return list(self._commands)
107
108 class Builtin (Plugin):
109     """A required collection of Hooke commands.
110
111     These "core" plugins provide essential administrative commands
112     (playlist handling, etc.).
113     """
114     pass
115
116 # Construct plugin dependency graph and load plugin instances.
117
118 PLUGIN_GRAPH = construct_graph(
119     this_modname=__name__,
120     submodnames=[name for name,include in PLUGIN_MODULES] + BUILTIN_MODULES,
121     class_selector=IsSubclass(Plugin, blacklist=[Plugin, Builtin]))
122 """Topologically sorted list of all possible :class:`Plugin`\s and
123 :class:`Builtin`\s.
124 """
125
126 def default_settings():
127     settings = [Setting(PLUGIN_SETTING_SECTION,
128                         help='Enable/disable default plugins.')]
129     for pnode in PLUGIN_GRAPH:
130         if pnode.data.name in BUILTIN_MODULES:
131             continue # builtin inclusion is not optional
132         plugin = pnode.data
133         default_include = [di for mod_name,di in PLUGIN_MODULES
134                            if mod_name == plugin.name][0]
135         help = 'Commands: ' + ', '.join([c.name for c in plugin.commands()])
136         settings.append(Setting(
137                 section=PLUGIN_SETTING_SECTION,
138                 option=plugin.name,
139                 value=str(default_include),
140                 help=help,
141                 ))
142     for pnode in PLUGIN_GRAPH:
143         plugin = pnode.data
144         settings.extend(plugin.default_settings())
145     return settings
146
147 def load_graph(graph, config, include_section):
148     items = []
149     for node in graph:
150         item = node.data
151         try:
152             include = config.getboolean(include_section, item.name)
153         except configparser.NoOptionError:
154             include = True # non-optional include (e.g. a Builtin)
155         if include == True:
156             try:
157                 item.config = dict(config.items(item.setting_section))
158             except configparser.NoSectionError:
159                 pass
160             items.append(item)
161     return items
162
163 def argument_to_setting(self, section_name, argument):
164     """Convert an :class:`~hooke.command.Argument` to a
165     `~hooke.conf.Setting`.
166
167     This is a lossy transition, because
168     :class:`~hooke.command.Argument`\s store more information than
169     `~hooke.conf.Setting`\s.
170     """
171     return Setting(section_name, option=argument.name, value=argument.default,
172                    help=argument._help)