Merge lp:~mnordhoff/bzr/statictuple-pickling into lp:bzr

Proposed by Matt Nordhoff
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~mnordhoff/bzr/statictuple-pickling
Merge into: lp:bzr
Diff against target: 112 lines
3 files modified
bzrlib/_static_tuple_c.c (+27/-3)
bzrlib/_static_tuple_py.py (+3/-0)
bzrlib/tests/test__static_tuple.py (+19/-0)
To merge this branch: bzr merge lp:~mnordhoff/bzr/statictuple-pickling
Reviewer Review Type Date Requested Status
John A Meinel Approve
Review via email: mp+13720@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

This adds pickling support to StaticTuple.

Before this, I had written approximately 4 lines of C in my life, and had never used Python's C API. Please review it carefully. I think I actually have the error handling and reference counting right, but I really can't be sure.

The Launchpad mirror for this branch is not up-to-date. Please get an up-to-date copy from my website, or look at my Loggerhead or a diff I made:

http://bzr.mattnordhoff.com/loggerhead/bzr/statictuple-pickling/changes
http://bzr.mattnordhoff.com/bzr/bzr/statictuple-pickling/statictuple-pickling-r4778.patch

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Matt Nordhoff wrote:
> Matt Nordhoff has proposed merging lp:~mnordhoff/bzr/statictuple-pickling into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
>
>
> This adds pickling support to StaticTuple.
>
> Before this, I had written approximately 4 lines of C in my life, and had never used Python's C API. Please review it carefully. I think I actually have the error handling and reference counting right, but I really can't be sure.
>
> The Launchpad mirror for this branch is not up-to-date. Please get an up-to-date copy from my website, or look at my Loggerhead or a diff I made:
>
> http://bzr.mattnordhoff.com/loggerhead/bzr/statictuple-pickling/changes
> http://bzr.mattnordhoff.com/bzr/bzr/statictuple-pickling/statictuple-pickling-r4778.patch
>
>

 static PyObject *
+StaticTuple_reduce(StaticTuple *self)
+{
+ PyObject *result = NULL, *as_tuple = NULL;
+
+ result = PyTuple_New(2);
+ if (!result) {
+ return NULL;
+ }
+ as_tuple = StaticTuple_as_tuple(self);
+ if (as_tuple == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
+ PyTuple_SET_ITEM(result, 1, as_tuple);
+ return result;
+}

^- PyTuple_SET_ITEM doesn't INCREF, it 'steals' a reference. This is ok
for 'as_tuple' because you are transferring ownership from
StaticTuple_as_tuple. However it is not ok for StaticTuple_Type. So you
just need to add

Py_INCREF(&StaticTuple_Type)

I think it would probably also be good to test that pickling a nested
StaticTuple works, but otherwise this looks good to me. (as long as we
add the one Py_INCREF call.)

 review: approve
 merge: approve
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkrfSSkACgkQJdeBCYSNAAN71gCg0Rnh2IJRMr5cjnTO0shD0wpz
CHQAoIA72B56GWBvg+9X9U0OLR08saz7
=MCX3
-----END PGP SIGNATURE-----

review: Approve
Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

John A Meinel wrote:
> ^- PyTuple_SET_ITEM doesn't INCREF, it 'steals' a reference. This is ok
> for 'as_tuple' because you are transferring ownership from
> StaticTuple_as_tuple. However it is not ok for StaticTuple_Type. So you
> just need to add
>
> Py_INCREF(&StaticTuple_Type)
>
> I think it would probably also be good to test that pickling a nested
> StaticTuple works, but otherwise this looks good to me. (as long as we
> add the one Py_INCREF call.)

Done and done. Thank you! :-)

I thought the test was getting a little unwieldy, so I split it up into
3 methods. I hope you don't mind.

Incremental diff:

<http://bzr.mattnordhoff.com/loggerhead/bzr/statictuple-pickling/revision/4779>
--

Updating diff...

