Author: Ronan Lamy <ronan.l...@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) + +@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) + +#_______________________________________________________________________ + +@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) + +@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) + +@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) + +@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) + +@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) + +@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 - -@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 - -@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 - -@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 - -@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 - -@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 - -@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 - -@cpython_api([PyObject], rffi.CCHARP) -def PyByteArray_AS_STRING(space, bytearray): - """Macro version of PyByteArray_AsString().""" - raise NotImplementedError - -@cpython_api([PyObject], Py_ssize_t, error=-1) -def PyByteArray_GET_SIZE(space, bytearray): - """Macro version of PyByteArray_Size().""" - raise NotImplementedError - -@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit