Почему протокол pickle 2 позволяет мне сериализовать открытый файловый объект? - PullRequest
16 голосов
/ 18 августа 2011

Рассмотрим:

>>> import pickle
>>> thing = open('foobar.txt','w')
>>> pickle.dumps(thing)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.6/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.6/pickle.py", line 306, in save
    rv = reduce(self.proto)
  File "/usr/lib/python2.6/copy_reg.py", line 70, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle file objects

Кажется вполне разумным - конечно, я не могу выбрать дескриптор открытого файла. Но:

>>> pickle.dumps(thing, 2)
'\x80\x02c__builtin__\nfile\nq\x00)\x81q\x01.'
>>> pickle.loads(pickle.dumps(thing, 2))
<closed file '<uninitialized file>', mode '<uninitialized file>' at 0x7ff3c078>

Видимо, я могу мариновать открытый файл, но не с пользой.

Это умышленно? Это скрывало ошибку в моем коде, когда я ошибочно выбирал объект, которому принадлежал файл. При некоторых условиях этот объект также содержит курсор pyodbc с тем же результатом.

Я ничего не вижу в PEP 307 об этом. Было ли это просто упущением или что-то важное, что я упускаю, которое могло бы позволить мне получить желаемое исключение даже при травлении с использованием протокола 2?

Я использую Python 2.6.5. Я знаю, я знаю, но это то, что приходит с моим распространением.

Ответы [ 2 ]

10 голосов
/ 18 августа 2011

На Python Wiki написано

Нельзя выбрать открытые файловые объекты, сетевые подключения или подключения к базе данных. Когда вы думаете об этом, это имеет смысл - Pickle не сможет сохранить соединение для файлового объекта, когда вы откроете объект, и процесс создания этого соединения выходит за рамки того, что Pickle может автоматически сделать для вас. Если вы действительно хотите выбрать что-то с атрибутом, вызывающим проблемы, посмотрите документацию на параметры __getstate__, __setstate__ и __getinitargs__ - с их помощью вы можете исключить проблемные атрибуты.

Однако я обнаружил этот отчет об ошибке , который указывает, что вы на самом деле можете извлекать файловые объекты. Это кажется непреднамеренным. Это было исправлено в Python 3.2.

Вы можете увидеть, можете ли вы адаптировать этот патч к Python 2.6, если хотите предотвратить его появление. В противном случае вам просто нужно быть осторожным с тем, что вы мариноваете.

0 голосов
/ 17 июля 2014

Если вы ищете лучшее поведение, вы можете использовать dill ..., который не сериализует файловые объекты, но знает, как сериализовать дескрипторы файлов.Поведение таково, что если файл существует, dill будет указывать десериализованный дескриптор файла на него ... а если файл не существует, то дескриптор файла будет закрыт.

Python 2.7.8 (default, Jul  3 2014, 05:59:29) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>>        
>>> thing = open('foobar.txt', 'w')
>>> thing
<open file 'foobar.txt', mode 'w' at 0x10e3c2c00>
>>> dill.loads(dill.dumps(thing))
<open file 'foobar.txt', mode 'w' at 0x10e3c2c90>
>>> 

Получить dill здесь: https://github.com/uqfoundation

...