Commit 6790e5ac authored by Alexandre Vassalotti's avatar Alexandre Vassalotti

Make framing optional in pickle protocol 4.

This will allow us to control in the future whether to use framing or not.
For example, we may want to turn it off for tiny pickle where it doesn't
help.

The change also improves performance slightly:

### fastpickle ###
Min: 0.608517 -> 0.557358: 1.09x faster
Avg: 0.798892 -> 0.694738: 1.15x faster
Significant (t=3.45)
Stddev: 0.17145 -> 0.12704: 1.3496x smaller
Timeline: http://goo.gl/3xQE1J

### pickle_dict ###
Min: 0.669920 -> 0.615271: 1.09x faster
Avg: 0.733633 -> 0.645058: 1.14x faster
Significant (t=5.05)
Stddev: 0.12041 -> 0.02961: 4.0662x smaller
Timeline: http://goo.gl/LpLSXI

### pickle_list ###
Min: 0.397583 -> 0.368112: 1.08x faster
Avg: 0.412784 -> 0.397223: 1.04x faster
Significant (t=2.78)
Stddev: 0.01518 -> 0.03653: 2.4068x larger
Timeline: http://goo.gl/v39E59

### unpickle_list ###
Min: 0.692935 -> 0.594870: 1.16x faster
Avg: 0.730012 -> 0.628395: 1.16x faster
Significant (t=17.76)
Stddev: 0.02720 -> 0.02995: 1.1012x larger
Timeline: http://goo.gl/2P9AEt

