【问题标题】:How to recover a broken python "cPickle" dump?如何恢复损坏的 python“cPickle”转储?
【发布时间】:2009-03-19 23:25:03
【问题描述】:

我正在使用rss2email 将多个 RSS 提要转换为邮件以便于使用。也就是说,我正在使用它,因为它今天以一种可怕的方式坏了:每次运行时,它只会给我这个回溯:

Traceback (most recent call last):
  File "/usr/share/rss2email/rss2email.py", line 740, in <module>
    elif action == "list": list()
  File "/usr/share/rss2email/rss2email.py", line 681, in list
    feeds, feedfileObject = load(lock=0)
  File "/usr/share/rss2email/rss2email.py", line 422, in load
    feeds = pickle.load(feedfileObject)
TypeError: ("'str' object is not callable", 'sxOYAAuyzSx0WqN3BVPjE+6pgPU', ((2009, 3, 19, 1, 19, 31, 3, 78, 0), {}))

我能够从这个回溯中构建的唯一有用的事实是,rss2email 保存其所有配置和运行时状态的文件 ~/.rss2email/feeds.dat 以某种方式损坏。显然,rss2email 在每次运行时都会读取其状态并使用cPickle 将其转储回去。

我什至在巨大的 (>12MB) feeds.dat 文件中找到了包含上面提到的 'sxOYAAuyzSx0WqN3BVPjE+6pgPU'string 的行。在未经训练的人看来,转储似乎没有被截断或以其他方式损坏。

我可以尝试哪些方法来重建文件?

在 Debian/不稳定系统上的 Python 版本是 2.5.4。

编辑

Peter Gibson 和 J.F. Sebastian 建议直接从 pickle 文件,我之前尝试过。显然,Feed 类 需要在rss2email.py 中定义,所以这是我的脚本:

#!/usr/bin/python

import sys
# import pickle
import cPickle as pickle
sys.path.insert(0,"/usr/share/rss2email")
from rss2email import Feed

feedfile = open("feeds.dat", 'rb')
feeds = pickle.load(feedfile)

“普通”pickle 变体产生以下回溯:

Traceback (most recent call last):
  File "./r2e-rescue.py", line 8, in <module>
    feeds = pickle.load(feedfile)
  File "/usr/lib/python2.5/pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "/usr/lib/python2.5/pickle.py", line 858, in load
    dispatch[key](self)
  File "/usr/lib/python2.5/pickle.py", line 1133, in load_reduce
    value = func(*args)
TypeError: 'str' object is not callable

cPickle 变体产生与调用基本相同的东西 r2e自己:

Traceback (most recent call last):
  File "./r2e-rescue.py", line 10, in <module>
    feeds = pickle.load(feedfile)
TypeError: ("'str' object is not callable", 'sxOYAAuyzSx0WqN3BVPjE+6pgPU', ((2009, 3, 19, 1, 19, 31, 3, 78, 0), {}))

编辑 2

遵循 J.F. Sebastian 的建议,将“printf 调试”到Feed.__setstate__ 到我的测试脚本中,这些是 Python 退出前的最后几行代码。

          u'http:/com/news.ars/post/20080924-everyone-declares-victory-in-smutfree-wireless-broadband-test.html': u'http:/com/news.ars/post/20080924-everyone-declares-victory-in-smutfree-wireless-broadband-test.html'},
 'to': None,
 'url': 'http://arstechnica.com/'}
Traceback (most recent call last):
  File "./r2e-rescue.py", line 23, in ?
    feeds = pickle.load(feedfile)
TypeError: ("'str' object is not callable", 'sxOYAAuyzSx0WqN3BVPjE+6pgPU', ((2009, 3, 19, 1, 19, 31, 3, 78, 0), {}))

同样的事情发生在使用 python 2.4.4-2 的 Debian/etch 机器上。

【问题讨论】:

  • pickle 格式非常简单。我记得我在解决pythonchallenge.com 谜题时手动解析了它(我是Python 的初学者,当时不认识pickle 格式)。因此,作为最后的手段,您可以手动从 feeds.dat 中提取必要的数据
  • 这里的“by hand”代表一个简单的基于正则表达式的python脚本

标签: python rss pickle


【解决方案1】:

我是如何解决我的问题的

pickle.py 的 Perl 端口

在 J.F. Sebastian 关于pickle 多么简单的评论之后 格式是,我将pickle.py 的部分内容移植到 Perl。一对夫妇 的快速正则表达式将是访问我的更快的方法 数据,但我觉得黑客价值和了解更多信息的机会 关于 Python 是值得的。另外,我仍然感觉更多 使用 Perl(和调试代码)比使用 Python 更舒服。

大部分移植工作(简单类型、元组、列表、字典) 非常直截了当。 Perl 和 Python 的不同概念 到目前为止,类和对象是唯一的问题 而不是简单的习语翻译是需要的。结果是一个模块 称为Pickle::Parse,经过一番打磨后将是 在 CPAN 上发布。

CPAN 上存在一个名为 Python::Serialise::Pickle 的模块,但我 发现它的解析能力不足:它会喷出所有的调试输出 在这个地方,似乎不支持类/对象。

解析、转换数据、检测流中的实际错误

