Bo Granlund wrote:
Hello zope users,
I have this little problem with bound methods and __init__. I have written something like:
class Root(Acquisition.Implicit, Persistent): def __init__(self): self.container = [] def register_func(self, func): self.container.append(func)
class Spam(Acquisition.Implicit, Persistent): def foo(self): pass def __init__(self): # This fails at Root.register_func, in self.container.append self.register_func(self.foo) def add_bound_method(self): # Works like charm, when called explicitly self.register_func(self.foo)
This results in UnpickleableError, 'Cannot pickle objects' when adding the product. Now if I call register_func from somewhere else than __init__ or manage_afterAdd, there is no problem, and everything works as I expect it to work. This is propably explained somewhere in deep detail, and I have managed to miss that documentation. Any pointers to documentation or ideas on how to set bound methods to other objects would be appreciated.
Best regards, Bo Granlund
The ZODB uses pickling to store values persistently. One rule of pickling is that you cannot pickle methods or functions. By appending the function itself to the list, you are causing this error. Another problem (which explains why you don't always get this error) is that the ZODB has no way of knowing that you have changed container when you use the append function. So, it does not commit the change. This is not a problem in __init__ because the whole instance is new and the ZODB commits it all. If you change a mutable object in place (such as a list or dictionary) you need to signal the ZODB to commit the change. To do this, add this code after the change it made: self._p_changed = 1 Attributes starting with "_p_" are reserved for the persistance machinery. Changes to immutable objects (strings, ints, etc) do not need this signalling. Now to get around the problem of storing functions, I would suggest storing the name of the function (a string) in the list instead of the function itself. The use getattr to retreive it when it is called. Here is your code revised to do this: class Root(Acquisition.Implicit, Persistent): def __init__(self): self.container = [] def register_func(self, func_name): self.container.append(func_name) self._p_changed = 1 def call_func(self, func_index): getattr(self.aq_base, self.container[func_index])() class Spam(Acquisition.Implicit, Persistent): def foo(self): pass def __init__(self): # This fails at Root.register_func, in self.container.append self.register_func('foo') def add_bound_method(self): # Works like charm, when called explicitly self.register_func('foo') The self.aq_base part in call_func will suppress acquisition making sure it is a real method of the object and not an acquired one. hth, -- | Casey Duncan | Kaivo, Inc. | cduncan@kaivo.com `------------------>