Как засолить цепь обратного вызова - PullRequest
3 голосов
/ 28 января 2011

У меня есть пользовательский конечный автомат в Twisted. Пользователь может определять обработчики для различных изменений состояния, которые я реализую, используя Twisted deferred, к которому я позволил им добавлять обратные вызовы. Всякий раз, когда я перехожу из одного состояния в другое, я просто запускаю соответствующую отсрочку.

Одним из требований проекта является возможность сохранения этого конечного автомата на диск вместе со всеми его обратными вызовами. Я думал, что могу просто перехватить конечный автомат, и все будет готово, но я получаю PickleError, когда пытаюсь сериализовать пользовательские функции.

Кто-нибудь знает способ сериализации функций? Ошибка воспроизводится в следующем примере кода:

import pickle
from twisted.internet.utils import defer

def foo(*args):
  def bar():
    print args
  return bar

d = defer.Deferred()
d.addCallback(foo("Hello", "world"))
pickle.dumps(d)

Последняя строка выдает следующую ошибку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.5/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 725, in save_inst
    save(stuff)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
    save(v)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 600, in save_list
    self._batch_appends(iter(obj))
  File "/usr/lib/python2.5/pickle.py", line 615, in _batch_appends
    save(x)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 748, in save_global
    (obj, module, name))
pickle.PicklingError: Can't pickle <function bar at 0xb753fe2c>: it's not found as __main__.bar

Есть ли какие-либо решения для этого? Может быть, мне нужно ограничить типы функций, которые пользователи могут добавлять в качестве обратных вызовов?

Спасибо
Jonathan

Ответы [ 2 ]

2 голосов
/ 28 января 2011

Не пытайтесь засолить Отложенных.Это не поддерживается Twisted.Даже если вам удастся создать что-то, что кажется работающим (а это не полностью невозможно), более поздняя версия Twisted может нарушить все ваше сохраненное состояние.

Отложенные средства предназначены для управления потокомсобытий через ваш код.Они не для хранения состояния приложения.Если вы хотите сохранить состояние вашего приложения, отделите его от всех отложенных и сериализуйте просто it.

Когда вы сделаете это, вы, вероятно, также захотите избежать использования pickle для формата сериализации.Рассол не является хорошим способом хранения данных.Это очень сложный формат, который очень чувствителен к изменениям в версиях Python и версиях библиотеки.У него нет средств для определения схемы, поэтому вы никогда не сможете точно убедиться, что вы сериализуете или что вы сериализовали.Очень трудно проверить рассол отдельно от его загрузки, поэтому, если он когда-либо сломается (как это произойдет, если вы решите переименовать класс, у которого есть экземпляры с засолками), восстановление данных представляет собой серьезную проблему.

0 голосов
/ 28 января 2011

Замените функции foo / bar экземпляром вызываемого класса:

class foo(object):
    def __init__(self, *args):
        self.args = args
    def __call__(self):
        print self.args

d = defer.Deferred()
d.addCallback(foo("Hello", "world"))
pickle.dumps(d)
...