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