Merge lp:~gz/brz/py3_static_tuple_start into lp:brz

Proposed by Martin Packman on 2017-06-22
Status: Merged
Approved by: Martin Packman on 2017-06-25
Approved revision: 6719
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~gz/brz/py3_static_tuple_start
Merge into: lp:brz
Diff against target: 432 lines (+118/-103)
4 files modified
breezy/_export_c_api.h (+3/-5)
breezy/_static_tuple_c.c (+61/-76)
breezy/python-compat.h (+31/-0)
breezy/tests/test__static_tuple.py (+23/-22)
To merge this branch: bzr merge lp:~gz/brz/py3_static_tuple_start
Reviewer Review Type Date Requested Status
Jelmer Vernooij 2017-06-22 Approve on 2017-06-22
Review via email: mp+326117@code.launchpad.net

Commit message

Start of making _static_tuple_c compatible with Python 3

Description of the change

This is not everything done to make StaticTuple work on Python 3, but is a reasonable checkpoint. The code compiles, runs tests, and fails in a few relevant ways. The C API level import is also broken.

To post a comment you must log in.
Jelmer Vernooij (jelmer) :
review: Approve
The Breezy Bot (the-breezy-bot) wrote :

Running landing tests failed
http://10.242.247.184:8080/job/brz-dev/168/

The Breezy Bot (the-breezy-bot) wrote :

Running landing tests failed
http://10.242.247.184:8080/job/brz-dev/172/

lp:~gz/brz/py3_static_tuple_start updated on 2017-06-25
6719. By Martin Packman on 2017-06-25

Correct name for init func

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'breezy/_export_c_api.h'
--- breezy/_export_c_api.h 2009-10-12 21:44:27 +0000
+++ breezy/_export_c_api.h 2017-06-25 23:38:37 +0000
@@ -15,6 +15,7 @@
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */16 */
1717
18/* GZ 2017-06-22: This header needs updating to use Capsules over CObjects */
1819
19/* This file contains helper functions for exporting a C API for a CPython20/* This file contains helper functions for exporting a C API for a CPython
20 * extension module.21 * extension module.
@@ -45,17 +46,14 @@
45 PyObject *d = NULL;46 PyObject *d = NULL;
46 PyObject *c_obj = NULL;47 PyObject *c_obj = NULL;
4748
48 /* (char *) is because python2.4 declares this api as 'char *' rather than49 d = PyObject_GetAttrString(module, _C_API_NAME);
49 * const char* which it really is.
50 */
51 d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
52 if (!d) {50 if (!d) {
53 PyErr_Clear();51 PyErr_Clear();
54 d = PyDict_New();52 d = PyDict_New();
55 if (!d)53 if (!d)
56 goto bad;54 goto bad;
57 Py_INCREF(d);55 Py_INCREF(d);
58 if (PyModule_AddObject(module, (char *)_C_API_NAME, d) < 0)56 if (PyModule_AddObject(module, _C_API_NAME, d) < 0)
59 goto bad;57 goto bad;
60 }58 }
61 c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0);59 c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0);
6260
=== modified file 'breezy/_static_tuple_c.c'
--- breezy/_static_tuple_c.c 2017-05-21 18:10:28 +0000
+++ breezy/_static_tuple_c.c 2017-06-25 23:38:37 +0000
@@ -26,12 +26,6 @@
26#include "_static_tuple_c.h"26#include "_static_tuple_c.h"
27#include "_export_c_api.h"27#include "_export_c_api.h"
2828
29/* Pyrex 0.9.6.4 exports _simple_set_pyx_api as
30 * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them
31 * as import_breezy___simple_set_pyx(). As such, we just #define one to be
32 * equivalent to the other in our internal code.
33 */
34#define import__simple_set_pyx import_breezy___simple_set_pyx
35#include "_simple_set_pyx_api.h"29#include "_simple_set_pyx_api.h"
3630
37#if defined(__GNUC__)31#if defined(__GNUC__)
@@ -242,11 +236,14 @@
242 " should not have a NULL entry.");236 " should not have a NULL entry.");
243 return 0;237 return 0;
244 }238 }
245 if (PyString_CheckExact(obj)239 if (PyBytes_CheckExact(obj)
246 || StaticTuple_CheckExact(obj)240 || StaticTuple_CheckExact(obj)
247 || obj == Py_None241 || obj == Py_None
248 || PyBool_Check(obj)242 || PyBool_Check(obj)
243#if PY_MAJOR_VERSION >= 3
244#else
249 || PyInt_CheckExact(obj)245 || PyInt_CheckExact(obj)
246#endif
250 || PyLong_CheckExact(obj)247 || PyLong_CheckExact(obj)
251 || PyFloat_CheckExact(obj)248 || PyFloat_CheckExact(obj)
252 || PyUnicode_CheckExact(obj)249 || PyUnicode_CheckExact(obj)
@@ -314,8 +311,12 @@
314 if (tuple_repr == NULL) {311 if (tuple_repr == NULL) {
315 return NULL;312 return NULL;
316 }313 }
314#if PY_MAJOR_VERSION >= 3
315 result = PyUnicode_FromFormat("StaticTuple%U", tuple_repr);
316#else
317 result = PyString_FromFormat("StaticTuple%s",317 result = PyString_FromFormat("StaticTuple%s",
318 PyString_AsString(tuple_repr));318 PyString_AsString(tuple_repr));
319#endif
319 return result;320 return result;
320}321}
321322
@@ -362,7 +363,7 @@
362{363{
363 PyObject *vt;364 PyObject *vt;
364 PyObject *result = NULL;365 PyObject *result = NULL;
365 366
366 vt = StaticTuple_as_tuple((StaticTuple *)v);367 vt = StaticTuple_as_tuple((StaticTuple *)v);
367 if (vt == NULL) {368 if (vt == NULL) {
368 goto done;369 goto done;
@@ -478,7 +479,7 @@
478 vlen = v_st->size;479 vlen = v_st->size;
479 wlen = w_st->size;480 wlen = w_st->size;
480 min_len = (vlen < wlen) ? vlen : wlen;481 min_len = (vlen < wlen) ? vlen : wlen;
481 string_richcompare = PyString_Type.tp_richcompare;482 string_richcompare = PyBytes_Type.tp_richcompare;
482 for (i = 0; i < min_len; i++) {483 for (i = 0; i < min_len; i++) {
483 PyObject *result = NULL;484 PyObject *result = NULL;
484 v_obj = StaticTuple_GET_ITEM(v_st, i);485 v_obj = StaticTuple_GET_ITEM(v_st, i);
@@ -487,7 +488,7 @@
487 /* Shortcut case, these must be identical */488 /* Shortcut case, these must be identical */
488 continue;489 continue;
489 }490 }
490 if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {491 if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj)) {
491 result = string_richcompare(v_obj, w_obj, Py_EQ);492 result = string_richcompare(v_obj, w_obj, Py_EQ);
492 } else if (StaticTuple_CheckExact(v_obj) &&493 } else if (StaticTuple_CheckExact(v_obj) &&
493 StaticTuple_CheckExact(w_obj))494 StaticTuple_CheckExact(w_obj))
@@ -547,7 +548,7 @@
547 return Py_True;548 return Py_True;
548 }549 }
549 /* It is some other comparison, go ahead and do the real check. */550 /* It is some other comparison, go ahead and do the real check. */
550 if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))551 if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj))
551 {552 {
552 return string_richcompare(v_obj, w_obj, op);553 return string_richcompare(v_obj, w_obj, op);
553 } else if (StaticTuple_CheckExact(v_obj) &&554 } else if (StaticTuple_CheckExact(v_obj) &&
@@ -679,6 +680,8 @@
679 return obj;680 return obj;
680}681}
681682
683#if PY_MAJOR_VERSION >= 3
684#else
682static PyObject *685static PyObject *
683StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)686StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
684{687{
@@ -692,6 +695,21 @@
692 Py_DECREF(as_tuple);695 Py_DECREF(as_tuple);
693 return result;696 return result;
694}697}
698#endif
699
700static PyObject *
701StaticTuple_subscript(StaticTuple *self, PyObject *key)
702{
703 PyObject *as_tuple, *result;
704
705 as_tuple = StaticTuple_as_tuple(self);
706 if (as_tuple == NULL) {
707 return NULL;
708 }
709 result = PyTuple_Type.tp_as_mapping->mp_subscript(as_tuple, key);
710 Py_DECREF(as_tuple);
711 return result;
712}
695713
696static int714static int
697StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)715StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
@@ -759,29 +777,36 @@
759 0, /* nb_or */777 0, /* nb_or */
760 0, /* nb_coerce */778 0, /* nb_coerce */
761};779};
762 780
763781
764static PySequenceMethods StaticTuple_as_sequence = {782static PySequenceMethods StaticTuple_as_sequence = {
765 (lenfunc)StaticTuple_length, /* sq_length */783 (lenfunc)StaticTuple_length, /* sq_length */
766 0, /* sq_concat */784 0, /* sq_concat */
767 0, /* sq_repeat */785 0, /* sq_repeat */
768 (ssizeargfunc)StaticTuple_item, /* sq_item */786 (ssizeargfunc)StaticTuple_item, /* sq_item */
787#if PY_MAJOR_VERSION >= 3
788#else
769 (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */789 (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */
790#endif
770 0, /* sq_ass_item */791 0, /* sq_ass_item */
771 0, /* sq_ass_slice */792 0, /* sq_ass_slice */
772 0, /* sq_contains */793 0, /* sq_contains */
773};794#if PY_MAJOR_VERSION >= 3
774795 0, /* sq_inplace_concat */
775/* TODO: Implement StaticTuple_as_mapping.796 0, /* sq_inplace_repeat */
776 * The only thing we really want to support from there is mp_subscript,797#endif
777 * so that we could support extended slicing (foo[::2]). Not worth it798};
778 * yet, though.799
779 */800
801static PyMappingMethods StaticTuple_as_mapping = {
802 (lenfunc)StaticTuple_length, /* mp_length */
803 (binaryfunc)StaticTuple_subscript, /* mp_subscript */
804 0, /* mp_ass_subscript */
805};
780806
781807
782PyTypeObject StaticTuple_Type = {808PyTypeObject StaticTuple_Type = {
783 PyObject_HEAD_INIT(NULL)809 PyVarObject_HEAD_INIT(NULL, 0)
784 0, /* ob_size */
785 "breezy._static_tuple_c.StaticTuple", /* tp_name */810 "breezy._static_tuple_c.StaticTuple", /* tp_name */
786 sizeof(StaticTuple), /* tp_basicsize */811 sizeof(StaticTuple), /* tp_basicsize */
787 sizeof(PyObject *), /* tp_itemsize */812 sizeof(PyObject *), /* tp_itemsize */
@@ -793,7 +818,7 @@
793 (reprfunc)StaticTuple_repr, /* tp_repr */818 (reprfunc)StaticTuple_repr, /* tp_repr */
794 &StaticTuple_as_number, /* tp_as_number */819 &StaticTuple_as_number, /* tp_as_number */
795 &StaticTuple_as_sequence, /* tp_as_sequence */820 &StaticTuple_as_sequence, /* tp_as_sequence */
796 0, /* tp_as_mapping */821 &StaticTuple_as_mapping, /* tp_as_mapping */
797 (hashfunc)StaticTuple_hash, /* tp_hash */822 (hashfunc)StaticTuple_hash, /* tp_hash */
798 0, /* tp_call */823 0, /* tp_call */
799 0, /* tp_str */824 0, /* tp_str */
@@ -887,72 +912,32 @@
887}912}
888913
889914
890static int915PYMOD_INIT_FUNC(_static_tuple_c)
891_workaround_pyrex_096(void)
892{
893 /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
894 * versus how pyrex 0.9.8 and cython 0.11 export it.
895 * Namely 0.9.6 exports import__simple_set_pyx and tries to
896 * "import _simple_set_pyx" but it is available only as
897 * "import breezy._simple_set_pyx"
898 * It is a shame to hack up sys.modules, but that is what we've got to do.
899 */
900 PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
901 int retval = -1;
902
903 /* Clear out the current ImportError exception, and try again. */
904 PyErr_Clear();
905 /* Note that this only seems to work if somewhere else imports
906 * breezy._simple_set_pyx before importing breezy._static_tuple_c
907 */
908 set_module = PyImport_ImportModule("breezy._simple_set_pyx");
909 if (set_module == NULL) {
910 goto end;
911 }
912 /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
913 sys_module = PyImport_ImportModule("sys");
914 if (sys_module == NULL) {
915 goto end;
916 }
917 modules = PyObject_GetAttrString(sys_module, "modules");
918 if (modules == NULL || !PyDict_Check(modules)) {
919 goto end;
920 }
921 PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
922 /* Now that we have hacked it in, try the import again. */
923 retval = import_breezy___simple_set_pyx();
924end:
925 Py_XDECREF(set_module);
926 Py_XDECREF(sys_module);
927 Py_XDECREF(modules);
928 return retval;
929}
930
931
932PyMODINIT_FUNC
933init_static_tuple_c(void)
934{916{
935 PyObject* m;917 PyObject* m;
936918
937 StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;919 StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
938 if (PyType_Ready(&StaticTuple_Type) < 0)920 if (PyType_Ready(&StaticTuple_Type) < 0) {
939 return;921 return PYMOD_ERROR;
922 }
940923
941 m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,924 PYMOD_CREATE(m, "_static_tuple_c",
942 "C implementation of a StaticTuple structure");925 "C implementation of a StaticTuple structure",
943 if (m == NULL)926 static_tuple_c_methods);
944 return;927 if (m == NULL) {
928 return PYMOD_ERROR;
929 }
945930
946 Py_INCREF(&StaticTuple_Type);931 Py_INCREF(&StaticTuple_Type);
947 PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);932 PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
948 if (import_breezy___simple_set_pyx() == -1933 if (import_breezy___simple_set_pyx() == -1) {
949 && _workaround_pyrex_096() == -1)934 return PYMOD_ERROR;
950 {
951 return;
952 }935 }
953 setup_interned_tuples(m);936 setup_interned_tuples(m);
954 setup_empty_tuple(m);937 setup_empty_tuple(m);
955 setup_c_api(m);938 setup_c_api(m);
939
940 return PYMOD_SUCCESS(m);
956}941}
957942
958// vim: tabstop=4 sw=4 expandtab943// vim: tabstop=4 sw=4 expandtab
959944
=== modified file 'breezy/python-compat.h'
--- breezy/python-compat.h 2017-06-05 23:59:08 +0000
+++ breezy/python-compat.h 2017-06-25 23:38:37 +0000
@@ -25,6 +25,37 @@
25#ifndef _BZR_PYTHON_COMPAT_H25#ifndef _BZR_PYTHON_COMPAT_H
26#define _BZR_PYTHON_COMPAT_H26#define _BZR_PYTHON_COMPAT_H
2727
28#if PY_MAJOR_VERSION >= 3
29
30#define PyInt_FromSsize_t PyLong_FromSsize_t
31
32/* In Python 3 the Py_TPFLAGS_CHECKTYPES behaviour is on by default */
33#define Py_TPFLAGS_CHECKTYPES 0
34
35#define PYMOD_ERROR NULL
36#define PYMOD_SUCCESS(val) val
37#define PYMOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void)
38#define PYMOD_CREATE(ob, name, doc, methods) do { \
39 static struct PyModuleDef moduledef = { \
40 PyModuleDef_HEAD_INIT, name, doc, -1, methods \
41 }; \
42 ob = PyModule_Create(&moduledef); \
43 } while(0)
44
45#else
46
47#define PyBytes_Type PyString_Type
48#define PyBytes_CheckExact PyString_CheckExact
49
50#define PYMOD_ERROR
51#define PYMOD_SUCCESS(val)
52#define PYMOD_INIT_FUNC(name) void init##name(void)
53#define PYMOD_CREATE(ob, name, doc, methods) do { \
54 ob = Py_InitModule3(name, methods, doc); \
55 } while(0)
56
57#endif
58
28#if defined(_WIN32) || defined(WIN32)59#if defined(_WIN32) || defined(WIN32)
29 /* Defining WIN32_LEAN_AND_MEAN makes including windows quite a bit60 /* Defining WIN32_LEAN_AND_MEAN makes including windows quite a bit
30 * lighter weight.61 * lighter weight.
3162
=== modified file 'breezy/tests/test__static_tuple.py'
--- breezy/tests/test__static_tuple.py 2017-05-23 14:08:03 +0000
+++ breezy/tests/test__static_tuple.py 2017-06-25 23:38:37 +0000
@@ -16,7 +16,10 @@
1616
17"""Tests for the StaticTuple type."""17"""Tests for the StaticTuple type."""
1818
19import cPickle19try:
20 import cPickle as pickle
21except ImportError:
22 import pickle
20import sys23import sys
2124
22from breezy import (25from breezy import (
@@ -26,6 +29,10 @@
26 static_tuple,29 static_tuple,
27 tests,30 tests,
28 )31 )
32from breezy.sixish import (
33 PY3,
34 text_type,
35 )
29from breezy.tests import (36from breezy.tests import (
30 features,37 features,
31 )38 )
@@ -213,6 +220,8 @@
213 self.assertRaises(TypeError, self.module.StaticTuple, subint(2))220 self.assertRaises(TypeError, self.module.StaticTuple, subint(2))
214221
215 def test_holds_long(self):222 def test_holds_long(self):
223 if PY3:
224 self.skipTest("No long type on Python 3")
216 k1 = self.module.StaticTuple(2**65)225 k1 = self.module.StaticTuple(2**65)
217 class sublong(long):226 class sublong(long):
218 pass227 pass
@@ -225,15 +234,15 @@
225 pass234 pass
226 self.assertRaises(TypeError, self.module.StaticTuple, subfloat(1.5))235 self.assertRaises(TypeError, self.module.StaticTuple, subfloat(1.5))
227236
228 def test_holds_str(self):237 def test_holds_bytes(self):
229 k1 = self.module.StaticTuple('astring')238 k1 = self.module.StaticTuple(b'astring')
230 class substr(str):239 class substr(bytes):
231 pass240 pass
232 self.assertRaises(TypeError, self.module.StaticTuple, substr('a'))241 self.assertRaises(TypeError, self.module.StaticTuple, substr(b'a'))
233242
234 def test_holds_unicode(self):243 def test_holds_unicode(self):
235 k1 = self.module.StaticTuple(u'\xb5')244 k1 = self.module.StaticTuple(u'\xb5')
236 class subunicode(unicode):245 class subunicode(text_type):
237 pass246 pass
238 self.assertRaises(TypeError, self.module.StaticTuple,247 self.assertRaises(TypeError, self.module.StaticTuple,
239 subunicode(u'\xb5'))248 subunicode(u'\xb5'))
@@ -416,16 +425,8 @@
416 k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')425 k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')
417 self.assertEqual(('foo', 'bar'), k[:2])426 self.assertEqual(('foo', 'bar'), k[:2])
418 self.assertEqual(('baz',), k[2:-1])427 self.assertEqual(('baz',), k[2:-1])
419 try:428 self.assertEqual(('foo', 'baz',), k[::2])
420 val = k[::2]429 self.assertRaises(TypeError, k.__getitem__, 'not_slice')
421 except TypeError:
422 # C implementation raises a TypeError, we don't need the
423 # implementation yet, so allow this to pass
424 pass
425 else:
426 # Python implementation uses a regular Tuple, so make sure it gives
427 # the right result
428 self.assertEqual(('foo', 'baz'), val)
429430
430 def test_referents(self):431 def test_referents(self):
431 # We implement tp_traverse so that things like 'meliae' can measure the432 # We implement tp_traverse so that things like 'meliae' can measure the
@@ -582,20 +583,20 @@
582583
583 def test_pickle(self):584 def test_pickle(self):
584 st = self.module.StaticTuple('foo', 'bar')585 st = self.module.StaticTuple('foo', 'bar')
585 pickled = cPickle.dumps(st)586 pickled = pickle.dumps(st)
586 unpickled = cPickle.loads(pickled)587 unpickled = pickle.loads(pickled)
587 self.assertEqual(unpickled, st)588 self.assertEqual(unpickled, st)
588589
589 def test_pickle_empty(self):590 def test_pickle_empty(self):
590 st = self.module.StaticTuple()591 st = self.module.StaticTuple()
591 pickled = cPickle.dumps(st)592 pickled = pickle.dumps(st)
592 unpickled = cPickle.loads(pickled)593 unpickled = pickle.loads(pickled)
593 self.assertIs(st, unpickled)594 self.assertIs(st, unpickled)
594595
595 def test_pickle_nested(self):596 def test_pickle_nested(self):
596 st = self.module.StaticTuple('foo', self.module.StaticTuple('bar'))597 st = self.module.StaticTuple('foo', self.module.StaticTuple('bar'))
597 pickled = cPickle.dumps(st)598 pickled = pickle.dumps(st)
598 unpickled = cPickle.loads(pickled)599 unpickled = pickle.loads(pickled)
599 self.assertEqual(unpickled, st)600 self.assertEqual(unpickled, st)
600601
601 def test_static_tuple_thunk(self):602 def test_static_tuple_thunk(self):

Subscribers

People subscribed via source and target branches