The following not significant results are hidden, use -v to show them:
fastunpickle.
parent 191ce796
...@@ -188,8 +188,18 @@ class _Framer: ...@@ -188,8 +188,18 @@ class _Framer:
self.file_write = file_write self.file_write = file_write
self.current_frame = None self.current_frame = None
def _commit_frame(self): def start_framing(self):
self.current_frame = io.BytesIO()
def end_framing(self):
if self.current_frame and self.current_frame.tell() > 0:
self.commit_frame(force=True)
self.current_frame = None
def commit_frame(self, force=False):
if self.current_frame:
f = self.current_frame f = self.current_frame
if f.tell() >= self._FRAME_SIZE_TARGET or force:
with f.getbuffer() as data: with f.getbuffer() as data:
n = len(data) n = len(data)
write = self.file_write write = self.file_write
...@@ -199,76 +209,51 @@ class _Framer: ...@@ -199,76 +209,51 @@ class _Framer:
f.seek(0) f.seek(0)
f.truncate() f.truncate()
def start_framing(self):
self.current_frame = io.BytesIO()
def end_framing(self):
if self.current_frame is not None:
self._commit_frame()
self.current_frame = None
def write(self, data): def write(self, data):
f = self.current_frame if self.current_frame:
if f is None: return self.current_frame.write(data)
return self.file_write(data)
else: else:
n = len(data) return self.file_write(data)
if f.tell() >= self._FRAME_SIZE_TARGET:
self._commit_frame()
return f.write(data)
class _Unframer: class _Unframer:
def __init__(self, file_read, file_readline, file_tell=None): def __init__(self, file_read, file_readline, file_tell=None):
self.file_read = file_read self.file_read = file_read
self.file_readline = file_readline self.file_readline = file_readline
self.file_tell = file_tell
self.framing_enabled = False
self.current_frame = None self.current_frame = None
self.frame_start = None
def read(self, n): def read(self, n):
if n == 0: if self.current_frame:
return b'' data = self.current_frame.read(n)
_file_read = self.file_read if not data and n != 0:
if not self.framing_enabled: self.current_frame = None
return _file_read(n) return self.file_read(n)
f = self.current_frame
if f is not None:
data = f.read(n)
if data:
if len(data) < n: if len(data) < n:
raise UnpicklingError( raise UnpicklingError(
"pickle exhausted before end of frame") "pickle exhausted before end of frame")
return data return data
frame_opcode = _file_read(1) else:
if frame_opcode != FRAME: return self.file_read(n)
raise UnpicklingError(
"expected a FRAME opcode, got {} instead".format(frame_opcode))
frame_size, = unpack("<Q", _file_read(8))
if frame_size > sys.maxsize:
raise ValueError("frame size > sys.maxsize: %d" % frame_size)
if self.file_tell is not None:
self.frame_start = self.file_tell()
f = self.current_frame = io.BytesIO(_file_read(frame_size))
self.readline = f.readline
data = f.read(n)
assert len(data) == n, (len(data), n)
return data
def readline(self): def readline(self):
if not self.framing_enabled: if self.current_frame:
data = self.current_frame.readline()
if not data:
self.current_frame = None
return self.file_readline() return self.file_readline()
if data[-1] != b'\n':
raise UnpicklingError(
"pickle exhausted before end of frame")
return data
else: else:
return self.current_frame.readline() return self.file_readline()
def tell(self): def load_frame(self, frame_size):
if self.file_tell is None: if self.current_frame and self.current_frame.read() != b'':
return None raise UnpicklingError(
elif self.current_frame is None: "beginning of a new frame before end of current frame")
return self.file_tell() self.current_frame = io.BytesIO(self.file_read(frame_size))
else:
return self.frame_start + self.current_frame.tell()
# Tools used for pickling. # Tools used for pickling.
...@@ -392,6 +377,8 @@ class _Pickler: ...@@ -392,6 +377,8 @@ class _Pickler:
self._file_write = file.write self._file_write = file.write
except AttributeError: except AttributeError:
raise TypeError("file must have a 'write' attribute") raise TypeError("file must have a 'write' attribute")
self.framer = _Framer(self._file_write)
self.write = self.framer.write
self.memo = {} self.memo = {}
self.proto = int(protocol) self.proto = int(protocol)
self.bin = protocol >= 1 self.bin = protocol >= 1
...@@ -417,18 +404,12 @@ class _Pickler: ...@@ -417,18 +404,12 @@ class _Pickler:
raise PicklingError("Pickler.__init__() was not called by " raise PicklingError("Pickler.__init__() was not called by "
"%s.__init__()" % (self.__class__.__name__,)) "%s.__init__()" % (self.__class__.__name__,))
if self.proto >= 2: if self.proto >= 2:
self._file_write(PROTO + pack("<B", self.proto)) self.write(PROTO + pack("<B", self.proto))
if self.proto >= 4: if self.proto >= 4:
framer = _Framer(self._file_write) self.framer.start_framing()
framer.start_framing()
self.write = framer.write
else:
framer = None
self.write = self._file_write
self.save(obj) self.save(obj)
self.write(STOP) self.write(STOP)
if framer is not None: self.framer.end_framing()
framer.end_framing()
def memoize(self, obj): def memoize(self, obj):
"""Store an object in the memo.""" """Store an object in the memo."""
...@@ -475,6 +456,8 @@ class _Pickler: ...@@ -475,6 +456,8 @@ class _Pickler:
return GET + repr(i).encode("ascii") + b'\n' return GET + repr(i).encode("ascii") + b'\n'
def save(self, obj, save_persistent_id=True): def save(self, obj, save_persistent_id=True):
self.framer.commit_frame()
# Check for persistent id (defined by a subclass) # Check for persistent id (defined by a subclass)
pid = self.persistent_id(obj) pid = self.persistent_id(obj)
if pid is not None and save_persistent_id: if pid is not None and save_persistent_id:
...@@ -1078,10 +1061,15 @@ class _Unpickler: ...@@ -1078,10 +1061,15 @@ class _Unpickler:
if not 0 <= proto <= HIGHEST_PROTOCOL: if not 0 <= proto <= HIGHEST_PROTOCOL:
raise ValueError("unsupported pickle protocol: %d" % proto) raise ValueError("unsupported pickle protocol: %d" % proto)
self.proto = proto self.proto = proto
if proto >= 4:
self._unframer.framing_enabled = True
dispatch[PROTO[0]] = load_proto dispatch[PROTO[0]] = load_proto
def load_frame(self):
frame_size, = unpack('<Q', self.read(8))
if frame_size > sys.maxsize:
raise ValueError("frame size > sys.maxsize: %d" % frame_size)
self._unframer.load_frame(frame_size)
dispatch[FRAME[0]] = load_frame
def load_persid(self): def load_persid(self):
pid = self.readline()[:-1].decode("ascii") pid = self.readline()[:-1].decode("ascii")
self.append(self.persistent_load(pid)) self.append(self.persistent_load(pid))
......
...@@ -1353,6 +1353,45 @@ class AbstractPickleTests(unittest.TestCase): ...@@ -1353,6 +1353,45 @@ class AbstractPickleTests(unittest.TestCase):
n_frames = pickled.count(b'\x00\x00\x00\x00\x00') n_frames = pickled.count(b'\x00\x00\x00\x00\x00')
self.assertGreaterEqual(n_frames, len(obj)) self.assertGreaterEqual(n_frames, len(obj))
def test_optional_frames(self):
if pickle.HIGHEST_PROTOCOL < 4:
return
def remove_frames(pickled, keep_frame=None):
"""Remove frame opcodes from the given pickle."""
frame_starts = []
# 1 byte for the opcode and 8 for the argument
frame_opcode_size = 9
for opcode, _, pos in pickletools.genops(pickled):
if opcode.name == 'FRAME':
frame_starts.append(pos)
newpickle = bytearray()
last_frame_end = 0
for i, pos in enumerate(frame_starts):
if keep_frame and keep_frame(i):
continue
newpickle += pickled[last_frame_end:pos]
last_frame_end = pos + frame_opcode_size
newpickle += pickled[last_frame_end:]
return newpickle
target_frame_size = 64 * 1024
num_frames = 20
obj = [bytes([i]) * target_frame_size for i in range(num_frames)]
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1):
pickled = self.dumps(obj, proto)
frameless_pickle = remove_frames(pickled)
self.assertEqual(count_opcode(pickle.FRAME, frameless_pickle), 0)
self.assertEqual(obj, self.loads(frameless_pickle))
some_frames_pickle = remove_frames(pickled, lambda i: i % 2 == 0)
self.assertLess(count_opcode(pickle.FRAME, some_frames_pickle),
count_opcode(pickle.FRAME, pickled))
self.assertEqual(obj, self.loads(some_frames_pickle))
def test_nested_names(self): def test_nested_names(self):
global Nested global Nested
class Nested: class Nested:
......
...@@ -110,10 +110,6 @@ enum { ...@@ -110,10 +110,6 @@ enum {
/* Initial size of the write buffer of Pickler. */ /* Initial size of the write buffer of Pickler. */
WRITE_BUF_SIZE = 4096, WRITE_BUF_SIZE = 4096,
/* Maximum size of the write buffer of Pickler when pickling to a
stream. This is ignored for in-memory pickling. */
MAX_WRITE_BUF_SIZE = 64 * 1024,
/* Prefetch size when unpickling (disabled on unpeekable streams) */ /* Prefetch size when unpickling (disabled on unpeekable streams) */
PREFETCH = 8192 * 16, PREFETCH = 8192 * 16,
...@@ -381,7 +377,6 @@ typedef struct UnpicklerObject { ...@@ -381,7 +377,6 @@ typedef struct UnpicklerObject {
char *input_line; char *input_line;
Py_ssize_t input_len; Py_ssize_t input_len;
Py_ssize_t next_read_idx; Py_ssize_t next_read_idx;
Py_ssize_t frame_end_idx;
Py_ssize_t prefetched_idx; /* index of first prefetched byte */ Py_ssize_t prefetched_idx; /* index of first prefetched byte */
PyObject *read; /* read() method of the input stream. */ PyObject *read; /* read() method of the input stream. */
...@@ -401,7 +396,6 @@ typedef struct UnpicklerObject { ...@@ -401,7 +396,6 @@ typedef struct UnpicklerObject {
int proto; /* Protocol of the pickle loaded. */ int proto; /* Protocol of the pickle loaded. */
int fix_imports; /* Indicate whether Unpickler should fix int fix_imports; /* Indicate whether Unpickler should fix
the name of globals pickled by Python 2.x. */ the name of globals pickled by Python 2.x. */
int framing; /* True when framing is enabled, proto >= 4 */
} UnpicklerObject; } UnpicklerObject;
/* Forward declarations */ /* Forward declarations */
...@@ -802,46 +796,6 @@ _Pickler_Write(PicklerObject *self, const char *s, Py_ssize_t data_len) ...@@ -802,46 +796,6 @@ _Pickler_Write(PicklerObject *self, const char *s, Py_ssize_t data_len)
n = data_len; n = data_len;
required = self->output_len + n; required = self->output_len + n;
if (self->write != NULL && required > MAX_WRITE_BUF_SIZE) {
/* XXX This reallocates a new buffer every time, which is a bit
wasteful. */
if (_Pickler_FlushToFile(self) < 0)
return -1;
if (_Pickler_ClearBuffer(self) < 0)
return -1;
/* The previous frame was just committed by _Pickler_FlushToFile */
need_new_frame = self->framing;
if (need_new_frame)
n = data_len + FRAME_HEADER_SIZE;
else
n = data_len;
required = self->output_len + n;
}
if (self->write != NULL && n > MAX_WRITE_BUF_SIZE) {
/* For large pickle chunks, we write directly to the output
file instead of buffering. Note the buffer is empty at this
point (it was flushed above, since required >= n). */
PyObject *output, *result;
if (need_new_frame) {
char frame_header[FRAME_HEADER_SIZE];
_Pickler_WriteFrameHeader(self, frame_header, (size_t) data_len);
output = PyBytes_FromStringAndSize(frame_header, FRAME_HEADER_SIZE);
if (output == NULL)
return -1;
result = _Pickler_FastCall(self, self->write, output);
Py_XDECREF(result);
if (result == NULL)
return -1;
}
/* XXX we could spare an intermediate copy and pass
a memoryview instead */
output = PyBytes_FromStringAndSize(s, data_len);
if (output == NULL)
return -1;
result = _Pickler_FastCall(self, self->write, output);
Py_XDECREF(result);
return (result == NULL) ? -1 : 0;
}
if (required > self->max_output_len) { if (required > self->max_output_len) {
/* Make place in buffer for the pickle chunk */ /* Make place in buffer for the pickle chunk */
if (self->output_len >= PY_SSIZE_T_MAX / 2 - n) { if (self->output_len >= PY_SSIZE_T_MAX / 2 - n) {
...@@ -987,7 +941,6 @@ _Unpickler_SetStringInput(UnpicklerObject *self, PyObject *input) ...@@ -987,7 +941,6 @@ _Unpickler_SetStringInput(UnpicklerObject *self, PyObject *input)
self->input_buffer = self->buffer.buf; self->input_buffer = self->buffer.buf;
self->input_len = self->buffer.len; self->input_len = self->buffer.len;
self->next_read_idx = 0; self->next_read_idx = 0;
self->frame_end_idx = -1;
self->prefetched_idx = self->input_len; self->prefetched_idx = self->input_len;
return self->input_len; return self->input_len;
} }
...@@ -1052,7 +1005,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) ...@@ -1052,7 +1005,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n)
return -1; return -1;
/* Prefetch some data without advancing the file pointer, if possible */ /* Prefetch some data without advancing the file pointer, if possible */
if (self->peek && !self->framing) { if (self->peek) {
PyObject *len, *prefetched; PyObject *len, *prefetched;
len = PyLong_FromSsize_t(PREFETCH); len = PyLong_FromSsize_t(PREFETCH);
if (len == NULL) { if (len == NULL) {
...@@ -1100,7 +1053,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) ...@@ -1100,7 +1053,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n)
Returns -1 (with an exception set) on failure. On success, return the Returns -1 (with an exception set) on failure. On success, return the
number of chars read. */ number of chars read. */
static Py_ssize_t static Py_ssize_t
_Unpickler_ReadUnframed(UnpicklerObject *self, char **s, Py_ssize_t n) _Unpickler_Read(UnpicklerObject *self, char **s, Py_ssize_t n)
{ {
Py_ssize_t num_read; Py_ssize_t num_read;
...@@ -1125,67 +1078,6 @@ _Unpickler_ReadUnframed(UnpicklerObject *self, char **s, Py_ssize_t n) ...@@ -1125,67 +1078,6 @@ _Unpickler_ReadUnframed(UnpicklerObject *self, char **s, Py_ssize_t n)
return n; return n;
} }
static Py_ssize_t
_Unpickler_Read(UnpicklerObject *self, char **s, Py_ssize_t n)
{
if (self->framing &&
(self->frame_end_idx == -1 ||
self->frame_end_idx <= self->next_read_idx)) {
/* Need to read new frame */
char *dummy = NULL;
unsigned char *frame_start;
size_t frame_len;
if (_Unpickler_ReadUnframed(self, &dummy, FRAME_HEADER_SIZE) < 0)
return -1;
frame_start = (unsigned char *) dummy;
if (frame_start[0] != (unsigned char)FRAME) {
PyErr_Format(UnpicklingError,
"expected FRAME opcode, got 0x%x instead",
frame_start[0]);
return -1;
}
frame_len = (size_t) frame_start[1];
frame_len |= (size_t) frame_start[2] << 8;
frame_len |= (size_t) frame_start[3] << 16;
frame_len |= (size_t) frame_start[4] << 24;
#if SIZEOF_SIZE_T >= 8
frame_len |= (size_t) frame_start[5] << 32;
frame_len |= (size_t) frame_start[6] << 40;
frame_len |= (size_t) frame_start[7] << 48;
frame_len |= (size_t) frame_start[8] << 56;
#else
if (frame_start[5] || frame_start[6] ||
frame_start[7] || frame_start[8]) {
PyErr_Format(PyExc_OverflowError,
"Frame size too large for 32-bit build");
return -1;
}
#endif
if (frame_len > PY_SSIZE_T_MAX) {
PyErr_Format(UnpicklingError, "Invalid frame length");
return -1;
}
if ((Py_ssize_t) frame_len < n) {
PyErr_Format(UnpicklingError, "Bad framing");
return -1;
}
if (_Unpickler_ReadUnframed(self, &dummy /* unused */,
frame_len) < 0)
return -1;
/* Rewind to start of frame */
self->frame_end_idx = self->next_read_idx;
self->next_read_idx -= frame_len;
}
if (self->framing) {
/* Check for bad input */
if (n + self->next_read_idx > self->frame_end_idx) {
PyErr_Format(UnpicklingError, "Bad framing");
return -1;
}
}
return _Unpickler_ReadUnframed(self, s, n);
}
static Py_ssize_t static Py_ssize_t
_Unpickler_CopyLine(UnpicklerObject *self, char *line, Py_ssize_t len, _Unpickler_CopyLine(UnpicklerObject *self, char *line, Py_ssize_t len,
char **result) char **result)
...@@ -1336,7 +1228,6 @@ _Unpickler_New(void) ...@@ -1336,7 +1228,6 @@ _Unpickler_New(void)
self->input_line = NULL; self->input_line = NULL;
self->input_len = 0; self->input_len = 0;
self->next_read_idx = 0; self->next_read_idx = 0;
self->frame_end_idx = -1;
self->prefetched_idx = 0; self->prefetched_idx = 0;
self->read = NULL; self->read = NULL;
self->readline = NULL; self->readline = NULL;
...@@ -1347,7 +1238,6 @@ _Unpickler_New(void) ...@@ -1347,7 +1238,6 @@ _Unpickler_New(void)
self->num_marks = 0; self->num_marks = 0;
self->marks_size = 0; self->marks_size = 0;
self->proto = 0; self->proto = 0;
self->framing = 0;
self->fix_imports = 0; self->fix_imports = 0;
memset(&self->buffer, 0, sizeof(Py_buffer)); memset(&self->buffer, 0, sizeof(Py_buffer));
self->memo_size = 32; self->memo_size = 32;
...@@ -1474,8 +1364,6 @@ memo_put(PicklerObject *self, PyObject *obj) ...@@ -1474,8 +1364,6 @@ memo_put(PicklerObject *self, PyObject *obj)
if (self->fast) if (self->fast)
return 0; return 0;
if (_Pickler_OpcodeBoundary(self))
return -1;
idx = PyMemoTable_Size(self->memo); idx = PyMemoTable_Size(self->memo);
if (PyMemoTable_Set(self->memo, obj, idx) < 0) if (PyMemoTable_Set(self->memo, obj, idx) < 0)
...@@ -3661,6 +3549,9 @@ save(PicklerObject *self, PyObject *obj, int pers_save) ...@@ -3661,6 +3549,9 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
PyObject *reduce_value = NULL; PyObject *reduce_value = NULL;
int status = 0; int status = 0;
if (_Pickler_OpcodeBoundary(self) < 0)
return -1;
if (Py_EnterRecursiveCall(" while pickling an object")) if (Py_EnterRecursiveCall(" while pickling an object"))
return -1; return -1;
...@@ -3855,8 +3746,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save) ...@@ -3855,8 +3746,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
status = -1; status = -1;
} }
done: done:
if (status == 0)
status = _Pickler_OpcodeBoundary(self);
Py_LeaveRecursiveCall(); Py_LeaveRecursiveCall();
Py_XDECREF(reduce_func); Py_XDECREF(reduce_func);
Py_XDECREF(reduce_value); Py_XDECREF(reduce_value);
...@@ -4514,7 +4404,7 @@ calc_binsize(char *bytes, int nbytes) ...@@ -4514,7 +4404,7 @@ calc_binsize(char *bytes, int nbytes)
int i; int i;
size_t x = 0; size_t x = 0;
for (i = 0; i < nbytes; i++) { for (i = 0; i < nbytes && i < sizeof(size_t); i++) {
x |= (size_t) s[i] << (8 * i); x |= (size_t) s[i] << (8 * i);
} }
...@@ -5972,7 +5862,6 @@ load_proto(UnpicklerObject *self) ...@@ -5972,7 +5862,6 @@ load_proto(UnpicklerObject *self)
i = (unsigned char)s[0]; i = (unsigned char)s[0];
if (i <= HIGHEST_PROTOCOL) { if (i <= HIGHEST_PROTOCOL) {
self->proto = i; self->proto = i;
self->framing = (self->proto >= 4);
return 0; return 0;
} }
...@@ -5980,16 +5869,39 @@ load_proto(UnpicklerObject *self) ...@@ -5980,16 +5869,39 @@ load_proto(UnpicklerObject *self)
return -1; return -1;
} }
static int
load_frame(UnpicklerObject *self)
{
char *s;
Py_ssize_t frame_len;
if (_Unpickler_Read(self, &s, 8) < 0)
return -1;
frame_len = calc_binsize(s, 8);
if (frame_len < 0) {
PyErr_Format(PyExc_OverflowError,
"FRAME length exceeds system's maximum of %zd bytes",
PY_SSIZE_T_MAX);
return -1;
}
if (_Unpickler_Read(self, &s, frame_len) < 0)
return -1;
/* Rewind to start of frame */
self->next_read_idx -= frame_len;
return 0;
}
static PyObject * static PyObject *
load(UnpicklerObject *self) load(UnpicklerObject *self)
{ {
PyObject *err;
PyObject *value = NULL; PyObject *value = NULL;
char *s; char *s;
self->num_marks = 0; self->num_marks = 0;
self->proto = 0; self->proto = 0;
self->framing = 0;
if (Py_SIZE(self->stack)) if (Py_SIZE(self->stack))
Pdata_clear(self->stack, 0); Pdata_clear(self->stack, 0);
...@@ -6063,6 +5975,7 @@ load(UnpicklerObject *self) ...@@ -6063,6 +5975,7 @@ load(UnpicklerObject *self)
OP(BINPERSID, load_binpersid) OP(BINPERSID, load_binpersid)
OP(REDUCE, load_reduce) OP(REDUCE, load_reduce)
OP(PROTO, load_proto) OP(PROTO, load_proto)
OP(FRAME, load_frame)
OP_ARG(EXT1, load_extension, 1) OP_ARG(EXT1, load_extension, 1)
OP_ARG(EXT2, load_extension, 2) OP_ARG(EXT2, load_extension, 2)
OP_ARG(EXT4, load_extension, 4) OP_ARG(EXT4, load_extension, 4)
...@@ -6084,11 +5997,7 @@ load(UnpicklerObject *self) ...@@ -6084,11 +5997,7 @@ load(UnpicklerObject *self)
break; /* and we are done! */ break; /* and we are done! */
} }
/* XXX: It is not clear what this is actually for. */ if (PyErr_Occurred()) {
if ((err = PyErr_Occurred())) {
if (err == PyExc_EOFError) {
PyErr_SetNone(PyExc_EOFError);
}
return NULL; return NULL;
} }
...@@ -6383,7 +6292,6 @@ Unpickler_init(UnpicklerObject *self, PyObject *args, PyObject *kwds) ...@@ -6383,7 +6292,6 @@ Unpickler_init(UnpicklerObject *self, PyObject *args, PyObject *kwds)
self->arg = NULL; self->arg = NULL;
self->proto = 0; self->proto = 0;
self->framing = 0;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment