[pypy-commit] pypy py3k: hg merge default

rlamy pypy.commits at gmail.com
Fri Jun 3 21:58:47 EDT 2016


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3k
Changeset: r84916:ef5a7f148b27
Date: 2016-06-04 02:58 +0100
http://bitbucket.org/pypy/pypy/changeset/ef5a7f148b27/

Log:	hg merge default

	Port cpyext changes to bytearray

diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/cffi/api.py
@@ -332,8 +332,8 @@
     def from_buffer(self, python_buffer):
         """Return a <cdata 'char[]'> that points to the data of the
         given Python object, which must support the buffer interface.
-        Note that this is not meant to be used on the built-in types str,
-        unicode, or bytearray (you can build 'char[]' arrays explicitly)
+        Note that this is not meant to be used on the built-in types
+        str or unicode (you can build 'char[]' arrays explicitly)
         but only on objects containing large quantities of raw data
         in some other format, like 'array.array' or numpy arrays.
         """
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -814,7 +814,7 @@
             try:
                 if ftype.is_integer_type() or fbitsize >= 0:
                     # accept all integers, but complain on float or double
-                    prnt("  (void)((p->%s) << 1);  /* check that '%s.%s' is "
+                    prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
                          "an integer */" % (fname, cname, fname))
                     continue
                 # only accept exactly the type declared, except that '[]'
@@ -991,7 +991,7 @@
             prnt('static int %s(unsigned long long *o)' % funcname)
             prnt('{')
             prnt('  int n = (%s) <= 0;' % (name,))
-            prnt('  *o = (unsigned long long)((%s) << 0);'
+            prnt('  *o = (unsigned long long)((%s) | 0);'
                  '  /* check that %s is an integer */' % (name, name))
             if check_value is not None:
                 if check_value > 0:
@@ -1250,7 +1250,7 @@
 
     def _emit_bytecode_UnknownIntegerType(self, tp, index):
         s = ('_cffi_prim_int(sizeof(%s), (\n'
-             '           ((%s)-1) << 0 /* check that %s is an integer type */\n'
+             '           ((%s)-1) | 0 /* check that %s is an integer type */\n'
              '         ) <= 0)' % (tp.name, tp.name, tp.name))
         self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
 
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -131,3 +131,15 @@
 
 Enable pickling of W_PyCFunctionObject by monkeypatching pickle.Pickler.dispatch
 at cpyext import time
+
+.. branch: nonmovable-list
+
+Add a way to ask "give me a raw pointer to this list's
+items".  Only for resizable lists of primitives.  Turns the GcArray
+nonmovable, possibly making a copy of it first.
+
+.. branch: cpyext-ext
+
+Finish the work already partially merged in cpyext-for-merge. Adds support
+for ByteArrayObject using the nonmovable-list, which also enables
+buffer(bytearray(<some-list>)) 
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -251,6 +251,9 @@
     def identifier_w(self, space):
         self._typed_unwrap_error(space, "string")
 
+    def bytearray_list_of_chars_w(self, space):
+        self._typed_unwrap_error(space, "bytearray")
+
     def int_w(self, space, allow_conversion=True):
         # note that W_IntObject.int_w has a fast path and W_FloatObject.int_w
         # raises w_TypeError
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -23,6 +23,7 @@
         else:
             bases = [__base]
         self.bases = bases
+        # Used in cpyext to fill tp_as_buffer slots
         assert __buffer in {None, 'read-write', 'read'}, "Unknown value for __buffer"
         self.buffer = __buffer
         self.heaptype = False
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -222,24 +222,43 @@
                 w_value.get_array_length() == length):
                 # fast path: copying from exactly the correct type
                 with w_value as source:
-                    rffi.c_memcpy(target, source, ctitemsize * length)
+                    source = rffi.cast(rffi.VOIDP, source)
+                    target = rffi.cast(rffi.VOIDP, target)
+                    size = rffi.cast(rffi.SIZE_T, ctitemsize * length)
+                    rffi.c_memcpy(target, source, size)
                 return
         #
-        # A fast path for <char[]>[0:N] = "somestring".
+        # A fast path for <char[]>[0:N] = "somestring" or some bytearray.
         from pypy.module._cffi_backend import ctypeprim
         space = self.space