基于Pickle::Parse,我尝试解析feeds.dat 文件。 在我的解析代码中修复琐碎错误的几次迭代之后,我得到了 与pickle.py 的原始错误消息惊人地相似 对象不可调用错误信息:

Can't use string ("sxOYAAuyzSx0WqN3BVPjE+6pgPU") as a subroutine
ref while "strict refs" in use at lib/Pickle/Parse.pm line 489,
<STDIN> line 187102.

哈!现在我们处于实际数据很可能 流坏了。另外,我们知道哪里坏了。

原来下面这个序列的第一行是错误的:

g7724
((I2009
I3
I19
I1
I19
I31
I3
I78
I0
t(dtRp62457

“备忘录”中的位置 7724 指向该字符串 "sxOYAAuyzSx0WqN3BVPjE+6pgPU"。从早先的类似记录 流,很明显需要time.struct_time 对象 反而。后来的所有记录都共享了这个错误的指针。用一个简单的 搜索/替换操作,解决这个问题很简单。

我觉得很讽刺的是我偶然发现了错误的根源 通过 Perl 的特性告诉用户它在输入中的位置 死亡时的数据流。

结论

  1. 一有时间我就会离开rss2email 自动将其腌制配置/状态混乱转换为 其他工具的格式。
  2. pickle.py 需要更有意义的错误消息来告诉用户 关于数据流的位置(不是它自己的位置 代码)哪里出错了。
  3. 将部分 pickle.py 移植到 Perl 很有趣,而且最终获得了回报。

【讨论】:

    【解决方案2】:

    您是否尝试过同时使用 cPickle 和 pickle 手动加载 feeds.dat 文件?如果输出不同,则可能暗示错误。

    类似(从您的主目录):

    import cPickle, pickle
    f = open('.rss2email/feeds.dat', 'r')
    obj1 = cPickle.load(f)
    obj2 = pickle.load(f)
    

    (如果 rss2email 没有在 ascii 中腌制,您可能需要以二进制模式“rb”打开)。

    皮特

    编辑:cPickle 和 pickle 给出相同错误的事实表明 feeds.dat 文件是问题所在。正如 Ubuntu 错误 J.F. Sebastian 链接中所建议的那样,可能是 rss2email 版本之间的 Feed 类发生了变化。

    【讨论】:

    • 你应该以二进制模式“rb”打开泡菜,因为它们很可能是以二进制模式编写的。
    • 嗯。 rss2email 本身不会以二进制模式打开文件。也许这就是问题的根源?
    • @Ted:在 Debian 'rb' 上无关紧要。引用:“默认情况下,pickle 数据格式使用可打印的 ASCII 表示。”。但你是对的,一般来说最好在“rb”模式下读取“腌制”数据(协议> = 1)
    • Pete,我已经尝试了您的建议并相应地更新了我的原始帖子。
    【解决方案3】:

    听起来 cPickle 的内部结构有些混乱。这个线程(http://bytes.com/groups/python/565085-cpickle-problems)看起来可能有线索..

    【讨论】:

    • 在谷歌搜索部分错误消息后,我发现了几个包含该线程的档案,但它并没有给我太多线索。
    【解决方案4】:
    1. 'sxOYAAuyzSx0WqN3BVPjE+6pgPU' 很可能与泡菜的问题无关
    2. 发布错误回溯(以确定哪个类定义了无法调用的属性(导致 TypeError 的属性):

      python -c "import pickle; pickle.load(open('feeds.dat'))"
      

    编辑:

    将以下内容添加到您的代码中并运行(将 stderr 重定向到文件,然后在其上使用 'tail -2' 以打印最后 2 行):

    from pprint import pprint
    def setstate(self, dict_):
        pprint(dict_, stream=sys.stderr, depth=None)
        self.__dict__.update(dict_)
    Feed.__setstate__ = setstate
    

    如果以上没有产生有趣的输出,则使用一般故障排除策略:

    确认'feeds.dat'是问题所在:

    • 备份~/.rss2email目录
    • 将 rss2email 安装到 virtualenv/pip 沙箱中(或使用 zc.buildout)以隔离环境(确保您使用的是来自主干的 feedparser.py)。
    • 添加几个提要,添加提要直到'feeds.dat' 的大小大于当前大小。运行一些测试。
    • 尝试旧的“feeds.dat”
    • 在现有的 rss2email 安装上尝试新的“feeds.dat”

    在 Ubuntu 上查看 r2e bails out with TypeError 错误。

    【讨论】:

    • 'sxOYAAuyzSx0WqN3BVPjE+6pgPU' 是错误消息的一部分,所以这就是我开始寻找的地方。我已经尝试了您的建议,并相应地更新了我的原始帖子。
    • 建议的故障排除策略对我不起作用,因为 feeds.dat 文件大于 12 MB。已经积累了2年多。
    • 感谢您将我指向 Ubuntu BTS。似乎是同一个问题。
    • 以编程方式填充 feeds.dat 一点也不难,只需运行 'r2e add stackoverflow.com/feeds/question/$id' (stackoverflow 上有超过 100000 个问题。
    猜你喜欢
    • 1970-01-01
    • 2010-12-05
    • 2014-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 2013-08-18
    相关资源
    最近更新 更多