Commits

Lenard Lindstrom committed 9d0fa8b

C macro cleanup and bug fixes for Python versions 2.5, 2.7, 3.2, and 3.3

Move a buffer related C macro definition to pgcompat.h.

Build and test with Python 3.2 and 2.5. Most of the new buffer support was
developed in Python 2.7 and 3.3. A Python 3.2 specific problem showed that
pygame.PixelArray, a container object, lacked GC support code. Python 2.5
exposed compatibility slip-ups with the new buffer protocol.

Comments (0)

Files changed (8)

         if e.name == 'newbuffer':
             posn = i
     if (posn is not None):
-        del extensions[i]
+        del extensions[posn]
 
 # if not building font, try replacing with ftfont
 alternate_font = os.path.join('lib', 'font.py')
         DECREF_MOD (module);
         MODINIT_ERROR;
     }
-    ecode = PyDict_SetItemString (dict, "BufferError", PyExc_SDLError);
+    ecode = PyDict_SetItemString (dict, "BufferError", PgExc_BufferError);
     if (ecode) {
         Py_DECREF (PgExc_BufferError);
         Py_XDECREF (atexit_register);

src/bufferproxy.c

 #include "pgcompat.h"
 #include "pgbufferproxy.h"
 
-/* No build will support the new and old buffer protocols simultaneously. */
-#if HAVE_OLD_BUFPROTO
-#define PG_ENABLE_OLDBUF 1
-#else
-#define PG_ENABLE_OLDBUF 0
-#endif
-
 
 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 #define BUFPROXY_MY_ENDIAN '<'
 } Pg_buffer_d;
 
 static int PgBufproxy_Trip(PyObject *);
-static Py_buffer *_proxy_get_view (PgBufproxyObject*);
+static Py_buffer *_proxy_get_view(PgBufproxyObject *);
+static int proxy_getbuffer(PgBufproxyObject *, Py_buffer *, int);
+static void proxy_releasebuffer(PgBufproxyObject *, Py_buffer *);
 
 static void _release_buffer_from_dict(Py_buffer *);
 
+#if PY_VERSION_HEX < 0x02060000
+static int
+_IsFortranContiguous(Py_buffer *view)
+{
+    Py_ssize_t sd, dim;
+    int i;
+
+    if (view->ndim == 0) return 1;
+    if (view->strides == NULL) return (view->ndim == 1);
+
+    sd = view->itemsize;
+    if (view->ndim == 1) return (view->shape[0] == 1 ||
+                               sd == view->strides[0]);
+    for (i=0; i<view->ndim; i++) {
+        dim = view->shape[i];
+        if (dim == 0) return 1;
+        if (view->strides[i] != sd) return 0;
+        sd *= dim;
+    }
+    return 1;
+}
+
+static int
+_IsCContiguous(Py_buffer *view)
+{
+    Py_ssize_t sd, dim;
+    int i;
+
+    if (view->ndim == 0) return 1;
+    if (view->strides == NULL) return 1;
+
+    sd = view->itemsize;
+    if (view->ndim == 1) return (view->shape[0] == 1 ||
+                               sd == view->strides[0]);
+    for (i=view->ndim-1; i>=0; i--) {
+        dim = view->shape[i];
+        if (dim == 0) return 1;
+        if (view->strides[i] != sd) return 0;
+        sd *= dim;
+    }
+    return 1;
+}
+
+static int
+PyBuffer_IsContiguous(Py_buffer *view, char fort)
+{
+
+    if (view->suboffsets != NULL) return 0;
+
+    if (fort == 'C')
+        return _IsCContiguous(view);
+    else if (fort == 'F')
+        return _IsFortranContiguous(view);
+    else if (fort == 'A')
+        return (_IsCContiguous(view) || _IsFortranContiguous(view));
+    return 0;
+}
+#endif /* #if PY_VERSION_HEX < 0x02060000 */
+
 static int
 _get_buffer_from_dict(PyObject *dict, Pg_buffer *pg_view_p, int flags) {
     PyObject *obj;
     }
 #undef ARG_FORMAT
 
