创建无法腌制的对象

时间:2016-03-01 13:29:17

标签: python testing pickle

如何在我的rpc代码中轻松创建一个无法用于测试边缘情况的对象?

需要:

  1. 简单
  2. 可靠(预计未来版本的python或pickle不会中断)
  3. 跨平台
  4. 编辑:预期用途如下所示:

    class TestRPCServer:
        def foo(self):
            return MagicalUnpicklableObject()
    
    def test():
        with run_rpc_server_and_connect_to_it() as proxy:
            with nose.assert_raises(pickle.PickleError):
                proxy.foo()
    

2 个答案:

答案 0 :(得分:6)

如果你只需要一个会在你挑选它时抛出异常的对象,那么对于测试的目的,你可以炸掉__getstate__ method

>>> class C:
...     def __getstate__(self):
...         raise Exception
... 
>>> pickle.dumps(C())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 723, in save_inst
    stuff = getstate()
  File "<stdin>", line 3, in __getstate__
Exception

如果您想要一个不太人为的场景,请考虑使用OS资源的对象,如文件句柄,套接字或线程等。

>>> with open('spam.txt', 'w') as f:
...     pickle.dumps(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps
    Pickler(file, protocol).dump(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 306, in save
    rv = reduce(self.proto)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle file objects

答案 1 :(得分:2)

如果你想要一个可以被腌制的对象的显式列表,而不是那些使用pickle和更高级的序列化器(如dill)进行腌制的对象,这个文件包含一些有点全面的列表用于标准库对象。它提供了一种构建每个对象(通常是单行)的简单方法,并显示了不同版本的python的变体(如果适用)。

https://github.com/uqfoundation/dill/blob/cccbea9b715e16b742288e1e5a21a687a4d4081b/dill/_objects.py#L255

例如,pickle将在以下对象上失败,而高级序列化程序(如dill)则不会:

>>> import dill
>>> dill.dumps(Ellipsis)
b'\x80\x03cdill.dill\n_eval_repr\nq\x00X\x08\x00\x00\x00Ellipsisq\x01\x85q\x02Rq\x03.'

高级序列化程序也可以处理文件对象等,btw:

>>> dill.dumps(open('foo.pkl', 'w'))
b'\x80\x03cdill.dill\n_create_filehandle\nq\x00(X\x07\x00\x00\x00foo.pklq\x01X\x01\x00\x00\x00wq\x02K\x00\x89cdill.dill\n_get_attr\nq\x03cdill.dill\n_import_module\nq\x04X\x02\x00\x00\x00ioq\x05\x85q\x06Rq\x07X\x04\x00\x00\x00openq\x08\x86q\tRq\n\x89K\x00X\x00\x00\x00\x00q\x0btq\x0cRq\r.'

但是,pickledill(以及其他高级序列化程序)将在与python FrameType直接绑定的任何类型上失败,如生成器:

>>> dill.dumps((i for i in []))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mmckerns/lib/python3.4/site-packages/dill-0.2.6.dev0-py3.4.egg/dill/dill.py", line 243, in dumps
    dump(obj, file, protocol, byref, fmode, recurse)#, strictio)
  File "/Users/mmckerns/lib/python3.4/site-packages/dill-0.2.6.dev0-py3.4.egg/dill/dill.py", line 236, in dump
    pik.dump(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/pickle.py", line 412, in dump
    self.save(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/pickle.py", line 499, in save
    rv = reduce(self.proto)
TypeError: can't pickle generator objects