Author: Matti Picus <matti.pi...@gmail.com> Branch: unicode-utf8 Changeset: r95106:31d343a2e948 Date: 2018-09-12 10:56 +0300 http://bitbucket.org/pypy/pypy/changeset/31d343a2e948/
Log: merge default into branch diff too long, truncating to 2000 out of 24129 lines diff --git a/pypy/module/cpyext/test/test_abstract.py b/pypy/module/cpyext/test/test_abstract.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_abstract.py @@ -0,0 +1,130 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +import pytest + +class AppTestBufferProtocol(AppTestCpythonExtensionBase): + """Tests for the old buffer protocol.""" + + def w_get_buffer_support(self): + return self.import_extension('buffer_support', [ + ("charbuffer_as_string", "METH_O", + """ + char *ptr; + Py_ssize_t size; + if (PyObject_AsCharBuffer(args, (const char **)&ptr, &size) < 0) + return NULL; + return PyString_FromStringAndSize(ptr, size); + """), + ("check_readbuffer", "METH_O", + """ + return PyBool_FromLong(PyObject_CheckReadBuffer(args)); + """), + ("readbuffer_as_string", "METH_O", + """ + const void *ptr; + Py_ssize_t size; + if (PyObject_AsReadBuffer(args, &ptr, &size) < 0) + return NULL; + return PyString_FromStringAndSize((char*)ptr, size); + """), + ("writebuffer_as_string", "METH_O", + """ + void *ptr; + Py_ssize_t size; + if (PyObject_AsWriteBuffer(args, &ptr, &size) < 0) + return NULL; + return PyString_FromStringAndSize((char*)ptr, size); + """), + ("zero_out_writebuffer", "METH_O", + """ + void *ptr; + Py_ssize_t size; + Py_ssize_t i; + if (PyObject_AsWriteBuffer(args, &ptr, &size) < 0) + return NULL; + for (i = 0; i < size; i++) { + ((char*)ptr)[i] = 0; + } + Py_RETURN_NONE; + """), + ]) + + def test_string(self): + buffer_support = self.get_buffer_support() + + s = 'a\0x' + + assert buffer_support.check_readbuffer(s) + assert s == buffer_support.readbuffer_as_string(s) + assert raises(TypeError, buffer_support.writebuffer_as_string, s) + assert s == buffer_support.charbuffer_as_string(s) + + def test_buffer(self): + buffer_support = self.get_buffer_support() + + s = 'a\0x' + buf = buffer(s) + + assert buffer_support.check_readbuffer(buf) + assert s == buffer_support.readbuffer_as_string(buf) + assert raises(TypeError, buffer_support.writebuffer_as_string, buf) + assert s == buffer_support.charbuffer_as_string(buf) + + def test_mmap(self): + import mmap + buffer_support = self.get_buffer_support() + + s = 'a\0x' + mm = mmap.mmap(-1, 3) + mm[:] = s + + assert buffer_support.check_readbuffer(mm) + assert s == buffer_support.readbuffer_as_string(mm) + assert s == buffer_support.writebuffer_as_string(mm) + assert s == buffer_support.charbuffer_as_string(mm) + + s = '\0' * 3 + buffer_support.zero_out_writebuffer(mm) + assert s == ''.join(mm) + assert s == buffer_support.readbuffer_as_string(mm) + assert s == buffer_support.writebuffer_as_string(mm) + assert s == buffer_support.charbuffer_as_string(mm) + + s = '\0' * 3 + ro_mm = mmap.mmap(-1, 3, access=mmap.ACCESS_READ) + assert buffer_support.check_readbuffer(ro_mm) + assert s == buffer_support.readbuffer_as_string(ro_mm) + assert raises(TypeError, buffer_support.writebuffer_as_string, ro_mm) + assert s == buffer_support.charbuffer_as_string(ro_mm) + + def test_array(self): + import array + buffer_support = self.get_buffer_support() + + s = 'a\0x' + a = array.array('B', [5, 0, 10]) + + buffer_support.zero_out_writebuffer(a) + assert list(a) == [0, 0, 0] + + def test_nonbuffer(self): + # e.g. int + buffer_support = self.get_buffer_support() + + assert not buffer_support.check_readbuffer(42) + assert raises(TypeError, buffer_support.readbuffer_as_string, 42) + assert raises(TypeError, buffer_support.writebuffer_as_string, 42) + assert raises(TypeError, buffer_support.charbuffer_as_string, 42) + + def test_user_class(self): + class MyBuf(str): + pass + s = 'a\0x' + buf = MyBuf(s) + buffer_support = self.get_buffer_support() + + assert buffer_support.check_readbuffer(buf) + assert s == buffer_support.readbuffer_as_string(buf) + assert raises(TypeError, buffer_support.writebuffer_as_string, buf) + assert s == buffer_support.charbuffer_as_string(buf) + + diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -0,0 +1,197 @@ +import pytest +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.conftest import option + +class AppTestArrayModule(AppTestCpythonExtensionBase): + enable_leak_checking = True + + def setup_class(cls): + from rpython.tool.udir import udir + AppTestCpythonExtensionBase.setup_class.im_func(cls) + if option.runappdirect: + cls.w_udir = str(udir) + else: + cls.w_udir = cls.space.wrap(str(udir)) + + + def test_basic(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3]) + assert arr.typecode == 'i' + assert arr.itemsize == 4 + assert arr[2] == 3 + assert len(arr.buffer_info()) == 2 + exc = raises(TypeError, module.array.append) + errstr = str(exc.value) + assert errstr.startswith("descriptor 'append' of") + arr.append(4) + assert arr.tolist() == [1, 2, 3, 4] + assert len(arr) == 4 + + def test_iter(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3]) + sum = 0 + for i in arr: + sum += i + assert sum == 6 + + def test_index(self): + module = self.import_module(name='array') + arr = module.array('i', [1, 2, 3, 4]) + assert arr[3] == 4 + raises(IndexError, arr.__getitem__, 10) + del arr[2] + assert arr.tolist() == [1, 2, 4] + arr[2] = 99 + assert arr.tolist() == [1, 2, 99] + + def test_slice_get(self): + module = self.import_module(name='array') + arr = module.array('i', [1, 2, 3, 4]) + assert arr[:].tolist() == [1, 2, 3, 4] + assert arr[1:].tolist() == [2, 3, 4] + assert arr[:2].tolist() == [1, 2] + assert arr[1:3].tolist() == [2, 3] + + def test_slice_object(self): + module = self.import_module(name='array') + arr = module.array('i', [1, 2, 3, 4]) + assert arr[slice(1, 3)].tolist() == [2,3] + arr[slice(1, 3)] = module.array('i', [21, 22, 23]) + assert arr.tolist() == [1, 21, 22, 23, 4] + del arr[slice(1, 3)] + assert arr.tolist() == [1, 23, 4] + raises(TypeError, 'arr[slice(1, 3)] = "abc"') + + def test_buffer(self): + import sys + module = self.import_module(name='array') + arr = module.array('i', [1, 2, 3, 4]) + buf = buffer(arr) + exc = raises(TypeError, "buf[1] = '1'") + assert str(exc.value) == "buffer is read-only" + if sys.byteorder == 'big': + expected = '\0\0\0\x01' '\0\0\0\x02' '\0\0\0\x03' '\0\0\0\x04' + else: + expected = '\x01\0\0\0' '\x02\0\0\0' '\x03\0\0\0' '\x04\0\0\0' + assert str(buf) == expected + assert str(buffer('a') + arr) == "a" + expected + # python2 special cases empty-buffer + obj + assert str(buffer('') + arr) == "array('i', [1, 2, 3, 4])" + + def test_releasebuffer(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3,4]) + assert module.get_releasebuffer_cnt() == 0 + module.create_and_release_buffer(arr) + assert module.get_releasebuffer_cnt() == 1 + + def test_Py_buffer(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3,4]) + assert module.get_releasebuffer_cnt() == 0 + m = memoryview(arr) + assert module.get_releasebuffer_cnt() == 0 + del m + self.debug_collect() + assert module.get_releasebuffer_cnt() == 1 + + def test_pickle(self): + import pickle + module = self.import_module(name='array') + arr = module.array('i', [1,2,3,4]) + s = pickle.dumps(arr) + # pypy exports __dict__ on cpyext objects, so the pickle picks up the {} state value + #assert s == "carray\n_reconstruct\np0\n(S'i'\np1\n(lp2\nI1\naI2\naI3\naI4\natp3\nRp4\n." + rra = pickle.loads(s) # rra is arr backwards + #assert arr.tolist() == rra.tolist() + + def test_binop_mul_impl(self): + # check that rmul is called + module = self.import_module(name='array') + arr = module.array('i', [2]) + res = [1, 2, 3] * arr + assert res == [1, 2, 3, 1, 2, 3] + module.switch_multiply() + res = [1, 2, 3] * arr + assert res == [2, 4, 6] + + @pytest.mark.xfail + def test_subclass_dealloc(self): + module = self.import_module(name='array') + class Sub(module.array): + pass + + arr = Sub('i', [2]) + module.readbuffer_as_string(arr) + class A(object): + pass + assert not module.same_dealloc(arr, module.array('i', [2])) + assert module.same_dealloc(arr, A()) + + def test_subclass(self): + import struct + module = self.import_module(name='array') + class Sub(module.array): + pass + + arr = Sub('i', [2]) + res = [1, 2, 3] * arr + assert res == [1, 2, 3, 1, 2, 3] + + val = module.readbuffer_as_string(arr) + assert val == struct.pack('i', 2) + + def test_unicode_readbuffer(self): + # Not really part of array, refactor + import struct + module = self.import_module(name='array') + val = module.readbuffer_as_string('abcd') + assert val == 'abcd' + val = module.readbuffer_as_string(u'\u03a3') + assert val is not None + + def test_readinto(self): + module = self.import_module(name='array') + a = module.array('c') + a.fromstring('0123456789') + filename = self.udir + "/_test_file" + f = open(filename, 'w+b') + f.write('foobar') + f.seek(0) + n = f.readinto(a) + f.close() + assert n == 6 + assert len(a) == 10 + assert a.tostring() == 'foobar6789' + + def test_iowrite(self): + module = self.import_module(name='array') + from io import BytesIO + a = module.array('c') + a.fromstring('0123456789') + fd = BytesIO() + # only test that it works + fd.write(a) + + def test_getitem_via_PySequence_GetItem(self): + module = self.import_module(name='array') + a = module.array('i', range(10)) + # call via tp_as_mapping.mp_subscript + assert 5 == a[-5] + # PySequence_ITEM used to call space.getitem() which + # prefers tp_as_mapping.mp_subscript over tp_as_sequence.sq_item + # Now fixed so this test raises (array_item does not add len(a), + # array_subscr does) + raises(IndexError, module.getitem, a, -5) + + def test_subclass_with_attribute(self): + module = self.import_module(name='array') + class Sub(module.array): + def addattrib(self): + print('called addattrib') + self.attrib = True + import gc + module.subclass_with_attribute(Sub, "addattrib", "attrib", gc.collect) + assert Sub.__module__ == __name__ diff --git a/pypy/module/cpyext/test/test_boolobject.py b/pypy/module/cpyext/test/test_boolobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_boolobject.py @@ -0,0 +1,48 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.boolobject import PyBool_FromLong + +class TestBoolObject(BaseApiTest): + def test_fromlong(self, space): + for i in range(-3, 3): + obj = PyBool_FromLong(space, i) + if i: + assert obj is space.w_True + else: + assert obj is space.w_False + +class AppTestBoolMacros(AppTestCpythonExtensionBase): + def test_macros(self): + module = self.import_extension('foo', [ + ("get_true", "METH_NOARGS", "Py_RETURN_TRUE;"), + ("get_false", "METH_NOARGS", "Py_RETURN_FALSE;"), + ]) + assert module.get_true() == True + assert module.get_false() == False + + def test_toint(self): + module = self.import_extension('foo', [ + ("to_int", "METH_O", + ''' + if (args->ob_type->tp_as_number && args->ob_type->tp_as_number->nb_int) { + return args->ob_type->tp_as_number->nb_int(args); + } + else { + PyErr_SetString(PyExc_TypeError,"cannot convert bool to int"); + return NULL; + } + '''), ]) + assert module.to_int(False) == 0 + assert module.to_int(True) == 1 + + def test_check(self): + module = self.import_extension('foo', [ + ("type_check", "METH_O", + ''' + return PyLong_FromLong(PyBool_Check(args)); + ''')]) + assert module.type_check(True) + assert module.type_check(False) + assert not module.type_check(None) + assert not module.type_check(1.0) + diff --git a/pypy/module/cpyext/test/test_borrow.py b/pypy/module/cpyext/test/test_borrow.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_borrow.py @@ -0,0 +1,71 @@ +import py +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.pyobject import make_ref + + +class AppTestBorrow(AppTestCpythonExtensionBase): + def test_tuple_borrowing(self): + module = self.import_extension('foo', [ + ("test_borrowing", "METH_NOARGS", + """ + PyObject *t = PyTuple_New(1); + PyObject *f = PyFloat_FromDouble(42.0); + PyObject *g = NULL; + printf("Refcnt1: %ld\\n", f->ob_refcnt); + PyTuple_SetItem(t, 0, f); // steals reference + printf("Refcnt2: %ld\\n", f->ob_refcnt); + f = PyTuple_GetItem(t, 0); // borrows reference + printf("Refcnt3: %ld\\n", f->ob_refcnt); + g = PyTuple_GetItem(t, 0); // borrows reference again + printf("Refcnt4: %ld\\n", f->ob_refcnt); + printf("COMPARE: %i\\n", f == g); + fflush(stdout); + Py_DECREF(t); + Py_RETURN_TRUE; + """), + ]) + assert module.test_borrowing() # the test should not leak + + def test_borrow_destroy(self): + module = self.import_extension('foo', [ + ("test_borrow_destroy", "METH_NOARGS", + """ + PyObject *i = PyInt_FromLong(42); + PyObject *j; + PyObject *t1 = PyTuple_Pack(1, i); + PyObject *t2 = PyTuple_Pack(1, i); + Py_DECREF(i); + + i = PyTuple_GetItem(t1, 0); + PyTuple_GetItem(t2, 0); + Py_DECREF(t2); + + j = PyInt_FromLong(PyInt_AsLong(i)); + Py_DECREF(t1); + return j; + """), + ]) + assert module.test_borrow_destroy() == 42 + + def test_double_borrow(self): + if self.runappdirect: + py.test.xfail('segfault') + module = self.import_extension('foo', [ + ("run", "METH_NOARGS", + """ + PyObject *t = PyTuple_New(1); + PyObject *s = PyRun_String("set()", Py_eval_input, + Py_None, Py_None); + PyObject *w = PyWeakref_NewRef(s, Py_None); + PyTuple_SetItem(t, 0, s); + PyTuple_GetItem(t, 0); + PyTuple_GetItem(t, 0); + Py_DECREF(t); + return w; + """), + ]) + wr = module.run() + # check that the set() object was deallocated + self.debug_collect() + assert wr() is None diff --git a/pypy/module/cpyext/test/test_bufferobject.py b/pypy/module/cpyext/test/test_bufferobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_bufferobject.py @@ -0,0 +1,123 @@ +from rpython.rtyper.lltypesystem import lltype +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.api import PyObject + +class AppTestBufferObject(AppTestCpythonExtensionBase): + + def test_FromMemory(self): + module = self.import_extension('foo', [ + ("get_FromMemory", "METH_NOARGS", + """ + cbuf = malloc(4); + cbuf[0] = 'a'; + cbuf[1] = 'b'; + cbuf[2] = 'c'; + cbuf[3] = '\\0'; + return PyBuffer_FromMemory(cbuf, 4); + """), + ("free_buffer", "METH_NOARGS", + """ + free(cbuf); + Py_RETURN_NONE; + """), + ("check_ascharbuffer", "METH_O", + """ + char *ptr; + Py_ssize_t size; + if (PyObject_AsCharBuffer(args, (const char **)&ptr, &size) < 0) + return NULL; + return PyString_FromStringAndSize(ptr, size); + """) + ], prologue = """ + static char* cbuf = NULL; + """) + buf = module.get_FromMemory() + assert str(buf) == 'abc\0' + + assert module.check_ascharbuffer(buf) == 'abc\0' + + module.free_buffer() + + def test_Buffer_New(self): + module = self.import_extension('foo', [ + ("buffer_new", "METH_NOARGS", + """ + return PyBuffer_New(150); + """), + ]) + b = module.buffer_new() + raises(AttributeError, getattr, b, 'x') + + def test_array_buffer(self): + if self.runappdirect: + skip('PyBufferObject not available outside buffer object.c') + module = self.import_extension('foo', [ + ("roundtrip", "METH_O", + """ + PyBufferObject *buf = (PyBufferObject *)args; + return PyString_FromStringAndSize(buf->b_ptr, buf->b_size); + """), + ]) + import array + a = array.array('c', 'text') + b = buffer(a) + assert module.roundtrip(b) == 'text' + + + def test_issue2752(self): + iterations = 10 + if self.runappdirect: + iterations = 2000 + module = self.import_extension('foo', [ + ("test_mod", 'METH_VARARGS', + """ + PyObject *obj; + Py_buffer bp; + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + + if (PyObject_GetBuffer(obj, &bp, PyBUF_SIMPLE) == -1) + return NULL; + + if (((unsigned char*)bp.buf)[0] != '0') { + void * buf = (void*)bp.buf; + unsigned char val[4]; + char * s = PyString_AsString(obj); + memcpy(val, bp.buf, 4); + PyBuffer_Release(&bp); + if (PyObject_GetBuffer(obj, &bp, PyBUF_SIMPLE) == -1) + return NULL; + PyErr_Format(PyExc_ValueError, + "mismatch: %p [%x %x %x %x...] now %p [%x %x %x %x...] as str '%s'", + buf, val[0], val[1], val[2], val[3], + (void *)bp.buf, + ((unsigned char*)bp.buf)[0], + ((unsigned char*)bp.buf)[1], + ((unsigned char*)bp.buf)[2], + ((unsigned char*)bp.buf)[3], + s); + PyBuffer_Release(&bp); + return NULL; + } + + PyBuffer_Release(&bp); + Py_RETURN_NONE; + """), + ]) + bufsize = 4096 + def getdata(bufsize): + data = b'01234567' + for x in range(18): + data += data + if len(data) >= bufsize: + break + return data + for j in range(iterations): + block = getdata(bufsize) + assert block[:8] == '01234567' + try: + module.test_mod(block) + except ValueError as e: + print("%s at it=%d" % (e, j)) + assert False 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,188 @@ +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; + + if(PyByteArray_Size(s) == 12) { + result = 1; + } + 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; + + 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; + 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", + """ + const char *c; + PyObject *s2, *s1 = PyByteArray_FromStringAndSize("test", 4); + if (s1 == NULL) + return NULL; + c = PyByteArray_AsString(s1); + s2 = PyByteArray_FromStringAndSize(c, 4); + Py_DECREF(s1); + return s2; + """), + ]) + s = module.getbytearray() + assert s == b'test' + + def test_manipulations(self): + import sys + 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' + if '__pypy__' in sys.builtin_module_names: + # CPython only makes an assert. + 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_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_bytesobject.py @@ -0,0 +1,611 @@ +# encoding: utf-8 +import pytest +from rpython.rtyper.lltypesystem import rffi, lltype +from pypy.interpreter.error import OperationError +from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.bytesobject import ( + new_empty_str, PyBytesObject, _PyString_Resize, PyString_Concat, + PyString_ConcatAndDel, PyString_Format, PyString_InternFromString, + PyString_AsEncodedObject, PyString_AsDecodedObject, _PyString_Eq, + _PyString_Join) +from pypy.module.cpyext.api import PyObjectP, PyObject, Py_ssize_tP, generic_cpy_call +from pypy.module.cpyext.pyobject import decref, from_ref, make_ref +from pypy.module.cpyext.buffer import PyObject_AsCharBuffer +from pypy.module.cpyext.api import PyTypeObjectPtr + + +class AppTestBytesObject(AppTestCpythonExtensionBase): + def test_bytesobject(self): + module = self.import_extension('foo', [ + ("get_hello1", "METH_NOARGS", + """ + return PyBytes_FromStringAndSize( + "Hello world<should not be included>", 11); + """), + ("get_hello2", "METH_NOARGS", + """ + return PyBytes_FromString("Hello world"); + """), + ("test_Size", "METH_NOARGS", + """ + PyObject* s = PyBytes_FromString("Hello world"); + int result = PyBytes_Size(s); + + Py_DECREF(s); + return PyLong_FromLong(result); + """), + ("test_Size_exception", "METH_NOARGS", + """ + PyObject* f = PyFloat_FromDouble(1.0); + PyBytes_Size(f); + + Py_DECREF(f); + return NULL; + """), + ("test_is_bytes", "METH_VARARGS", + """ + return PyBool_FromLong(PyBytes_Check(PyTuple_GetItem(args, 0))); + """)], prologue='#include <stdlib.h>') + assert module.get_hello1() == b'Hello world' + assert module.get_hello2() == b'Hello world' + assert module.test_Size() == 11 + raises(TypeError, module.test_Size_exception) + + assert module.test_is_bytes(b"") + assert not module.test_is_bytes(()) + + def test_bytes_buffer_init(self): + module = self.import_extension('foo', [ + ("getbytes", "METH_NOARGS", + """ + PyObject *s, *t; + char* c; + + s = PyBytes_FromStringAndSize(NULL, 4); + if (s == NULL) + return NULL; + t = PyBytes_FromStringAndSize(NULL, 3); + if (t == NULL) + return NULL; + Py_DECREF(t); + c = PyBytes_AS_STRING(s); + c[0] = 'a'; + c[1] = 'b'; + c[2] = 0; + c[3] = 'c'; + return s; + """), + ]) + s = module.getbytes() + assert len(s) == 4 + assert s == b'ab\x00c' + + def test_bytes_tp_alloc(self): + module = self.import_extension('foo', [ + ("tpalloc", "METH_NOARGS", + """ + PyObject *base; + PyTypeObject * type; + PyBytesObject *obj; + base = PyBytes_FromString("test"); + if (PyBytes_GET_SIZE(base) != 4) + return PyLong_FromLong(-PyBytes_GET_SIZE(base)); + type = base->ob_type; + if (type->tp_itemsize != 1) + return PyLong_FromLong(type->tp_itemsize); + obj = (PyBytesObject*)type->tp_alloc(type, 10); + if (PyBytes_GET_SIZE(obj) != 10) + return PyLong_FromLong(PyBytes_GET_SIZE(obj)); + /* cannot work, there is only RO access + memcpy(PyBytes_AS_STRING(obj), "works", 6); */ + Py_INCREF(obj); + return (PyObject*)obj; + """), + ('alloc_rw', "METH_NOARGS", + ''' + PyObject *obj = (PyObject*)_PyObject_NewVar(&PyBytes_Type, 10); + memcpy(PyBytes_AS_STRING(obj), "works", 6); + return (PyObject*)obj; + '''), + ]) + s = module.alloc_rw() + assert s[:6] == b'works\0' # s[6:10] contains random garbage + s = module.tpalloc() + assert s == b'\x00' * 10 + + def test_AsString(self): + module = self.import_extension('foo', [ + ("getbytes", "METH_NOARGS", + """ + char *c; + PyObject* s2, *s1 = PyBytes_FromStringAndSize("test", 4); + c = PyBytes_AsString(s1); + s2 = PyBytes_FromStringAndSize(c, 4); + Py_DECREF(s1); + return s2; + """), + ]) + s = module.getbytes() + assert s == b'test' + + def test_manipulations(self): + module = self.import_extension('foo', [ + ("bytes_as_string", "METH_VARARGS", + ''' + return PyBytes_FromStringAndSize(PyBytes_AsString( + PyTuple_GetItem(args, 0)), 4); + ''' + ), + ("concat", "METH_VARARGS", + """ + PyObject ** v; + PyObject * left = PyTuple_GetItem(args, 0); + Py_INCREF(left); /* the reference will be stolen! */ + v = &left; + PyBytes_Concat(v, PyTuple_GetItem(args, 1)); + return *v; + """)]) + assert module.bytes_as_string(b"huheduwe") == b"huhe" + ret = module.concat(b'abc', b'def') + assert ret == b'abcdef' + ret = module.concat('abc', u'def') + assert not isinstance(ret, str) + assert isinstance(ret, unicode) + assert ret == 'abcdef' + + def test_py_bytes_as_string_None(self): + module = self.import_extension('foo', [ + ("string_None", "METH_VARARGS", + ''' + if (PyBytes_AsString(Py_None)) { + Py_RETURN_NONE; + } + return NULL; + ''' + )]) + raises(TypeError, module.string_None) + + def test_AsStringAndSize(self): + module = self.import_extension('foo', [ + ("getbytes", "METH_NOARGS", + """ + PyObject* s1 = PyBytes_FromStringAndSize("te\\0st", 5); + char *buf; + Py_ssize_t len; + if (PyBytes_AsStringAndSize(s1, &buf, &len) < 0) + return NULL; + if (len != 5) { + PyErr_SetString(PyExc_AssertionError, "Bad Length"); + return NULL; + } + if (PyBytes_AsStringAndSize(s1, &buf, NULL) >= 0) { + PyErr_SetString(PyExc_AssertionError, "Should Have failed"); + return NULL; + } + PyErr_Clear(); + Py_DECREF(s1); + Py_INCREF(Py_None); + return Py_None; + """), + ("c_only", "METH_NOARGS", + """ + int ret; + char * buf2; + PyObject * obj = PyBytes_FromStringAndSize(NULL, 1024); + if (!obj) + return NULL; + buf2 = PyBytes_AsString(obj); + if (!buf2) + return NULL; + /* buf should not have been forced, issue #2395 */ + ret = _PyBytes_Resize(&obj, 512); + if (ret < 0) + return NULL; + Py_DECREF(obj); + Py_INCREF(Py_None); + return Py_None; + """), + ]) + module.getbytes() + module.c_only() + + def test_py_string_as_string_Unicode(self): + module = self.import_extension('foo', [ + ("getstring_unicode", "METH_NOARGS", + """ + Py_UNICODE chars[] = {'t', 'e', 's', 't'}; + PyObject* u1 = PyUnicode_FromUnicode(chars, 4); + char *buf; + buf = PyString_AsString(u1); + if (buf == NULL) + return NULL; + if (buf[3] != 't') { + PyErr_SetString(PyExc_AssertionError, "Bad conversion"); + return NULL; + } + Py_DECREF(u1); + Py_INCREF(Py_None); + return Py_None; + """), + ("getstringandsize_unicode", "METH_NOARGS", + """ + Py_UNICODE chars[] = {'t', 'e', 's', 't'}; + PyObject* u1 = PyUnicode_FromUnicode(chars, 4); + char *buf; + Py_ssize_t len; + if (PyString_AsStringAndSize(u1, &buf, &len) < 0) + return NULL; + if (len != 4) { + PyErr_SetString(PyExc_AssertionError, "Bad Length"); + return NULL; + } + Py_DECREF(u1); + Py_INCREF(Py_None); + return Py_None; + """), + ]) + module.getstring_unicode() + module.getstringandsize_unicode() + + def test_format_v(self): + module = self.import_extension('foo', [ + ("test_string_format_v", "METH_VARARGS", + ''' + return helper("bla %d ble %s\\n", + PyInt_AsLong(PyTuple_GetItem(args, 0)), + PyString_AsString(PyTuple_GetItem(args, 1))); + ''' + ) + ], prologue=''' + PyObject* helper(char* fmt, ...) + { + va_list va; + PyObject* res; + va_start(va, fmt); + res = PyString_FromFormatV(fmt, va); + va_end(va); + return res; + } + ''') + res = module.test_string_format_v(1, "xyz") + assert res == "bla 1 ble xyz\n" + + def test_format(self): + module = self.import_extension('foo', [ + ("test_string_format", "METH_VARARGS", + ''' + return PyString_FromFormat("bla %d ble %s\\n", + PyInt_AsLong(PyTuple_GetItem(args, 0)), + PyString_AsString(PyTuple_GetItem(args, 1))); + ''' + ) + ]) + res = module.test_string_format(1, "xyz") + assert res == "bla 1 ble xyz\n" + + def test_intern_inplace(self): + module = self.import_extension('foo', [ + ("test_intern_inplace", "METH_O", + ''' + PyObject *s = args; + Py_INCREF(s); + PyString_InternInPlace(&s); + if (((PyBytesObject*)s)->ob_sstate == SSTATE_NOT_INTERNED) + { + Py_DECREF(s); + s = PyString_FromString("interned error"); + } + return s; + ''' + ) + ]) + # This does not test much, but at least the refcounts are checked. + assert module.test_intern_inplace('s') == 's' + + def test_bytes_macros(self): + """The PyString_* macros cast, and calls expecting that build.""" + module = self.import_extension('foo', [ + ("test_macro_invocations", "METH_NOARGS", + """ + PyObject* o = PyString_FromString(""); + PyBytesObject* u = (PyBytesObject*)o; + + PyString_GET_SIZE(u); + PyString_GET_SIZE(o); + + PyString_AS_STRING(o); + PyString_AS_STRING(u); + + return o; + """)]) + assert module.test_macro_invocations() == '' + + def test_hash_and_state(self): + module = self.import_extension('foo', [ + ("test_hash", "METH_VARARGS", + ''' + PyObject* obj = (PyTuple_GetItem(args, 0)); + long hash = ((PyBytesObject*)obj)->ob_shash; + return PyLong_FromLong(hash); + ''' + ), + ("test_sstate", "METH_NOARGS", + ''' + PyObject *s = PyString_FromString("xyz"); + /*int sstate = ((PyBytesObject*)s)->ob_sstate; + printf("sstate now %d\\n", sstate);*/ + PyString_InternInPlace(&s); + /*sstate = ((PyBytesObject*)s)->ob_sstate; + printf("sstate now %d\\n", sstate);*/ + Py_DECREF(s); + return PyBool_FromLong(1); + '''), + ], prologue='#include <stdlib.h>') + res = module.test_hash("xyz") + assert res == hash('xyz') + # doesn't really test, but if printf is enabled will prove sstate + assert module.test_sstate() + + def test_subclass(self): + # taken from PyStringArrType_Type in numpy's scalartypes.c.src + module = self.import_extension('bar', [ + ("newsubstr", "METH_O", + """ + PyObject * obj; + char * data; + int len; + + data = PyString_AS_STRING(args); + len = PyString_GET_SIZE(args); + if (data == NULL) + Py_RETURN_NONE; + obj = PyArray_Scalar(data, len); + return obj; + """), + ("get_len", "METH_O", + """ + return PyLong_FromLong(PyObject_Size(args)); + """), + ('has_nb_add', "METH_O", + ''' + if (args->ob_type->tp_as_number == NULL) { + Py_RETURN_FALSE; + } + if (args->ob_type->tp_as_number->nb_add == NULL) { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; + '''), + ], prologue=""" + #include <Python.h> + PyTypeObject PyStringArrType_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "bar.string_", /* tp_name*/ + sizeof(PyBytesObject), /* tp_basicsize*/ + 0 /* tp_itemsize */ + }; + + static PyObject * + stringtype_repr(PyObject *self) + { + const char *dptr, *ip; + int len; + PyObject *new; + + ip = dptr = PyString_AS_STRING(self); + len = PyString_GET_SIZE(self); + dptr += len-1; + while(len > 0 && *dptr-- == 0) { + len--; + } + new = PyString_FromStringAndSize(ip, len); + if (new == NULL) { + return PyString_FromString(""); + } + return new; + } + + static PyObject * + stringtype_str(PyObject *self) + { + const char *dptr, *ip; + int len; + PyObject *new; + + ip = dptr = PyString_AS_STRING(self); + len = PyString_GET_SIZE(self); + dptr += len-1; + while(len > 0 && *dptr-- == 0) { + len--; + } + new = PyString_FromStringAndSize(ip, len); + if (new == NULL) { + return PyString_FromString(""); + } + return new; + } + + PyObject * + PyArray_Scalar(char *data, int n) + { + PyTypeObject *type = &PyStringArrType_Type; + PyObject *obj; + void *destptr; + int itemsize = n; + obj = type->tp_alloc(type, itemsize); + if (obj == NULL) { + return NULL; + } + destptr = PyString_AS_STRING(obj); + ((PyBytesObject *)obj)->ob_shash = -1; + memcpy(destptr, data, itemsize); + return obj; + } + """, more_init = ''' + PyStringArrType_Type.tp_alloc = NULL; + PyStringArrType_Type.tp_free = NULL; + + PyStringArrType_Type.tp_repr = stringtype_repr; + PyStringArrType_Type.tp_str = stringtype_str; + PyStringArrType_Type.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE; + PyStringArrType_Type.tp_itemsize = sizeof(char); + PyStringArrType_Type.tp_base = &PyString_Type; + PyStringArrType_Type.tp_hash = PyString_Type.tp_hash; + if (PyType_Ready(&PyStringArrType_Type) < 0) INITERROR; + ''') + + a = module.newsubstr('abc') + assert module.has_nb_add('a') is False + assert module.has_nb_add(a) is False + assert type(a).__name__ == 'string_' + assert a == 'abc' + assert 3 == module.get_len(a) + b = module.newsubstr('') + assert 0 == module.get_len(b) + +class TestBytes(BaseApiTest): + def test_bytes_resize(self, space): + py_str = new_empty_str(space, 10) + ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw') + py_str.c_ob_sval[0] = 'a' + py_str.c_ob_sval[1] = 'b' + py_str.c_ob_sval[2] = 'c' + ar[0] = rffi.cast(PyObject, py_str) + _PyString_Resize(space, ar, 3) + py_str = rffi.cast(PyBytesObject, ar[0]) + assert py_str.c_ob_size == 3 + assert py_str.c_ob_sval[1] == 'b' + assert py_str.c_ob_sval[3] == '\x00' + # the same for growing + ar[0] = rffi.cast(PyObject, py_str) + _PyString_Resize(space, ar, 10) + py_str = rffi.cast(PyBytesObject, ar[0]) + assert py_str.c_ob_size == 10 + assert py_str.c_ob_sval[1] == 'b' + assert py_str.c_ob_sval[10] == '\x00' + decref(space, ar[0]) + lltype.free(ar, flavor='raw') + + def test_string_buffer(self, space): + py_str = new_empty_str(space, 10) + c_buf = py_str.c_ob_type.c_tp_as_buffer + assert c_buf + py_obj = rffi.cast(PyObject, py_str) + assert generic_cpy_call(space, c_buf.c_bf_getsegcount, + py_obj, lltype.nullptr(Py_ssize_tP.TO)) == 1 + ref = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') + assert generic_cpy_call(space, c_buf.c_bf_getsegcount, + py_obj, ref) == 1 + assert ref[0] == 10 + lltype.free(ref, flavor='raw') + ref = lltype.malloc(rffi.VOIDPP.TO, 1, flavor='raw') + assert generic_cpy_call(space, c_buf.c_bf_getreadbuffer, + py_obj, 0, ref) == 10 + lltype.free(ref, flavor='raw') + decref(space, py_obj) + + def test_Concat(self, space): + ref = make_ref(space, space.wrap('abc')) + ptr = lltype.malloc(PyObjectP.TO, 1, flavor='raw') + ptr[0] = ref + prev_refcnt = ref.c_ob_refcnt + PyString_Concat(space, ptr, space.wrap('def')) + assert ref.c_ob_refcnt == prev_refcnt - 1 + assert space.str_w(from_ref(space, ptr[0])) == 'abcdef' + with pytest.raises(OperationError): + PyString_Concat(space, ptr, space.w_None) + assert not ptr[0] + ptr[0] = lltype.nullptr(PyObject.TO) + PyString_Concat(space, ptr, space.wrap('def')) # should not crash + lltype.free(ptr, flavor='raw') + + def test_ConcatAndDel(self, space): + ref1 = make_ref(space, space.wrap('abc')) + ref2 = make_ref(space, space.wrap('def')) + ptr = lltype.malloc(PyObjectP.TO, 1, flavor='raw') + ptr[0] = ref1 + prev_refcnf = ref2.c_ob_refcnt + PyString_ConcatAndDel(space, ptr, ref2) + assert space.str_w(from_ref(space, ptr[0])) == 'abcdef' + assert ref2.c_ob_refcnt == prev_refcnf - 1 + decref(space, ptr[0]) + ptr[0] = lltype.nullptr(PyObject.TO) + ref2 = make_ref(space, space.wrap('foo')) + prev_refcnf = ref2.c_ob_refcnt + PyString_ConcatAndDel(space, ptr, ref2) # should not crash + assert ref2.c_ob_refcnt == prev_refcnf - 1 + lltype.free(ptr, flavor='raw') + + def test_format(self, space): + assert "1 2" == space.unwrap( + PyString_Format(space, space.wrap('%s %d'), space.wrap((1, 2)))) + + def test_asbuffer(self, space): + bufp = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw') + lenp = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') + + w_text = space.wrap("text") + ref = make_ref(space, w_text) + prev_refcnt = ref.c_ob_refcnt + assert PyObject_AsCharBuffer(space, ref, bufp, lenp) == 0 + assert ref.c_ob_refcnt == prev_refcnt + assert lenp[0] == 4 + assert rffi.charp2str(bufp[0]) == 'text' + lltype.free(bufp, flavor='raw') + lltype.free(lenp, flavor='raw') + decref(space, ref) + + def test_intern(self, space): + buf = rffi.str2charp("test") + w_s1 = PyString_InternFromString(space, buf) + w_s2 = PyString_InternFromString(space, buf) + rffi.free_charp(buf) + assert w_s1 is w_s2 + + def test_AsEncodedObject(self, space): + ptr = space.wrap('abc') + + errors = rffi.str2charp("strict") + + encoding = rffi.str2charp("hex") + res = PyString_AsEncodedObject(space, ptr, encoding, errors) + assert space.unwrap(res) == "616263" + + res = PyString_AsEncodedObject(space, + ptr, encoding, lltype.nullptr(rffi.CCHARP.TO)) + assert space.unwrap(res) == "616263" + rffi.free_charp(encoding) + + encoding = rffi.str2charp("unknown_encoding") + with raises_w(space, LookupError): + PyString_AsEncodedObject(space, ptr, encoding, errors) + rffi.free_charp(encoding) + + rffi.free_charp(errors) + + NULL = lltype.nullptr(rffi.CCHARP.TO) + res = PyString_AsEncodedObject(space, ptr, NULL, NULL) + assert space.unwrap(res) == "abc" + with raises_w(space, TypeError): + PyString_AsEncodedObject(space, space.wrap(2), NULL, NULL) + + def test_AsDecodedObject(self, space): + w_str = space.wrap('caf\xe9') + encoding = rffi.str2charp("latin-1") + w_res = PyString_AsDecodedObject(space, w_str, encoding, None) + rffi.free_charp(encoding) + assert space.unwrap(w_res) == u"caf\xe9" + + def test_eq(self, space): + assert 1 == _PyString_Eq( + space, space.wrap("hello"), space.wrap("hello")) + assert 0 == _PyString_Eq( + space, space.wrap("hello"), space.wrap("world")) + + def test_join(self, space): + w_sep = space.wrap('<sep>') + w_seq = space.wrap(['a', 'b']) + w_joined = _PyString_Join(space, w_sep, w_seq) + assert space.unwrap(w_joined) == 'a<sep>b' diff --git a/pypy/module/cpyext/test/test_capsule.py b/pypy/module/cpyext/test/test_capsule.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_capsule.py @@ -0,0 +1,29 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + +class AppTestCapsule(AppTestCpythonExtensionBase): + def test_capsule_import(self): + module = self.import_extension('foo', [ + ("set_ptr", "METH_O", + """ + PyObject *capsule, *module; + void *ptr = PyLong_AsVoidPtr(args); + if (PyErr_Occurred()) return NULL; + capsule = PyCapsule_New(ptr, "foo._ptr", NULL); + if (PyErr_Occurred()) return NULL; + module = PyImport_ImportModule("foo"); + PyModule_AddObject(module, "_ptr", capsule); + Py_DECREF(module); + if (PyErr_Occurred()) return NULL; + Py_RETURN_NONE; + """), + ("get_ptr", "METH_NOARGS", + """ + void *ptr = PyCapsule_Import("foo._ptr", 0); + if (PyErr_Occurred()) return NULL; + return PyLong_FromVoidPtr(ptr); + """)]) + module.set_ptr(1234) + assert 'capsule object "foo._ptr" at ' in str(module._ptr) + import gc; gc.collect() + assert module.get_ptr() == 1234 + del module._ptr diff --git a/pypy/module/cpyext/test/test_cell.py b/pypy/module/cpyext/test/test_cell.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_cell.py @@ -0,0 +1,20 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + + +class AppTestCell(AppTestCpythonExtensionBase): + def test_cell_type(self): + module = self.import_extension('foo', [ + ("cell_type", "METH_O", + """ + PyDict_SetItemString(args, "cell", (PyObject*)&PyCell_Type); + Py_RETURN_NONE; + """)]) + d = {} + module.cell_type(d) + def f(o): + def g(): + return o + return g + + cell_type = type(f(0).func_closure[0]) + assert d["cell"] is cell_type diff --git a/pypy/module/cpyext/test/test_classobject.py b/pypy/module/cpyext/test/test_classobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_classobject.py @@ -0,0 +1,93 @@ +from pypy.interpreter.function import Function +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.classobject import ( + PyClass_Check, PyClass_New, PyInstance_Check, PyInstance_New, + PyInstance_NewRaw, _PyInstance_Lookup) +from pypy.module.cpyext.object import PyObject_GetAttr +from pypy.module.cpyext.pyobject import get_w_obj_and_decref + +class TestClassObject(BaseApiTest): + def test_newinstance(self, space): + w_class = space.appexec([], """(): + class C: + x = None + def __init__(self, *args, **kwargs): + self.x = 1 + self.args = args + self.__dict__.update(kwargs) + return C + """) + + assert PyClass_Check(space, w_class) + + w_instance = PyInstance_NewRaw(space, w_class, None) + assert PyInstance_Check(space, w_instance) + assert space.getattr(w_instance, space.wrap('x')) is space.w_None + + w_instance = PyInstance_NewRaw(space, w_class, space.wrap(dict(a=3))) + assert space.getattr(w_instance, space.wrap('x')) is space.w_None + assert space.unwrap(space.getattr(w_instance, space.wrap('a'))) == 3 + + w_instance = PyInstance_New(space, w_class, + space.wrap((3,)), space.wrap(dict(y=2))) + assert space.unwrap(space.getattr(w_instance, space.wrap('x'))) == 1 + assert space.unwrap(space.getattr(w_instance, space.wrap('y'))) == 2 + assert space.unwrap(space.getattr(w_instance, space.wrap('args'))) == (3,) + + def test_lookup(self, space): + w_instance = space.appexec([], """(): + class C: + def __init__(self): + self.x = None + def f(self): pass + return C() + """) + + assert PyInstance_Check(space, w_instance) + py_obj = PyObject_GetAttr(space, w_instance, space.wrap('x')) + assert get_w_obj_and_decref(space, py_obj) is space.w_None + assert _PyInstance_Lookup(space, w_instance, space.wrap('x')) is space.w_None + assert _PyInstance_Lookup(space, w_instance, space.wrap('y')) is None + + # getattr returns a bound method + py_obj = PyObject_GetAttr(space, w_instance, space.wrap('f')) + assert not isinstance(get_w_obj_and_decref(space, py_obj), Function) + # _PyInstance_Lookup returns the raw descriptor + assert isinstance( + _PyInstance_Lookup(space, w_instance, space.wrap('f')), Function) + + def test_pyclass_new(self, space): + w_bases = space.newtuple([]) + w_dict = space.newdict() + w_name = space.wrap("C") + w_class = PyClass_New(space, w_bases, w_dict, w_name) + assert not space.isinstance_w(w_class, space.w_type) + w_instance = space.call_function(w_class) + assert PyInstance_Check(space, w_instance) + assert space.is_true(space.call_method(space.builtin, "isinstance", + w_instance, w_class)) + +class AppTestStringObject(AppTestCpythonExtensionBase): + def test_class_type(self): + module = self.import_extension('foo', [ + ("get_classtype", "METH_NOARGS", + """ + Py_INCREF(&PyClass_Type); + return (PyObject*)&PyClass_Type; + """)]) + class C: + pass + assert module.get_classtype() is type(C) + + def test_pyclass_new_no_bases(self): + module = self.import_extension('foo', [ + ("new_foo", "METH_O", + """ + return PyClass_New(NULL, PyDict_New(), args); + """)]) + FooClass = module.new_foo("FooClass") + class Cls1: + pass + assert type(FooClass) is type(Cls1) + assert FooClass.__bases__ == Cls1.__bases__ diff --git a/pypy/module/cpyext/test/test_codecs.py b/pypy/module/cpyext/test/test_codecs.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_codecs.py @@ -0,0 +1,15 @@ +# encoding: iso-8859-15 +from pypy.module.cpyext.test.test_api import BaseApiTest +from rpython.rtyper.lltypesystem import rffi +from pypy.module.cpyext.codecs import ( + PyCodec_IncrementalEncoder, PyCodec_IncrementalDecoder) + +class TestCodecs(BaseApiTest): + def test_incremental(self, space): + utf8 = rffi.str2charp('utf-8') + w_encoder = PyCodec_IncrementalEncoder(space, utf8, None) + w_encoded = space.call_method(w_encoder, 'encode', space.wrap(u'späm')) + w_decoder = PyCodec_IncrementalDecoder(space, utf8, None) + w_decoded = space.call_method(w_decoder, 'decode', w_encoded) + assert space.unicode_w(w_decoded) == u'späm' + rffi.free_charp(utf8) diff --git a/pypy/module/cpyext/test/test_complexobject.py b/pypy/module/cpyext/test/test_complexobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_complexobject.py @@ -0,0 +1,64 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w +from pypy.module.cpyext.complexobject import ( + PyComplex_FromDoubles, PyComplex_RealAsDouble, PyComplex_ImagAsDouble) + +class TestComplexObject(BaseApiTest): + def test_complexobject(self, space): + w_value = PyComplex_FromDoubles(space, 1.2, 3.4) + assert space.unwrap(w_value) == 1.2+3.4j + assert PyComplex_RealAsDouble(space, w_value) == 1.2 + assert PyComplex_ImagAsDouble(space, w_value) == 3.4 + + assert PyComplex_RealAsDouble(space, space.wrap(42)) == 42 + assert PyComplex_RealAsDouble(space, space.wrap(1.5)) == 1.5 + assert PyComplex_ImagAsDouble(space, space.wrap(1.5)) == 0.0 + + # cpython accepts anything for PyComplex_ImagAsDouble + assert PyComplex_ImagAsDouble(space, space.w_None) == 0.0 + with raises_w(space, TypeError): + PyComplex_RealAsDouble(space, space.w_None) + +class AppTestCComplex(AppTestCpythonExtensionBase): + def test_AsCComplex(self): + module = self.import_extension('foo', [ + ("as_tuple", "METH_O", + """ + Py_complex c = PyComplex_AsCComplex(args); + if (PyErr_Occurred()) return NULL; + return Py_BuildValue("dd", c.real, c.imag); + """)]) + assert module.as_tuple(12-34j) == (12, -34) + assert module.as_tuple(-3.14) == (-3.14, 0.0) + raises(TypeError, module.as_tuple, "12") + + def test_FromCComplex(self): + module = self.import_extension('foo', [ + ("test", "METH_NOARGS", + """ + Py_complex c = {1.2, 3.4}; + return PyComplex_FromCComplex(c); + """)]) + assert module.test() == 1.2 + 3.4j + + def test_PyComplex_to_WComplex(self): + module = self.import_extension('foo', [ + ("test", "METH_NOARGS", + """ + Py_complex c = {1.2, 3.4}; + PyObject *obj = PyObject_Malloc(sizeof(PyComplexObject)); + obj = PyObject_Init(obj, &PyComplex_Type); + assert(obj != NULL); + ((PyComplexObject *)obj)->cval = c; + return obj; + """)]) + assert module.test() == 1.2 + 3.4j + + def test_WComplex_to_PyComplex(self): + module = self.import_extension('foo', [ + ("test", "METH_O", + """ + Py_complex c = ((PyComplexObject *)args)->cval; + return Py_BuildValue("dd", c.real, c.imag); + """)]) + assert module.test(1.2 + 3.4j) == (1.2, 3.4) diff --git a/pypy/module/cpyext/test/test_cparser.py b/pypy/module/cpyext/test/test_cparser.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_cparser.py @@ -0,0 +1,260 @@ +from rpython.flowspace.model import const +from rpython.flowspace.objspace import build_flow +from rpython.translator.simplify import simplify_graph +from rpython.rtyper.lltypesystem import rffi, lltype +from pypy.module.cpyext.cparser import parse_source, CTypeSpace + +def test_configure(): + decl = """ + typedef ssize_t Py_ssize_t; + + typedef struct { + Py_ssize_t ob_refcnt; + Py_ssize_t ob_pypy_link; + double ob_fval; + } TestFloatObject; + """ + cts = parse_source(decl) + TestFloatObject = cts.definitions['TestFloatObject'] + assert isinstance(TestFloatObject, lltype.Struct) + assert TestFloatObject.c_ob_refcnt == rffi.SSIZE_T + assert TestFloatObject.c_ob_pypy_link == rffi.SSIZE_T + assert TestFloatObject.c_ob_fval == rffi.DOUBLE + +def test_simple(): + decl = "typedef ssize_t Py_ssize_t;" + cts = parse_source(decl) + assert cts.definitions == {'Py_ssize_t': rffi.SSIZE_T} + +def test_macro(): + decl = """ + typedef ssize_t Py_ssize_t; + + #define PyObject_HEAD \ + Py_ssize_t ob_refcnt; \ + Py_ssize_t ob_pypy_link; \ + + typedef struct { + PyObject_HEAD + double ob_fval; + } PyFloatObject; + """ + cts = parse_source(decl) + assert 'PyFloatObject' in cts.definitions + assert 'PyObject_HEAD' in cts.macros + +def test_include(): + cdef1 = """ + typedef ssize_t Py_ssize_t; + + #define PyObject_HEAD \ + Py_ssize_t ob_refcnt; \ + Py_ssize_t ob_pypy_link; \ + + typedef struct { + char *name; + } Type; + """ + cdef2 = """ + typedef struct { + PyObject_HEAD + Py_ssize_t ob_foo; + Type *type; + } Object; + """ + cts1 = parse_source(cdef1) + Type = cts1.definitions['Type'] + assert isinstance(Type, lltype.Struct) + cts2 = parse_source(cdef2, includes=[cts1]) + assert 'Type' not in cts2.definitions + Object = cts2.definitions['Object'] + assert Object.c_type.TO is Type + +def test_multiple_sources(): + cdef1 = """ + typedef ssize_t Py_ssize_t; + + #define PyObject_HEAD \ + Py_ssize_t ob_refcnt; \ + Py_ssize_t ob_pypy_link; \ + + typedef struct { + char *name; + } Type; + """ + cdef2 = """ + typedef struct { + PyObject_HEAD + Py_ssize_t ob_foo; + Type *type; + } Object; + """ + cts = CTypeSpace() + cts.parse_source(cdef1) + Type = cts.definitions['Type'] + assert isinstance(Type, lltype.Struct) + assert 'Object' not in cts.definitions + cts.parse_source(cdef2) + Object = cts.definitions['Object'] + assert Object.c_type.TO is Type + +def test_incomplete(): + cdef = """ + typedef ssize_t Py_ssize_t; + + typedef struct { + Py_ssize_t ob_refcnt; + Py_ssize_t ob_pypy_link; + struct _typeobject *ob_type; + } Object; + + typedef struct { + void *buf; + Object *obj; + } Buffer; + + """ + cts = parse_source(cdef) + Object = cts.gettype('Object') + assert isinstance(Object, lltype.Struct) + +def test_recursive(): + cdef = """ + typedef ssize_t Py_ssize_t; + + typedef struct { + Py_ssize_t ob_refcnt; + Py_ssize_t ob_pypy_link; + struct _typeobject *ob_type; + } Object; + + typedef struct { + void *buf; + Object *obj; + } Buffer; + + typedef struct _typeobject { + Object *obj; + } Type; + """ + cts = parse_source(cdef) + Object = cts.definitions['Object'] + assert isinstance(Object, lltype.Struct) + hash(Object) + +def test_nested_struct(): + cdef = """ + typedef struct { + int x; + } foo; + typedef struct { + foo y; + } bar; + """ + cts = parse_source(cdef) + bar = cts.gettype('bar') + assert isinstance(bar, lltype.Struct) + hash(bar) # bar is hashable + +def test_const(): + cdef = """ + typedef struct { + const char * const foo; + } bar; + """ + cts = parse_source(cdef) + assert cts.definitions['bar'].c_foo == rffi.CONST_CCHARP != rffi.CCHARP + +def test_gettype(): + decl = """ + typedef ssize_t Py_ssize_t; + + #define PyObject_HEAD \ + Py_ssize_t ob_refcnt; \ + Py_ssize_t ob_pypy_link; \ + + typedef struct { + PyObject_HEAD + double ob_fval; + } TestFloatObject; + """ + cts = parse_source(decl) + assert cts.gettype('Py_ssize_t') == rffi.SSIZE_T + assert cts.gettype('TestFloatObject *').TO.c_ob_refcnt == rffi.SSIZE_T + assert cts.cast('Py_ssize_t', 42) == rffi.cast(rffi.SSIZE_T, 42) + +def test_parse_funcdecl(): + decl = """ + typedef ssize_t Py_ssize_t; + + #define PyObject_HEAD \ + Py_ssize_t ob_refcnt; \ + Py_ssize_t ob_pypy_link; \ + + typedef struct { + PyObject_HEAD + double ob_fval; + } TestFloatObject; + + typedef TestFloatObject* (*func_t)(int, int); + """ + cts = parse_source(decl) + func_decl = cts.parse_func("func_t * some_func(TestFloatObject*)") + assert func_decl.name == 'some_func' + assert func_decl.get_llresult(cts) == cts.gettype('func_t*') + assert func_decl.get_llargs(cts) == [cts.gettype('TestFloatObject *')] + +def test_write_func(): + from ..api import ApiFunction + from rpython.translator.c.database import LowLevelDatabase + db = LowLevelDatabase() + cdef = """ + typedef ssize_t Py_ssize_t; + """ + cts = parse_source(cdef) + cdecl = "Py_ssize_t * some_func(Py_ssize_t*)" + decl = cts.parse_func(cdecl) + api_function = ApiFunction( + decl.get_llargs(cts), decl.get_llresult(cts), lambda space, x: None, + cdecl=decl) + assert (api_function.get_api_decl('some_func', db) == + "PyAPI_FUNC(Py_ssize_t *) some_func(Py_ssize_t * arg0);") + + +def test_wchar_t(): + cdef = """ + typedef struct { wchar_t* x; } test; + """ + cts = parse_source(cdef, headers=['stddef.h']) + obj = lltype.malloc(cts.gettype('test'), flavor='raw') + obj.c_x = cts.cast('wchar_t*', 0) + obj.c_x = lltype.nullptr(rffi.CWCHARP.TO) + lltype.free(obj, flavor='raw') + + +def test_translate_cast(): + cdef = "typedef ssize_t Py_ssize_t;" + cts = parse_source(cdef) + + def f(): + return cts.cast('Py_ssize_t*', 0) + graph = build_flow(f) + simplify_graph(graph) + assert len(graph.startblock.operations) == 1 + op = graph.startblock.operations[0] + assert op.args[0] == const(rffi.cast) + assert op.args[1].value is cts.gettype('Py_ssize_t*') + +def test_translate_gettype(): + cdef = "typedef ssize_t Py_ssize_t;" + cts = parse_source(cdef) + + def f(): + return cts.gettype('Py_ssize_t*') + graph = build_flow(f) + simplify_graph(graph) + # Check that the result is constant-folded + assert graph.startblock.operations == [] + [link] = graph.startblock.exits + assert link.target is graph.returnblock + assert link.args[0] == const(rffi.CArrayPtr(rffi.SSIZE_T)) diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_datetime.py @@ -0,0 +1,354 @@ +import pytest + +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase +from pypy.module.cpyext.test.test_api import BaseApiTest +from pypy.module.cpyext.cdatetime import * +from pypy.module.cpyext.cdatetime import ( + _PyDateTime_Import, _PyDateTime_FromDateAndTime, _PyDate_FromDate, + _PyTime_FromTime, _PyDelta_FromDelta) +import datetime + +class TestDatetime(BaseApiTest): + def test_date(self, space): + date_api = _PyDateTime_Import(space) + w_date = _PyDate_FromDate(space, 2010, 06, 03, date_api.c_DateType) + assert space.unwrap(space.str(w_date)) == '2010-06-03' + + assert PyDate_Check(space, w_date) + assert PyDate_CheckExact(space, w_date) + + assert PyDateTime_GET_YEAR(space, w_date) == 2010 + assert PyDateTime_GET_MONTH(space, w_date) == 6 + assert PyDateTime_GET_DAY(space, w_date) == 3 + + def test_time(self, space): + date_api = _PyDateTime_Import(space) + w_time = _PyTime_FromTime( + space, 23, 15, 40, 123456, space.w_None, date_api.c_TimeType) + assert space.unwrap(space.str(w_time)) == '23:15:40.123456' + + assert PyTime_Check(space, w_time) + assert PyTime_CheckExact(space, w_time) + + assert PyDateTime_TIME_GET_HOUR(space, w_time) == 23 + assert PyDateTime_TIME_GET_MINUTE(space, w_time) == 15 + assert PyDateTime_TIME_GET_SECOND(space, w_time) == 40 + assert PyDateTime_TIME_GET_MICROSECOND(space, w_time) == 123456 + + def test_datetime(self, space): + date_api = _PyDateTime_Import(space) + w_date = _PyDateTime_FromDateAndTime( + space, 2010, 06, 03, 23, 15, 40, 123456, space.w_None, + date_api.c_DateTimeType) + assert space.unwrap(space.str(w_date)) == '2010-06-03 23:15:40.123456' + + assert PyDateTime_Check(space, w_date) + assert PyDateTime_CheckExact(space, w_date) + assert PyDate_Check(space, w_date) + assert not PyDate_CheckExact(space, w_date) + + assert PyDateTime_GET_YEAR(space, w_date) == 2010 + assert PyDateTime_GET_MONTH(space, w_date) == 6 + assert PyDateTime_GET_DAY(space, w_date) == 3 + assert PyDateTime_DATE_GET_HOUR(space, w_date) == 23 + assert PyDateTime_DATE_GET_MINUTE(space, w_date) == 15 + assert PyDateTime_DATE_GET_SECOND(space, w_date) == 40 + assert PyDateTime_DATE_GET_MICROSECOND(space, w_date) == 123456 + + def test_delta(self, space): + date_api = _PyDateTime_Import(space) + w_delta = space.appexec( + [space.wrap(3), space.wrap(15)], """(days, seconds): + from datetime import timedelta + return timedelta(days, seconds) + """) + assert PyDelta_Check(space, w_delta) + assert PyDelta_CheckExact(space, w_delta) + + w_delta = _PyDelta_FromDelta(space, 10, 20, 30, True, date_api.c_DeltaType) + assert PyDelta_Check(space, w_delta) + assert PyDelta_CheckExact(space, w_delta) + + assert PyDateTime_DELTA_GET_DAYS(space, w_delta) == 10 + assert PyDateTime_DELTA_GET_SECONDS(space, w_delta) == 20 + assert PyDateTime_DELTA_GET_MICROSECONDS(space, w_delta) == 30 + + def test_fromtimestamp(self, space): + w_args = space.wrap((0,)) + w_date = PyDate_FromTimestamp(space, w_args) + date = datetime.date.fromtimestamp(0) + assert space.unwrap(space.str(w_date)) == str(date) + _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit