X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=hooke%2Fhooke.py;h=ccea160687edaa714e5bbbd15204224682104a12;hb=9cc225356407dd2836d9c4d44c2989d17a549ca4;hp=72fab311d59bd49f50c0ae682e7a4eaf29189e1d;hpb=07dda3e11adf448e31ce80a5dbf713960262a91c;p=hooke.git diff --git a/hooke/hooke.py b/hooke/hooke.py index 72fab31..ccea160 100644 --- a/hooke/hooke.py +++ b/hooke/hooke.py @@ -22,10 +22,45 @@ """Hooke - A force spectroscopy review & analysis tool. """ +if False: # Queue pickle error debugging code + """The Hooke class is passed back from the CommandEngine process + to the main process via a :class:`multiprocessing.queues.Queue`, + which uses :mod:`pickle` for serialization. There are a number of + objects that are unpicklable, and the error messages are not + always helpful. This block of code hooks you into the Queue's + _feed method so you can print out useful tidbits to help find the + particular object that is gumming up the pickle works. + """ + import multiprocessing.queues + import sys + feed = multiprocessing.queues.Queue._feed + def new_feed (buffer, notempty, send, writelock, close): + def s(obj): + print 'SEND:', obj, dir(obj) + for a in dir(obj): + attr = getattr(obj, a) + #print ' ', a, attr, type(attr) + if obj.__class__.__name__ == 'Hooke': + # Set suspect attributes to None until you resolve the + # PicklingError. Then fix whatever is breaking the + # pickling. + #obj.commands = None + #obj.drivers = None + #obj.plugins = None + #obj.ui = None + pass + sys.stdout.flush() + send(obj) + feed(buffer, notempty, s, writelock, close) + multiprocessing.queues.Queue._feed = staticmethod(new_feed) + +import logging +import logging.config import multiprocessing import optparse import os.path import unittest +import StringIO import sys from . import engine as engine @@ -49,13 +84,22 @@ class Hooke (object): default_settings=default_settings) config.read() self.config = config + self.load_log() self.load_plugins() self.load_drivers() self.load_ui() self.command = engine.CommandEngine() - self.playlists = playlist.NoteIndexList() + def load_log(self): + config_file = StringIO.StringIO() + self.config.write(config_file) + x = config_file.getvalue() + logging.config.fileConfig(StringIO.StringIO(config_file.getvalue())) + # Don't attach the logger because it contains an unpicklable + # thread.lock. Instead, grab it directly every time you need it. + #self.log = logging.getLogger('hooke') + def load_plugins(self): self.plugins = plugin_mod.load_graph( plugin_mod.PLUGIN_GRAPH, self.config, include_section='plugins') @@ -73,44 +117,51 @@ class Hooke (object): def close(self): self.config.write() # Does not preserve original comments - def run(self): +class HookeRunner (object): + def run(self, hooke): """Run Hooke's main execution loop. Spawns a :class:`hooke.engine.CommandEngine` subprocess and then runs the UI, rejoining the `CommandEngine` process after the UI exits. """ - ui_to_command,command_to_ui,command = self._setup_run() + ui_to_command,command_to_ui,command = self._setup_run(hooke) try: - self.ui.run(self.commands, ui_to_command, command_to_ui) + hooke.ui.run(hooke.commands, ui_to_command, command_to_ui) finally: - self._cleanup_command(ui_to_command, command_to_ui, command) + hooke = self._cleanup_run(ui_to_command, command_to_ui, command) + return hooke - def run_lines(self, lines): + def run_lines(self, hooke, lines): """Run the pre-set commands `lines` with the "command line" UI. Allows for non-interactive sessions that are otherwise equivalent to :meth:'.run'. """ - cmdline = ui.load_ui(self.config, 'command line') - ui_to_command,command_to_ui,command = self._setup_run() + cmdline = ui.load_ui(hooke.config, 'command line') + ui_to_command,command_to_ui,command = self._setup_run(hooke) try: cmdline.run_lines( - self.commands, ui_to_command, command_to_ui, lines) + hooke.commands, ui_to_command, command_to_ui, lines) finally: - self._cleanup_command(ui_to_command, command_to_ui, command) + hooke = self._cleanup_run(ui_to_command, command_to_ui, command) + return hooke - def _setup_run(self): + def _setup_run(self, hooke): ui_to_command = multiprocessing.Queue() command_to_ui = multiprocessing.Queue() + manager = multiprocessing.Manager() command = multiprocessing.Process(name='command engine', - target=self.command.run, args=(self, ui_to_command, command_to_ui)) + target=hooke.command.run, args=(hooke, ui_to_command, command_to_ui)) command.start() return (ui_to_command, command_to_ui, command) def _cleanup_run(self, ui_to_command, command_to_ui, command): ui_to_command.put(ui.CloseEngine()) + hooke = command_to_ui.get() + assert isinstance(hooke, Hooke) command.join() + return hooke def main(): @@ -124,25 +175,26 @@ def main(): help='Add a command line Hooke command to run.') options,arguments = p.parse_args() if len(arguments) > 0: - print >> sys.stderr, 'Too many arguments to %s: %d > 0' \ - % (sys.argv[0], len(arguments)) - print >> sys.stderr, p.help() + print >> sys.stderr, 'More than 0 arguments to %s: %s' \ + % (sys.argv[0], arguments) + p.print_help(sys.stderr) sys.exit(1) - app = Hooke(debug=__debug__) + hooke = Hooke(debug=__debug__) + runner = HookeRunner() if options.script != None: f = open(os.path.expanduser(options.script), 'r') options.commands.extend(f.readlines()) f.close if len(options.commands) > 0: - app.run_lines(options.commands) + try: + hooke = runner.run_lines(hooke, options.commands) + finally: + hooke.close() sys.exit(0) try: - app.run() + hooke = runner.run(hooke) finally: - app.close() - -if __name__ == '__main__': - main() + hooke.close()