An updated diff will be available in a few minutes. Reload to see the changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/_static_tuple_c.c'
--- bzrlib/_static_tuple_c.c 2009-10-21 14:27:00 +0000
+++ bzrlib/_static_tuple_c.c 2009-10-21 22:09:14 +0000
@@ -304,8 +304,8 @@
304 if (tuple_repr == NULL) {304 if (tuple_repr == NULL) {
305 return NULL;305 return NULL;
306 }306 }
307 result = PyString_FromFormat("%s%s", Py_TYPE(self)->tp_name,307 result = PyString_FromFormat("StaticTuple%s",
308 PyString_AsString(tuple_repr));308 PyString_AsString(tuple_repr));
309 return result;309 return result;
310}310}
311311
@@ -574,6 +574,29 @@
574574
575575
576static PyObject *576static PyObject *
577StaticTuple_reduce(StaticTuple *self)
578{
579 PyObject *result = NULL, *as_tuple = NULL;
580
581 result = PyTuple_New(2);
582 if (!result) {
583 return NULL;
584 }
585 as_tuple = StaticTuple_as_tuple(self);
586 if (as_tuple == NULL) {
587 Py_DECREF(result);
588 return NULL;
589 }
590 Py_INCREF(&StaticTuple_Type);
591 PyTuple_SET_ITEM(result, 0, (PyObject *)&StaticTuple_Type);
592 PyTuple_SET_ITEM(result, 1, as_tuple);
593 return result;
594}
595
596static char StaticTuple_reduce_doc[] = "__reduce__() => tuple\n";
597
598
599static PyObject *
577StaticTuple_add(PyObject *v, PyObject *w)600StaticTuple_add(PyObject *v, PyObject *w)
578{601{
579 Py_ssize_t i, len_v, len_w;602 Py_ssize_t i, len_v, len_w;
@@ -688,6 +711,7 @@
688 METH_STATIC | METH_VARARGS,711 METH_STATIC | METH_VARARGS,
689 "Create a StaticTuple from a given sequence. This functions"712 "Create a StaticTuple from a given sequence. This functions"
690 " the same as the tuple() constructor."},713 " the same as the tuple() constructor."},
714 {"__reduce__", (PyCFunction)StaticTuple_reduce, METH_NOARGS, StaticTuple_reduce_doc},
691 {NULL, NULL} /* sentinel */715 {NULL, NULL} /* sentinel */
692};716};
693717
@@ -735,7 +759,7 @@
735PyTypeObject StaticTuple_Type = {759PyTypeObject StaticTuple_Type = {
736 PyObject_HEAD_INIT(NULL)760 PyObject_HEAD_INIT(NULL)
737 0, /* ob_size */761 0, /* ob_size */
738 "StaticTuple", /* tp_name */762 "bzrlib._static_tuple_c.StaticTuple", /* tp_name */
739 sizeof(StaticTuple), /* tp_basicsize */763 sizeof(StaticTuple), /* tp_basicsize */
740 sizeof(PyObject *), /* tp_itemsize */764 sizeof(PyObject *), /* tp_itemsize */
741 (destructor)StaticTuple_dealloc, /* tp_dealloc */765 (destructor)StaticTuple_dealloc, /* tp_dealloc */
742766
=== modified file 'bzrlib/_static_tuple_py.py'
--- bzrlib/_static_tuple_py.py 2009-10-21 05:02:35 +0000
+++ bzrlib/_static_tuple_py.py 2009-10-21 22:09:14 +0000
@@ -48,6 +48,9 @@
48 def __repr__(self):48 def __repr__(self):
49 return '%s%s' % (self.__class__.__name__, tuple.__repr__(self))49 return '%s%s' % (self.__class__.__name__, tuple.__repr__(self))
5050
51 def __reduce__(self):
52 return (StaticTuple, tuple(self))
53
51 def __add__(self, other):54 def __add__(self, other):
52 """Concatenate self with other"""55 """Concatenate self with other"""
53 return StaticTuple.from_sequence(tuple.__add__(self,other))56 return StaticTuple.from_sequence(tuple.__add__(self,other))
5457
=== modified file 'bzrlib/tests/test__static_tuple.py'
--- bzrlib/tests/test__static_tuple.py 2009-10-21 14:27:00 +0000
+++ bzrlib/tests/test__static_tuple.py 2009-10-21 22:09:14 +0000
@@ -16,6 +16,7 @@
1616
17"""Tests for the StaticTuple type."""17"""Tests for the StaticTuple type."""
1818
19import cPickle
19import gc20import gc
20import sys21import sys
2122
@@ -577,6 +578,24 @@
577 self.assertRaises(TypeError,578 self.assertRaises(TypeError,
578 self.module.StaticTuple.from_sequence, foo='a')579 self.module.StaticTuple.from_sequence, foo='a')
579580
581 def test_pickle(self):
582 st = self.module.StaticTuple('foo', 'bar')
583 pickled = cPickle.dumps(st)
584 unpickled = cPickle.loads(pickled)
585 self.assertEqual(unpickled, st)
586
587 def test_pickle_empty(self):
588 st = self.module.StaticTuple()
589 pickled = cPickle.dumps(st)
590 unpickled = cPickle.loads(pickled)
591 self.assertIs(st, unpickled)
592
593 def test_pickle_nested(self):
594 st = self.module.StaticTuple('foo', self.module.StaticTuple('bar'))
595 pickled = cPickle.dumps(st)
596 unpickled = cPickle.loads(pickled)
597 self.assertEqual(unpickled, st)
598
580 def test_static_tuple_thunk(self):599 def test_static_tuple_thunk(self):
581 # Make sure the right implementation is available from600 # Make sure the right implementation is available from
582 # bzrlib.static_tuple.StaticTuple.601 # bzrlib.static_tuple.StaticTuple.