-    if (PyObject_GetBuffer((PyObject *)self, &view, PyBUF_RECORDS)) {
+    if (proxy_getbuffer(self, &view, PyBUF_RECORDS)) {
         return 0;
     }
     if (!PyBuffer_IsContiguous(&view, 'A')) {
-        PyBuffer_Release(&view);
+        proxy_releasebuffer(self, &view);
+        Py_DECREF(self);
         PyErr_SetString(PyExc_ValueError,
                         "the BufferProxy bytes are not contiguous");
         return 0;
     }
     if (buflen > view.len) {
-        PyBuffer_Release(&view);
+        proxy_releasebuffer(self, &view);
+        Py_DECREF(self);
         PyErr_SetString(PyExc_ValueError,
                         "'buffer' object length is too large");
         return 0;
     }
     if (offset < 0 || buflen + offset > view.len) {
-        PyBuffer_Release(&view);
+        proxy_releasebuffer(self, &view);
+        Py_DECREF(self);
         PyErr_SetString(PyExc_IndexError,
                         "'offset' is out of range");
         return 0;
     }
     memcpy((char *)view.buf + offset, buf, (size_t)buflen);
-    PyBuffer_Release(&view);
+    proxy_releasebuffer(self, &view);
+    Py_DECREF(self);
     Py_RETURN_NONE;
 }
 
 };
 
 
-#if PG_ENABLE_NEWBUF || PG_ENABLE_OLDBUF
-
-#if PG_ENABLE_NEWBUF
 static int
 proxy_getbuffer(PgBufproxyObject *self, Py_buffer *view_p, int flags)
 {
     PyMem_Free(view_p->internal);
 }
 
-#endif /* #if PG_ENABLE_NEWBUF */
+#if PG_ENABLE_NEWBUF || PG_ENABLE_OLDBUF
 
 
 #if PG_ENABLE_OLDBUF
 #include "doc/mixer_doc.h"
 #include "mixer.h"
 
-#if !defined(EXPORT_BUFFER)
-#define EXPORT_BUFFER HAVE_NEW_BUFPROTO
-#endif
-
 #define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F))
 
 /* The SDL audio format constants are not defined for anything larger
     return size;
 }
 
-#if HAVE_NEW_BUFPROTO
 static PG_sample_format_t
 _format_view_to_audio(Py_buffer *view)
 {
     }
     return format;
 }
-#endif
 
 static void
 endsound_callback (int channel)
         return 0;
     }
     dict = PgBuffer_AsArrayInterface (&view);
-    PyBuffer_Release (&view);
+    snd_releasebuffer (self, &view);
+    Py_DECREF(self);
     return dict;
 }
 
     }
     if (channels != 1 &&
         PyBUF_HAS_FLAG (flags, PyBUF_F_CONTIGUOUS)) {
-        PyErr_SetString(PyExc_BufferError,
+        PyErr_SetString(PgExc_BufferError,
                         "polyphonic sound is not Fortran contiguous");
         return -1;
     }
     }
 }
 
-#if EXPORT_BUFFER
-
-#if HAVE_OLD_BUFPROTO
-#define snd_getreadbuffer 0
-#define snd_getwritebuffer 0
-#define snd_getsegcount 0
-#define snd_getcharbuffer 0
-#endif
+#if PG_ENABLE_NEWBUF
 
 static PyBufferProcs sound_as_buffer[] =
 {
     {
 #if HAVE_OLD_BUFPROTO
-        snd_getreadbuffer,
-        snd_getwritebuffer,
-        snd_getsegcount,
-        snd_getcharbuffer,
+        0,
+        0,
+        0,
+        0,
 #endif
         snd_getbuffer,
         snd_releasebuffer
 };
 #else
 #define sound_as_buffer 0
-#endif /* #if EXPORT_BUFFER */
+#endif /* #if PG_ENABLE_NEWBUF */
 
 
 /*sound object internals*/
 
 #define HAVE_OLD_BUFPROTO PY2
 
+#if HAVE_OLD_BUFPROTO
+#define PG_ENABLE_OLDBUF 1
+#else
+#define PG_ENABLE_OLDBUF 0
+#endif
+
 #ifndef Py_TPFLAGS_HAVE_NEWBUFFER
 #define Py_TPFLAGS_HAVE_NEWBUFFER 0
 #endif
 static PyObject *_pxarray_new(
     PyTypeObject *type, PyObject *args, PyObject *kwds);
 static void _pxarray_dealloc(PyPixelArray *self);
+static int _pxarray_traverse(PyPixelArray *self, visitproc visit, void *arg);
 
 static PyObject *_pxarray_get_dict(PyPixelArray *self, void *closure);
 static PyObject *_pxarray_get_surface(PyPixelArray *self, void *closure);
 static int _pxarray_ass_subscript(
     PyPixelArray *array, PyObject* op, PyObject* value);
 
