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&#228;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&#228;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

Reply via email to