2014-06-16 14 views
1

东西是令人费解的我有点...在测试过程中,Pickle无法在django locmem缓存中存储对象?

>>> from django.core.cache import get_cache 
>>> 
>>> cache = get_cache('django.core.cache.backends.locmem.LocMemCache') 
>>> 
>>> # Set the 'content' cache key to a string 
>>> cache.set('content', 'a string') 
>>> cache.get('content') 
'a string' 
>>> 
>>> class TestObj(object): 
...  pass 
>>> 
>>> a = TestObj() 
>>> cache.set('content', a) 
>>> 
>>> # cache hasn't updated... 
>>> cache.get('content') 
'a string' 
>>> 
>>> cache.set('content', 1) 
>>> # this is fine however.. 
>>> cache.get('content') 
1 
>>> 

好了,所以缓存不接受某种原因的对象。

# in locmem.py, set() method 
try: 
    pickled = pickle.dumps(new_value, pickle.HIGHEST_PROTOCOL) 
    self._cache[key] = pickled 
except pickle.PickleError: 
    pass 

这将是为什么,这显然击中PickleError

>>> import pickle 
>>> pickled = pickle.dumps(a, pickle.HIGHEST_PROTOCOL) 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "/usr/lib/python2.7/pickle.py", line 1374, in dumps 
    Pickler(file, protocol).dump(obj) 
    File "/usr/lib/python2.7/pickle.py", line 224, in dump 
    self.save(obj) 
    File "/usr/lib/python2.7/pickle.py", line 331, in save 
    self.save_reduce(obj=obj, *rv) 
    File "/usr/lib/python2.7/pickle.py", line 396, in save_reduce 
    save(cls) 
    File "/usr/lib/python2.7/pickle.py", line 286, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "/usr/lib/python2.7/pickle.py", line 748, in save_global 
    (obj, module, name)) 
PicklingError: Can't pickle <class 'TestObj'>: it's not found as __builtin__.TestObj 

没问题,但为什么会出现这种情况?它在python控制台中工作得很好,但不是django shell?

# Works fine in python shell... 
>>> import pickle 
>>> class TestObj(object): 
...  pass 
... 
>>> testobj = TestObj() 
>>> pickled = pickle.dumps(testobj, pickle.HIGHEST_PROTOCOL) 
>>> pickled 
'\x80\x02c__main__\nTestObj\nq\x00)\x81q\x01}q\x02b.' 
>>> 

出现此问题是因为我试图在缓存中存储Mock()对象以进行测试。不知道我是否以这种错误的方式去...

回答

2

这是因为django LocMemCache默认使用cPickle而不是pickle。你可以看到它在LocMemCache类:

try: 
    from django.utils.six.moves import cPickle as pickle 
except ImportError: 
    import pickle 

如果你会尝试在壳里做:

from django.utils.six.moves import cPickle as pickle 
testobj = TestObj() 
pickled = pickle.dumps(testobj, pickle.HIGHEST_PROTOCOL) 

这将是同样的错误。

至于可能的解决办法,我建议你在测试中使用的泡菜手动打包对象和做cache.set()后:原来

a = TestObj() 
pickled = pickle.dumps(a, pickle.HIGHEST_PROTOCOL) 
cache.set('content', a) 
+0

啊,解释它。谢谢,我错过了。我不认为我可以根据你的解决方案预先腌制对象,因为它是我测试的方法,调用cache.set()而不是我的测试。不过谢谢。 – ptr

+0

对不起,我刚刚在python shell中尝试了这一点,并且在使用'from django.utils.six.moves import cPickle'时没有遇到错误,但是我在django shell中获取了它。那么使用django shell会导致这个错误是什么?我不认为这本质上是一个cPickle问题 – ptr

+0

我不太确定它。但是这种行为的可能原因之一可能是shell中的python版本不同,因为django.utils.six.moves存在于python2和python3之间的兼容性(https://pythonhosted.org/six/#module-six.moves) 。要检查它,你可以在django/core/cache/backends/locmem.py中插入“print pickle .__ file__” –

0

this follow-up question从马亭帮助简短的回答是:

“是”。

你不能腌制Mock()对象,因为它们不提供他们正在嘲笑的顶级对象,所以pickle因此不知道从哪里导入。由于缓存需要对象进行腌制来存储它,因此无法在LocMemCache中存储Mock()实例。将不得不重新思考我如何去测试这个。

0

示例代码

import pickle # or from django.utils.six.moves import cPickle as pickle 
lass TestObj(object): 
    pass 
testobj = TestObj() 
pickled = pickle.dumps(testobj, pickle.HIGHEST_PROTOCOL) 
pickled 
'\x80\x02c__main__\nTestObj\nq\x00)\x81q\x01}q\x02b.' 

我不明白一个问题,当我打开了“蟒蛇manage.py壳”控制台会话和执行:_pickle.PicklingError:不能咸菜:属性查找builtins.TestObj失败

但是,当打开一个单一的python控制台并执行相同的代码,工作正常!有理由吗?

我也观察过,如果我打开单个控制台,并从django.utils.six.moves导入导入cPickle作为pickle也可以正常工作。当代码在django上下文中执行时,问题就出现了。 :(

5

问题是,pickle通过引用序列化类,所以你可以不只是使用一个更好的序列化器,通过序列化类定义而不是通过引用来挑选类吗?那么你会腌制一个模拟对象,它会然后腌制类源代码,然后你就可以将它传递给django缓存。我是dill的作者,这是一个更好的序列化程序...也是klepto的作者,它是一个缓存包...这正是我所做的将任何对象存储在SQL表,磁盘或in - 内存缓存。

基本上(不是想这一点,但猜测它的工作原理基于与我自己的缓存包的经验),它应该像这样工作:

>>> from django.core.cache import get_cache 
>>> import dill 
>>> 
>>> cache = get_cache('django.core.cache.backends.locmem.LocMemCache') 
>>> 
>>> # Set the 'content' cache key to a string 
>>> cache.set('content', dill.dumps('a string')) 
>>> dill.loads(cache.get('content')) 
'a string' 
>>> 
>>> class TestObj(object): 
...  pass 
>>> 
>>> a = TestObj() 
>>> cache.set('content', dill.dumps(a)) 
>>> 
>>> dill.loads(cache.get('content')) 
<__main__.TestObj object at 0x10235e510> 
>>> 
>>> # this is pickling classes w/o using a reference 
>>> dill.dumps(a) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x07TestObjq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13b.' 
>>> # and here's using a reference, which is exactly how pickle does it 
>>> dill.dumps(a, byref=True) 
'\x80\x02c__main__\nTestObj\nq\x00)\x81q\x01}q\x02b.' 

如果你想尝试一下自己,让dill(和klepto )在这里:https://github.com/uqfoundation