Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 91712fb

Browse files
author
Anselm Kruis
committed
Issue #127: use C-Python pickling for 'iterator' and 'callable_iterator'
Disable the Stackless specific code for pickling 'iterator' and 'callable_iterator' objects. C-Python 3.3 already pickles them. Add tests to ensure, that Stackless can still unpickle old pickles. https://bitbucket.org/stackless-dev/stackless/issues/127
1 parent 18a3e59 commit 91712fb

File tree

3 files changed

+87
-10
lines changed

3 files changed

+87
-10
lines changed

Stackless/changelog.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ What's New in Stackless 3.X.X?
1010
*Release date: 20XX-XX-XX*
1111

1212
- https://bitbucket.org/stackless-dev/stackless/issues/127
13-
Fix pickling of 'callable-iterator' and 'method-wrapper' objects.
14-
Fix copy.copy() for 'callable-iterator', 'method', 'dict_keys', 'dict_values'
15-
and 'dict_items' objects.
13+
Disable the Stackless specific code for pickling 'iterator' and
14+
'callable_iterator' objects. C-Python 3.3 already pickles them.
15+
Fix pickling of 'method-wrapper' objects.
16+
Fix copy.copy() for 'method', 'dict_keys', 'dict_values' and 'dict_items'
17+
objects.
1618

1719
- https://bitbucket.org/stackless-dev/stackless/issues/126
1820
Load the module stackless early in all C-Python tests. This ensures a defined

Stackless/pickling/prickelpit.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ static struct PyMethodDef _new_methoddef[] = {
201201
{0}
202202
};
203203

204-
static int init_type(PyTypeObject *t, int (*initchain)(void))
204+
static int
205+
init_type_ex(PyTypeObject *t, int (*initchain)(void), int register_reduce)
205206
{
206207
PyMethodDescrObject *reduce;
207208
PyWrapperDescrObject *init;
@@ -230,15 +231,18 @@ static int init_type(PyTypeObject *t, int (*initchain)(void))
230231
if (func == NULL || PyDict_SetItemString(t->tp_dict, "__new__", func))
231232
return -1;
232233
/* register with copy_reg */
233-
if (pickle_reg != NULL &&
234-
(retval = PyObject_CallFunction(pickle_reg, "OO",
235-
t->tp_base, reduce)) == NULL)
236-
ret = -1;
234+
if (register_reduce) {
235+
if (pickle_reg != NULL &&
236+
(retval = PyObject_CallFunction(pickle_reg, "OO",
237+
t->tp_base, reduce)) == NULL)
238+
ret = -1;
239+
}
237240
Py_XDECREF(retval);
238241
if (ret == 0 && initchain != NULL)
239242
ret = initchain();
240243
return ret;
241244
}
245+
#define init_type(t_, initchain_) init_type_ex((t_), (initchain_), 1)
242246

243247
/* root of init function chain */
244248

@@ -1427,8 +1431,8 @@ MAKE_WRAPPERTYPE(PyCallIter_Type, calliter, "callable_iterator",
14271431

14281432
static int init_itertype(void)
14291433
{
1430-
return init_type(&wrap_PySeqIter_Type, NULL)
1431-
|| init_type(&wrap_PyCallIter_Type, initchain);
1434+
return init_type_ex(&wrap_PySeqIter_Type, NULL, 0)
1435+
|| init_type_ex(&wrap_PyCallIter_Type, initchain, 0);
14321436
}
14331437
#undef initchain
14341438
#define initchain init_itertype

Stackless/unittests/test_pickle.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,77 @@ def test_dict_items(self):
716716
obj = d.items()
717717
self._test(obj)
718718

719+
720+
class SimpleSequence:
721+
def __getitem__(self, n):
722+
if n < 3:
723+
return n
724+
raise IndexError
725+
726+
727+
class TestOldStackless_WrapFactories(StacklessPickleTestCase):
728+
def test_iterator(self):
729+
# Stackless prior to version 3.3.7 used to register its own __reduce__
730+
# method for 'iterator' with copy_reg. This method returned the function
731+
# stackless._wrap.iterator as iterator factory.
732+
#
733+
# This test case ensures that stackless can still unpickle old pickles:
734+
735+
# an old pickle, protocol 0 of iter(SimpleSequence())
736+
p = b'c_stackless._wrap\niterator\n(ccopy_reg\n_reconstructor\n(ctest_pickle\nSimpleSequence\nc__builtin__\nobject\nNtRL0L\ntR(tb.'
737+
738+
# Output of
739+
# from pickletools import dis; dis(p)
740+
# 0: c GLOBAL '_stackless._wrap iterator'
741+
# 27: ( MARK
742+
# 28: c GLOBAL 'copy_reg _reconstructor'
743+
# 53: ( MARK
744+
# 54: c GLOBAL 'test_pickle SimpleSequence'
745+
# 82: c GLOBAL '__builtin__ object'
746+
# 102: N NONE
747+
# 103: t TUPLE (MARK at 53)
748+
# 104: R REDUCE
749+
# 105: L LONG 0
750+
# 109: t TUPLE (MARK at 27)
751+
# 110: R REDUCE
752+
# 111: ( MARK
753+
# 112: t TUPLE (MARK at 111)
754+
# 113: b BUILD
755+
# 114: . STOP
756+
# highest protocol among opcodes = 0
757+
758+
x = self.loads(p)
759+
self.assertIs(type(x), type(iter(SimpleSequence())))
760+
self.assertListEqual(list(x), [0, 1, 2])
761+
762+
def test_callable_iterator(self):
763+
# Stackless prior to version 3.3.7 used to register its own __reduce__
764+
# method for 'callable_iterator' with copy_reg. This method returned the function
765+
# stackless._wrap.callable_iterator as callable_iterator factory.
766+
#
767+
# This test case ensures that stackless can still unpickle old pickles:
768+
769+
# an old pickle, protocol 0 of iter(bool, False)
770+
p = b'c_stackless._wrap\ncallable_iterator\n(c__builtin__\nbool\nI00\ntR.'
771+
772+
# Output of
773+
# from pickletools import dis; dis(p)
774+
# 0: c GLOBAL '_stackless._wrap callable_iterator'
775+
# 36: ( MARK
776+
# 37: c GLOBAL '__builtin__ bool'
777+
# 55: I INT False
778+
# 59: t TUPLE (MARK at 36)
779+
# 60: R REDUCE
780+
# 61: . STOP
781+
# highest protocol among opcodes = 0
782+
783+
x = self.loads(p)
784+
# Use assertIsInstance, because x may be a subclass of i.
785+
# See bug https://bitbucket.org/stackless-dev/stackless/issues/127
786+
self.assertIsInstance(x, type(iter(bool, False)))
787+
self.assertListEqual(list(x), [])
788+
789+
719790
if __name__ == '__main__':
720791
if not sys.argv[1:]:
721792
sys.argv.append('-v')

0 commit comments

Comments
 (0)