[pypy-commit] pypy rffi-parser-2: hg merge default
rlamy
pypy.commits at gmail.com
Mon Jan 16 20:12:25 EST 2017
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: rffi-parser-2
Changeset: r89623:ebbd6a0d8773
Date: 2017-01-17 01:11 +0000
http://bitbucket.org/pypy/pypy/changeset/ebbd6a0d8773/
Log: hg merge default
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
@@ -220,6 +220,7 @@
self.__statements_counter = 0
self.__rawstatements = set()
self._statement_cache = _StatementCache(self, cached_statements)
+ self.__statements_already_committed = []
self.__func_cache = {}
self.__aggregates = {}
@@ -363,6 +364,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):
@@ -418,15 +427,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,
@@ -827,8 +839,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
@@ -2780,6 +2780,9 @@
m = Py_InitModule("_testcapi", TestMethods);
if (m == NULL)
return;
+
+ if (PyType_Ready(&_MemoryViewTester_Type) < 0)
+ return;
Py_TYPE(&_HashInheritanceTester_Type)=&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, 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, cp1, 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
@@ -483,11 +483,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
@@ -101,3 +101,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
@@ -140,7 +140,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
@@ -276,12 +276,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):
@@ -483,7 +486,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__'
@@ -515,7 +518,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/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -194,6 +194,17 @@
from ..interp_ssl import SOCKET_STORAGE
SOCKET_STORAGE._dict.clear()
+ def test_warmup_connection(self):
+ # not sure it is gmail.com's fault, but on some machines the
+ # very first connection attempt fails. So we make one here and
+ # ignore the result. The first real test is test_connect().
+ import socket, ssl
+ try:
+ ss = socket.ssl(self.s)
+ self.s.close()
+ except ssl.SSLError:
+ pass
+
def test_connect(self):
import socket, gc
ss = socket.ssl(self.s)
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
@@ -40,6 +40,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
from pypy.module.cpyext.cparser import CTypeSpace
DEBUG_WRAPPER = True
@@ -906,6 +907,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,
@@ -924,7 +926,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:
@@ -970,6 +971,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
@@ -1130,6 +1132,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):
@@ -1150,7 +1180,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
#
@@ -1160,14 +1189,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
@@ -512,6 +513,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]
@@ -551,6 +556,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__'),
@@ -755,7 +773,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:
@@ -818,7 +836,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"),
@@ -966,9 +984,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 | Py_TPFLAGS_CHECKTYPES
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}};
/* Initialize this module. */
@@ -69,6 +135,16 @@
initfoo3(void)
{
PyObject *mod, *d;
+ PyObject *module = NULL;
+ module = PyImport_ImportModule("datetime");
+ typ = PyObject_GetAttr(module, PyString_FromString("datetime"));
+ Py_DECREF(module);
+ if (!PyType_Check(typ)) {
+ PyErr_Format(PyExc_TypeError, "datetime.datetime is not a type object");
+ return;
+ }
+ datetimetype.tp_base = (PyTypeObject*)typ;
+ PyType_Ready(&datetimetype);
footype.tp_base = &PyType_Type;
PyType_Ready(&footype);
mod = Py_InitModule("foo3", sbkMethods);
@@ -79,5 +155,7 @@
return;
if (PyDict_SetItemString(d, "footype", (PyObject *)&footype) < 0)
return;
+ if (PyDict_SetItemString(d, "datetimetype", (PyObject *)&datetimetype) < 0)
+ return;
Py_INCREF(&footype);
}
diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -358,11 +358,15 @@
data = PyString_AS_STRING(args);
len = PyString_GET_SIZE(args);
- if (data == NULL || len < 1)
+ if (data == NULL)
Py_RETURN_NONE;
obj = PyArray_Scalar(data, len);
return obj;
"""),
+ ("get_len", "METH_O",
+ """
+ return PyLong_FromLong(PyObject_Size(args));
+ """),
], prologue="""
#include <Python.h>
PyTypeObject PyStringArrType_Type = {
@@ -438,12 +442,16 @@
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 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):
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,13 +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_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
@@ -663,30 +664,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 C:
- def __str__(self):
- return "text"
- assert module.tp_str(type(C()), C()) == "text"
- 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",
@@ -978,9 +955,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
@@ -1165,7 +1145,6 @@
self._check_type_object(FooType)
class X(object):
__metaclass__ = FooType
- print repr(X)
X()
def test_multiple_inheritance3(self):
diff --git a/pypy/module/cpyext/test/test_userslots.py b/pypy/module/cpyext/test/test_userslots.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_userslots.py
@@ -0,0 +1,208 @@
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.pyobject import make_ref, from_ref
+from pypy.module.cpyext.api import generic_cpy_call
+from pypy.module.cpyext.typeobject import PyTypeObjectPtr
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+
+class TestAppLevelObject(BaseApiTest):
+ def test_nb_add_from_python(self, space, api):
+ w_date = space.appexec([], """():
+ class DateType(object):
+ def __add__(self, other):
+ return 'sum!'
+ return DateType()
+ """)
+ w_datetype = space.type(w_date)
+ py_date = make_ref(space, w_date)
+ py_datetype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_datetype))
+ assert py_datetype.c_tp_as_number
+ assert py_datetype.c_tp_as_number.c_nb_add
+ w_obj = generic_cpy_call(space, py_datetype.c_tp_as_number.c_nb_add,
+ py_date, py_date)
+ assert space.str_w(w_obj) == 'sum!'
+
+ def test_tp_new_from_python(self, space, api):
+ w_date = space.appexec([], """():
+ class Date(object):
+ def __new__(cls, year, month, day):
+ self = object.__new__(cls)
+ self.year = year
+ self.month = month
+ self.day = day
+ return self
+ return Date
+ """)
+ py_datetype = rffi.cast(PyTypeObjectPtr, make_ref(space, w_date))
+ one = space.newint(1)
+ arg = space.newtuple([one, one, one])
+ # call w_date.__new__
+ w_obj = space.call_function(w_date, one, one, one)
+ w_year = space.getattr(w_obj, space.newbytes('year'))
+ assert space.int_w(w_year) == 1
+
+ w_obj = generic_cpy_call(space, py_datetype.c_tp_new, py_datetype,
+ arg, space.newdict({}))
+ w_year = space.getattr(w_obj, space.newbytes('year'))
+ assert space.int_w(w_year) == 1
+
+class AppTestUserSlots(AppTestCpythonExtensionBase):
+ def test_tp_hash_from_python(self):
+ # to see that the functions are being used,
+ # run pytest with -s
+ module = self.import_extension('foo', [
+ ("use_hash", "METH_O",
+ '''
+ long hash = args->ob_type->tp_hash(args);
+ return PyLong_FromLong(hash);
+ ''')])
+ class C(object):
+ def __hash__(self):
+ return -23
+ c = C()
+ # uses the userslot slot_tp_hash
+ ret = module.use_hash(C())
+ assert hash(c) == ret
+ # uses the slotdef renamed cpyext_tp_hash_int
+ ret = module.use_hash(3)
+ assert hash(3) == ret
+
+ 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_SetString(PyExc_ValueError, "no tp_str");
+ return NULL;
+ }
+ return type->tp_str(obj);
+ '''
+ )
+ ])
+ class C:
+ def __str__(self):
+ return "text"
+ assert module.tp_str(type(C()), C()) == "text"
+ class D(int):
+ def __str__(self):
+ return "more text"
+ assert module.tp_str(int, D(42)) == "42"
+ class A(object):
+ pass
+ s = module.tp_str(type(A()), A())
+ assert 'A object' in s
+
+ def test_tp_deallocate(self):
+ module = self.import_extension('foo', [
+ ("get_cnt", "METH_NOARGS",
+ '''
+ return PyLong_FromLong(foocnt);
+ '''),
+ ("get__timestamp", "METH_NOARGS",
+ '''
+ PyObject * one = PyLong_FromLong(1);
+ PyObject * a = PyTuple_Pack(3, one, one, one);
+ PyObject * k = NULL;
+ obj = _Timestamp.tp_new(&_Timestamp, a, k);
+ Py_DECREF(one);
+ return obj;
+ '''),
+ ("get_timestamp", "METH_NOARGS",
+ '''
+ PyObject * one = PyLong_FromLong(1);
+ PyObject * a = PyTuple_Pack(3, one, one, one);
+ PyObject * k = NULL;
+ obj = Timestamp.tp_new(&Timestamp, a, k);
+ Py_DECREF(one);
+ return obj;
+ '''),
+ ], prologue='''
+ static int foocnt = 0;
+ static PyTypeObject* datetime_cls = NULL;
+ static PyObject * obj = NULL;
+ static PyObject*
+ _timestamp_new(PyTypeObject* t, PyObject* a, PyObject* k)
+ {
+ foocnt ++;
+ return datetime_cls->tp_new(t, a, k);
+ }
+
+ static PyObject*
+ timestamp_new(PyTypeObject* t, PyObject* a, PyObject* k)
+ {
+ return datetime_cls->tp_new(t, a, k);
+ }
+
+ static void
+ _timestamp_dealloc(PyObject *op)
+ {
+ foocnt --;
+ datetime_cls->tp_dealloc(op);
+ }
+
+
+ static PyTypeObject _Timestamp = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "foo._Timestamp", /* tp_name*/
+ 0, /* tp_basicsize*/
+ 0, /* tp_itemsize */
+ _timestamp_dealloc /* tp_dealloc */
+ };
+ static PyTypeObject Timestamp = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "foo.Timestamp", /* tp_name*/
+ 0, /* tp_basicsize*/
+ 0 /* tp_itemsize */
+ };
+ ''', more_init='''
+ PyObject * mod = PyImport_ImportModule("datetime");
+ if (mod == NULL) INITERROR;
+ PyObject * dt = PyString_FromString("datetime");
+ datetime_cls = (PyTypeObject*)PyObject_GetAttr(mod, dt);
+ if (datetime_cls == NULL) INITERROR;
+ _Timestamp.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+ _Timestamp.tp_base = datetime_cls;
+ _Timestamp.tp_new = _timestamp_new;
+ Py_DECREF(mod);
+ Py_DECREF(dt);
+ if (PyType_Ready(&_Timestamp) < 0) INITERROR;
+
+ Timestamp.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+ Timestamp.tp_base = &_Timestamp;
+ Timestamp.tp_new = timestamp_new;
+ Timestamp.tp_dealloc = datetime_cls->tp_dealloc;
+ if (PyType_Ready(&Timestamp) < 0) INITERROR;
+ ''')
+ # _Timestamp has __new__, __del__ and
+ # inherits from datetime.datetime
+ # Timestamp has __new__, default __del__ (subtype_dealloc) and
+ # inherits from _Timestamp
+ import gc, sys
+ cnt = module.get_cnt()
+ assert cnt == 0
+ obj = module.get__timestamp() #_Timestamp
+ cnt = module.get_cnt()
+ assert cnt == 1
+ assert obj.year == 1
+ del obj
+ self.debug_collect()
+ cnt = module.get_cnt()
+ assert cnt == 0
+
+ obj = module.get_timestamp() #Timestamp
+ cnt = module.get_cnt()
+ assert cnt == 0
+ assert obj.year == 1
+ # XXX calling Timestamp.tp_dealloc which is subtype_dealloc
+ # causes infinite recursion
+ del obj
+ self.debug_collect()
+ cnt = module.get_cnt()
+ assert cnt == 0
+
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -1,7 +1,7 @@
import os
from rpython.rlib import jit
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.baseobjspace import W_Root, DescrMismatch
@@ -32,12 +32,12 @@
from pypy.module.cpyext.state import State
from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
from pypy.module.cpyext.typeobjectdefs import (
- PyGetSetDef, PyMemberDef,
+ PyGetSetDef, PyMemberDef, PyMappingMethods,
PyNumberMethods, PySequenceMethods, PyBufferProcs)
from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
-WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False
+#WARN_ABOUT_MISSING_SLOT_FUNCTIONS = False
PyType_Check, PyType_CheckExact = build_type_checkers("Type", "w_type")
@@ -250,6 +250,7 @@
dict_w[name] = w_descr
i += 1
+missing_slots={}
def update_all_slots(space, w_type, pto):
# fill slots in pto
# Not very sure about it, but according to
@@ -257,22 +258,46 @@
# overwrite slots that are already set: these ones are probably
# coming from a parent C type.
- typedef = w_type.layout.typedef
+ if w_type.is_heaptype():
+ typedef = None
+ search_dict_w = w_type.dict_w
+ else:
+ typedef = w_type.layout.typedef
+ search_dict_w = None
+
for method_name, slot_name, slot_names, slot_apifunc in slotdefs_for_tp_slots:
- w_descr = w_type.lookup(method_name)
- if w_descr is None:
- # XXX special case iternext
- continue
+ slot_func_helper = None
+ if search_dict_w is None:
+ # built-in types: expose as many slots as possible, even
+ # if it happens to come from some parent class
+ slot_apifunc = None # use get_slot_tp_function
+ else:
+ # For heaptypes, w_type.layout.typedef will be object's typedef, and
+ # get_slot_tp_function will fail
+ w_descr = search_dict_w.get(method_name, None)
+ if w_descr:
+ # use the slot_apifunc (userslots) to lookup at runtime
+ pass
+ elif len(slot_names) ==1:
+ # 'inherit' from tp_base
+ slot_func_helper = getattr(pto.c_tp_base, slot_names[0])
+ else:
+ struct = getattr(pto.c_tp_base, slot_names[0])
+ if struct:
+ slot_func_helper = getattr(struct, slot_names[1])
- if slot_apifunc is None and typedef is not None:
- slot_apifunc = get_slot_tp_function(space, typedef, slot_name)
- if not slot_apifunc:
- if WARN_ABOUT_MISSING_SLOT_FUNCTIONS:
- os.write(2,
- "%s defined by %s but no slot function defined!\n" % (
- method_name, w_type.getname(space)))
- continue
- slot_func_helper = slot_apifunc.get_llhelper(space)
+ if not slot_func_helper:
+ if typedef is not None:
+ if slot_apifunc is None:
+ slot_apifunc = get_slot_tp_function(space, typedef, slot_name)
+ if not slot_apifunc:
+ if not we_are_translated():
+ if slot_name not in missing_slots:
+ missing_slots[slot_name] = w_type.getname(space)
+ print "missing slot %r/%r, discovered on %r" % (
+ method_name, slot_name, w_type.getname(space))
+ continue
+ slot_func_helper = slot_apifunc.get_llhelper(space)
# XXX special case wrapper-functions and use a "specific" slot func
@@ -300,6 +325,8 @@
STRUCT_TYPE = PySequenceMethods
elif slot_names[0] == 'c_tp_as_buffer':
STRUCT_TYPE = PyBufferProcs
+ elif slot_names[0] == 'c_tp_as_mapping':
+ STRUCT_TYPE = PyMappingMethods
else:
raise AssertionError(
"Structure not allocated: %s" % (slot_names[0],))
@@ -396,13 +423,6 @@
pto.c_tp_itemsize = base_pto.c_tp_itemsize
pto.c_tp_flags |= base_pto.c_tp_flags & Py_TPFLAGS_CHECKTYPES
pto.c_tp_flags |= base_pto.c_tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS
- flags = rffi.cast(lltype.Signed, pto.c_tp_flags)
- base_object_pyo = make_ref(space, space.w_object)
- base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo)
- if base_pto != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
- if not pto.c_tp_new:
- pto.c_tp_new = base_pto.c_tp_new
- Py_DecRef(space, base_object_pyo)
def check_descr(space, w_self, w_type):
if not space.isinstance_w(w_self, w_type):
@@ -502,9 +522,25 @@
pto = obj.c_ob_type
base = pto
this_func_ptr = llslot(space, subtype_dealloc)
+ w_obj = from_ref(space, rffi.cast(PyObject, base))
+ # This wrapper is created on a specific type, call it w_A.
+ # We wish to call the dealloc function from one of the base classes of w_A,
+ # the first of which is not this function itself.
+ # w_obj is an instance of w_A or one of its subclasses. So climb up the
+ # inheritance chain until base.c_tp_dealloc is exactly this_func, and then
+ # continue on up until they differ.
+ #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name)
+ while base.c_tp_dealloc != this_func_ptr:
+ base = base.c_tp_base
+ assert base
+ #print ' ne move to', rffi.charp2str(base.c_tp_name)
+ w_obj = from_ref(space, rffi.cast(PyObject, base))
while base.c_tp_dealloc == this_func_ptr:
base = base.c_tp_base
assert base
+ #print ' eq move to', rffi.charp2str(base.c_tp_name)
+ w_obj = from_ref(space, rffi.cast(PyObject, base))
+ #print ' end with', rffi.charp2str(base.c_tp_name)
dealloc = base.c_tp_dealloc
# XXX call tp_del if necessary
generic_cpy_call(space, dealloc, obj)
@@ -676,13 +712,6 @@
typedescr = get_typedescr(w_type.layout.typedef)
- # dealloc
- if space.gettypeobject(w_type.layout.typedef) is w_type:
- # only for the exact type, like 'space.w_tuple' or 'space.w_list'
- pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space)
- else:
- # for all subtypes, use subtype_dealloc()
- pto.c_tp_dealloc = llslot(space, subtype_dealloc)
if space.is_w(w_type, space.w_str):
pto.c_tp_itemsize = 1
elif space.is_w(w_type, space.w_tuple):
@@ -713,6 +742,17 @@
w_base = best_base(space, w_type.bases_w)
pto.c_tp_base = rffi.cast(PyTypeObjectPtr, make_ref(space, w_base))
+ # dealloc
+ if space.gettypeobject(w_type.layout.typedef) is w_type:
+ # only for the exact type, like 'space.w_tuple' or 'space.w_list'
+ pto.c_tp_dealloc = typedescr.get_dealloc().get_llhelper(space)
+ else:
+ # for all subtypes, use base's dealloc (requires sorting in attach_all)
+ pto.c_tp_dealloc = pto.c_tp_base.c_tp_dealloc
+ if not pto.c_tp_dealloc:
+ # strange, but happens (ABCMeta)
+ pto.c_tp_dealloc = llslot(space, subtype_dealloc)
+
if builder.cpyext_type_init is not None:
builder.cpyext_type_init.append((pto, w_type))
else:
@@ -726,11 +766,18 @@
if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize:
pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize
- # will be filled later on with the correct value
- # may not be 0
if space.is_w(w_type, space.w_object):
+ # will be filled later on with the correct value
+ # may not be 0
pto.c_tp_new = cts.cast('newfunc', 1)
update_all_slots(space, w_type, pto)
+ if not pto.c_tp_new:
+ base_object_pyo = make_ref(space, space.w_object)
+ base_object_pto = rffi.cast(PyTypeObjectPtr, base_object_pyo)
+ flags = rffi.cast(lltype.Signed, pto.c_tp_flags)
+ if pto.c_tp_base != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
+ pto.c_tp_new = pto.c_tp_base.c_tp_new
+ Py_DecRef(space, base_object_pyo)
pto.c_tp_flags |= Py_TPFLAGS_READY
return pto
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -65,9 +65,10 @@
def unicode_attach(space, py_obj, w_obj, w_userdata=None):
"Fills a newly allocated PyUnicodeObject with a unicode string"
py_unicode = rffi.cast(PyUnicodeObject, py_obj)
- py_unicode.c_length = len(space.unicode_w(w_obj))
+ s = space.unicode_w(w_obj)
+ py_unicode.c_length = len(s)
py_unicode.c_str = lltype.nullptr(rffi.CWCHARP.TO)
- py_unicode.c_hash = space.hash_w(w_obj)
+ py_unicode.c_hash = space.hash_w(space.newunicode(s))
py_unicode.c_defenc = lltype.nullptr(PyObject.TO)
def unicode_realize(space, py_obj):
@@ -80,7 +81,7 @@
w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type))
w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type)
w_obj.__init__(s)
- py_uni.c_hash = space.hash_w(w_obj)
+ py_uni.c_hash = space.hash_w(space.newunicode(s))
track_reference(space, py_obj, w_obj)
return w_obj
@@ -205,9 +206,8 @@
@cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
def PyUnicode_GET_SIZE(space, w_obj):
- """Return the size of the object. o has to be a PyUnicodeObject (not
+ """Return the size of the object. obj is a PyUnicodeObject (not
checked)."""
- assert isinstance(w_obj, unicodeobject.W_UnicodeObject)
return space.len_w(w_obj)
@cpython_api([rffi.VOIDP], rffi.CWCHARP, error=CANNOT_FAIL)
diff --git a/pypy/module/cpyext/userslot.py b/pypy/module/cpyext/userslot.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/userslot.py
@@ -0,0 +1,112 @@
+"""
+These are the default implementation for type slots that we put
+in user-defined app-level Python classes, if the class implements
+the corresponding '__xxx__' special method. It should mostly just
+call back the general version of the space operation.
+
+This is only approximately correct. One problem is that some
+details are likely subtly wrong. Another problem is that we don't
+track changes to an app-level Python class (addition or removal of
+'__xxx__' special methods) after initalization of the PyTypeObject.
+"""
+
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.argument import Arguments
+from pypy.module.cpyext.api import slot_function, PyObject, Py_ssize_t
+from pypy.module.cpyext.api import PyTypeObjectPtr
+from rpython.rtyper.lltypesystem import rffi, lltype
+
+ at slot_function([PyObject], Py_ssize_t, error=-1)
+def slot_sq_length(space, w_obj):
+ return space.int_w(space.len(w_obj))
+
+ at slot_function([PyObject], lltype.Signed, error=-1)
+def slot_tp_hash(space, w_obj):
+ return space.int_w(space.hash(w_obj))
+
+ at slot_function([PyObject, Py_ssize_t], PyObject)
+def slot_sq_item(space, w_obj, index):
+ return space.getitem(w_obj, space.wrap(index))
+
+ at slot_function([PyTypeObjectPtr, PyObject, PyObject], PyObject)
+def slot_tp_new(space, w_type, w_args, w_kwds):
+ # XXX problem - we need to find the actual __new__ function to call.
+ # but we have no 'self' argument. Theoretically, self will be
+ # w_type, but if w_type is a subclass of self, and w_type has a
+ # __new__ function that calls super().__new__, and that call ends
+ # up here, we will get infinite recursion. Prevent the recursion
+ # in the simple case (from cython) where w_type is a cpytype, but
+ # we know (since we are in this function) that self is not a cpytype
+ from pypy.module.cpyext.typeobject import W_PyCTypeObject
+ w_type0 = w_type
+ mro_w = space.listview(space.getattr(w_type0, space.wrap('__mro__')))
+ for w_m in mro_w[1:]:
+ if not w_type0.is_cpytype():
+ break
+ w_type0 = w_m
+ w_impl = space.getattr(w_type0, space.wrap('__new__'))
+ args = Arguments(space, [w_type],
+ w_stararg=w_args, w_starstararg=w_kwds)
+ return space.call_args(w_impl, args)
+
+# unary functions
+
+ at slot_function([PyObject], PyObject)
+def slot_tp_str(space, w_obj):
+ return space.str(w_obj)
+
+ at slot_function([PyObject], PyObject)
+def slot_tp_repr(space, w_obj):
+ return space.repr(w_obj)
+
+#binary functions
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_add(space, w_obj1, w_obj2):
+ return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_subtract(space, w_obj1, w_obj2):
+ return space.sub(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_multiply(space, w_obj1, w_obj2):
+ return space.mul(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_divide(space, w_obj1, w_obj2):
+ return space.div(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_add(space, w_obj1, w_obj2):
+ return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_subtract(space, w_obj1, w_obj2):
+ return space.sub(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_multiply(space, w_obj1, w_obj2):
+ return space.mul(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_nb_inplace_divide(space, w_obj1, w_obj2):
+ return space.div(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_sq_concat(space, w_obj1, w_obj2):
+ return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_sq_inplace_concat(space, w_obj1, w_obj2):
+ return space.add(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_mp_subscript(space, w_obj1, w_obj2):
+ return space.getitem(w_obj1, w_obj2)
+
+ at slot_function([PyObject, PyObject], PyObject)
+def slot_tp_getattr(space, w_obj1, w_obj2):
+ return space.getattr(w_obj1, w_obj2)
+
+
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -1,5 +1,6 @@
"""The builtin str implementation"""
+from rpython.rlib import jit
from rpython.rlib.jit import we_are_jitted
from rpython.rlib.objectmodel import (
compute_hash, compute_unique_id, import_from_mixin)
@@ -950,6 +951,7 @@
W_BytesObject.typedef.flag_sequence_bug_compat = True
+ at jit.elidable
def string_escape_encode(s, quote):
buf = StringBuilder(len(s) + 2)
diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py
--- a/pypy/objspace/std/test/test_typeobject.py
+++ b/pypy/objspace/std/test/test_typeobject.py
@@ -1329,3 +1329,8 @@
pass
assert X.__dict__['__dict__'].__objclass__ is X
assert X.__dict__['__weakref__'].__objclass__ is X
+ assert object.__dict__['__class__'].__objclass__ is object
+ assert int.__dict__['imag'].__objclass__ is int
+ assert file.closed.__objclass__ is file
+ assert type.__dict__['__name__'].__objclass__ is type
+ assert type.__dict__['__doc__'].__objclass__ is type
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -958,7 +958,7 @@
__base__ = GetSetProperty(descr__base),
__mro__ = GetSetProperty(descr_get__mro__),
__dict__=GetSetProperty(type_get_dict),
- __doc__ = GetSetProperty(descr__doc),
+ __doc__ = GetSetProperty(descr__doc, cls=W_TypeObject),
mro = gateway.interp2app(descr_mro),
__flags__ = GetSetProperty(descr__flags),
__module__ = GetSetProperty(descr_get__module, descr_set__module),
@@ -1312,10 +1312,13 @@
def build(self, typedef):
"NOT_RPYTHON: initialization-time only."
from pypy.objspace.std.objectobject import W_ObjectObject
+ from pypy.interpreter.typedef import GetSetProperty
+ from rpython.rlib.objectmodel import instantiate
space = self.space
rawdict = typedef.rawdict
lazyloaders = {}
+ w_type = instantiate(W_TypeObject)
# compute the bases
if typedef is W_ObjectObject.typedef:
@@ -1327,13 +1330,16 @@
# wrap everything
dict_w = {}
for descrname, descrvalue in rawdict.items():
+ # special case for GetSetProperties' __objclass__:
+ if isinstance(descrvalue, GetSetProperty):
+ descrvalue = descrvalue.copy_for_type(w_type)
dict_w[descrname] = space.wrap(descrvalue)
if typedef.applevel_subclasses_base is not None:
overridetypedef = typedef.applevel_subclasses_base.typedef
else:
overridetypedef = typedef
- w_type = W_TypeObject(space, typedef.name, bases_w, dict_w,
+ w_type.__init__(space, typedef.name, bases_w, dict_w,
overridetypedef=overridetypedef,
is_heaptype=overridetypedef.heaptype)
if typedef is not overridetypedef:
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -405,7 +405,8 @@
i += 1
self.optimizer.send_extra_operation(op)
# force all of them except the virtuals
- for arg in args_no_virtuals + short_jump_args:
+ for arg in (args_no_virtuals +
+ self._map_args(mapping, short_jump_args)):
self.optimizer.force_box(self.get_box_replacement(arg))
self.optimizer.flush()
# done unless "short" has grown again
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4620,3 +4620,19 @@
time.clock()
return 0
self.interp_operations(g, [])
+
+ def test_issue2465(self):
+ driver = JitDriver(greens=[], reds=['i', 'a', 'b'])
+ class F(object):
+ def __init__(self, floatval):
+ self.floatval = floatval
+ def f(i):
+ a = F(0.0)
+ b = None
+ while i > 0:
+ driver.jit_merge_point(i=i, a=a, b=b)
+ b = F(a.floatval / 1.)
+ i -= 1
+ return i
+
+ self.meta_interp(f, [10])
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -1601,6 +1601,8 @@
resulttype=llmemory.Address)
hop.genop("cast_adr_to_ptr", [v_ret],
resultvar = hop.spaceop.result)
+ else:
+ hop.rename("same_as")
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -112,6 +112,7 @@
def get_raw_address(self):
from rpython.rtyper.lltypesystem import rffi
+ # may still raise ValueError on some GCs
return rffi.get_raw_address_of_string(self.value)
class SubBuffer(Buffer):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -542,8 +542,13 @@
become identical to the one returned.
NOTE: Only use for immutable objects!
+
+ NOTE: Might fail on some GCs! You have to check again
+ can_move() afterwards. It should always work with the default
+ GC. With Boehm, can_move() is always False so
+ move_out_of_nursery() should never be called in the first place.
"""
- pass
+ return obj
class MoveOutOfNurseryEntry(ExtRegistryEntry):
_about_ = move_out_of_nursery
diff --git a/rpython/rlib/runicode.py b/rpython/rlib/runicode.py
--- a/rpython/rlib/runicode.py
+++ b/rpython/rlib/runicode.py
@@ -1378,7 +1378,12 @@
CHR = chr
def unicode_escape(s, size, errors, errorhandler=None):
- # errorhandler is not used: this function cannot cause Unicode errors
+ # errors and errorhandler are not used: this function cannot cause
+ # Unicode errors
+ return _unicode_escape(s, size)
+
+ @jit.elidable
+ def _unicode_escape(s, size):
result = STRING_BUILDER(size)
if quotes:
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -1336,6 +1336,8 @@
as key is alive. If key goes out of scope, the buffer will eventually
be freed. `string` cannot go out of scope until the RawBytes object
referencing it goes out of scope.
+
+ NOTE: may raise ValueError on some GCs, but not the default one.
"""
assert isinstance(string, str)
from rpython.rtyper.annlowlevel import llstr
@@ -1346,6 +1348,8 @@
if we_are_translated():
if rgc.can_move(string):
string = rgc.move_out_of_nursery(string)
+ if rgc.can_move(string):
+ raise ValueError("cannot make string immovable")
# string cannot move now! return the address
lldata = llstr(string)
diff --git a/rpython/rtyper/lltypesystem/test/test_ztranslated.py b/rpython/rtyper/lltypesystem/test/test_ztranslated.py
--- a/rpython/rtyper/lltypesystem/test/test_ztranslated.py
+++ b/rpython/rtyper/lltypesystem/test/test_ztranslated.py
@@ -37,7 +37,10 @@
return ptr
def main(argv=[]):
- use_str()
+ try:
+ use_str()
+ except ValueError:
+ return 42
gc.collect()
mystr = b"12341234aa"*4096*10
#debug_assert(not rgc.can_move(mystr), "long string can move... why?")
@@ -56,4 +59,15 @@
def test_compiled_incminimark():
fn = compile(main, [], gcpolicy="incminimark")
- fn()
+ res = fn()
+ assert res == 0
+
+def test_compiled_semispace():
+ fn = compile(main, [], gcpolicy="semispace")
+ res = fn()
+ assert res == 42 # get_raw_address_of_string() raises ValueError
+
+def test_compiled_boehm():
+ fn = compile(main, [], gcpolicy="boehm")
+ res = fn()
+ assert res == 0
More information about the pypy-commit
mailing list