2014-06-16 15 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