From 0d3854b008677657711c70b854152f3d3583fc9e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 19 May 2010 00:38:51 -0400 Subject: [PATCH] Fix a pickling problems with hooke.plugin.playlist.FilterCommand. Pickle doesn't like functions that aren't defined the top level of modules (such as NoteFilterCommand's old lambda filter_fn). Add a warning to that effect to the FilterCommand doc string, and rearrange to encourage the use of methods (which are less likely to be defined on the fly than functions). Also: * Added pickle queue debugging code to hooke.hooke. This isn't the first tiem I've had a pickling problem. This code (when enabled), makes it easier for me to track down the failing code. * Fix a typo in hooke.hooke.HookeRunner.run (self.ui -> hooke.ui). * Remove a snip of trailing whitespace in hooke.hooke. --- hooke/hooke.py | 36 ++++++++++++++++++++++++++++++++++-- hooke/plugin/playlist.py | 26 ++++++++++++++++++-------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/hooke/hooke.py b/hooke/hooke.py index e8a0050..07d5d5a 100644 --- a/hooke/hooke.py +++ b/hooke/hooke.py @@ -22,6 +22,38 @@ """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 multiprocessing import optparse import os.path @@ -83,7 +115,7 @@ class HookeRunner (object): """ ui_to_command,command_to_ui,command = self._setup_run(hooke) try: - self.ui.run(hooke.commands, ui_to_command, command_to_ui) + hooke.ui.run(hooke.commands, ui_to_command, command_to_ui) finally: hooke = self._cleanup_run(ui_to_command, command_to_ui, command) return hooke @@ -106,7 +138,7 @@ class HookeRunner (object): def _setup_run(self, hooke): ui_to_command = multiprocessing.Queue() command_to_ui = multiprocessing.Queue() - manager = multiprocessing.Manager() + manager = multiprocessing.Manager() command = multiprocessing.Process(name='command engine', target=hooke.command.run, args=(hooke, ui_to_command, command_to_ui)) command.start() diff --git a/hooke/plugin/playlist.py b/hooke/plugin/playlist.py index 21a6419..dd6e626 100644 --- a/hooke/plugin/playlist.py +++ b/hooke/plugin/playlist.py @@ -260,8 +260,18 @@ class FilterCommand (Command): Removing lots of curves one at a time can be tedious. With this command you can use a function `filter` to select the curves you wish to keep. + + Notes + ----- + There are issues with pickling functions bound to class + attributes, because the pickle module doesn't know where those + functions were originally defined (where it should point the + loader). Because of this, subclasses with hard-coded filter + functions are encouraged to define their filter function as a + method of their subclass. See, for example, + :meth:`NoteFilterCommand.filter`. """ - def __init__(self, plugin, name='filter playlist', filter_fn=None): + def __init__(self, plugin, name='filter playlist'): super(FilterCommand, self).__init__( name=name, arguments=[ @@ -269,8 +279,7 @@ class FilterCommand (Command): PlaylistNameArgument, ], help=self.__doc__, plugin=plugin) - self.filter_fn = filter_fn - if filter_fn == None: + if not hasattr(self, 'filter'): self.arguments.append( Argument(name='filter', type='function', optional=False, help=""" @@ -279,10 +288,10 @@ Function returning `True` for "good" curves. """.strip())) def _run(self, hooke, inqueue, outqueue, params): - if self.filter_fn == None: + if not hasattr(self, 'filter'): filter_fn = params['filter'] else: - filter_fn = self.filter_fn + filter_fn = self.filter p = params['playlist'].filter(filter_fn, hooke=hooke, inqueue=inqueue, outqueue=outqueue, params=params) hooke.playlists.add(p) @@ -293,6 +302,7 @@ class NoteFilterCommand (FilterCommand): """ def __init__(self, plugin): super(NoteFilterCommand, self).__init__( - plugin, name='note filter playlist', - filter_fn=lambda curve, hooke, inqueue, outqueue, params : \ - 'note' in curve.info and curve.info['note'] != None) + plugin, name='note filter playlist') + + def filter(self, curve, hooke, inqueue, outqueue, params): + return 'note' in curve.info and curve.info['note'] != None -- 2.26.2