Remove if __name__ == '__main__' section of hooke.hooke
[hooke.git] / hooke / hooke.py
1 # Copyright (C) 2008-2010 Fabrizio Benedetti
2 #                         Massimo Sandal <devicerandom@gmail.com>
3 #                         Rolf Schmidt <rschmidt@alcor.concordia.ca>
4 #                         W. Trevor King <wking@drexel.edu>
5 #
6 # This file is part of Hooke.
7 #
8 # Hooke is free software: you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation, either
11 # version 3 of the License, or (at your option) any later version.
12 #
13 # Hooke is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with Hooke.  If not, see
20 # <http://www.gnu.org/licenses/>.
21
22 """Hooke - A force spectroscopy review & analysis tool.
23 """
24
25 if False: # Queue pickle error debugging code
26     """The Hooke class is passed back from the CommandEngine process
27     to the main process via a :class:`multiprocessing.queues.Queue`,
28     which uses :mod:`pickle` for serialization.  There are a number of
29     objects that are unpicklable, and the error messages are not
30     always helpful.  This block of code hooks you into the Queue's
31     _feed method so you can print out useful tidbits to help find the
32     particular object that is gumming up the pickle works.
33     """
34     import multiprocessing.queues
35     import sys
36     feed = multiprocessing.queues.Queue._feed
37     def new_feed (buffer, notempty, send, writelock, close):
38         def s(obj):
39             print 'SEND:', obj, dir(obj)
40             for a in dir(obj):
41                 attr = getattr(obj, a)
42                 #print '  ', a, attr, type(attr)
43             if obj.__class__.__name__ == 'Hooke':
44                 # Set suspect attributes to None until you resolve the
45                 # PicklingError.  Then fix whatever is breaking the
46                 # pickling.
47                 #obj.commands = None
48                 #obj.drivers = None
49                 #obj.plugins = None
50                 #obj.ui = None
51                 pass
52             sys.stdout.flush()
53             send(obj)
54         feed(buffer, notempty, s, writelock, close)
55     multiprocessing.queues.Queue._feed = staticmethod(new_feed)
56
57 import multiprocessing
58 import optparse
59 import os.path
60 import unittest
61 import sys
62
63 from . import engine as engine
64 from . import config as config_mod
65 from . import playlist as playlist
66 from . import plugin as plugin_mod
67 from . import driver as driver_mod
68 from . import ui as ui
69
70
71 class Hooke (object):
72     def __init__(self, config=None, debug=0):
73         self.debug = debug
74         default_settings = (config_mod.DEFAULT_SETTINGS
75                             + plugin_mod.default_settings()
76                             + driver_mod.default_settings()
77                             + ui.default_settings())
78         if config == None:
79             config = config_mod.HookeConfigParser(
80                 paths=config_mod.DEFAULT_PATHS,
81                 default_settings=default_settings)
82             config.read()
83         self.config = config
84         self.load_plugins()
85         self.load_drivers()
86         self.load_ui()
87         self.command = engine.CommandEngine()
88
89         self.playlists = playlist.NoteIndexList()
90
91     def load_plugins(self):
92         self.plugins = plugin_mod.load_graph(
93             plugin_mod.PLUGIN_GRAPH, self.config, include_section='plugins')
94         self.commands = []
95         for plugin in self.plugins:
96             self.commands.extend(plugin.commands())
97
98     def load_drivers(self):
99         self.drivers = plugin_mod.load_graph(
100             driver_mod.DRIVER_GRAPH, self.config, include_section='drivers')
101
102     def load_ui(self):
103         self.ui = ui.load_ui(self.config)
104
105     def close(self):
106         self.config.write() # Does not preserve original comments
107
108 class HookeRunner (object):
109     def run(self, hooke):
110         """Run Hooke's main execution loop.
111
112         Spawns a :class:`hooke.engine.CommandEngine` subprocess and
113         then runs the UI, rejoining the `CommandEngine` process after
114         the UI exits.
115         """
116         ui_to_command,command_to_ui,command = self._setup_run(hooke)
117         try:
118             hooke.ui.run(hooke.commands, ui_to_command, command_to_ui)
119         finally:
120             hooke = self._cleanup_run(ui_to_command, command_to_ui, command)
121         return hooke
122
123     def run_lines(self, hooke, lines):
124         """Run the pre-set commands `lines` with the "command line" UI.
125
126         Allows for non-interactive sessions that are otherwise
127         equivalent to :meth:'.run'.
128         """
129         cmdline = ui.load_ui(hooke.config, 'command line')
130         ui_to_command,command_to_ui,command = self._setup_run(hooke)
131         try:
132             cmdline.run_lines(
133                 hooke.commands, ui_to_command, command_to_ui, lines)
134         finally:
135             hooke = self._cleanup_run(ui_to_command, command_to_ui, command)
136         return hooke
137
138     def _setup_run(self, hooke):
139         ui_to_command = multiprocessing.Queue()
140         command_to_ui = multiprocessing.Queue()
141         manager = multiprocessing.Manager()
142         command = multiprocessing.Process(name='command engine',
143             target=hooke.command.run, args=(hooke, ui_to_command, command_to_ui))
144         command.start()
145         return (ui_to_command, command_to_ui, command)
146
147     def _cleanup_run(self, ui_to_command, command_to_ui, command):
148         ui_to_command.put(ui.CloseEngine())
149         hooke = command_to_ui.get()
150         assert isinstance(hooke, Hooke)
151         command.join()
152         return hooke
153
154
155 def main():
156     p = optparse.OptionParser()
157     p.add_option(
158         '-s', '--script', dest='script', metavar='FILE',
159         help='Script of command line Hooke commands to run.')
160     p.add_option(
161         '-c', '--command', dest='commands', metavar='COMMAND',
162         action='append', default=[],
163         help='Add a command line Hooke command to run.')
164     options,arguments = p.parse_args()
165     if len(arguments) > 0:
166         print >> sys.stderr, 'Too many arguments to %s: %d > 0' \
167             % (sys.argv[0], len(arguments))
168         print >> sys.stderr, p.help()
169         sys.exit(1)
170
171     hooke = Hooke(debug=__debug__)
172     runner = HookeRunner()
173
174     if options.script != None:
175         f = open(os.path.expanduser(options.script), 'r')
176         options.commands.extend(f.readlines())
177         f.close
178     if len(options.commands) > 0:
179         try:
180             hooke = runner.run_lines(hooke, options.commands)
181         finally:
182             hooke.close()
183         sys.exit(0)
184
185     try:
186         hooke = runner.run(hooke)
187     finally:
188         hooke.close()