-        if (space.isinstance_w(w_value, space.w_str) and
-                isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)):
-            from rpython.rtyper.annlowlevel import llstr
-            from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
-            value = space.str_w(w_value)
-            if len(value) != length:
-                raise oefmt(space.w_ValueError,
-                            "need a string of length %d, got %d",
-                            length, len(value))
-            copy_string_to_raw(llstr(value), target, 0, length)
-            return
+        if isinstance(ctitem, ctypeprim.W_CTypePrimitive) and ctitem.size == 1:
+            if space.isinstance_w(w_value, space.w_str):
+                from rpython.rtyper.annlowlevel import llstr
+                from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
+                value = space.str_w(w_value)
+                if len(value) != length:
+                    raise oefmt(space.w_ValueError,
+                                "need a string of length %d, got %d",
+                                length, len(value))
+                copy_string_to_raw(llstr(value), target, 0, length)
+                return
+            if space.isinstance_w(w_value, space.w_bytearray):
+                value = w_value.bytearray_list_of_chars_w(space)
+                if len(value) != length:
+                    raise oefmt(space.w_ValueError,
+                                "need a bytearray of length %d, got %d",
+                                length, len(value))
+                self._copy_list_of_chars_to_raw(value, target, length)
+                return
         #
+        self._do_setslice_iterate(space, ctitem, w_value, target, ctitemsize,
+                                  length)
+
+    @staticmethod
+    def _do_setslice_iterate(space, ctitem, w_value, target, ctitemsize,
+                             length):
+        # general case, contains a loop
+        # (XXX is it worth adding a jitdriver here?)
         w_iter = space.iter(w_value)
         for i in range(length):
             try:
@@ -260,6 +279,12 @@
             raise oefmt(space.w_ValueError,
                         "got more than %d values to unpack", length)
 
+    @staticmethod
+    def _copy_list_of_chars_to_raw(value, target, length):
+        # contains a loop, moved out of _do_setslice()
+        for i in range(length):
+            target[i] = value[i]
+
     def _add_or_sub(self, w_other, sign):
         space = self.space
         i = sign * space.getindex_w(w_other, space.w_OverflowError)
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -43,9 +43,6 @@
         else:
             return 'NULL'
 
-    def is_char_ptr_or_array(self):
-        return False
-
     def is_unichar_ptr_or_array(self):
         return False
 
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -31,9 +31,6 @@
         self.ctitem = ctitem
         self.can_cast_anything = could_cast_anything and ctitem.cast_anything
 
-    def is_char_ptr_or_array(self):
-        return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveChar)
-
     def is_unichar_ptr_or_array(self):
         return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar)
 
@@ -56,12 +53,8 @@
             value = rffi.cast(rffi.CCHARP, value)
         return cdataobj.W_CData(space, value, self)
 
-    def _convert_array_from_listview(self, cdata, w_ob):
-        if self.ctitem.pack_list_of_items(cdata, w_ob):   # fast path
-            return
-        #
+    def _convert_array_from_listview(self, cdata, lst_w):
         space = self.space
-        lst_w = space.listview(w_ob)
         if self.length >= 0 and len(lst_w) > self.length:
             raise oefmt(space.w_IndexError,
                         "too many initializers for '%s' (got %d)",
@@ -75,7 +68,10 @@
         space = self.space
         if (space.isinstance_w(w_ob, space.w_list) or
             space.isinstance_w(w_ob, space.w_tuple)):
-            self._convert_array_from_listview(cdata, w_ob)
+            if self.ctitem.pack_list_of_items(cdata, w_ob):   # fast path
+                pass
+            else:
+                self._convert_array_from_listview(cdata, space.listview(w_ob))
         elif (self.can_cast_anything or
               (self.ctitem.is_primitive_integer and
                self.ctitem.size == rffi.sizeof(lltype.Char))):
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -347,7 +347,7 @@
         """\
 Return a <cdata 'char[]'> that points to the data of the given Python
 object, which must support the buffer interface.  Note that this is
-not meant to be used on the built-in types str, unicode, or bytearray
+not meant to be used on the built-in types str or unicode
 (you can build 'char[]' arrays explicitly) but only on objects
 containing large quantities of raw data in some other format, like
 'array.array' or numpy arrays."""
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -2686,10 +2686,19 @@
     BCharArray = new_array_type(
         new_pointer_type(new_primitive_type("char")), None)
     py.test.raises(TypeError, newp, BCharArray, bytearray(b"foo"))
-    p = newp(BCharArray, 4)
-    buffer(p)[:] = bytearray(b"foo\x00")
-    assert len(p) == 4
-    assert list(p) == [b"f", b"o", b"o", b"\x00"]
+    p = newp(BCharArray, 5)
+    buffer(p)[:] = bytearray(b"foo.\x00")
+    assert len(p) == 5
+    assert list(p) == [b"f", b"o", b"o", b".", b"\x00"]
+    p[1:3] = bytearray(b"XY")
+    assert list(p) == [b"f", b"X", b"Y", b".", b"\x00"]
+
+def test_string_assignment_to_byte_array():
+    BByteArray = new_array_type(
+        new_pointer_type(new_primitive_type("unsigned char")), None)
+    p = newp(BByteArray, 5)
+    p[0:3] = bytearray(b"XYZ")
+    assert list(p) == [ord("X"), ord("Y"), ord("Z"), 0, 0]
 
 # XXX hack
 if sys.version_info >= (3,):
@@ -3317,13 +3326,12 @@
     cast(p, c)[1] += 500
     assert list(a) == [10000, 20500, 30000]
 
-def test_from_buffer_not_str_unicode_bytearray():
+def test_from_buffer_not_str_unicode():
     BChar = new_primitive_type("char")
     BCharP = new_pointer_type(BChar)
     BCharA = new_array_type(BCharP, None)
     py.test.raises(TypeError, from_buffer, BCharA, b"foo")
     py.test.raises(TypeError, from_buffer, BCharA, u+"foo")
-    py.test.raises(TypeError, from_buffer, BCharA, bytearray(b"foo"))
     try:
         from __builtin__ import buffer
     except ImportError:
@@ -3331,16 +3339,27 @@
     else:
         py.test.raises(TypeError, from_buffer, BCharA, buffer(b"foo"))
         py.test.raises(TypeError, from_buffer, BCharA, buffer(u+"foo"))
-        py.test.raises(TypeError, from_buffer, BCharA,
-                       buffer(bytearray(b"foo")))
     try:
         from __builtin__ import memoryview
     except ImportError:
         pass
     else:
         py.test.raises(TypeError, from_buffer, BCharA, memoryview(b"foo"))
-        py.test.raises(TypeError, from_buffer, BCharA,
-                       memoryview(bytearray(b"foo")))
+
+def test_from_buffer_bytearray():
+    a = bytearray(b"xyz")
+    BChar = new_primitive_type("char")
+    BCharP = new_pointer_type(BChar)
+    BCharA = new_array_type(BCharP, None)
+    p = from_buffer(BCharA, a)
+    assert typeof(p) is BCharA
+    assert len(p) == 3
+    assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>"
+    assert p[2] == b"z"
+    p[2] = b"."
+    assert a[2] == ord(".")
+    a[2] = ord("?")
+    assert p[2] == b"?"
 
 def test_from_buffer_more_cases():
     try:
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -42,6 +42,7 @@
 import pypy.module.cpyext.typeobject
 import pypy.module.cpyext.object
 import pypy.module.cpyext.bytesobject
+import pypy.module.cpyext.bytearrayobject
 import pypy.module.cpyext.tupleobject
 import pypy.module.cpyext.setobject
 import pypy.module.cpyext.dictobject
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1433,7 +1433,7 @@
                                             prefix=prefix)
     code = "#include <Python.h>\n"
     if use_micronumpy:
-        code += "#include <pypy_numpy.h> /* api.py line 1290 */"
+        code += "#include <pypy_numpy.h> /* api.py line 1290 */\n"
     code  += "\n".join(functions)
 
     eci = build_eci(False, export_symbols, code, use_micronumpy)
diff --git a/pypy/module/cpyext/bytearrayobject.py b/pypy/module/cpyext/bytearrayobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/bytearrayobject.py
@@ -0,0 +1,134 @@
+from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib.objectmodel import specialize, we_are_translated
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.objspace.std.bytearrayobject import new_bytearray
+from pypy.module.cpyext.api import (
+    cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
+    PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
+from pypy.module.cpyext.pyerrors import PyErr_BadArgument
+from pypy.module.cpyext.pyobject import (
+    PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
+    make_typedescr, get_typedescr, Py_IncRef)
+# Type PyByteArrayObject represents a mutable array of bytes.
+# The Python API is that of a sequence;
+# the bytes are mapped to ints in [0, 256).
+# Bytes are not characters; they may be used to encode characters.
+# The only way to go between bytes and str/unicode is via encoding
+# and decoding.
+# For the convenience of C programmers, the bytes type is considered
+# to contain a char pointer, not an unsigned char pointer.
+
+# Expose data as a rw cchar* only through PyByteArray_AsString
+# Under this strategy the pointer could loose its synchronization with
+# the underlying space.w_bytearray if PyByteArray_Resize is called, so
+# hopefully the use of the pointer is short-lived
+
+PyByteArrayObjectStruct = lltype.ForwardReference()
+PyByteArrayObject = lltype.Ptr(PyByteArrayObjectStruct)
+PyByteArrayObjectFields = PyVarObjectFields
+#    (("ob_exports", rffi.INT), ("ob_alloc", rffi.LONG), ("ob_bytes", rffi.CCHARP))
+cpython_struct("PyByteArrayObject", PyByteArrayObjectFields, PyByteArrayObjectStruct)
+
+ at bootstrap_function
+def init_bytearrayobject(space):
+    "Type description of PyByteArrayObject"
+    #make_typedescr(space.w_bytearray.layout.typedef,
+    #               basestruct=PyByteArrayObject.TO,
+    #               attach=bytearray_attach,
+    #               dealloc=bytearray_dealloc,
+    #               realize=bytearray_realize)
+
+PyByteArray_Check, PyByteArray_CheckExact = build_type_checkers("ByteArray", "w_bytearray")
+
+# XXX dead code to be removed
+#def bytearray_attach(space, py_obj, w_obj):
+#    """
+#    Fills a newly allocated PyByteArrayObject with the given bytearray object
+#    """
+#    py_ba = rffi.cast(PyByteArrayObject, py_obj)
+#    py_ba.c_ob_size = len(space.str_w(w_obj))
+#    py_ba.c_ob_bytes = lltype.nullptr(rffi.CCHARP.TO)
+#    py_ba.c_ob_exports = rffi.cast(rffi.INT, 0)
+
+#def bytearray_realize(space, py_obj):
+#    """
+#    Creates the bytearray in the interpreter.
+#    """
+#    py_ba = rffi.cast(PyByteArrayObject, py_obj)
+#    if not py_ba.c_ob_bytes:
+#        py_ba.c_buffer = lltype.malloc(rffi.CCHARP.TO, py_ba.c_ob_size + 1,
+#                                    flavor='raw', zero=True)
+#    s = rffi.charpsize2str(py_ba.c_ob_bytes, py_ba.c_ob_size)
+#    w_obj = space.wrap(s)
+#    py_ba.c_ob_exports = rffi.cast(rffi.INT, 0)
+#    track_reference(space, py_obj, w_obj)
+#    return w_obj
+
+#@cpython_api([PyObject], lltype.Void, header=None)
+#def bytearray_dealloc(space, py_obj):
+#    """Frees allocated PyByteArrayObject resources.
+#    """
+#    py_ba = rffi.cast(PyByteArrayObject, py_obj)
+#    if py_ba.c_ob_bytes:
+#        lltype.free(py_ba.c_ob_bytes, flavor="raw")
+#    from pypy.module.cpyext.object import PyObject_dealloc
+#    PyObject_dealloc(space, py_obj)
+
+#_______________________________________________________________________
+
+ at cpython_api([PyObject], PyObject, result_is_ll=True)
+def PyByteArray_FromObject(space, w_obj):
+    """Return a new bytearray object from any object, o, that implements the
+    buffer protocol.
+
+    XXX expand about the buffer protocol, at least somewhere"""
+    w_buffer = space.call_function(space.w_bytearray, w_obj)
+    return make_ref(space, w_buffer)
+
+ at cpython_api([rffi.CCHARP, Py_ssize_t], PyObject, result_is_ll=True)
+def PyByteArray_FromStringAndSize(space, char_p, length):
+    """Create a new bytearray object from string and its length, len.  On
+    failure, NULL is returned."""
+    if char_p:
+        w_s = space.wrapbytes(rffi.charpsize2str(char_p, length))
+    else:
+        w_s = space.newint(length)
+    w_buffer = space.call_function(space.w_bytearray, w_s)
+    return make_ref(space, w_buffer)
+
+ at cpython_api([PyObject, PyObject], PyObject)
+def PyByteArray_Concat(space, w_left, w_right):
+    """Concat bytearrays a and b and return a new bytearray with the result."""
+    return space.add(w_left, w_right)
+
+ at cpython_api([PyObject], Py_ssize_t, error=-1)
+def PyByteArray_Size(space, w_obj):
+    """Return the size of bytearray after checking for a NULL pointer."""
+    if not w_obj:
+        return 0
+    return space.len_w(w_obj)
+
+ at cpython_api([PyObject], rffi.CCHARP, error=0)
+def PyByteArray_AsString(space, w_obj):
+    """Return the contents of bytearray as a char array after checking for a
+    NULL pointer."""
+    if space.isinstance_w(w_obj, space.w_bytearray):
+        return w_obj.nonmovable_carray(space)
+    else:
+        raise oefmt(space.w_TypeError,
+                    "expected bytearray object, %T found", w_obj)
+
+ at cpython_api([PyObject, Py_ssize_t], rffi.INT_real, error=-1)
+def PyByteArray_Resize(space, w_obj, newlen):
+    """Resize the internal buffer of bytearray to len."""
+    if space.isinstance_w(w_obj, space.w_bytearray):
+        oldlen = space.len_w(w_obj)
+        if newlen > oldlen:
+            space.call_method(w_obj, 'extend', space.wrapbytes('\x00' * (newlen - oldlen)))
+        elif oldlen > newlen:
+            assert newlen >= 0
+            space.delslice(w_obj, space.wrap(newlen), space.wrap(oldlen))
+        return 0
+    else:
+        raise oefmt(space.w_TypeError,
+                    "expected bytearray object, %T found", w_obj)
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -2,7 +2,7 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
     cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
-    PyObjectFields, PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
+    PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
 from pypy.module.cpyext.pyerrors import PyErr_BadArgument
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
diff --git a/pypy/module/cpyext/include/Python.h b/pypy/module/cpyext/include/Python.h
--- a/pypy/module/cpyext/include/Python.h
+++ b/pypy/module/cpyext/include/Python.h
@@ -114,6 +114,7 @@
 #include "pythonrun.h"
 #include "pyerrors.h"
 #include "sysmodule.h"
+#include "bytearrayobject.h"
 #include "descrobject.h"
 #include "tupleobject.h"
 #include "dictobject.h"
diff --git a/pypy/module/cpyext/include/bytearrayobject.h b/pypy/module/cpyext/include/bytearrayobject.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/include/bytearrayobject.h
@@ -0,0 +1,36 @@
+/* ByteArray object interface */
+
+#ifndef Py_BYTEARRAYOBJECT_H
+#define Py_BYTEARRAYOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+/* Type PyByteArrayObject represents a mutable array of bytes.
+ * The Python API is that of a sequence;
+ * the bytes are mapped to ints in [0, 256).
+ * Bytes are not characters; they may be used to encode characters.
+ * The only way to go between bytes and str/unicode is via encoding
+ * and decoding.
+ * While CPython exposes interfaces to this object, pypy does not
+ */
+
+#define PyByteArray_GET_SIZE(op) PyByteArray_Size((PyObject*)(op))
+#define PyByteArray_AS_STRING(op) PyByteArray_AsString((PyObject*)(op))
+
+/* Object layout */
+typedef struct {
+    PyObject_VAR_HEAD
+#if 0
+    int ob_exports; /* how many buffer exports */
+    Py_ssize_t ob_alloc; /* How many bytes allocated */
+    char *ob_bytes;
+#endif
+} PyByteArrayObject;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_BYTEARRAYOBJECT_H */
diff --git a/pypy/module/cpyext/include/bytesobject.h b/pypy/module/cpyext/include/bytesobject.h
--- a/pypy/module/cpyext/include/bytesobject.h
+++ b/pypy/module/cpyext/include/bytesobject.h
@@ -61,9 +61,6 @@
 #define PyString_CHECK_INTERNED(op) (((PyStringObject *)(op))->ob_sstate)
 
 
-#define PyByteArray_Check(obj) \
-    PyObject_IsInstance(obj, (PyObject *)&PyByteArray_Type)
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -41,57 +41,6 @@
     raise NotImplementedError
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
-def PyByteArray_CheckExact(space, o):
-    """Return true if the object o is a bytearray object, but not an instance of a
-    subtype of the bytearray type."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], PyObject)
-def PyByteArray_FromObject(space, o):
-    """Return a new bytearray object from any object, o, that implements the
-    buffer protocol.
-
-    XXX expand about the buffer protocol, at least somewhere"""
-    raise NotImplementedError
-
- at cpython_api([rffi.CCHARP, Py_ssize_t], PyObject)
-def PyByteArray_FromStringAndSize(space, string, len):
-    """Create a new bytearray object from string and its length, len.  On
-    failure, NULL is returned."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, PyObject], PyObject)
-def PyByteArray_Concat(space, a, b):
-    """Concat bytearrays a and b and return a new bytearray with the result."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], Py_ssize_t, error=-1)
-def PyByteArray_Size(space, bytearray):
-    """Return the size of bytearray after checking for a NULL pointer."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], rffi.CCHARP)
-def PyByteArray_AsString(space, bytearray):
-    """Return the contents of bytearray as a char array after checking for a
-    NULL pointer."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, Py_ssize_t], rffi.INT_real, error=-1)
-def PyByteArray_Resize(space, bytearray, len):
-    """Resize the internal buffer of bytearray to len."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], rffi.CCHARP)
-def PyByteArray_AS_STRING(space, bytearray):
-    """Macro version of PyByteArray_AsString()."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], Py_ssize_t, error=-1)
-def PyByteArray_GET_SIZE(space, bytearray):
-    """Macro version of PyByteArray_Size()."""
-    raise NotImplementedError
-
- at cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyCell_Check(space, ob):
     """Return true if ob is a cell object; ob must not be NULL."""
     raise NotImplementedError
diff --git a/pypy/module/cpyext/test/test_bytearrayobject.py b/pypy/module/cpyext/test/test_bytearrayobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_bytearrayobject.py
@@ -0,0 +1,198 @@
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+class AppTestStringObject(AppTestCpythonExtensionBase):
+    def test_basic(self):
+        module = self.import_extension('foo', [
+            ("get_hello1", "METH_NOARGS",
+             """
+                 return PyByteArray_FromStringAndSize(
+                     "Hello world<should not be included>", 11);
+             """),
+            ("get_hello2", "METH_NOARGS",
+             """
+                 return PyByteArray_FromStringAndSize("Hello world", 12);
+             """),
+            ("test_Size", "METH_NOARGS",
+             """
+                 PyObject* s = PyByteArray_FromStringAndSize("Hello world", 12);
+                 int result = 0;
+                 size_t expected_size;
+
+                 if(PyByteArray_Size(s) == 12) {
+                     result = 1;
+                 }
+                 #ifdef PYPY_VERSION
+                    expected_size = sizeof(void*)*3;
+                 #elif defined Py_DEBUG
+                    expected_size = 64;
+                 #else
+                    expected_size = 48;
+                 #endif
+                 if(s->ob_type->tp_basicsize != expected_size)
+                 {
+                     printf("tp_basicsize==%ld\\n", s->ob_type->tp_basicsize);
+                     result = 0;
+                 }
+                 Py_DECREF(s);
+                 return PyBool_FromLong(result);
+             """),
+             ("test_is_bytearray", "METH_VARARGS",
+             """
+                return PyBool_FromLong(PyByteArray_Check(PyTuple_GetItem(args, 0)));
+             """)], prologue='#include <stdlib.h>')
+        assert module.get_hello1() == b'Hello world'
+        assert module.get_hello2() == b'Hello world\x00'
+        assert module.test_Size()
+        assert module.test_is_bytearray(bytearray(b""))
+        assert not module.test_is_bytearray(())
+
+    def test_bytearray_buffer_init(self):
+        module = self.import_extension('foo', [
+            ("getbytearray", "METH_NOARGS",
+             """
+                 PyObject *s, *t;
+                 char* c;
+                 Py_ssize_t len;
+
+                 s = PyByteArray_FromStringAndSize(NULL, 4);
+                 if (s == NULL)
+                    return NULL;
+                 t = PyByteArray_FromStringAndSize(NULL, 3);
+                 if (t == NULL)
+                    return NULL;
+                 Py_DECREF(t);
+                 c = PyByteArray_AsString(s);
+                 if (c == NULL)
+                 {
+                     PyErr_SetString(PyExc_ValueError, "non-null bytearray object expected");
+                     return NULL;
+                 }
+                 c[0] = 'a';
+                 c[1] = 'b';
+                 c[2] = 0;
+                 c[3] = 'c';
+                 return s;
+             """),
+            ])
+        s = module.getbytearray()
+        assert len(s) == 4
+        assert s == b'ab\x00c'
+
+    def test_bytearray_mutable(self):
+        module = self.import_extension('foo', [
+            ("mutable", "METH_NOARGS",
+             """
+                PyObject *base;
+                char * p_str;
+                base = PyByteArray_FromStringAndSize("test", 10);
+                if (PyByteArray_GET_SIZE(base) != 10)
+                    return PyLong_FromLong(-PyByteArray_GET_SIZE(base));
+                memcpy(PyByteArray_AS_STRING(base), "works", 6);
+                Py_INCREF(base);
+                return base;
+             """),
+            ])
+        s = module.mutable()
+        if s == b'\x00' * 10:
+            assert False, "no RW access to bytearray"
+        assert s[:6] == b'works\x00'
+
+    def test_AsByteArray(self):
+        module = self.import_extension('foo', [
+            ("getbytearray", "METH_NOARGS",
+             """
+                 PyObject* s1 = PyByteArray_FromStringAndSize("test", 4);
+                 if (s1 == NULL)
+                     return NULL;
+                 char* c = PyByteArray_AsString(s1);
+                 PyObject* s2 = PyByteArray_FromStringAndSize(c, 4);
+                 Py_DECREF(s1);
+                 return s2;
+             """),
+            ])
+        s = module.getbytearray()
+        assert s == b'test'
+
+    def test_manipulations(self):
+        module = self.import_extension('foo', [
+            ("bytearray_from_bytes", "METH_VARARGS",
+             '''
+             return PyByteArray_FromStringAndSize(PyBytes_AsString(
+                       PyTuple_GetItem(args, 0)), 4);
+             '''
+            ),
+            ("bytes_from_bytearray", "METH_VARARGS",
+             '''
+                char * buf;
+                int n;
+                PyObject * obj;
+                obj = PyTuple_GetItem(args, 0);
+                buf = PyByteArray_AsString(obj);
+                if (buf == NULL)
+                {
+                    PyErr_SetString(PyExc_ValueError, "non-null bytearray object expected");
+                    return NULL;
+                }
+                n = PyByteArray_Size(obj);
+                return PyBytes_FromStringAndSize(buf, n);
+             '''
+            ),
+            ("concat", "METH_VARARGS",
+             """
+                PyObject * ret, *right, *left;
+                PyObject *ba1, *ba2;
+                if (!PyArg_ParseTuple(args, "OO", &left, &right)) {
+                    return PyUnicode_FromString("parse failed");
+                }
+                ba1 = PyByteArray_FromObject(left);
+                ba2 = PyByteArray_FromObject(right);
+                if (ba1 == NULL || ba2 == NULL)
+                {
+                    /* exception should be set */
+                    return NULL;
+                }
+                ret = PyByteArray_Concat(ba1, ba2);
+                return ret;
+             """)])
+        assert module.bytearray_from_bytes(b"huheduwe") == b"huhe"
+        assert module.bytes_from_bytearray(bytearray(b'abc')) == b'abc'
+        raises(ValueError, module.bytes_from_bytearray, 4.0)
+        ret = module.concat(b'abc', b'def')
+        assert ret == b'abcdef'
+        assert not isinstance(ret, str)
+        assert isinstance(ret, bytearray)
+        raises(TypeError, module.concat, b'abc', u'def')
+
+    def test_bytearray_resize(self):
+        module = self.import_extension('foo', [
+            ("bytearray_resize", "METH_VARARGS",
+             '''
+             PyObject *obj, *ba;
+             int newsize, oldsize, ret;
+             if (!PyArg_ParseTuple(args, "Oi", &obj, &newsize)) {
+                 return PyUnicode_FromString("parse failed");
+             }
+
+             ba = PyByteArray_FromObject(obj);
+             if (ba == NULL)
+                 return NULL;
+             oldsize = PyByteArray_Size(ba);
+             if (oldsize == 0)
+             {
+                  return PyUnicode_FromString("oldsize is 0");
+             }
+             ret = PyByteArray_Resize(ba, newsize);
+             if (ret != 0)
+             {
+                  printf("ret, oldsize, newsize= %d, %d, %d\\n", ret, oldsize, newsize);
+                  return NULL;
+             }
+             return ba;
+             '''
+            )])
+        ret = module.bytearray_resize(b'abc', 6)
+        assert len(ret) == 6,"%s, len=%d" % (ret, len(ret))
+        assert ret == b'abc\x00\x00\x00'
+        ret = module.bytearray_resize(b'abcdefghi', 4)
+        assert len(ret) == 4,"%s, len=%d" % (ret, len(ret))
+        assert ret == b'abcd'
diff --git a/pypy/module/cpyext/test/test_getargs.py b/pypy/module/cpyext/test/test_getargs.py
--- a/pypy/module/cpyext/test/test_getargs.py
+++ b/pypy/module/cpyext/test/test_getargs.py
@@ -123,7 +123,8 @@
             return result;
             ''')
         assert b'foo\0bar\0baz' == pybuffer(b'foo\0bar\0baz')
-
+        return  # XXX
+        assert b'foo\0bar\0baz' == pybuffer(bytearray(b'foo\0bar\0baz'))
 
     def test_pyarg_parse_string_fails(self):
         """
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -5,8 +5,8 @@
 from pypy.module.cpyext.pyobject import make_ref, from_ref
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 
+import sys
 import pytest
-import sys
 
 class AppTestTypeObject(AppTestCpythonExtensionBase):
     def test_typeobject(self):
@@ -124,7 +124,6 @@
         obj = module.fooType.classmeth()
         assert obj is module.fooType
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='cpython segfaults')
     def test_new(self):
         # XXX cpython segfaults but if run singly (with -k test_new) this passes
         module = self.import_module(name='foo')
@@ -179,8 +178,6 @@
         x = module.MetaType('name', (), {})
         assert isinstance(x, type)
         assert isinstance(x, module.MetaType)
-        if self.runappdirect and '__pypy__' in sys.builtin_module_names:
-            skip('x is not callable when runappdirect??')
         x()
 
     def test_metaclass_compatible(self):
@@ -190,13 +187,11 @@
         assert type(module.fooType).__mro__ == (type, object)
         y = module.MetaType('other', (module.MetaType,), {})
         assert isinstance(y, module.MetaType)
-        if self.runappdirect and '__pypy__' in sys.builtin_module_names:
-            skip('y is not callable when runappdirect??')
         x = y('something', (type(y),), {})
         del x, y
 
     def test_metaclass_compatible2(self):
-        skip('type.__new__ does not check acceptable_as_base_class')
+        skip('fails even with -A, fooType has BASETYPE flag')
         # XXX FIX - must raise since fooType (which is a base type)
         # does not have flag Py_TPFLAGS_BASETYPE
         module = self.import_module(name='foo')
@@ -880,7 +875,6 @@
         #print('calling module.footype()...')
         module.footype("X", (object,), {})
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='cpython fails')
     def test_app_subclass_of_c_type(self):
         # on cpython, the size changes (6 bytes added)
         module = self.import_module(name='foo')
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -792,7 +792,7 @@
 @cpython_api([PyObject, PyObject], PyObject)
 def PyUnicode_Concat(space, w_left, w_right):
     """Concat two strings giving a new Unicode string."""
-    return space.call_method(w_left, '__add__', w_right)
+    return space.add(w_left, w_right)
 
 @cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=CANNOT_FAIL)
 def PyUnicode_CompareWithASCIIString(space, w_uni, string):
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -5,6 +5,9 @@
 from rpython.rlib.buffer import Buffer
 from rpython.rlib.rstring import StringBuilder, ByteListBuilder
 from rpython.rlib.debug import check_list_of_chars
+from rpython.rtyper.lltypesystem import rffi
+from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr,
+        nonmoving_raw_ptr_for_resizable_list)
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
@@ -25,7 +28,7 @@
 
     def __init__(self, data):
         check_list_of_chars(data)
-        self.data = data
+        self.data = resizable_list_supporting_raw_ptr(data)
 
     def __repr__(self):
         """representation for debugging purposes"""
@@ -34,6 +37,12 @@
     def buffer_w(self, space, flags):
         return BytearrayBuffer(self.data, False)
 
+    def bytearray_list_of_chars_w(self, space):
+        return self.data
+
+    def nonmovable_carray(self, space):
+        return BytearrayBuffer(self.data, False).get_raw_address()
+
     def _new(self, value):
         if value is self.data:
             value = value[:]
@@ -176,7 +185,8 @@
     @unwrap_spec(encoding='str_or_None', errors='str_or_None')
     def descr_init(self, space, w_source=None, encoding=None, errors=None):
         assert isinstance(self, W_BytearrayObject)
-        self.data = newbytesdata_w(space, w_source, encoding, errors)
+        data = newbytesdata_w(space, w_source, encoding, errors)
+        self.data = resizable_list_supporting_raw_ptr(data)
 
     def descr_repr(self, space):
         s = self.data
@@ -457,6 +467,8 @@
         return val - 87
     return -1
 
+NON_HEX_MSG = "non-hexadecimal number found in fromhex() arg at position %d"
+
 def _hexstring_to_array(space, s):
     data = []
     length = len(s)
@@ -467,21 +479,15 @@
         if i >= length:
             break
         if i + 1 == length:
-            raise oefmt(space.w_ValueError,
-                        "non-hexadecimal number found in fromhex() arg at "
-                        "position %d", i)
+            raise oefmt(space.w_ValueError, NON_HEX_MSG, i)
 
         top = _hex_digit_to_int(s[i])
         if top == -1:
-            raise oefmt(space.w_ValueError,
-                        "non-hexadecimal number found in fromhex() arg at "
-                        "position %d", i)
-        bot = _hex_digit_to_int(s[i+1])
+            raise oefmt(space.w_ValueError, NON_HEX_MSG, i)
+        bot = _hex_digit_to_int(s[i + 1])
         if bot == -1:
-            raise oefmt(space.w_ValueError,
-                        "non-hexadecimal number found in fromhex() arg at "
-                        "position %d", i + 1)
-        data.append(chr(top*16 + bot))
+            raise oefmt(space.w_ValueError, NON_HEX_MSG, i + 1)
+        data.append(chr(top * 16 + bot))
         i += 2
     return data
 
@@ -1184,6 +1190,9 @@
         for i in range(len(string)):
             self.data[start + i] = string[i]
 
+    def get_raw_address(self):
+        return nonmoving_raw_ptr_for_resizable_list(self.data)
+
 
 @specialize.argtype(1)
 def _memcmp(selfvalue, buffer, length):
diff --git a/pypy/objspace/std/test/test_memoryobject.py b/pypy/objspace/std/test/test_memoryobject.py
--- a/pypy/objspace/std/test/test_memoryobject.py
+++ b/pypy/objspace/std/test/test_memoryobject.py
@@ -159,5 +159,5 @@
 
     def test_pypy_raw_address_base(self):
         raises(ValueError, memoryview(b"foobar")._pypy_raw_address)
-        e = raises(ValueError, memoryview(bytearray(b"foobar"))._pypy_raw_address)
-        assert 'BytearrayBuffer' in str(e.value)
+        a = memoryview(bytearray(b"foobar"))._pypy_raw_address()
+        assert a != 0


More information about the pypy-commit mailing list