-#if PG_ENABLE_NEWBUF
-/* New buffer protocol */
+/* New buffer protocol; also used for internally for array interface */
 static int _pxarray_getbuffer(PyPixelArray *self, Py_buffer *view_p, int flags);
-#endif
 
 static PyObject *_pxarray_subscript_internal(
     PyPixelArray *array,
 
 #else
 #define PXARRAY_BUFFERPROCS 0
-#endif
+#endif /* #if PG_ENABLE_NEWBUF */
 
 #if PY2 && PG_ENABLE_NEWBUF
 #define PXARRAY_TPFLAGS \
     PXARRAY_BUFFERPROCS,        /* tp_as_buffer */
     PXARRAY_TPFLAGS,
     DOC_PYGAMEPIXELARRAY,       /* tp_doc */
-    0,                          /* tp_traverse */
+    (traverseproc)_pxarray_traverse,  /* tp_traverse */
     0,                          /* tp_clear */
     0,                          /* tp_richcompare */
     offsetof(PyPixelArray, weakrefs),  /* tp_weaklistoffset */
 static void
 _pxarray_dealloc(PyPixelArray *self)
 {
+    PyObject_GC_UnTrack(self);
     if (self->weakrefs) {
         PyObject_ClearWeakRefs ((PyObject *) self);
     }
     Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
+/**
+ * Garbage collector support
+ */
+static int
+_pxarray_traverse(PyPixelArray *self, visitproc visit, void *arg)
+{
+    Py_VISIT(self->surface);
+    if (self->dict) {
+        Py_VISIT(self->dict);
+    }
+    if (self->lock) {
+        Py_VISIT(self->lock);
+    }
+    if (self->parent) {
+        Py_VISIT((PyObject *)self->parent);
+    }
+    return 0;
+}
+
 /**** Getter and setter access ****/
 
 /**
     view_p->obj = 0;
     if (PyBUF_HAS_FLAG(flags, PyBUF_C_CONTIGUOUS) &&
         !array_is_contiguous(self, 'C')) {
-        PyErr_SetString(PyExc_BufferError,
+        PyErr_SetString(PgExc_BufferError,
                         "this pixel array is not C contiguous");
         return -1;
     }
     if (PyBUF_HAS_FLAG(flags, PyBUF_F_CONTIGUOUS) &&
         !array_is_contiguous(self, 'F')) {
-        PyErr_SetString(PyExc_BufferError,
+        PyErr_SetString(PgExc_BufferError,
                         "this pixel array is not F contiguous");
         return -1;
     }
     if (PyBUF_HAS_FLAG(flags, PyBUF_ANY_CONTIGUOUS) &&
         !array_is_contiguous(self, 'A')) {
-        PyErr_SetString(PyExc_BufferError,
+        PyErr_SetString(PgExc_BufferError,
                         "this pixel array is not contiguous");
         return -1;
     }
             strides = self->strides;
         }
         else if (!array_is_contiguous(self, 'C')) {
-            PyErr_SetString(PyExc_BufferError,
+            PyErr_SetString(PgExc_BufferError,
                             "this pixel array is not contiguous: need strides");
             return -1;
         }
         ndim = 0;
     }
     else {
-        PyErr_SetString(PyExc_BufferError,
+        PyErr_SetString(PgExc_BufferError,
                         "this pixel array is not C contiguous: need strides");
         return -1;
     }

test/bufferproxy_test.py

 import pygame
 from pygame.bufferproxy import BufferProxy
 from pygame.compat import as_bytes
+try:
+    BufferError
+except NameError:
+    from pygame import BufferError
 
 class BufferProxyTest(unittest.TestCase):
     view_keywords = {'shape': (5, 4, 3),

test/surface_test.py

         length = s.get_pitch() * s.get_height()
         v = s.get_buffer()
         self.assert_(isinstance(v, BufferProxy))
-        # Simple buffer length check using ctype, which is portable,
-        # unlike buffer, and works ndim 0 views, unlike memoryview.
-        c_byte_Array = ctypes.c_byte * length
-        b = c_byte_Array.from_buffer(v)
-        c_byte_Array_plus_1 = ctypes.c_byte * (length + 1)
-        self.assertRaises(ValueError, c_byte_Array_plus_1.from_buffer, v)
+        self.assertEqual(v.length, length)
 
         # Check locking.
         s = pygame.Surface((2, 4), 0, 32)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.