[pypy-commit] pypy py3.5: hg merge default

arigo pypy.commits at gmail.com
Tue Jan 17 12:03:53 EST 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r89636:6d7e97a686d7
Date: 2017-01-17 18:03 +0100
http://bitbucket.org/pypy/pypy/changeset/6d7e97a686d7/

Log:	hg merge default

diff too long, truncating to 2000 out of 3163 lines

diff --git a/lib-python/2.7/sqlite3/test/regression.py b/lib-python/2.7/sqlite3/test/regression.py
--- a/lib-python/2.7/sqlite3/test/regression.py
+++ b/lib-python/2.7/sqlite3/test/regression.py
@@ -351,10 +351,7 @@
         self.assertRaises(ValueError, cur.execute, " \0select 2")
         self.assertRaises(ValueError, cur.execute, "select 2\0")
 
-    @test_support.impl_detail(pypy=False)
     def CheckCommitCursorReset(self):
-        # This test is for logic added in 2.7.13 which PyPy doesn't
-        # implement.  See http://bugs.python.org/issue29006
         """
         Connection.commit() did reset cursors, which made sqlite3
         to return rows multiple times when fetched from cursors
diff --git a/lib-python/2.7/test/test_capi.py b/lib-python/2.7/test/test_capi.py
--- a/lib-python/2.7/test/test_capi.py
+++ b/lib-python/2.7/test/test_capi.py
@@ -26,7 +26,6 @@
 skips = []
 if support.check_impl_detail(pypy=True):
     skips += [
-            'test_broken_memoryview',
             'test_buildvalue_N',
             'test_capsule',
             'test_lazy_hash_inheritance',
diff --git a/lib-python/2.7/test/test_multiprocessing.py b/lib-python/2.7/test/test_multiprocessing.py
--- a/lib-python/2.7/test/test_multiprocessing.py
+++ b/lib-python/2.7/test/test_multiprocessing.py
@@ -1969,9 +1969,10 @@
         if not gc.isenabled():
             gc.enable()
             self.addCleanup(gc.disable)
-        #thresholds = gc.get_threshold()
-        #self.addCleanup(gc.set_threshold, *thresholds)
-        #gc.set_threshold(10)
+        if test_support.check_impl_detail(cpython=True):
+            thresholds = gc.get_threshold()
+            self.addCleanup(gc.set_threshold, *thresholds)
+            gc.set_threshold(10)
 
         # perform numerous block allocations, with cyclic references to make
         # sure objects are collected asynchronously by the gc
diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -231,6 +231,7 @@
         self.__statements_counter = 0
         self.__rawstatements = set()
         self._statement_cache = _StatementCache(self, cached_statements)
+        self.__statements_already_committed = []
 
         self.__func_cache = {}
         self.__aggregates = {}
@@ -374,6 +375,14 @@
                 if cursor is not None:
                     cursor._reset = True
 
+    def _reset_already_committed_statements(self):
+        lst = self.__statements_already_committed
+        self.__statements_already_committed = []
+        for weakref in lst:
+            statement = weakref()
+            if statement is not None:
+                statement._reset()
+
     @_check_thread_wrap
     @_check_closed_wrap
     def __call__(self, sql):
@@ -429,15 +438,18 @@
         if not self._in_transaction:
             return
 
-        # The following line is a KNOWN DIFFERENCE with CPython 2.7.13.
-        # More precisely, the corresponding line was removed in the
-        # version 2.7.13 of CPython, but this is causing troubles for
-        # PyPy (and potentially for CPython too):
-        #
-        #     http://bugs.python.org/issue29006
-        #
-        # So for now, we keep this line.
-        self.__do_all_statements(Statement._reset, False)
+        # PyPy fix for non-refcounting semantics: since 2.7.13 (and in
+        # <= 2.6.x), the statements are not automatically reset upon
+        # commit.  However, if this is followed by some specific SQL
+        # operations like "drop table", these open statements come in
+        # the way and cause the "drop table" to fail.  On CPython the
+        # problem is much less important because typically all the old
+        # statements are freed already by reference counting.  So here,
+        # we copy all the still-alive statements to another list which
+        # is usually ignored, except if we get SQLITE_LOCKED
+        # afterwards---at which point we reset all statements in this
+        # list.
+        self.__statements_already_committed = self.__statements[:]
 
         statement_star = _ffi.new('sqlite3_stmt **')
         ret = _lib.sqlite3_prepare_v2(self._db, b"COMMIT", -1,
@@ -866,8 +878,18 @@
                 self.__statement._set_params(params)
 
                 # Actually execute the SQL statement
+
                 ret = _lib.sqlite3_step(self.__statement._statement)
 
+                # PyPy: if we get SQLITE_LOCKED, it's probably because
+                # one of the cursors created previously is still alive
+                # and not reset and the operation we're trying to do
+                # makes Sqlite unhappy about that.  In that case, we
+                # automatically reset all old cursors and try again.
+                if ret == _lib.SQLITE_LOCKED:
+                    self.__connection._reset_already_committed_statements()
+                    ret = _lib.sqlite3_step(self.__statement._statement)
+
                 if ret == _lib.SQLITE_ROW:
                     if multiple:
                         raise ProgrammingError("executemany() can only execute DML statements.")
diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c
--- a/lib_pypy/_testcapimodule.c
+++ b/lib_pypy/_testcapimodule.c
@@ -2856,7 +2856,7 @@
     m = PyModule_Create(&_testcapimodule);
     if (m == NULL)
         return NULL;
-
+    
     Py_TYPE(&_HashInheritanceTester_Type)=&PyType_Type;
 
     Py_TYPE(&test_structmembersType)=&PyType_Type;
diff --git a/lib_pypy/audioop.py b/lib_pypy/audioop.py
--- a/lib_pypy/audioop.py
+++ b/lib_pypy/audioop.py
@@ -374,7 +374,7 @@
 
     sample_count = _sample_count(cp, size)
 
-    rv = ffi.new("unsigned char[]", len(cp) * 2)
+    rv = ffi.new("char[]", len(cp) * 2)
     lib.tostereo(rv, ffi.from_buffer(cp), len(cp), size, fac1, fac2)
     return ffi.buffer(rv)[:]
 
@@ -385,7 +385,7 @@
     if len(cp1) != len(cp2):
         raise error("Lengths should be the same")
 
-    rv = ffi.new("unsigned char[]", len(cp1))
+    rv = ffi.new("char[]", len(cp1))
     lib.add(rv, ffi.from_buffer(cp1), ffi.from_buffer(cp2), len(cp1), size)
     return ffi.buffer(rv)[:]
 
@@ -488,7 +488,7 @@
     ceiling = (q + 1) * outrate
     nbytes = ceiling * bytes_per_frame
 
-    rv = ffi.new("unsigned char[]", nbytes)
+    rv = ffi.new("char[]", nbytes)
     trim_index = lib.ratecv(rv, cp, frame_count, size,
                             nchannels, inrate, outrate,
                             state_d, prev_i, cur_i,
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -496,11 +496,6 @@
   the rest is kept.  If you return an unexpected string from
   ``__hex__()`` you get an exception (or a crash before CPython 2.7.13).
 
-* The ``sqlite`` module was updated on 2.7.13 to no longer reset all
-  cursors when there is a commit.  This causes troubles for PyPy (and
-  potentially for CPython too), and so for now we didn't port this change:
-  see http://bugs.python.org/issue29006.
-
 .. _`is ignored in PyPy`: http://bugs.python.org/issue14621
 .. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html
 .. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/
diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst
--- a/pypy/doc/faq.rst
+++ b/pypy/doc/faq.rst
@@ -26,16 +26,16 @@
 
 Almost!
 
-The mostly likely stumbling block for any given project is support for
+The most likely stumbling block for any given project is support for
 :ref:`extension modules <extension-modules>`.  PyPy supports a continually growing
 number of extension modules, but so far mostly only those found in the
 standard library.
 
 The language features (including builtin types and functions) are very
-complete and well tested, so if your project does not use many
+refined and well tested, so if your project doesn't use many
 extension modules there is a good chance that it will work with PyPy.
 
-We list the differences we know about in :doc:`cpython differences <cpython_differences>`.
+We list the known differences in :doc:`cpython differences <cpython_differences>`.
 
 
 Module xyz does not work with PyPy: ImportError
@@ -76,10 +76,10 @@
 
 You cannot import *any* extension module in a `sandboxed PyPy`_,
 sorry.  Even the built-in modules available are very limited.
-Sandboxing in PyPy is a good proof of concept, really safe IMHO, but
-it is only a proof of concept.  It seriously requires someone working
-on it.  Before this occurs, it can only be used it for "pure Python"
-examples: programs that import mostly nothing (or only pure Python
+Sandboxing in PyPy is a good proof of concept, and is without a doubt
+safe IMHO, however it is only a proof of concept.  It currently requires 
+some work from a motivated developer. However, until then it can only be used for "pure Python"
+example: programs that import mostly nothing (or only pure Python
 modules, recursively).
 
 .. _`sandboxed PyPy`: sandbox.html
diff --git a/pypy/doc/getting-started-dev.rst b/pypy/doc/getting-started-dev.rst
--- a/pypy/doc/getting-started-dev.rst
+++ b/pypy/doc/getting-started-dev.rst
@@ -336,6 +336,6 @@
    that fixes some bugs and is translatable.
 
 *  :source:`pypy/objspace/std` contains the :ref:`Standard object space <standard-object-space>`.  The main file
-   is :source:`pypy/objspace/std/objspace.py`.  For each type, the files ``xxxtype.py`` and
-   ``xxxobject.py`` contain respectively the definition of the type and its
-   (default) implementation.
+   is :source:`pypy/objspace/std/objspace.py`.  For each type, the file
+   ``xxxobject.py`` contains the implementation for objects of type ``xxx``,
+   as a first approximation.  (Some types have multiple implementations.)
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -110,3 +110,21 @@
 
 Do not recreate the object in PyMemoryView_FromBuffer, rather pass it to
 the returned PyMemoryViewObject, to take ownership of it. Fixes a ref leak.
+
+.. branch: issue2464
+
+Give (almost?) all GetSetProperties a valid __objclass__.
+
+.. branch: TreeStain/fixed-typo-line-29-mostly-to-most-1484469416419
+.. branch: TreeStain/main-lines-changed-in-l77-l83-made-para-1484471558033
+
+.. branch: missing-tp_new
+
+Improve mixing app-level classes in c-extensions, especially if the app-level
+class has a ``tp_new`` or ``tp_dealloc``. The issue is that c-extensions expect
+all the method slots to be filled with a function pointer, where app-level will
+search up the mro for an appropriate function at runtime. With this branch we
+now fill many more slots in the c-extenion type objects.
+Also fix for c-extension type that calls ``tp_hash`` during initialization
+(str, unicode types), and fix instantiating c-extension types from built-in
+classes by enforcing an order of instaniation.
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -143,7 +143,11 @@
             pass
         def fget(self, space, w_self):
             assert self is prop
-        prop = typedef.GetSetProperty(fget, use_closure=True)
+        # NB. this GetSetProperty is not copied when creating the
+        # W_TypeObject because of 'cls'.  Without it, a duplicate of the
+        # GetSetProperty is taken and it is given the w_objclass that is
+        # the W_TypeObject
+        prop = typedef.GetSetProperty(fget, use_closure=True, cls=W_SomeType)
         W_SomeType.typedef = typedef.TypeDef(
             'some_type',
             x=prop)
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -277,12 +277,15 @@
         self.use_closure = use_closure
 
     def copy_for_type(self, w_objclass):
-        new = instantiate(GetSetProperty)
-        new._init(self.fget, self.fset, self.fdel, self.doc, self.reqcls,
-                  None, self.use_closure)
-        new.name = self.name
-        new.w_objclass = w_objclass
-        return new
+        if self.objclass_getter is None:
+            new = instantiate(GetSetProperty)
+            new._init(self.fget, self.fset, self.fdel, self.doc, self.reqcls,
+                      None, self.use_closure)
+            new.name = self.name
+            new.w_objclass = w_objclass
+            return new
+        else:
+            return self
 
     @unwrap_spec(w_cls = WrappedDefault(None))
     def descr_property_get(self, space, w_obj, w_cls=None):
@@ -513,7 +516,7 @@
     return lifeline.get_any_weakref(space)
 
 dict_descr = GetSetProperty(descr_get_dict, descr_set_dict, descr_del_dict,
-                            doc="dictionary for instance variables")
+                            doc="dictionary for instance variables (if defined)")
 dict_descr.name = '__dict__'
 
 
@@ -548,7 +551,7 @@
     return space.newtuple([w_docstring])
 
 weakref_descr = GetSetProperty(descr_get_weakref,
-                               doc="list of weak references to the object")
+                    doc="list of weak references to the object (if defined)")
 weakref_descr.name = '__weakref__'
 
 def make_weakref_descr(cls):
diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py
--- a/pypy/module/_pypyjson/interp_decoder.py
+++ b/pypy/module/_pypyjson/interp_decoder.py
@@ -109,7 +109,7 @@
         else:
             raise DecoderError(
                 "No JSON object could be decoded: unexpected '%s' at" % ch,
-                self.pos)
+                i)
 
     def decode_null(self, i):
         if (self.ll_chars[i]   == 'u' and
@@ -247,7 +247,7 @@
                 raise DecoderError("Unterminated array starting at", start)
             else:
                 raise DecoderError("Unexpected '%s' when decoding array" % ch,
-                                   self.pos)
+                                   i-1)
 
     def decode_object(self, i):
         start = i
@@ -287,7 +287,7 @@
                 raise DecoderError("Unterminated object starting at", start)
             else:
                 raise DecoderError("Unexpected '%s' when decoding object" % ch,
-                                   self.pos)
+                                   i-1)
 
 
     def decode_string(self, i):
@@ -312,18 +312,17 @@
                 self.last_type = TYPE_STRING
                 self.pos = i
                 return self.space.wrap(content_unicode)
-            elif ch == '\\':
-                content_so_far = self.getslice(start, i-1)
+            elif ch == '\\' or ch < '\x20':
                 self.pos = i-1
-                return self.decode_string_escaped(start, content_so_far)
-            elif ch < '\x20':
-                raise DecoderError("Invalid control character at", self.pos-1)
+                return self.decode_string_escaped(start)
 
 
-    def decode_string_escaped(self, start, content_so_far):
-        builder = StringBuilder(len(content_so_far)*2) # just an estimate
-        builder.append(content_so_far)
+    def decode_string_escaped(self, start):
         i = self.pos
+        builder = StringBuilder((i - start) * 2) # just an estimate
+        assert start >= 0
+        assert i >= 0
+        builder.append_slice(self.s, start, i)
         while True:
             ch = self.ll_chars[i]
             i += 1
@@ -336,27 +335,31 @@
                 return self.space.wrap(content_unicode)
             elif ch == '\\':
                 i = self.decode_escape_sequence(i, builder)
-            elif ch == '\0':
-                raise DecoderError("Unterminated string starting at", start)
+            elif ch < '\x20':
+                if ch == '\0':
+                    raise DecoderError("Unterminated string starting at",
+                                       start - 1)
+                else:
+                    raise DecoderError("Invalid control character at", i-1)
             else:
-                builder.append_multiple_char(ch, 1) # we should implement append_char
+                builder.append(ch)
 
     def decode_escape_sequence(self, i, builder):
         ch = self.ll_chars[i]
         i += 1
-        put = builder.append_multiple_char
-        if ch == '\\':  put('\\', 1)
-        elif ch == '"': put('"' , 1)
-        elif ch == '/': put('/' , 1)
-        elif ch == 'b': put('\b', 1)
-        elif ch == 'f': put('\f', 1)
-        elif ch == 'n': put('\n', 1)
-        elif ch == 'r': put('\r', 1)
-        elif ch == 't': put('\t', 1)
+        put = builder.append
+        if ch == '\\':  put('\\')
+        elif ch == '"': put('"' )
+        elif ch == '/': put('/' )
+        elif ch == 'b': put('\b')
+        elif ch == 'f': put('\f')
+        elif ch == 'n': put('\n')
+        elif ch == 'r': put('\r')
+        elif ch == 't': put('\t')
         elif ch == 'u':
             return self.decode_escape_sequence_unicode(i, builder)
         else:
-            raise DecoderError("Invalid \\escape: %s" % ch, self.pos-1)
+            raise DecoderError("Invalid \\escape: %s" % ch, i-1)
         return i
 
     def decode_escape_sequence_unicode(self, i, builder):
diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py
--- a/pypy/module/_pypyjson/test/test__pypyjson.py
+++ b/pypy/module/_pypyjson/test/test__pypyjson.py
@@ -215,6 +215,23 @@
         assert check("\\\"\b\f\n\r\t") == '\\\\\\"\\b\\f\\n\\r\\t'
         assert check("\x07") == "\\u0007"
 
+    def test_error_position(self):
+        import _pypyjson
+        test_cases = [
+            ('[,', "No JSON object could be decoded: unexpected ',' at", 1),
+            ('{"spam":[}', "No JSON object could be decoded: unexpected '}' at", 9),
+            ('[42:', "Unexpected ':' when decoding array", 3),
+            ('[42 "spam"', "Unexpected '\"' when decoding array", 4),
+            ('[42,]', "No JSON object could be decoded: unexpected ']' at", 4),
+            ('{"spam":[42}', "Unexpected '}' when decoding array", 11),
+            ('["]', 'Unterminated string starting at', 1),
+            ('["spam":', "Unexpected ':' when decoding array", 7),
+            ('[{]', "No JSON object could be decoded: unexpected ']' at", 2),
+        ]
+        for inputtext, errmsg, errpos in test_cases:
+            exc = raises(ValueError, _pypyjson.loads, inputtext)
+            assert exc.value.args == (errmsg, inputtext, errpos)
+
     def test_keys_reuse(self):
         import _pypyjson
         s = '[{"a_key": 1, "b_\xe9": 2}, {"a_key": 3, "b_\xe9": 4}]'
diff --git a/pypy/module/_ssl/test/allsans.pem b/pypy/module/_ssl/test/allsans.pem
deleted file mode 100644
--- a/pypy/module/_ssl/test/allsans.pem
+++ /dev/null
@@ -1,37 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOoy7/QOtTjQ0niE
-6uDcTwtkC0R2Tvy1AjVnXohCntZfdzbTGDoYTgXSOLsP8A697jUiJ8VCePGH50xG
-Z4DKnAF3a9O3a9nr2pLXb0iY3XOMv+YEBii7CfI+3oxFYgCl0sMgHzDD2ZTVYAsm
-DWgLUVsE2gHEccRwrM2tPf2EgR+FAgMBAAECgYEA3qyfyYVSeTrTYxO93x6ZaVMu
-A2IZp9zSxMQL9bKiI2GRj+cV2ebSCGbg2btFnD6qBor7FWsmYz+8g6FNN/9sY4az
-61rMqMtQvLBe+7L8w70FeTze4qQ4Y1oQri0qD6tBWhDVlpnbI5Py9bkZKD67yVUk
-elcEA/5x4PrYXkuqsAECQQD80NjT0mDvaY0JOOaQFSEpMv6QiUA8GGX8Xli7IoKb
-tAolPG8rQBa+qSpcWfDMTrWw/aWHuMEEQoP/bVDH9W4FAkEA7SYQbBAKnojZ5A3G
-kOHdV7aeivRQxQk/JN8Fb8oKB9Csvpv/BsuGxPKXHdhFa6CBTTsNRtHQw/szPo4l
-xMIjgQJAPoMxqibR+0EBM6+TKzteSL6oPXsCnBl4Vk/J5vPgkbmR7KUl4+7j8N8J
-b2554TrxKEN/w7CGYZRE6UrRd7ATNQJAWD7Yz41sli+wfPdPU2xo1BHljyl4wMk/
-EPZYbI/PCbdyAH/F935WyQTIjNeEhZc1Zkq6FwdOWw8ns3hrv3rKgQJAHXv1BqUa
-czGPIFxX2TNoqtcl6/En4vrxVB1wzsfzkkDAg98kBl7qsF+S3qujSzKikjeaVbI2
-/CyWR2P3yLtOmA==
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIDcjCCAtugAwIBAgIJAN5dc9TOWjB7MA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV
-BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u
-IFNvZnR3YXJlIEZvdW5kYXRpb24xEDAOBgNVBAMMB2FsbHNhbnMwHhcNMTYwODA1
-MTAyMTExWhcNMjYwODAzMTAyMTExWjBdMQswCQYDVQQGEwJYWTEXMBUGA1UEBwwO
-Q2FzdGxlIEFudGhyYXgxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0
-aW9uMRAwDgYDVQQDDAdhbGxzYW5zMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
-gQDqMu/0DrU40NJ4hOrg3E8LZAtEdk78tQI1Z16IQp7WX3c20xg6GE4F0ji7D/AO
-ve41IifFQnjxh+dMRmeAypwBd2vTt2vZ69qS129ImN1zjL/mBAYouwnyPt6MRWIA
-pdLDIB8ww9mU1WALJg1oC1FbBNoBxHHEcKzNrT39hIEfhQIDAQABo4IBODCCATQw
-ggEwBgNVHREEggEnMIIBI4IHYWxsc2Fuc6AeBgMqAwSgFwwVc29tZSBvdGhlciBp
-ZGVudGlmaWVyoDUGBisGAQUCAqArMCmgEBsOS0VSQkVST1MuUkVBTE2hFTAToAMC
-AQGhDDAKGwh1c2VybmFtZYEQdXNlckBleGFtcGxlLm9yZ4IPd3d3LmV4YW1wbGUu
-b3JnpGcwZTELMAkGA1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMw
-IQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UEAwwPZGly
-bmFtZSBleGFtcGxlhhdodHRwczovL3d3dy5weXRob24ub3JnL4cEfwAAAYcQAAAA
-AAAAAAAAAAAAAAAAAYgEKgMEBTANBgkqhkiG9w0BAQsFAAOBgQAy16h+F+nOmeiT
-VWR0fc8F/j6FcadbLseAUaogcC15OGxCl4UYpLV88HBkABOoGCpP155qwWTwOrdG
-iYPGJSusf1OnJEbvzFejZf6u078bPd9/ZL4VWLjv+FPGkjd+N+/OaqMvgj8Lu99f
-3Y/C4S7YbHxxwff6C6l2Xli+q6gnuQ==
------END CERTIFICATE-----
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -39,6 +39,7 @@
 from rpython.rlib import rawrefcount
 from rpython.rlib import rthread
 from rpython.rlib.debug import fatalerror_notb
+from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 
 DEBUG_WRAPPER = True
 
@@ -903,6 +904,7 @@
         retval = fatal_value
         boxed_args = ()
         tb = None
+        state = space.fromcache(State)
         try:
             if not we_are_translated() and DEBUG_WRAPPER:
                 print >>sys.stderr, callable,
@@ -921,7 +923,6 @@
                 boxed_args += (arg_conv, )
             if pygilstate_ensure:
                 boxed_args += (args[-1], )
-            state = space.fromcache(State)
             try:
                 result = callable(space, *boxed_args)
                 if not we_are_translated() and DEBUG_WRAPPER:
@@ -967,6 +968,7 @@
         except Exception as e:
             unexpected_exception(pname, e, tb)
             _restore_gil_state(pygilstate_release, gilstate, gil_release, _gil_auto, tid)
+            state.check_and_raise_exception(always=True)
             return fatal_value
 
         assert lltype.typeOf(retval) == restype
@@ -1127,6 +1129,34 @@
     setup_init_functions(eci, prefix)
     return modulename.new(ext='')
 
+def attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i):
+    # Start at i but make sure all the base classes are already attached
+    from pypy.module.cpyext.pyobject import get_typedescr, make_ref
+    if i in attached_objs:
+        return
+    py_obj = static_pyobjs[i]
+    w_obj = static_objs_w[i]
+    w_base = None
+    # w_obj can be NotImplemented, which is not a W_TypeObject
+    if isinstance(w_obj, W_TypeObject):
+        bases_w = w_obj.bases_w
+        if bases_w:
+            w_base = find_best_base(bases_w)
+        if w_base:
+            try:
+                j = static_objs_w.index(w_base)
+            except ValueError:
+                j = -1
+            if j >=0 and j not in attached_objs:
+                attach_recursively(space, static_pyobjs, static_objs_w,
+                                                 attached_objs, j)
+    w_type = space.type(w_obj)
+    typedescr = get_typedescr(w_type.layout.typedef)
+    py_obj.c_ob_type = rffi.cast(PyTypeObjectPtr,
+                                 make_ref(space, w_type))
+    typedescr.attach(space, py_obj, w_obj)
+    attached_objs.append(i)
+
 
 class StaticObjectBuilder(object):
     def __init__(self):
@@ -1147,7 +1177,6 @@
 
     def attach_all(self, space):
         # this is RPython, called once in pypy-c when it imports cpyext
-        from pypy.module.cpyext.pyobject import get_typedescr, make_ref
         from pypy.module.cpyext.typeobject import finish_type_1, finish_type_2
         from pypy.module.cpyext.pyobject import track_reference
         #
@@ -1157,14 +1186,9 @@
             track_reference(space, static_pyobjs[i], static_objs_w[i])
         #
         self.cpyext_type_init = []
+        attached_objs = []
         for i in range(len(static_objs_w)):
-            py_obj = static_pyobjs[i]
-            w_obj = static_objs_w[i]
-            w_type = space.type(w_obj)
-            typedescr = get_typedescr(w_type.layout.typedef)
-            py_obj.c_ob_type = rffi.cast(PyTypeObjectPtr,
-                                         make_ref(space, w_type))
-            typedescr.attach(space, py_obj, w_obj)
+            attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i)
         cpyext_type_init = self.cpyext_type_init
         self.cpyext_type_init = None
         for pto, w_type in cpyext_type_init:
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -80,14 +80,18 @@
     """
     py_str = rffi.cast(PyBytesObject, py_obj)
     s = space.str_w(w_obj)
-    if py_str.c_ob_size  < len(s):
+    len_s = len(s)
+    if py_str.c_ob_size  < len_s:
         raise oefmt(space.w_ValueError,
             "bytes_attach called on object with ob_size %d but trying to store %d",
-            py_str.c_ob_size, len(s))
+            py_str.c_ob_size, len_s)
     with rffi.scoped_nonmovingbuffer(s) as s_ptr:
-        rffi.c_memcpy(py_str.c_ob_sval, s_ptr, len(s))
-    py_str.c_ob_sval[len(s)] = '\0'
-    py_str.c_ob_shash = space.hash_w(w_obj)
+        rffi.c_memcpy(py_str.c_ob_sval, s_ptr, len_s)
+    py_str.c_ob_sval[len_s] = '\0'
+    # if py_obj has a tp_hash, this will try to call it, but the objects are
+    # not fully linked yet
+    #py_str.c_ob_shash = space.hash_w(w_obj)
+    py_str.c_ob_shash = space.hash_w(space.newbytes(s))
     py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL
 
 def bytes_realize(space, py_obj):
@@ -100,7 +104,9 @@
     w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type))
     w_obj = space.allocate_instance(W_BytesObject, w_type)
     w_obj.__init__(s)
-    py_str.c_ob_shash = space.hash_w(w_obj)
+    # if py_obj has a tp_hash, this will try to call it but the object is
+    # not realized yet
+    py_str.c_ob_shash = space.hash_w(space.newbytes(s))
     py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL
     track_reference(space, py_obj, w_obj)
     return w_obj
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -90,11 +90,10 @@
     return 0
 
 @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
-def PyList_GET_SIZE(space, w_list):
+def PyList_GET_SIZE(space, w_obj):
     """Macro form of PyList_Size() without error checking.
     """
-    assert isinstance(w_list, W_ListObject)
-    return w_list.length()
+    return space.len_w(w_obj)
 
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -18,6 +18,7 @@
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.module.cpyext.memoryobject import fill_Py_buffer
 from pypy.module.cpyext.state import State
+from pypy.module.cpyext import userslot
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.argument import Arguments
 from rpython.rlib.buffer import Buffer
@@ -506,6 +507,10 @@
 
 @specialize.memo()
 def get_slot_tp_function(space, typedef, name):
+    """Return a description of the slot C function to use for the built-in
+    type for 'typedef'.  The 'name' is the slot name.  This is a memo
+    function that, after translation, returns one of a built-in finite set.
+    """
     key = (typedef, name)
     try:
         return SLOTS[key]
@@ -543,6 +548,19 @@
                 return space.call_function(slot_fn, w_self)
             handled = True
 
+    for tp_name, attr in [('tp_hash', '__hash__'),
+                         ]:
+        if name == tp_name:
+            slot_fn = w_type.getdictvalue(space, attr)
+            if slot_fn is None:
+                return
+            @slot_function([PyObject], lltype.Signed, error=-1)
+            @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name))
+            def slot_func(space, w_obj):
+                return space.int_w(space.call_function(slot_fn, w_obj))
+            handled = True
+
+
     # binary functions
     for tp_name, attr in [('tp_as_number.c_nb_add', '__add__'),
                           ('tp_as_number.c_nb_subtract', '__sub__'),
@@ -748,7 +766,7 @@
         else:
             assert False
 
-    function = globals().get(FUNCTION, None)
+    function = getattr(userslot, FUNCTION or '!missing', None)
     assert FLAGS == 0 or FLAGS == PyWrapperFlag_KEYWORDS
     if FLAGS:
         if wrapper is Ellipsis:
@@ -811,7 +829,7 @@
 static slotdef slotdefs[] = {
         SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
                "x.__len__() <==> len(x)"),
-        SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
+        SQSLOT("__add__", sq_concat, slot_sq_concat, wrap_binaryfunc,
           "x.__add__(y) <==> x+y"),
         SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
           "x.__mul__(n) <==> x*n"),
@@ -928,9 +946,7 @@
                "x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS),
         TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
                wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"),
-        TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""),
-        TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
-        TPSLOT("__getattr__", tp_getattr, NULL, NULL, ""),
+        TPSLOT("__getattr__", tp_getattro, slot_tp_getattr, NULL, ""),
         TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
                "x.__setattr__('name', value) <==> x.name = value"),
         TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""),
diff --git a/pypy/module/cpyext/test/foo3.c b/pypy/module/cpyext/test/foo3.c
--- a/pypy/module/cpyext/test/foo3.c
+++ b/pypy/module/cpyext/test/foo3.c
@@ -8,6 +8,24 @@
     return newType;
 }
 
+PyObject * typ = NULL;
+PyObject* datetimetype_tp_new(PyTypeObject* metatype, PyObject* args, PyObject* kwds)
+{
+    PyObject* newType;
+    if (typ == NULL)
+    {
+        PyErr_Format(PyExc_TypeError, "could not import datetime.datetime");
+        return NULL;
+    }
+    newType = ((PyTypeObject*)typ)->tp_new(metatype, args, kwds);
+    return newType;
+}
+
+void datetimetype_tp_dealloc(PyObject* self)
+{
+    return ((PyTypeObject*)typ)->tp_dealloc(self);
+}
+
 #define BASEFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
 
 PyTypeObject footype = {
@@ -58,6 +76,54 @@
     /*tp_weaklist*/         0
 };
 
+PyTypeObject datetimetype = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    /*tp_name*/             "foo3.datetimetype",
+    /*tp_basicsize*/        sizeof(PyTypeObject),
+    /*tp_itemsize*/         0,
+    /*tp_dealloc*/          datetimetype_tp_dealloc,
+    /*tp_print*/            0,
+    /*tp_getattr*/          0,
+    /*tp_setattr*/          0,
+    /*tp_compare*/          0,
+    /*tp_repr*/             0,
+    /*tp_as_number*/        0,
+    /*tp_as_sequence*/      0,
+    /*tp_as_mapping*/       0,
+    /*tp_hash*/             0,
+    /*tp_call*/             0,
+    /*tp_str*/              0,
+    /*tp_getattro*/         0,
+    /*tp_setattro*/         0,
+    /*tp_as_buffer*/        0,
+    /*tp_flags*/            BASEFLAGS,
+    /*tp_doc*/              0,
+    /*tp_traverse*/         0,
+    /*tp_clear*/            0,
+    /*tp_richcompare*/      0,
+    /*tp_weaklistoffset*/   0,
+    /*tp_iter*/             0,
+    /*tp_iternext*/         0,
+    /*tp_methods*/          0,
+    /*tp_members*/          0,
+    /*tp_getset*/           0,
+    /*tp_base*/             0,  //  set to &PyType_Type in module init function (why can it not be done here?)
+    /*tp_dict*/             0,
+    /*tp_descr_get*/        0,
+    /*tp_descr_set*/        0,
+    /*tp_dictoffset*/       0,
+    /*tp_init*/             0,
+    /*tp_alloc*/            0,
+    /*tp_new*/              datetimetype_tp_new,
+    /*tp_free*/             0,
+    /*tp_is_gc*/            0,
+    /*tp_bases*/            0,
+    /*tp_mro*/              0,
+    /*tp_cache*/            0,
+    /*tp_subclasses*/       0,
+    /*tp_weaklist*/         0
+};
+
 static PyMethodDef sbkMethods[] = {{NULL, NULL, 0, NULL}};
 
 static struct PyModuleDef moduledef = {
@@ -81,6 +147,16 @@
 PyInit_foo3(void)
 {
     PyObject *mod, *d;
+    PyObject *module = NULL;
+    module = PyImport_ImportModule("datetime");
+    typ = PyObject_GetAttrString(module, "datetime");
+    Py_DECREF(module);
+    if (!PyType_Check(typ)) {
+        PyErr_Format(PyExc_TypeError, "datetime.datetime is not a type object");
+        return NULL;
+    }
+    datetimetype.tp_base = (PyTypeObject*)typ;
+    PyType_Ready(&datetimetype);
     footype.tp_base = &PyType_Type;
     PyType_Ready(&footype);
     mod = PyModule_Create(&moduledef);
@@ -91,6 +167,8 @@
         return NULL;
     if (PyDict_SetItemString(d, "footype", (PyObject *)&footype) < 0)
         return NULL;
+    if (PyDict_SetItemString(d, "datetimetype", (PyObject *)&datetimetype) < 0)
+        return NULL;
     Py_INCREF(&footype);
     return mod;
 }
diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py
--- a/pypy/module/cpyext/test/test_dictobject.py
+++ b/pypy/module/cpyext/test/test_dictobject.py
@@ -1,83 +1,82 @@
 import py
 from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
 from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP, PyTypeObjectPtr
 from pypy.module.cpyext.pyobject import make_ref, from_ref
 from pypy.interpreter.error import OperationError
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+from pypy.module.cpyext.dictproxyobject import *
+from pypy.module.cpyext.dictobject import *
+from pypy.module.cpyext.pyobject import decref
 
 class TestDictObject(BaseApiTest):
-    def test_dict(self, space, api):
-        d = api.PyDict_New()
+    def test_dict(self, space):
+        d = PyDict_New(space)
         assert space.eq_w(d, space.newdict())
 
-        assert space.eq_w(api.PyDict_GetItem(space.wrap({"a": 72}),
+        assert space.eq_w(PyDict_GetItem(space, space.wrap({"a": 72}),
                                              space.wrap("a")),
                           space.wrap(72))
 
-        assert api.PyDict_SetItem(d, space.wrap("c"), space.wrap(42)) >= 0
+        PyDict_SetItem(space, d, space.wrap("c"), space.wrap(42))
         assert space.eq_w(space.getitem(d, space.wrap("c")),
                           space.wrap(42))
 
         space.setitem(d, space.wrap("name"), space.wrap(3))
-        assert space.eq_w(api.PyDict_GetItem(d, space.wrap("name")),
+        assert space.eq_w(PyDict_GetItem(space, d, space.wrap("name")),
                           space.wrap(3))
 
         space.delitem(d, space.wrap("name"))
-        assert not api.PyDict_GetItem(d, space.wrap("name"))
-        assert not api.PyErr_Occurred()
+        assert not PyDict_GetItem(space, d, space.wrap("name"))
 
         buf = rffi.str2charp("name")
-        assert not api.PyDict_GetItemString(d, buf)
+        assert not PyDict_GetItemString(space, d, buf)
         rffi.free_charp(buf)
-        assert not api.PyErr_Occurred()
 
-        assert api.PyDict_Contains(d, space.wrap("c"))
-        assert not api.PyDict_Contains(d, space.wrap("z"))
+        assert PyDict_Contains(space, d, space.wrap("c"))
+        assert not PyDict_Contains(space, d, space.wrap("z"))
 
-        assert api.PyDict_DelItem(d, space.wrap("c")) == 0
-        assert api.PyDict_DelItem(d, space.wrap("name")) < 0
-        assert api.PyErr_Occurred() is space.w_KeyError
-        api.PyErr_Clear()
-        assert api.PyDict_Size(d) == 0
+        PyDict_DelItem(space, d, space.wrap("c"))
+        with raises_w(space, KeyError):
+            PyDict_DelItem(space, d, space.wrap("name"))
+        assert PyDict_Size(space, d) == 0
 
         space.setitem(d, space.wrap("some_key"), space.wrap(3))
         buf = rffi.str2charp("some_key")
-        assert api.PyDict_DelItemString(d, buf) == 0
-        assert api.PyDict_Size(d) == 0
-        assert api.PyDict_DelItemString(d, buf) < 0
-        assert api.PyErr_Occurred() is space.w_KeyError
-        api.PyErr_Clear()
+        PyDict_DelItemString(space, d, buf)
+        assert PyDict_Size(space, d) == 0
+        with raises_w(space, KeyError):
+            PyDict_DelItemString(space, d, buf)
         rffi.free_charp(buf)
 
         d = space.wrap({'a': 'b'})
-        api.PyDict_Clear(d)
-        assert api.PyDict_Size(d) == 0
+        PyDict_Clear(space, d)
+        assert PyDict_Size(space, d) == 0
 
-    def test_check(self, space, api):
-        d = api.PyDict_New()
-        assert api.PyDict_Check(d)
-        assert api.PyDict_CheckExact(d)
+    def test_check(self, space):
+        d = PyDict_New(space, )
+        assert PyDict_Check(space, d)
+        assert PyDict_CheckExact(space, d)
         sub = space.appexec([], """():
             class D(dict):
                 pass
             return D""")
         d = space.call_function(sub)
-        assert api.PyDict_Check(d)
-        assert not api.PyDict_CheckExact(d)
+        assert PyDict_Check(space, d)
+        assert not PyDict_CheckExact(space, d)
         i = space.wrap(2)
-        assert not api.PyDict_Check(i)
-        assert not api.PyDict_CheckExact(i)
+        assert not PyDict_Check(space, i)
+        assert not PyDict_CheckExact(space, i)
 
-    def test_keys(self, space, api):
+    def test_keys(self, space):
         w_d = space.newdict()
         space.setitem(w_d, space.wrap("a"), space.wrap("b"))
 
-        assert space.eq_w(api.PyDict_Keys(w_d), space.wrap(["a"]))
-        assert space.eq_w(api.PyDict_Values(w_d), space.wrap(["b"]))
-        assert space.eq_w(api.PyDict_Items(w_d), space.wrap([("a", "b")]))
+        assert space.eq_w(PyDict_Keys(space, w_d), space.wrap(["a"]))
+        assert space.eq_w(PyDict_Values(space, w_d), space.wrap(["b"]))
+        assert space.eq_w(PyDict_Items(space, w_d), space.wrap([("a", "b")]))
 
-    def test_merge(self, space, api):
+    def test_merge(self, space):
         w_d = space.newdict()
         space.setitem(w_d, space.wrap("a"), space.wrap("b"))
 
@@ -86,35 +85,34 @@
         space.setitem(w_d2, space.wrap("c"), space.wrap("d"))
         space.setitem(w_d2, space.wrap("e"), space.wrap("f"))
 
-        api.PyDict_Merge(w_d, w_d2, 0)
+        PyDict_Merge(space, w_d, w_d2, 0)
         assert space.unwrap(w_d) == dict(a='b', c='d', e='f')
-        api.PyDict_Merge(w_d, w_d2, 1)
+        PyDict_Merge(space, w_d, w_d2, 1)
         assert space.unwrap(w_d) == dict(a='c', c='d', e='f')
 
-    def test_update(self, space, api):
+    def test_update(self, space):
         w_d = space.newdict()
         space.setitem(w_d, space.wrap("a"), space.wrap("b"))
 
-        w_d2 = api.PyDict_Copy(w_d)
+        w_d2 = PyDict_Copy(space, w_d)
         assert not space.is_w(w_d2, w_d)
         space.setitem(w_d, space.wrap("c"), space.wrap("d"))
         space.setitem(w_d2, space.wrap("e"), space.wrap("f"))
 
-        api.PyDict_Update(w_d, w_d2)
+        PyDict_Update(space, w_d, w_d2)
         assert space.unwrap(w_d) == dict(a='b', c='d', e='f')
 
-    def test_update_doesnt_accept_list_of_tuples(self, space, api):
+    def test_update_doesnt_accept_list_of_tuples(self, space):
         w_d = space.newdict()
         space.setitem(w_d, space.wrap("a"), space.wrap("b"))
 
         w_d2 = space.wrap([("c", "d"), ("e", "f")])
 
-        api.PyDict_Update(w_d, w_d2)
-        assert api.PyErr_Occurred() is space.w_AttributeError
-        api.PyErr_Clear()
+        with raises_w(space, AttributeError):
+            PyDict_Update(space, w_d, w_d2)
         assert space.unwrap(w_d) == dict(a='b') # unchanged
 
-    def test_iter(self, space, api):
+    def test_iter(self, space):
         w_dict = space.sys.getdict(space)
         py_dict = make_ref(space, w_dict)
 
@@ -125,7 +123,7 @@
 
         try:
             w_copy = space.newdict()
-            while api.PyDict_Next(w_dict, ppos, pkey, pvalue):
+            while PyDict_Next(space, w_dict, ppos, pkey, pvalue):
                 w_key = from_ref(space, pkey[0])
                 w_value = from_ref(space, pvalue[0])
                 space.setitem(w_copy, w_key, w_value)
@@ -134,12 +132,12 @@
             lltype.free(pkey, flavor='raw')
             lltype.free(pvalue, flavor='raw')
 
-        api.Py_DecRef(py_dict) # release borrowed references
+        decref(space, py_dict) # release borrowed references
 
         assert space.eq_w(space.len(w_copy), space.len(w_dict))
         assert space.eq_w(w_copy, w_dict)
 
-    def test_iterkeys(self, space, api):
+    def test_iterkeys(self, space):
         w_dict = space.sys.getdict(space)
         py_dict = make_ref(space, w_dict)
 
@@ -151,11 +149,11 @@
         values_w = []
         try:
             ppos[0] = 0
-            while api.PyDict_Next(w_dict, ppos, pkey, None):
+            while PyDict_Next(space, w_dict, ppos, pkey, None):
                 w_key = from_ref(space, pkey[0])
                 keys_w.append(w_key)
             ppos[0] = 0
-            while api.PyDict_Next(w_dict, ppos, None, pvalue):
+            while PyDict_Next(space, w_dict, ppos, None, pvalue):
                 w_value = from_ref(space, pvalue[0])
                 values_w.append(w_value)
         finally:
@@ -163,7 +161,7 @@
             lltype.free(pkey, flavor='raw')
             lltype.free(pvalue, flavor='raw')
 
-        api.Py_DecRef(py_dict) # release borrowed references
+        decref(space, py_dict) # release borrowed references
 
         assert space.eq_w(space.newlist(keys_w),
                           space.call_function(
@@ -174,18 +172,18 @@
                              space.w_list,
                              space.call_method(w_dict, "values")))
 
-    def test_dictproxy(self, space, api):
+    def test_dictproxy(self, space):
         w_dict = space.sys.get('modules')
-        w_proxy = api.PyDictProxy_New(w_dict)
+        w_proxy = PyDictProxy_New(space, w_dict)
         assert space.contains_w(w_proxy, space.wrap('sys'))
         raises(OperationError, space.setitem,
                w_proxy, space.wrap('sys'), space.w_None)
         raises(OperationError, space.delitem,
                w_proxy, space.wrap('sys'))
         raises(OperationError, space.call_method, w_proxy, 'clear')
-        assert api.PyDictProxy_Check(w_proxy)
+        assert PyDictProxy_Check(space, w_proxy)
 
-    def test_typedict1(self, space, api):
+    def test_typedict1(self, space):
         py_type = make_ref(space, space.w_int)
         py_dict = rffi.cast(PyTypeObjectPtr, py_type).c_tp_dict
         ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
@@ -195,7 +193,7 @@
         pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
         try:
             w_copy = space.newdict()
-            while api.PyDict_Next(py_dict, ppos, pkey, pvalue):
+            while PyDict_Next(space, py_dict, ppos, pkey, pvalue):
                 w_key = from_ref(space, pkey[0])
                 w_value = from_ref(space, pvalue[0])
                 space.setitem(w_copy, w_key, w_value)
@@ -203,7 +201,7 @@
             lltype.free(ppos, flavor='raw')
             lltype.free(pkey, flavor='raw')
             lltype.free(pvalue, flavor='raw')
-        api.Py_DecRef(py_type) # release borrowed references
+        decref(space, py_type) # release borrowed references
         # do something with w_copy ?
 
 class AppTestDictObject(AppTestCpythonExtensionBase):
diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -1,17 +1,26 @@
+import sys
+import os
+import pytest
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
-from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
+from pypy.module.cpyext.object import PyObject_Size, PyObject_GetItem
+from pypy.module.cpyext.pythonrun import Py_AtExit
 from pypy.module.cpyext.eval import (
-    Py_single_input, Py_file_input, Py_eval_input, PyCompilerFlags)
-from pypy.module.cpyext.api import (c_fopen, c_fclose, c_fileno, 
-                Py_ssize_tP, is_valid_fd)
+    Py_single_input, Py_file_input, Py_eval_input, PyCompilerFlags,
+    PyEval_CallObjectWithKeywords, PyObject_CallObject, PyEval_EvalCode,
+    PyRun_SimpleString, PyRun_String, PyRun_StringFlags, PyRun_File,
+    PyEval_GetBuiltins, PyEval_GetLocals, PyEval_GetGlobals,
+    _PyEval_SliceIndex)
+from pypy.module.cpyext.api import (
+    c_fopen, c_fclose, c_fileno, Py_ssize_tP, is_valid_fd)
 from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.astcompiler import consts
 from rpython.tool.udir import udir
-import sys, os
 
 class TestEval(BaseApiTest):
-    def test_eval(self, space, api):
+    def test_eval(self, space):
         w_l, w_f = space.fixedview(space.appexec([], """():
         l = []
         def f(arg1, arg2):
@@ -22,7 +31,7 @@
         """))
 
         w_t = space.newtuple([space.wrap(1), space.wrap(2)])
-        w_res = api.PyEval_CallObjectWithKeywords(w_f, w_t, None)
+        w_res = PyEval_CallObjectWithKeywords(space, w_f, w_t, None)
         assert space.int_w(w_res) == 2
         assert space.len_w(w_l) == 2
         w_f = space.appexec([], """():
@@ -35,10 +44,10 @@
         w_t = space.newtuple([space.w_None, space.w_None])
         w_d = space.newdict()
         space.setitem(w_d, space.wrap("xyz"), space.wrap(3))
-        w_res = api.PyEval_CallObjectWithKeywords(w_f, w_t, w_d)
+        w_res = PyEval_CallObjectWithKeywords(space, w_f, w_t, w_d)
         assert space.int_w(w_res) == 21
 
-    def test_call_object(self, space, api):
+    def test_call_object(self, space):
         w_l, w_f = space.fixedview(space.appexec([], """():
         l = []
         def f(arg1, arg2):
@@ -49,7 +58,7 @@
         """))
 
         w_t = space.newtuple([space.wrap(1), space.wrap(2)])
-        w_res = api.PyObject_CallObject(w_f, w_t)
+        w_res = PyObject_CallObject(space, w_f, w_t)
         assert space.int_w(w_res) == 2
         assert space.len_w(w_l) == 2
 
@@ -61,11 +70,11 @@
             """)
 
         w_t = space.newtuple([space.wrap(1), space.wrap(2)])
-        w_res = api.PyObject_CallObject(w_f, w_t)
+        w_res = PyObject_CallObject(space, w_f, w_t)
 
         assert space.int_w(w_res) == 10
 
-    def test_evalcode(self, space, api):
+    def test_evalcode(self, space):
         w_f = space.appexec([], """():
             def f(*args):
                 assert isinstance(args, tuple)
@@ -77,84 +86,78 @@
         w_globals = space.newdict()
         w_locals = space.newdict()
         space.setitem(w_locals, space.wrap("args"), w_t)
-        w_res = api.PyEval_EvalCode(w_f.code, w_globals, w_locals)
+        w_res = PyEval_EvalCode(space, w_f.code, w_globals, w_locals)
 
         assert space.int_w(w_res) == 10
 
-    def test_run_simple_string(self, space, api):
+    def test_run_simple_string(self, space):
         def run(code):
             buf = rffi.str2charp(code)
             try:
-                return api.PyRun_SimpleString(buf)
+                return PyRun_SimpleString(space, buf)
             finally:
                 rffi.free_charp(buf)
 
-        assert 0 == run("42 * 43")
+        assert run("42 * 43") == 0  # no error
+        with pytest.raises(OperationError):
+            run("4..3 * 43")
 
-        assert -1 == run("4..3 * 43")
-
-        assert api.PyErr_Occurred()
-        api.PyErr_Clear()
-
-    def test_run_string(self, space, api):
+    def test_run_string(self, space):
         def run(code, start, w_globals, w_locals):
             buf = rffi.str2charp(code)
             try:
-                return api.PyRun_String(buf, start, w_globals, w_locals)
+                return PyRun_String(space, buf, start, w_globals, w_locals)
             finally:
                 rffi.free_charp(buf)
 
         w_globals = space.newdict()
         assert 42 * 43 == space.unwrap(
             run("42 * 43", Py_eval_input, w_globals, w_globals))
-        assert api.PyObject_Size(w_globals) == 0
+        assert PyObject_Size(space, w_globals) == 0
 
         assert run("a = 42 * 43", Py_single_input,
                    w_globals, w_globals) == space.w_None
         assert 42 * 43 == space.unwrap(
-            api.PyObject_GetItem(w_globals, space.wrap("a")))
+            PyObject_GetItem(space, w_globals, space.wrap("a")))
 
-    def test_run_string_flags(self, space, api):
+    def test_run_string_flags(self, space):
         flags = lltype.malloc(PyCompilerFlags, flavor='raw')
         flags.c_cf_flags = rffi.cast(rffi.INT, consts.PyCF_SOURCE_IS_UTF8)
         w_globals = space.newdict()
         buf = rffi.str2charp("a = 'caf\xc3\xa9'")
         try:
-            api.PyRun_StringFlags(buf, Py_single_input,
-                                  w_globals, w_globals, flags)
+            PyRun_StringFlags(space, buf, Py_single_input, w_globals,
+                              w_globals, flags)
         finally:
             rffi.free_charp(buf)
         w_a = space.getitem(w_globals, space.wrap("a"))
         assert space.unwrap(w_a) == u'caf\xe9'
         lltype.free(flags, flavor='raw')
 
-    def test_run_file(self, space, api):
+    def test_run_file(self, space):
         filepath = udir / "cpyext_test_runfile.py"
         filepath.write("raise ZeroDivisionError")
         fp = c_fopen(str(filepath), "rb")
         filename = rffi.str2charp(str(filepath))
         w_globals = w_locals = space.newdict()
-        api.PyRun_File(fp, filename, Py_file_input, w_globals, w_locals)
+        with raises_w(space, ZeroDivisionError):
+            PyRun_File(space, fp, filename, Py_file_input, w_globals, w_locals)
         c_fclose(fp)
-        assert api.PyErr_Occurred() is space.w_ZeroDivisionError
-        api.PyErr_Clear()
 
         # try again, but with a closed file
         fp = c_fopen(str(filepath), "rb")
         os.close(c_fileno(fp))
-        api.PyRun_File(fp, filename, Py_file_input, w_globals, w_locals)
+        with raises_w(space, IOError):
+            PyRun_File(space, fp, filename, Py_file_input, w_globals, w_locals)
         if is_valid_fd(c_fileno(fp)):
             c_fclose(fp)
-        assert api.PyErr_Occurred() is space.w_IOError
-        api.PyErr_Clear()
-
         rffi.free_charp(filename)
 
-    def test_getbuiltins(self, space, api):
-        assert api.PyEval_GetBuiltins() is space.builtin.w_dict
+    def test_getbuiltins(self, space):
+        assert PyEval_GetBuiltins(space) is space.builtin.w_dict
 
         def cpybuiltins(space):
-            return api.PyEval_GetBuiltins()
+            return PyEval_GetBuiltins(space)
         w_cpybuiltins = space.wrap(interp2app(cpybuiltins))
 
         w_result = space.appexec([w_cpybuiltins], """(cpybuiltins):
@@ -168,13 +171,13 @@
         """)
         assert space.len_w(w_result) == 1
 
-    def test_getglobals(self, space, api):
-        assert api.PyEval_GetLocals() is None
-        assert api.PyEval_GetGlobals() is None
+    def test_getglobals(self, space):
+        assert PyEval_GetLocals(space) is None
+        assert PyEval_GetGlobals(space) is None
 
         def cpyvars(space):
-            return space.newtuple([api.PyEval_GetGlobals(),
-                                   api.PyEval_GetLocals()])
+            return space.newtuple([PyEval_GetGlobals(space),
+                                   PyEval_GetLocals(space)])
         w_cpyvars = space.wrap(interp2app(cpyvars))
 
         w_result = space.appexec([w_cpyvars], """(cpyvars):
@@ -186,26 +189,26 @@
         assert sorted(locals) == ['cpyvars', 'x']
         assert sorted(globals) == ['__builtins__', 'anonymous', 'y']
 
-    def test_sliceindex(self, space, api):
+    def test_sliceindex(self, space):
         pi = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
-        assert api._PyEval_SliceIndex(space.w_None, pi) == 0
-        api.PyErr_Clear()
+        with pytest.raises(OperationError):
+            _PyEval_SliceIndex(space, space.w_None, pi)
 
-        assert api._PyEval_SliceIndex(space.wrap(123), pi) == 1
+        assert _PyEval_SliceIndex(space, space.wrap(123), pi) == 1
         assert pi[0] == 123
 
-        assert api._PyEval_SliceIndex(space.wrap(1 << 66), pi) == 1
+        assert _PyEval_SliceIndex(space, space.wrap(1 << 66), pi) == 1
         assert pi[0] == sys.maxint
 
         lltype.free(pi, flavor='raw')
 
-    def test_atexit(self, space, api):
+    def test_atexit(self, space):
         lst = []
         def func():
             lst.append(42)
-        api.Py_AtExit(func)
+        Py_AtExit(space, func)
         cpyext = space.getbuiltinmodule('cpyext')
-        cpyext.shutdown(space) # simulate shutdown
+        cpyext.shutdown(space)  # simulate shutdown
         assert lst == [42]
 
 class AppTestCall(AppTestCpythonExtensionBase):
@@ -269,6 +272,7 @@
                 return res;
              """),
             ])
+
         def f(*args):
             return args
         assert module.call_func(f) == (None,)
@@ -323,7 +327,7 @@
             ])
         assert module.get_flags() == (0, 0)
 
-        ns = {'module':module}
+        ns = {'module': module}
         if not hasattr(sys, 'pypy_version_info'):  # no barry_as_FLUFL on pypy
             exec("""from __future__ import barry_as_FLUFL    \nif 1:
                     def nested_flags():
diff --git a/pypy/module/cpyext/test/test_floatobject.py b/pypy/module/cpyext/test/test_floatobject.py
--- a/pypy/module/cpyext/test/test_floatobject.py
+++ b/pypy/module/cpyext/test/test_floatobject.py
@@ -1,36 +1,40 @@
+import pytest
+from pypy.interpreter.error import OperationError
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.floatobject import (
+    PyFloat_FromDouble, PyFloat_AsDouble, PyFloat_AS_DOUBLE, PyNumber_Float,
+    _PyFloat_Unpack4, _PyFloat_Unpack8)
 
 class TestFloatObject(BaseApiTest):
-    def test_floatobject(self, space, api):
-        assert space.unwrap(api.PyFloat_FromDouble(3.14)) == 3.14
-        assert api.PyFloat_AsDouble(space.wrap(23.45)) == 23.45
-        assert api.PyFloat_AS_DOUBLE(space.wrap(23.45)) == 23.45
+    def test_floatobject(self, space):
+        assert space.unwrap(PyFloat_FromDouble(space, 3.14)) == 3.14
+        assert PyFloat_AsDouble(space, space.wrap(23.45)) == 23.45
+        assert PyFloat_AS_DOUBLE(space, space.wrap(23.45)) == 23.45
+        with pytest.raises(OperationError):
+            PyFloat_AsDouble(space, space.w_None)
 
-        assert api.PyFloat_AsDouble(space.w_None) == -1
-        api.PyErr_Clear()
-
-    def test_coerce(self, space, api):
-        assert space.type(api.PyNumber_Float(space.wrap(3))) is space.w_float
-        assert space.type(api.PyNumber_Float(space.wrap("3"))) is space.w_float
+    def test_coerce(self, space):
+        assert space.type(PyNumber_Float(space, space.wrap(3))) is space.w_float
+        assert space.type(PyNumber_Float(space, space.wrap("3"))) is space.w_float
 
         w_obj = space.appexec([], """():
             class Coerce(object):
                 def __float__(self):
                     return 42.5
             return Coerce()""")
-        assert space.eq_w(api.PyNumber_Float(w_obj), space.wrap(42.5))
+        assert space.eq_w(PyNumber_Float(space, w_obj), space.wrap(42.5))
 
-    def test_unpack(self, space, api):
+    def test_unpack(self, space):
         with rffi.scoped_str2charp("\x9a\x99\x99?") as ptr:
-            assert abs(api._PyFloat_Unpack4(ptr, 1) - 1.2) < 1e-7
+            assert abs(_PyFloat_Unpack4(space, ptr, 1) - 1.2) < 1e-7
         with rffi.scoped_str2charp("?\x99\x99\x9a") as ptr:
-            assert abs(api._PyFloat_Unpack4(ptr, 0) - 1.2) < 1e-7
+            assert abs(_PyFloat_Unpack4(space, ptr, 0) - 1.2) < 1e-7
         with rffi.scoped_str2charp("\x1f\x85\xebQ\xb8\x1e\t@") as ptr:
-            assert abs(api._PyFloat_Unpack8(ptr, 1) - 3.14) < 1e-15
+            assert abs(_PyFloat_Unpack8(space, ptr, 1) - 3.14) < 1e-15
         with rffi.scoped_str2charp("@\t\x1e\xb8Q\xeb\x85\x1f") as ptr:
-            assert abs(api._PyFloat_Unpack8(ptr, 0) - 3.14) < 1e-15
+            assert abs(_PyFloat_Unpack8(space, ptr, 0) - 3.14) < 1e-15
 
 class AppTestFloatObject(AppTestCpythonExtensionBase):
     def test_fromstring(self):
@@ -79,8 +83,6 @@
         assert math.isinf(neginf)
 
     def test_macro_accepts_wrong_pointer_type(self):
-        import math
-
         module = self.import_extension('foo', [
             ("test_macros", "METH_NOARGS",
              """
diff --git a/pypy/module/cpyext/test/test_funcobject.py b/pypy/module/cpyext/test/test_funcobject.py
--- a/pypy/module/cpyext/test/test_funcobject.py
+++ b/pypy/module/cpyext/test/test_funcobject.py
@@ -1,16 +1,18 @@
-from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+from rpython.rtyper.lltypesystem import rffi
 from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
+from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref, decref
+from pypy.module.cpyext.methodobject import PyClassMethod_New
 from pypy.module.cpyext.funcobject import (
-    PyFunctionObject, PyCodeObject, CODE_FLAGS)
-from pypy.interpreter.function import Function, Method
+    PyFunctionObject, PyCodeObject, CODE_FLAGS, PyMethod_Function,
+    PyMethod_Self, PyMethod_New, PyFunction_GetCode,
+    PyCode_NewEmpty, PyCode_GetNumFree)
+from pypy.interpreter.function import Function
 from pypy.interpreter.pycode import PyCode
 
 globals().update(CODE_FLAGS)
 
 class TestFunctionObject(BaseApiTest):
-    def test_function(self, space, api):
+    def test_function(self, space):
         w_function = space.appexec([], """():
             def f(): pass
             return f
@@ -19,10 +21,10 @@
         assert (from_ref(space, rffi.cast(PyObject, ref.c_ob_type)) is
                 space.gettypeobject(Function.typedef))
         assert "f" == space.unwrap(
-           from_ref(space, rffi.cast(PyFunctionObject, ref).c_func_name))
-        api.Py_DecRef(ref)
+            from_ref(space, rffi.cast(PyFunctionObject, ref).c_func_name))
+        decref(space, ref)
 
-    def test_method(self, space, api):
+    def test_method(self, space):
         w_method = space.appexec([], """():
             class C(list):
                 def method(self): pass
@@ -32,29 +34,29 @@
         w_function = space.getattr(w_method, space.wrap("__func__"))
         w_self = space.getattr(w_method, space.wrap("__self__"))
 
-        assert space.is_w(api.PyMethod_Function(w_method), w_function)
-        assert space.is_w(api.PyMethod_Self(w_method), w_self)
+        assert space.is_w(PyMethod_Function(space, w_method), w_function)
+        assert space.is_w(PyMethod_Self(space, w_method), w_self)
 
-        w_method2 = api.PyMethod_New(w_function, w_self)
+        w_method2 = PyMethod_New(space, w_function, w_self)
         assert space.eq_w(w_method, w_method2)
 
-    def test_getcode(self, space, api):
+    def test_getcode(self, space):
         w_function = space.appexec([], """():
             def func(x, y, z): return x
             return func
         """)
-        w_code = api.PyFunction_GetCode(w_function)
+        w_code = PyFunction_GetCode(space, w_function)
         assert w_code.co_name == "func"
 
         ref = make_ref(space, w_code)
         assert (from_ref(space, rffi.cast(PyObject, ref.c_ob_type)) is
                 space.gettypeobject(PyCode.typedef))
         assert "func" == space.unwrap(
-           from_ref(space, rffi.cast(PyCodeObject, ref).c_co_name))
+            from_ref(space, rffi.cast(PyCodeObject, ref).c_co_name))
         assert 3 == rffi.cast(PyCodeObject, ref).c_co_argcount
-        api.Py_DecRef(ref)
+        decref(space, ref)
 
-    def test_co_flags(self, space, api):
+    def test_co_flags(self, space):
         def get_flags(signature, body="pass"):
             w_code = space.appexec([], """():
                 def func(%s): %s
@@ -62,36 +64,36 @@
             """ % (signature, body))
             ref = make_ref(space, w_code)
             co_flags = rffi.cast(PyCodeObject, ref).c_co_flags
-            api.Py_DecRef(ref)
+            decref(space, ref)
             return co_flags
         assert get_flags("x") == CO_NESTED | CO_OPTIMIZED | CO_NEWLOCALS
         assert get_flags("x, *args") & CO_VARARGS
         assert get_flags("x, **kw") & CO_VARKEYWORDS
         assert get_flags("x", "yield x") & CO_GENERATOR
 
-    def test_newcode(self, space, api):
+    def test_newcode(self, space):
         filename = rffi.str2charp('filename')
         funcname = rffi.str2charp('funcname')
-        w_code = api.PyCode_NewEmpty(filename, funcname, 3)
+        w_code = PyCode_NewEmpty(space, filename, funcname, 3)
         assert w_code.co_filename == 'filename'
         assert w_code.co_firstlineno == 3
 
         ref = make_ref(space, w_code)
         assert "filename" == space.unwrap(
             from_ref(space, rffi.cast(PyCodeObject, ref).c_co_filename))
-        api.Py_DecRef(ref)
+        decref(space, ref)
         rffi.free_charp(filename)
         rffi.free_charp(funcname)
 
-    def test_getnumfree(self, space, api):
+    def test_getnumfree(self, space):
         w_function = space.appexec([], """():
             a = 5
             def method(x): return a, x
             return method
         """)
-        assert api.PyCode_GetNumFree(w_function.code) == 1
+        assert PyCode_GetNumFree(space, w_function.code) == 1
 
-    def test_classmethod(self, space, api):
+    def test_classmethod(self, space):
         w_function = space.appexec([], """():
             def method(x): return x
             return method
@@ -103,6 +105,7 @@
         space.setattr(w_class, space.wrap("method"), w_function)
         assert space.is_w(space.call_method(w_instance, "method"), w_instance)
         # now a classmethod
-        w_classmethod = api.PyClassMethod_New(w_function)
+        w_classmethod = PyClassMethod_New(space, w_function)
         space.setattr(w_class, space.wrap("classmethod"), w_classmethod)
-        assert space.is_w(space.call_method(w_instance, "classmethod"), w_class)
+        assert space.is_w(
+            space.call_method(w_instance, "classmethod"), w_class)
diff --git a/pypy/module/cpyext/test/test_import.py b/pypy/module/cpyext/test/test_import.py
--- a/pypy/module/cpyext/test/test_import.py
+++ b/pypy/module/cpyext/test/test_import.py
@@ -1,48 +1,51 @@
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+from pypy.module.cpyext.import_ import *
+from pypy.module.cpyext.import_ import (
+    _PyImport_AcquireLock, _PyImport_ReleaseLock)
 from rpython.rtyper.lltypesystem import rffi
 
 class TestImport(BaseApiTest):
-    def test_import(self, space, api):
-        stat = api.PyImport_Import(space.wrap("stat"))
+    def test_import(self, space):
+        stat = PyImport_Import(space, space.wrap("stat"))
         assert stat
         assert space.getattr(stat, space.wrap("S_IMODE"))
 
-    def test_addmodule(self, space, api):
+    def test_addmodule(self, space):
         with rffi.scoped_str2charp("sys") as modname:
-            w_sys = api.PyImport_AddModule(modname)
+            w_sys = PyImport_AddModule(space, modname)
         assert w_sys is space.sys
 
         with rffi.scoped_str2charp("foobar") as modname:
-            w_foobar = api.PyImport_AddModule(modname)
+            w_foobar = PyImport_AddModule(space, modname)
         assert space.str_w(space.getattr(w_foobar,
                                          space.wrap('__name__'))) == 'foobar'
 
     def test_getmoduledict(self, space, api):
         testmod = "contextlib"
-        w_pre_dict = api.PyImport_GetModuleDict()
+        w_pre_dict = PyImport_GetModuleDict(space, )
         assert not space.contains_w(w_pre_dict, space.wrap(testmod))
 
         with rffi.scoped_str2charp(testmod) as modname:
-            w_module = api.PyImport_ImportModule(modname)
+            w_module = PyImport_ImportModule(space, modname)
             print w_module
             assert w_module
 
-        w_dict = api.PyImport_GetModuleDict()
+        w_dict = PyImport_GetModuleDict(space, )
         assert space.contains_w(w_dict, space.wrap(testmod))
 
-    def test_reload(self, space, api):
-        stat = api.PyImport_Import(space.wrap("stat"))
+    def test_reload(self, space):
+        stat = PyImport_Import(space, space.wrap("stat"))
         space.delattr(stat, space.wrap("S_IMODE"))
-        stat = api.PyImport_ReloadModule(stat)
+        stat = PyImport_ReloadModule(space, stat)
         assert space.getattr(stat, space.wrap("S_IMODE"))
 
-    def test_lock(self, space, api):
+    def test_lock(self, space):
         # "does not crash"
-        api._PyImport_AcquireLock()
-        api._PyImport_AcquireLock()
-        api._PyImport_ReleaseLock()
-        api._PyImport_ReleaseLock()
+        _PyImport_AcquireLock(space, )
+        _PyImport_AcquireLock(space, )
+        _PyImport_ReleaseLock(space, )
+        _PyImport_ReleaseLock(space, )
 
 
 class AppTestImportLogic(AppTestCpythonExtensionBase):
diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -30,12 +30,14 @@
         assert view.c_len == 5
         o = rffi.charp2str(view.c_buf)
         assert o == 'hello'
-        w_mv = from_ref(space, api.PyMemoryView_FromBuffer(view))
+        ref = api.PyMemoryView_FromBuffer(view)
+        w_mv = from_ref(space, ref)
         for f in ('format', 'itemsize', 'ndim', 'readonly',
                   'shape', 'strides', 'suboffsets'):
             w_f = space.wrap(f)
             assert space.eq_w(space.getattr(w_mv, w_f),
                               space.getattr(w_memoryview, w_f))
+        api.Py_DecRef(ref)
 
 class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
     def test_fillWithObject(self):
diff --git a/pypy/module/cpyext/test/test_pyerrors.py b/pypy/module/cpyext/test/test_pyerrors.py
--- a/pypy/module/cpyext/test/test_pyerrors.py
+++ b/pypy/module/cpyext/test/test_pyerrors.py
@@ -90,7 +90,7 @@
         api.PyErr_WriteUnraisable(w_where)
         space.call_method(space.sys.get('stderr'), "flush")
         out, err = capfd.readouterr()
-        assert "Exception ValueError: 'message' in 'location' ignored" == err.strip()
+        assert "Exception ignored in: 'location'\nValueError: message" == err.strip()
 
     @pytest.mark.skipif(True, reason='not implemented yet')
     def test_interrupt_occurred(self, space, api):
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -2,6 +2,7 @@
 from rpython.rtyper.lltypesystem import rffi
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.api import generic_cpy_call
 from pypy.module.cpyext.pyobject import make_ref, from_ref
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 
@@ -634,26 +635,6 @@
         assert module.tp_init(list, x, ("hi",)) is None
         assert x == ["h", "i"]
 
-    def test_tp_str(self):
-        module = self.import_extension('foo', [
-           ("tp_str", "METH_VARARGS",
-            '''
-                 PyTypeObject *type = (PyTypeObject *)PyTuple_GET_ITEM(args, 0);
-                 PyObject *obj = PyTuple_GET_ITEM(args, 1);
-                 if (!type->tp_str)
-                 {
-                     PyErr_SetNone(PyExc_ValueError);
-                     return NULL;
-                 }
-                 return type->tp_str(obj);
-             '''
-             )
-            ])
-        class D(int):
-            def __str__(self):
-                return "more text"
-        assert module.tp_str(int, D(42)) == "42"
-
     def test_mp_ass_subscript(self):
         module = self.import_extension('foo', [
            ("new_obj", "METH_NOARGS",
@@ -942,9 +923,12 @@
         assert (d + a) == 5
         assert pow(d,b) == 16
 
-    def test_tp_new_in_subclass_of_type(self):
+    def test_tp_new_in_subclass(self):
+        import datetime
         module = self.import_module(name='foo3')
         module.footype("X", (object,), {})
+        a = module.datetimetype(1, 1, 1)
+        assert isinstance(a, module.datetimetype)
 
     def test_app_subclass_of_c_type(self):
         import sys
@@ -1130,7 +1114,6 @@
         # class X(object, metaclass=FooType): pass
         X = FooType('X', (object,), {})
 
-        print(repr(X))
         X()
 
     def test_multiple_inheritance3(self):
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -1,5 +1,6 @@
 # encoding: utf-8
-from pypy.module.cpyext.test.test_api import BaseApiTest
+import pytest
+from pypy.module.cpyext.test.test_api import BaseApiTest, raises_w
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.unicodeobject import (
     Py_UNICODE, PyUnicodeObject, new_empty_unicode)
@@ -7,6 +8,7 @@
 from pypy.module.cpyext.pyobject import Py_DecRef, from_ref
 from rpython.rtyper.lltypesystem import rffi, lltype
 import sys, py
+from pypy.module.cpyext.unicodeobject import *
 
 class AppTestUnicodeObject(AppTestCpythonExtensionBase):
     def test_unicodeobject(self):
@@ -209,44 +211,45 @@
         assert module.test_macro_invocations() == u''
 
 class TestUnicode(BaseApiTest):
-    def test_unicodeobject(self, space, api):
-        assert api.PyUnicode_GET_SIZE(space.wrap(u'späm')) == 4
-        assert api.PyUnicode_GetSize(space.wrap(u'späm')) == 4
+    def test_unicodeobject(self, space):
+        assert PyUnicode_GET_SIZE(space, space.wrap(u'späm')) == 4
+        assert PyUnicode_GetSize(space, space.wrap(u'späm')) == 4
         unichar = rffi.sizeof(Py_UNICODE)
-        assert api.PyUnicode_GET_DATA_SIZE(space.wrap(u'späm')) == 4 * unichar
+        assert PyUnicode_GET_DATA_SIZE(space, space.wrap(u'späm')) == 4 * unichar
 
-        encoding = rffi.charp2str(api.PyUnicode_GetDefaultEncoding())
+        encoding = rffi.charp2str(PyUnicode_GetDefaultEncoding(space, ))
         w_default_encoding = space.call_function(
             space.sys.get('getdefaultencoding')
         )
         assert encoding == space.unwrap(w_default_encoding)
 
-    def test_AS(self, space, api):
+    def test_AS(self, space):
         word = space.wrap(u'spam')
-        array = rffi.cast(rffi.CWCHARP, api.PyUnicode_AS_DATA(word))
-        array2 = api.PyUnicode_AS_UNICODE(word)
-        array3 = api.PyUnicode_AsUnicode(word)
+        array = rffi.cast(rffi.CWCHARP, PyUnicode_AS_DATA(space, word))
+        array2 = PyUnicode_AS_UNICODE(space, word)
+        array3 = PyUnicode_AsUnicode(space, word)
         for (i, char) in enumerate(space.unwrap(word)):
             assert array[i] == char
             assert array2[i] == char
             assert array3[i] == char
-        self.raises(space, api, TypeError, api.PyUnicode_AsUnicode,
-                    space.newbytes('spam'))
+        with raises_w(space, TypeError):
+            PyUnicode_AsUnicode(space, space.newbytes('spam'))
 
         utf_8 = rffi.str2charp('utf-8')
-        encoded = api.PyUnicode_AsEncodedString(space.wrap(u'späm'),
+        encoded = PyUnicode_AsEncodedString(space, space.wrap(u'späm'),
                                                 utf_8, None)
         assert space.unwrap(encoded) == 'sp\xc3\xa4m'
-        encoded_obj = api.PyUnicode_AsEncodedObject(space.wrap(u'späm'),
+        encoded_obj = PyUnicode_AsEncodedObject(space, space.wrap(u'späm'),
                                                 utf_8, None)
         assert space.eq_w(encoded, encoded_obj)
-        self.raises(space, api, TypeError, api.PyUnicode_AsEncodedString,
-               space.newtuple([1, 2, 3]), None, None)
-        self.raises(space, api, TypeError, api.PyUnicode_AsEncodedString,
-               space.newbytes(''), None, None)
+        with raises_w(space, TypeError):
+            PyUnicode_AsEncodedString(
+               space, space.newtuple([1, 2, 3]), None, None)
+        with raises_w(space, TypeError):
+            PyUnicode_AsEncodedString(space, space.newbytes(''), None, None)
         ascii = rffi.str2charp('ascii')
         replace = rffi.str2charp('replace')
-        encoded = api.PyUnicode_AsEncodedString(space.wrap(u'späm'),
+        encoded = PyUnicode_AsEncodedString(space, space.wrap(u'späm'),
                                                 ascii, replace)
         assert space.unwrap(encoded) == 'sp?m'
         rffi.free_charp(utf_8)
@@ -254,45 +257,45 @@
         rffi.free_charp(ascii)
 
         buf = rffi.unicode2wcharp(u"12345")
-        api.PyUnicode_AsWideChar(space.wrap(u'longword'), buf, 5)
+        PyUnicode_AsWideChar(space, space.wrap(u'longword'), buf, 5)
         assert rffi.wcharp2unicode(buf) == 'longw'
-        api.PyUnicode_AsWideChar(space.wrap(u'a'), buf, 5)
+        PyUnicode_AsWideChar(space, space.wrap(u'a'), buf, 5)
         assert rffi.wcharp2unicode(buf) == 'a'
         rffi.free_wcharp(buf)
 
-    def test_fromstring(self, space, api):
+    def test_fromstring(self, space):
         s = rffi.str2charp(u'sp\x09m'.encode("utf-8"))
-        w_res = api.PyUnicode_FromString(s)
+        w_res = PyUnicode_FromString(space, s)
         assert space.unwrap(w_res) == u'sp\x09m'
 
-        res = api.PyUnicode_FromStringAndSize(s, 4)
+        res = PyUnicode_FromStringAndSize(space, s, 4)
         w_res = from_ref(space, res)
-        api.Py_DecRef(res)
+        Py_DecRef(space, res)
         assert space.unwrap(w_res) == u'sp\x09m'
         rffi.free_charp(s)
 
-    def test_internfromstring(self, space, api):
+    def test_internfromstring(self, space):
         with rffi.scoped_str2charp('foo') as s:
-            w_res = api.PyUnicode_InternFromString(s)
+            w_res = PyUnicode_InternFromString(space, s)
             assert space.unwrap(w_res) == u'foo'
-            w_res2 = api.PyUnicode_InternFromString(s)
+            w_res2 = PyUnicode_InternFromString(space, s)
             assert w_res is w_res2
 
-    def test_unicode_resize(self, space, api):
+    def test_unicode_resize(self, space):
         py_uni = new_empty_unicode(space, 10)
         ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
         py_uni.c_buffer[0] = u'a'
         py_uni.c_buffer[1] = u'b'
         py_uni.c_buffer[2] = u'c'
         ar[0] = rffi.cast(PyObject, py_uni)
-        api.PyUnicode_Resize(ar, 3)
+        PyUnicode_Resize(space, ar, 3)
         py_uni = rffi.cast(PyUnicodeObject, ar[0])
         assert py_uni.c_length == 3
         assert py_uni.c_buffer[1] == u'b'
         assert py_uni.c_buffer[3] == u'\x00'
         # the same for growing
         ar[0] = rffi.cast(PyObject, py_uni)
-        api.PyUnicode_Resize(ar, 10)
+        PyUnicode_Resize(space, ar, 10)
         py_uni = rffi.cast(PyUnicodeObject, ar[0])
         assert py_uni.c_length == 10
         assert py_uni.c_buffer[1] == 'b'
@@ -300,47 +303,46 @@
         Py_DecRef(space, ar[0])
         lltype.free(ar, flavor='raw')
 
-    def test_AsUTF8String(self, space, api):
+    def test_AsUTF8String(self, space):
         w_u = space.wrap(u'sp\x09m')
-        w_res = api.PyUnicode_AsUTF8String(w_u)
+        w_res = PyUnicode_AsUTF8String(space, w_u)
         assert space.type(w_res) is space.w_str
         assert space.unwrap(w_res) == 'sp\tm'
 
-    def test_decode_utf8(self, space, api):
+    def test_decode_utf8(self, space):
         u = rffi.str2charp(u'sp\x134m'.encode("utf-8"))
-        w_u = api.PyUnicode_DecodeUTF8(u, 5, None)
+        w_u = PyUnicode_DecodeUTF8(space, u, 5, None)
         assert space.type(w_u) is space.w_unicode
         assert space.unwrap(w_u) == u'sp\x134m'
 
-        w_u = api.PyUnicode_DecodeUTF8(u, 2, None)
+        w_u = PyUnicode_DecodeUTF8(space, u, 2, None)
         assert space.type(w_u) is space.w_unicode
         assert space.unwrap(w_u) == 'sp'
         rffi.free_charp(u)
 
-    def test_encode_utf8(self, space, api):
+    def test_encode_utf8(self, space):
         u = rffi.unicode2wcharp(u'sp\x09m')
-        w_s = api.PyUnicode_EncodeUTF8(u, 4, None)
+        w_s = PyUnicode_EncodeUTF8(space, u, 4, None)
         assert space.unwrap(w_s) == u'sp\x09m'.encode('utf-8')
         rffi.free_wcharp(u)
 
-    def test_encode_decimal(self, space, api):
+    def test_encode_decimal(self, space):
         with rffi.scoped_unicode2wcharp(u' (12, 35 ABC)') as u:
             with rffi.scoped_alloc_buffer(20) as buf:
-                res = api.PyUnicode_EncodeDecimal(u, 13, buf.raw, None)
+                res = PyUnicode_EncodeDecimal(space, u, 13, buf.raw, None)
                 s = rffi.charp2str(buf.raw)
         assert res == 0
         assert s == ' (12, 35 ABC)'
 
         with rffi.scoped_unicode2wcharp(u' (12, \u1234\u1235)') as u:
             with rffi.scoped_alloc_buffer(20) as buf:
-                res = api.PyUnicode_EncodeDecimal(u, 9, buf.raw, None)
-        assert res == -1
-        api.PyErr_Clear()
+                with pytest.raises(OperationError):
+                    PyUnicode_EncodeDecimal(space, u, 9, buf.raw, None)
 
         with rffi.scoped_unicode2wcharp(u' (12, \u1234\u1235)') as u:
             with rffi.scoped_alloc_buffer(20) as buf:
                 with rffi.scoped_str2charp("replace") as errors:
-                    res = api.PyUnicode_EncodeDecimal(u, 9, buf.raw,
+                    res = PyUnicode_EncodeDecimal(space, u, 9, buf.raw,
                                                       errors)
                 s = rffi.charp2str(buf.raw)
         assert res == 0
@@ -349,117 +351,117 @@
         with rffi.scoped_unicode2wcharp(u'12\u1234') as u:
             with rffi.scoped_alloc_buffer(20) as buf:
                 with rffi.scoped_str2charp("xmlcharrefreplace") as errors:
-                    res = api.PyUnicode_EncodeDecimal(u, 3, buf.raw,
+                    res = PyUnicode_EncodeDecimal(space, u, 3, buf.raw,
                                                       errors)
                 s = rffi.charp2str(buf.raw)
         assert res == 0
         assert s == "12ሴ"
 
-    def test_encode_fsdefault(self, space, api):
+    def test_encode_fsdefault(self, space):
         w_u = space.wrap(u'späm')
-        w_s = api.PyUnicode_EncodeFSDefault(w_u)
+        w_s = PyUnicode_EncodeFSDefault(space, w_u)
         if w_s is None:
-            api.PyErr_Clear()
+            PyErr_Clear(space)
             py.test.skip("Requires a unicode-aware fsencoding")
         with rffi.scoped_str2charp(space.str_w(w_s)) as encoded:
-            w_decoded = api.PyUnicode_DecodeFSDefaultAndSize(encoded, space.len_w(w_s))
+            w_decoded = PyUnicode_DecodeFSDefaultAndSize(space, encoded, space.len_w(w_s))
             assert space.eq_w(w_decoded, w_u)
-            w_decoded = api.PyUnicode_DecodeFSDefault(encoded)
+            w_decoded = PyUnicode_DecodeFSDefault(space, encoded)
             assert space.eq_w(w_decoded, w_u)
 
-    def test_fsconverter(self, space, api):
+    def test_fsconverter(self, space):
         # Input is bytes
         w_input = space.newbytes("test")
         with lltype.scoped_alloc(PyObjectP.TO, 1) as result:
             # Decoder
-            ret = api.PyUnicode_FSDecoder(w_input, result)
+            ret = PyUnicode_FSDecoder(space, w_input, result)
             assert ret == Py_CLEANUP_SUPPORTED
             assert space.isinstance_w(from_ref(space, result[0]), space.w_unicode)
-            assert api.PyUnicode_FSDecoder(None, result) == 1
+            assert PyUnicode_FSDecoder(space, None, result) == 1
             # Converter
-            ret = api.PyUnicode_FSConverter(w_input, result)
+            ret = PyUnicode_FSConverter(space, w_input, result)
             assert ret == Py_CLEANUP_SUPPORTED
             assert space.eq_w(from_ref(space, result[0]), w_input)
-            assert api.PyUnicode_FSDecoder(None, result) == 1
+            assert PyUnicode_FSDecoder(space, None, result) == 1
         # Input is unicode
         w_input = space.wrap("test")
         with lltype.scoped_alloc(PyObjectP.TO, 1) as result:
             # Decoder
-            ret = api.PyUnicode_FSDecoder(w_input, result)
+            ret = PyUnicode_FSDecoder(space, w_input, result)
             assert ret == Py_CLEANUP_SUPPORTED
             assert space.eq_w(from_ref(space, result[0]), w_input)
-            assert api.PyUnicode_FSDecoder(None, result) == 1
+            assert PyUnicode_FSDecoder(space, None, result) == 1
             # Converter
-            ret = api.PyUnicode_FSConverter(w_input, result)
+            ret = PyUnicode_FSConverter(space, w_input, result)
             assert ret == Py_CLEANUP_SUPPORTED
             assert space.isinstance_w(from_ref(space, result[0]), space.w_bytes)
-            assert api.PyUnicode_FSDecoder(None, result) == 1
+            assert PyUnicode_FSDecoder(space, None, result) == 1
 
-    def test_IS(self, space, api):
+    def test_IS(self, space):
         for char in [0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x1c, 0x1d, 0x1e, 0x1f,
                      0x20, 0x85, 0xa0, 0x1680, 0x2000, 0x2001, 0x2002,
                      0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008,
                      0x2009, 0x200a,
                      #0x200b is in Other_Default_Ignorable_Code_Point in 4.1.0
                      0x2028, 0x2029, 0x202f, 0x205f, 0x3000]:
-            assert api.Py_UNICODE_ISSPACE(unichr(char))
-        assert not api.Py_UNICODE_ISSPACE(u'a')
+            assert Py_UNICODE_ISSPACE(space, unichr(char))
+        assert not Py_UNICODE_ISSPACE(space, u'a')
 
-        assert api.Py_UNICODE_ISALPHA(u'a')
-        assert not api.Py_UNICODE_ISALPHA(u'0')
-        assert api.Py_UNICODE_ISALNUM(u'a')
-        assert api.Py_UNICODE_ISALNUM(u'0')
-        assert not api.Py_UNICODE_ISALNUM(u'+')
+        assert Py_UNICODE_ISALPHA(space, u'a')
+        assert not Py_UNICODE_ISALPHA(space, u'0')
+        assert Py_UNICODE_ISALNUM(space, u'a')
+        assert Py_UNICODE_ISALNUM(space, u'0')
+        assert not Py_UNICODE_ISALNUM(space, u'+')
 
-        assert api.Py_UNICODE_ISDECIMAL(u'\u0660')
-        assert not api.Py_UNICODE_ISDECIMAL(u'a')
-        assert api.Py_UNICODE_ISDIGIT(u'9')
-        assert not api.Py_UNICODE_ISDIGIT(u'@')
-        assert api.Py_UNICODE_ISNUMERIC(u'9')
-        assert not api.Py_UNICODE_ISNUMERIC(u'@')
+        assert Py_UNICODE_ISDECIMAL(space, u'\u0660')
+        assert not Py_UNICODE_ISDECIMAL(space, u'a')
+        assert Py_UNICODE_ISDIGIT(space, u'9')
+        assert not Py_UNICODE_ISDIGIT(space, u'@')
+        assert Py_UNICODE_ISNUMERIC(space, u'9')
+        assert not Py_UNICODE_ISNUMERIC(space, u'@')
 
         for char in [0x0a, 0x0d, 0x1c, 0x1d, 0x1e, 0x85, 0x2028, 0x2029]:
-            assert api.Py_UNICODE_ISLINEBREAK(unichr(char))
+            assert Py_UNICODE_ISLINEBREAK(space, unichr(char))
 
-        assert api.Py_UNICODE_ISLOWER(u'\xdf') # sharp s
-        assert api.Py_UNICODE_ISUPPER(u'\xde') # capital thorn
-        assert api.Py_UNICODE_ISLOWER(u'a')
-        assert not api.Py_UNICODE_ISUPPER(u'a')
-        assert not api.Py_UNICODE_ISTITLE(u'\xce')
-        assert api.Py_UNICODE_ISTITLE(
+        assert Py_UNICODE_ISLOWER(space, u'\xdf') # sharp s


More information about the pypy-commit mailing list