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
1=== modified file 'breezy/_export_c_api.h'
2--- breezy/_export_c_api.h 2009-10-12 21:44:27 +0000
3+++ breezy/_export_c_api.h 2017-06-25 23:38:37 +0000
4@@ -15,6 +15,7 @@
5 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 */
7
8+/* GZ 2017-06-22: This header needs updating to use Capsules over CObjects */
9
10 /* This file contains helper functions for exporting a C API for a CPython
11 * extension module.
12@@ -45,17 +46,14 @@
13 PyObject *d = NULL;
14 PyObject *c_obj = NULL;
15
16- /* (char *) is because python2.4 declares this api as 'char *' rather than
17- * const char* which it really is.
18- */
19- d = PyObject_GetAttrString(module, (char *)_C_API_NAME);
20+ d = PyObject_GetAttrString(module, _C_API_NAME);
21 if (!d) {
22 PyErr_Clear();
23 d = PyDict_New();
24 if (!d)
25 goto bad;
26 Py_INCREF(d);
27- if (PyModule_AddObject(module, (char *)_C_API_NAME, d) < 0)
28+ if (PyModule_AddObject(module, _C_API_NAME, d) < 0)
29 goto bad;
30 }
31 c_obj = PyCObject_FromVoidPtrAndDesc(func, signature, 0);
32
33=== modified file 'breezy/_static_tuple_c.c'
34--- breezy/_static_tuple_c.c 2017-05-21 18:10:28 +0000
35+++ breezy/_static_tuple_c.c 2017-06-25 23:38:37 +0000
36@@ -26,12 +26,6 @@
37 #include "_static_tuple_c.h"
38 #include "_export_c_api.h"
39
40-/* Pyrex 0.9.6.4 exports _simple_set_pyx_api as
41- * import__simple_set_pyx(), while Pyrex 0.9.8.5 and Cython 0.11.3 export them
42- * as import_breezy___simple_set_pyx(). As such, we just #define one to be
43- * equivalent to the other in our internal code.
44- */
45-#define import__simple_set_pyx import_breezy___simple_set_pyx
46 #include "_simple_set_pyx_api.h"
47
48 #if defined(__GNUC__)
49@@ -242,11 +236,14 @@
50 " should not have a NULL entry.");
51 return 0;
52 }
53- if (PyString_CheckExact(obj)
54+ if (PyBytes_CheckExact(obj)
55 || StaticTuple_CheckExact(obj)
56 || obj == Py_None
57 || PyBool_Check(obj)
58+#if PY_MAJOR_VERSION >= 3
59+#else
60 || PyInt_CheckExact(obj)
61+#endif
62 || PyLong_CheckExact(obj)
63 || PyFloat_CheckExact(obj)
64 || PyUnicode_CheckExact(obj)
65@@ -314,8 +311,12 @@
66 if (tuple_repr == NULL) {
67 return NULL;
68 }
69+#if PY_MAJOR_VERSION >= 3
70+ result = PyUnicode_FromFormat("StaticTuple%U", tuple_repr);
71+#else
72 result = PyString_FromFormat("StaticTuple%s",
73 PyString_AsString(tuple_repr));
74+#endif
75 return result;
76 }
77
78@@ -362,7 +363,7 @@
79 {
80 PyObject *vt;
81 PyObject *result = NULL;
82-
83+
84 vt = StaticTuple_as_tuple((StaticTuple *)v);
85 if (vt == NULL) {
86 goto done;
87@@ -478,7 +479,7 @@
88 vlen = v_st->size;
89 wlen = w_st->size;
90 min_len = (vlen < wlen) ? vlen : wlen;
91- string_richcompare = PyString_Type.tp_richcompare;
92+ string_richcompare = PyBytes_Type.tp_richcompare;
93 for (i = 0; i < min_len; i++) {
94 PyObject *result = NULL;
95 v_obj = StaticTuple_GET_ITEM(v_st, i);
96@@ -487,7 +488,7 @@
97 /* Shortcut case, these must be identical */
98 continue;
99 }
100- if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj)) {
101+ if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj)) {
102 result = string_richcompare(v_obj, w_obj, Py_EQ);
103 } else if (StaticTuple_CheckExact(v_obj) &&
104 StaticTuple_CheckExact(w_obj))
105@@ -547,7 +548,7 @@
106 return Py_True;
107 }
108 /* It is some other comparison, go ahead and do the real check. */
109- if (PyString_CheckExact(v_obj) && PyString_CheckExact(w_obj))
110+ if (PyBytes_CheckExact(v_obj) && PyBytes_CheckExact(w_obj))
111 {
112 return string_richcompare(v_obj, w_obj, op);
113 } else if (StaticTuple_CheckExact(v_obj) &&
114@@ -679,6 +680,8 @@
115 return obj;
116 }
117
118+#if PY_MAJOR_VERSION >= 3
119+#else
120 static PyObject *
121 StaticTuple_slice(StaticTuple *self, Py_ssize_t ilow, Py_ssize_t ihigh)
122 {
123@@ -692,6 +695,21 @@
124 Py_DECREF(as_tuple);
125 return result;
126 }
127+#endif
128+
129+static PyObject *
130+StaticTuple_subscript(StaticTuple *self, PyObject *key)
131+{
132+ PyObject *as_tuple, *result;
133+
134+ as_tuple = StaticTuple_as_tuple(self);
135+ if (as_tuple == NULL) {
136+ return NULL;
137+ }
138+ result = PyTuple_Type.tp_as_mapping->mp_subscript(as_tuple, key);
139+ Py_DECREF(as_tuple);
140+ return result;
141+}
142
143 static int
144 StaticTuple_traverse(StaticTuple *self, visitproc visit, void *arg)
145@@ -759,29 +777,36 @@
146 0, /* nb_or */
147 0, /* nb_coerce */
148 };
149-
150+
151
152 static PySequenceMethods StaticTuple_as_sequence = {
153 (lenfunc)StaticTuple_length, /* sq_length */
154 0, /* sq_concat */
155 0, /* sq_repeat */
156 (ssizeargfunc)StaticTuple_item, /* sq_item */
157+#if PY_MAJOR_VERSION >= 3
158+#else
159 (ssizessizeargfunc)StaticTuple_slice, /* sq_slice */
160+#endif
161 0, /* sq_ass_item */
162 0, /* sq_ass_slice */
163 0, /* sq_contains */
164-};
165-
166-/* TODO: Implement StaticTuple_as_mapping.
167- * The only thing we really want to support from there is mp_subscript,
168- * so that we could support extended slicing (foo[::2]). Not worth it
169- * yet, though.
170- */
171+#if PY_MAJOR_VERSION >= 3
172+ 0, /* sq_inplace_concat */
173+ 0, /* sq_inplace_repeat */
174+#endif
175+};
176+
177+
178+static PyMappingMethods StaticTuple_as_mapping = {
179+ (lenfunc)StaticTuple_length, /* mp_length */
180+ (binaryfunc)StaticTuple_subscript, /* mp_subscript */
181+ 0, /* mp_ass_subscript */
182+};
183
184
185 PyTypeObject StaticTuple_Type = {
186- PyObject_HEAD_INIT(NULL)
187- 0, /* ob_size */
188+ PyVarObject_HEAD_INIT(NULL, 0)
189 "breezy._static_tuple_c.StaticTuple", /* tp_name */
190 sizeof(StaticTuple), /* tp_basicsize */
191 sizeof(PyObject *), /* tp_itemsize */
192@@ -793,7 +818,7 @@
193 (reprfunc)StaticTuple_repr, /* tp_repr */
194 &StaticTuple_as_number, /* tp_as_number */
195 &StaticTuple_as_sequence, /* tp_as_sequence */
196- 0, /* tp_as_mapping */
197+ &StaticTuple_as_mapping, /* tp_as_mapping */
198 (hashfunc)StaticTuple_hash, /* tp_hash */
199 0, /* tp_call */
200 0, /* tp_str */
201@@ -887,72 +912,32 @@
202 }
203
204
205-static int
206-_workaround_pyrex_096(void)
207-{
208- /* Work around an incompatibility in how pyrex 0.9.6 exports a module,
209- * versus how pyrex 0.9.8 and cython 0.11 export it.
210- * Namely 0.9.6 exports import__simple_set_pyx and tries to
211- * "import _simple_set_pyx" but it is available only as
212- * "import breezy._simple_set_pyx"
213- * It is a shame to hack up sys.modules, but that is what we've got to do.
214- */
215- PyObject *sys_module = NULL, *modules = NULL, *set_module = NULL;
216- int retval = -1;
217-
218- /* Clear out the current ImportError exception, and try again. */
219- PyErr_Clear();
220- /* Note that this only seems to work if somewhere else imports
221- * breezy._simple_set_pyx before importing breezy._static_tuple_c
222- */
223- set_module = PyImport_ImportModule("breezy._simple_set_pyx");
224- if (set_module == NULL) {
225- goto end;
226- }
227- /* Add the _simple_set_pyx into sys.modules at the appropriate location. */
228- sys_module = PyImport_ImportModule("sys");
229- if (sys_module == NULL) {
230- goto end;
231- }
232- modules = PyObject_GetAttrString(sys_module, "modules");
233- if (modules == NULL || !PyDict_Check(modules)) {
234- goto end;
235- }
236- PyDict_SetItemString(modules, "_simple_set_pyx", set_module);
237- /* Now that we have hacked it in, try the import again. */
238- retval = import_breezy___simple_set_pyx();
239-end:
240- Py_XDECREF(set_module);
241- Py_XDECREF(sys_module);
242- Py_XDECREF(modules);
243- return retval;
244-}
245-
246-
247-PyMODINIT_FUNC
248-init_static_tuple_c(void)
249+PYMOD_INIT_FUNC(_static_tuple_c)
250 {
251 PyObject* m;
252
253 StaticTuple_Type.tp_getattro = PyObject_GenericGetAttr;
254- if (PyType_Ready(&StaticTuple_Type) < 0)
255- return;
256+ if (PyType_Ready(&StaticTuple_Type) < 0) {
257+ return PYMOD_ERROR;
258+ }
259
260- m = Py_InitModule3("_static_tuple_c", static_tuple_c_methods,
261- "C implementation of a StaticTuple structure");
262- if (m == NULL)
263- return;
264+ PYMOD_CREATE(m, "_static_tuple_c",
265+ "C implementation of a StaticTuple structure",
266+ static_tuple_c_methods);
267+ if (m == NULL) {
268+ return PYMOD_ERROR;
269+ }
270
271 Py_INCREF(&StaticTuple_Type);
272 PyModule_AddObject(m, "StaticTuple", (PyObject *)&StaticTuple_Type);
273- if (import_breezy___simple_set_pyx() == -1
274- && _workaround_pyrex_096() == -1)
275- {
276- return;
277+ if (import_breezy___simple_set_pyx() == -1) {
278+ return PYMOD_ERROR;
279 }
280 setup_interned_tuples(m);
281 setup_empty_tuple(m);
282 setup_c_api(m);
283+
284+ return PYMOD_SUCCESS(m);
285 }
286
287 // vim: tabstop=4 sw=4 expandtab
288
289=== modified file 'breezy/python-compat.h'
290--- breezy/python-compat.h 2017-06-05 23:59:08 +0000
291+++ breezy/python-compat.h 2017-06-25 23:38:37 +0000
292@@ -25,6 +25,37 @@
293 #ifndef _BZR_PYTHON_COMPAT_H
294 #define _BZR_PYTHON_COMPAT_H
295
296+#if PY_MAJOR_VERSION >= 3
297+
298+#define PyInt_FromSsize_t PyLong_FromSsize_t
299+
300+/* In Python 3 the Py_TPFLAGS_CHECKTYPES behaviour is on by default */
301+#define Py_TPFLAGS_CHECKTYPES 0
302+
303+#define PYMOD_ERROR NULL
304+#define PYMOD_SUCCESS(val) val
305+#define PYMOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void)
306+#define PYMOD_CREATE(ob, name, doc, methods) do { \
307+ static struct PyModuleDef moduledef = { \
308+ PyModuleDef_HEAD_INIT, name, doc, -1, methods \
309+ }; \
310+ ob = PyModule_Create(&moduledef); \
311+ } while(0)
312+
313+#else
314+
315+#define PyBytes_Type PyString_Type
316+#define PyBytes_CheckExact PyString_CheckExact
317+
318+#define PYMOD_ERROR
319+#define PYMOD_SUCCESS(val)
320+#define PYMOD_INIT_FUNC(name) void init##name(void)
321+#define PYMOD_CREATE(ob, name, doc, methods) do { \
322+ ob = Py_InitModule3(name, methods, doc); \
323+ } while(0)
324+
325+#endif
326+
327 #if defined(_WIN32) || defined(WIN32)
328 /* Defining WIN32_LEAN_AND_MEAN makes including windows quite a bit
329 * lighter weight.
330
331=== modified file 'breezy/tests/test__static_tuple.py'
332--- breezy/tests/test__static_tuple.py 2017-05-23 14:08:03 +0000
333+++ breezy/tests/test__static_tuple.py 2017-06-25 23:38:37 +0000
334@@ -16,7 +16,10 @@
335
336 """Tests for the StaticTuple type."""
337
338-import cPickle
339+try:
340+ import cPickle as pickle
341+except ImportError:
342+ import pickle
343 import sys
344
345 from breezy import (
346@@ -26,6 +29,10 @@
347 static_tuple,
348 tests,
349 )
350+from breezy.sixish import (
351+ PY3,
352+ text_type,
353+ )
354 from breezy.tests import (
355 features,
356 )
357@@ -213,6 +220,8 @@
358 self.assertRaises(TypeError, self.module.StaticTuple, subint(2))
359
360 def test_holds_long(self):
361+ if PY3:
362+ self.skipTest("No long type on Python 3")
363 k1 = self.module.StaticTuple(2**65)
364 class sublong(long):
365 pass
366@@ -225,15 +234,15 @@
367 pass
368 self.assertRaises(TypeError, self.module.StaticTuple, subfloat(1.5))
369
370- def test_holds_str(self):
371- k1 = self.module.StaticTuple('astring')
372- class substr(str):
373+ def test_holds_bytes(self):
374+ k1 = self.module.StaticTuple(b'astring')
375+ class substr(bytes):
376 pass
377- self.assertRaises(TypeError, self.module.StaticTuple, substr('a'))
378+ self.assertRaises(TypeError, self.module.StaticTuple, substr(b'a'))
379
380 def test_holds_unicode(self):
381 k1 = self.module.StaticTuple(u'\xb5')
382- class subunicode(unicode):
383+ class subunicode(text_type):
384 pass
385 self.assertRaises(TypeError, self.module.StaticTuple,
386 subunicode(u'\xb5'))
387@@ -416,16 +425,8 @@
388 k = self.module.StaticTuple('foo', 'bar', 'baz', 'bing')
389 self.assertEqual(('foo', 'bar'), k[:2])
390 self.assertEqual(('baz',), k[2:-1])
391- try:
392- val = k[::2]
393- except TypeError:
394- # C implementation raises a TypeError, we don't need the
395- # implementation yet, so allow this to pass
396- pass
397- else:
398- # Python implementation uses a regular Tuple, so make sure it gives
399- # the right result
400- self.assertEqual(('foo', 'baz'), val)
401+ self.assertEqual(('foo', 'baz',), k[::2])
402+ self.assertRaises(TypeError, k.__getitem__, 'not_slice')
403
404 def test_referents(self):
405 # We implement tp_traverse so that things like 'meliae' can measure the
406@@ -582,20 +583,20 @@
407
408 def test_pickle(self):
409 st = self.module.StaticTuple('foo', 'bar')
410- pickled = cPickle.dumps(st)
411- unpickled = cPickle.loads(pickled)
412+ pickled = pickle.dumps(st)
413+ unpickled = pickle.loads(pickled)
414 self.assertEqual(unpickled, st)
415
416 def test_pickle_empty(self):
417 st = self.module.StaticTuple()
418- pickled = cPickle.dumps(st)
419- unpickled = cPickle.loads(pickled)
420+ pickled = pickle.dumps(st)
421+ unpickled = pickle.loads(pickled)
422 self.assertIs(st, unpickled)
423
424 def test_pickle_nested(self):
425 st = self.module.StaticTuple('foo', self.module.StaticTuple('bar'))
426- pickled = cPickle.dumps(st)
427- unpickled = cPickle.loads(pickled)
428+ pickled = pickle.dumps(st)
429+ unpickled = pickle.loads(pickled)
430 self.assertEqual(unpickled, st)
431
432 def test_static_tuple_thunk(self):

Subscribers

People subscribed via source and target branches