[pypy-commit] pypy pyparser-improvements: merge default
cfbolz
pypy.commits at gmail.com
Thu Mar 29 05:50:11 EDT 2018
Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: pyparser-improvements
Changeset: r94168:fbbadbbb888b
Date: 2018-03-29 10:43 +0200
http://bitbucket.org/pypy/pypy/changeset/fbbadbbb888b/
Log: merge default
diff too long, truncating to 2000 out of 4540 lines
diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -4,42 +4,40 @@
Welcome to PyPy!
-PyPy is both an implementation of the Python programming language, and
-an extensive compiler framework for dynamic language implementations.
-You can build self-contained Python implementations which execute
-independently from CPython.
+PyPy is an interperter that implements the Python programming language, based
+on the RPython compiler framework for dynamic language implementations.
-The home page is:
+The home page for the interpreter is:
http://pypy.org/
-If you want to help developing PyPy, this document might help you:
+If you want to help developing PyPy, this documentation might help you:
http://doc.pypy.org/
-It will also point you to the rest of the documentation which is generated
-from files in the pypy/doc directory within the source repositories. Enjoy
-and send us feedback!
+More documentation about the RPython framework can be found here
- the pypy-dev team <pypy-dev at python.org>
+ http://rpython.readthedocs.io
+The source for the documentation is in the pypy/doc directory
+
+Using PyPy instead of CPython
+=============================
+
+Please read the information at http://pypy.org to find the correct way to
+download and use PyPy as an alternative to CPython.
Building
========
-First switch to or download the correct branch. The basic choices are
-``default`` for Python 2.7 and, for Python 3.X, the corresponding py3.X
-branch (e.g. ``py3.5``).
+Building PyPy is not the recommended way to obtain the PyPy alternative python
+interpreter. It is time-consuming and requires significant computing resources.
+More information can be found here
-Build with:
+ http://doc.pypy.org/en/latest/build.html
-.. code-block:: console
+Enjoy and send us feedback!
- $ rpython/bin/rpython -Ojit pypy/goal/targetpypystandalone.py
+ the pypy-dev team <pypy-dev at python.org>
-This ends up with a ``pypy-c`` or ``pypy3-c`` binary in the main pypy
-directory. We suggest to use virtualenv with the resulting
-pypy-c/pypy3-c as the interpreter; you can find more details about
-various installation schemes here:
- http://doc.pypy.org/en/latest/install.html
diff --git a/pypy/doc/install.rst b/pypy/doc/install.rst
--- a/pypy/doc/install.rst
+++ b/pypy/doc/install.rst
@@ -17,13 +17,18 @@
~~~~~~~~~~~~~~~~~~~~~~~~~
The quickest way to start using PyPy is to download a prebuilt binary for your
-OS and architecture. You can either use the `most recent release`_ or one of
-our `development nightly build`_. Please note that the nightly builds are not
+OS and architecture. You may be able to use either use the
+`most recent release`_ or one of our `development nightly build`_. These
+builds depend on dynamically linked libraries that may not be available on your
+OS. See the section about `Linux binaries` for more info and alternatives that
+may work on your system.
+
+Please note that the nightly builds are not
guaranteed to be as stable as official releases, use them at your own risk.
.. _most recent release: http://pypy.org/download.html
.. _development nightly build: http://buildbot.pypy.org/nightly/trunk/
-
+.. _Linux binaries: http://pypy.org/download.html#linux-binaries-and-common-distributions
Installing PyPy
~~~~~~~~~~~~~~~
@@ -69,9 +74,9 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is often convenient to run pypy inside a virtualenv. To do this
-you need a recent version of virtualenv -- 1.6.1 or greater. You can
+you need a version of virtualenv -- 1.6.1 or greater. You can
then install PyPy both from a precompiled tarball or from a mercurial
-checkout::
+checkout after translation::
# from a tarball
$ virtualenv -p /opt/pypy-xxx/bin/pypy my-pypy-env
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
@@ -54,3 +54,28 @@
Speed up branchy code that does a lot of function inlining by saving one call
to read the TLS in most bridges.
+
+.. branch: rpython-sprint
+
+Refactor in rpython signatures
+
+.. branch: cpyext-tls-operror2
+
+Store error state thread-locally in executioncontext, fixes issue #2764
+
+.. branch: cpyext-fast-typecheck
+
+Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify
+`W_PyCWrapperObject` which is used to call slots from the C-API, greatly
+improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks
+
+
+.. branch: fix-sre-problems
+
+Fix two (unrelated) JIT bugs manifesting in the re module:
+
+- green fields are broken and were thus disabled, plus their usage removed from
+ the _sre implementation
+
+- in rare "trace is too long" situations, the JIT could break behaviour
+ arbitrarily.
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -232,7 +232,9 @@
"different from the 'ffi.h' file seen at compile-time)")
def py_invoke(self, ll_res, ll_args):
+ key_pycode = self.key_pycode
jitdriver1.jit_merge_point(callback=self,
+ key_pycode=key_pycode,
ll_res=ll_res,
ll_args=ll_args)
self.do_invoke(ll_res, ll_args)
@@ -294,7 +296,7 @@
return 'cffi_callback ' + key_pycode.get_repr()
jitdriver1 = jit.JitDriver(name='cffi_callback',
- greens=['callback.key_pycode'],
+ greens=['key_pycode'],
reds=['ll_res', 'll_args', 'callback'],
get_printable_location=get_printable_location1)
diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py
--- a/pypy/module/_sre/interp_sre.py
+++ b/pypy/module/_sre/interp_sre.py
@@ -77,15 +77,15 @@
w_import = space.getattr(w_builtin, space.newtext("__import__"))
return space.call_function(w_import, space.newtext("re"))
-def matchcontext(space, ctx):
+def matchcontext(space, ctx, pattern):
try:
- return rsre_core.match_context(ctx)
+ return rsre_core.match_context(ctx, pattern)
except rsre_core.Error as e:
raise OperationError(space.w_RuntimeError, space.newtext(e.msg))
-def searchcontext(space, ctx):
+def searchcontext(space, ctx, pattern):
try:
- return rsre_core.search_context(ctx)
+ return rsre_core.search_context(ctx, pattern)
except rsre_core.Error as e:
raise OperationError(space.w_RuntimeError, space.newtext(e.msg))
@@ -114,7 +114,7 @@
pos = len(unicodestr)
if endpos > len(unicodestr):
endpos = len(unicodestr)
- return rsre_core.UnicodeMatchContext(self.code, unicodestr,
+ return rsre_core.UnicodeMatchContext(unicodestr,
pos, endpos, self.flags)
elif space.isinstance_w(w_string, space.w_bytes):
str = space.bytes_w(w_string)
@@ -122,7 +122,7 @@
pos = len(str)
if endpos > len(str):
endpos = len(str)
- return rsre_core.StrMatchContext(self.code, str,
+ return rsre_core.StrMatchContext(str,
pos, endpos, self.flags)
else:
buf = space.readbuf_w(w_string)
@@ -132,7 +132,7 @@
pos = size
if endpos > size:
endpos = size
- return rsre_core.BufMatchContext(self.code, buf,
+ return rsre_core.BufMatchContext(buf,
pos, endpos, self.flags)
def getmatch(self, ctx, found):
@@ -144,12 +144,12 @@
@unwrap_spec(pos=int, endpos=int)
def match_w(self, w_string, pos=0, endpos=sys.maxint):
ctx = self.make_ctx(w_string, pos, endpos)
- return self.getmatch(ctx, matchcontext(self.space, ctx))
+ return self.getmatch(ctx, matchcontext(self.space, ctx, self.code))
@unwrap_spec(pos=int, endpos=int)
def search_w(self, w_string, pos=0, endpos=sys.maxint):
ctx = self.make_ctx(w_string, pos, endpos)
- return self.getmatch(ctx, searchcontext(self.space, ctx))
+ return self.getmatch(ctx, searchcontext(self.space, ctx, self.code))
@unwrap_spec(pos=int, endpos=int)
def findall_w(self, w_string, pos=0, endpos=sys.maxint):
@@ -157,7 +157,7 @@
matchlist_w = []
ctx = self.make_ctx(w_string, pos, endpos)
while ctx.match_start <= ctx.end:
- if not searchcontext(space, ctx):
+ if not searchcontext(space, ctx, self.code):
break
num_groups = self.num_groups
w_emptystr = space.newtext("")
@@ -182,7 +182,7 @@
# this also works as the implementation of the undocumented
# scanner() method.
ctx = self.make_ctx(w_string, pos, endpos)
- scanner = W_SRE_Scanner(self, ctx)
+ scanner = W_SRE_Scanner(self, ctx, self.code)
return scanner
@unwrap_spec(maxsplit=int)
@@ -193,7 +193,7 @@
last = 0
ctx = self.make_ctx(w_string)
while not maxsplit or n < maxsplit:
- if not searchcontext(space, ctx):
+ if not searchcontext(space, ctx, self.code):
break
if ctx.match_start == ctx.match_end: # zero-width match
if ctx.match_start == ctx.end: # or end of string
@@ -274,13 +274,14 @@
else:
sublist_w = []
n = last_pos = 0
+ pattern = self.code
while not count or n < count:
sub_jitdriver.jit_merge_point(
self=self,
use_builder=use_builder,
filter_is_callable=filter_is_callable,
filter_type=type(w_filter),
- ctx=ctx,
+ ctx=ctx, pattern=pattern,
w_filter=w_filter,
strbuilder=strbuilder,
unicodebuilder=unicodebuilder,
@@ -291,7 +292,7 @@
n=n, last_pos=last_pos, sublist_w=sublist_w
)
space = self.space
- if not searchcontext(space, ctx):
+ if not searchcontext(space, ctx, pattern):
break
if last_pos < ctx.match_start:
_sub_append_slice(
@@ -355,7 +356,7 @@
filter_as_unicode
w_string sublist_w
self""".split(),
- greens=["filter_is_callable", "use_builder", "filter_type", "ctx.pattern"])
+ greens=["filter_is_callable", "use_builder", "filter_type", "pattern"])
def _sub_append_slice(ctx, space, use_builder, sublist_w,
@@ -387,7 +388,11 @@
srepat.space = space
srepat.w_pattern = w_pattern # the original uncompiled pattern
srepat.flags = flags
- srepat.code = code
+ # note: we assume that the app-level is caching SRE_Pattern objects,
+ # so that we don't need to do it here. Creating new SRE_Pattern
+ # objects all the time would be bad for the JIT, which relies on the
+ # identity of the CompiledPattern() object.
+ srepat.code = rsre_core.CompiledPattern(code)
srepat.num_groups = groups
srepat.w_groupindex = w_groupindex
srepat.w_indexgroup = w_indexgroup
@@ -610,10 +615,11 @@
# Our version is also directly iterable, to make finditer() easier.
class W_SRE_Scanner(W_Root):
- def __init__(self, pattern, ctx):
+ def __init__(self, pattern, ctx, code):
self.space = pattern.space
self.srepat = pattern
self.ctx = ctx
+ self.code = code
# 'self.ctx' is always a fresh context in which no searching
# or matching succeeded so far.
@@ -623,19 +629,19 @@
def next_w(self):
if self.ctx.match_start > self.ctx.end:
raise OperationError(self.space.w_StopIteration, self.space.w_None)
- if not searchcontext(self.space, self.ctx):
+ if not searchcontext(self.space, self.ctx, self.code):
raise OperationError(self.space.w_StopIteration, self.space.w_None)
return self.getmatch(True)
def match_w(self):
if self.ctx.match_start > self.ctx.end:
return self.space.w_None
- return self.getmatch(matchcontext(self.space, self.ctx))
+ return self.getmatch(matchcontext(self.space, self.ctx, self.code))
def search_w(self):
if self.ctx.match_start > self.ctx.end:
return self.space.w_None
- return self.getmatch(searchcontext(self.space, self.ctx))
+ return self.getmatch(searchcontext(self.space, self.ctx, self.code))
def getmatch(self, found):
if found:
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
@@ -133,6 +133,11 @@
'TYPE', 'STRING'): # 'STRING' -> 'BYTES' in py3
constant_names.append('Py_TPFLAGS_%s_SUBCLASS' % name)
+# PyPy-specific flags
+for name in ('FLOAT',):
+ constant_names.append('Py_TPPYPYFLAGS_%s_SUBCLASS' % name)
+
+
for name in constant_names:
setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
globals().update(rffi_platform.configure(CConfig_constants))
diff --git a/pypy/module/cpyext/boolobject.py b/pypy/module/cpyext/boolobject.py
--- a/pypy/module/cpyext/boolobject.py
+++ b/pypy/module/cpyext/boolobject.py
@@ -1,9 +1,5 @@
-from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import (cpython_api, PyObject, CANNOT_FAIL,
- build_type_checkers)
-
-# Inheriting from bool isn't actually possible.
-PyBool_Check = build_type_checkers("Bool")[1]
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.api import cpython_api, PyObject
@cpython_api([rffi.LONG], PyObject)
def PyBool_FromLong(space, value):
diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py
--- a/pypy/module/cpyext/floatobject.py
+++ b/pypy/module/cpyext/floatobject.py
@@ -1,7 +1,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (PyObjectFields, bootstrap_function,
cpython_struct,
- CANNOT_FAIL, cpython_api, PyObject, build_type_checkers, CONST_STRING)
+ CANNOT_FAIL, cpython_api, PyObject, CONST_STRING)
from pypy.module.cpyext.pyobject import (
make_typedescr, track_reference, from_ref)
from pypy.interpreter.error import OperationError
@@ -38,8 +38,6 @@
track_reference(space, obj, w_obj)
return w_obj
-PyFloat_Check, PyFloat_CheckExact = build_type_checkers("Float")
-
@cpython_api([lltype.Float], PyObject)
def PyFloat_FromDouble(space, value):
return space.newfloat(value)
diff --git a/pypy/module/cpyext/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -82,10 +82,10 @@
def PyTraceBack_Here(space, w_frame):
from pypy.interpreter.pytraceback import record_application_traceback
state = space.fromcache(State)
- if state.operror is None:
+ if state.get_exception() is None:
return -1
frame = space.interp_w(PyFrame, w_frame)
- record_application_traceback(space, state.operror, frame, 0)
+ record_application_traceback(space, state.get_exception(), frame, 0)
return 0
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h
--- a/pypy/module/cpyext/include/boolobject.h
+++ b/pypy/module/cpyext/include/boolobject.h
@@ -16,6 +16,8 @@
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#define PyBool_Check(op) ((op)->ob_type == &PyBool_Type)
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/include/floatobject.h b/pypy/module/cpyext/include/floatobject.h
--- a/pypy/module/cpyext/include/floatobject.h
+++ b/pypy/module/cpyext/include/floatobject.h
@@ -32,6 +32,11 @@
return PyFloat_FromDouble(-Py_HUGE_VAL); \
} while(0)
+#define PyFloat_Check(op) \
+ _PyPy_Type_FastSubclass((op)->ob_type, Py_TPPYPYFLAGS_FLOAT_SUBCLASS)
+#define PyFloat_CheckExact(op) ((op)->ob_type == &PyFloat_Type)
+
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -228,6 +228,11 @@
#define Py_TPFLAGS_BASE_EXC_SUBCLASS (1L<<30)
#define Py_TPFLAGS_TYPE_SUBCLASS (1L<<31)
+/* These are conceptually the same as the flags above, but they are
+ PyPy-specific and are stored inside tp_pypy_flags */
+#define Py_TPPYPYFLAGS_FLOAT_SUBCLASS (1L<<0)
+
+
#define Py_TPFLAGS_DEFAULT_EXTERNAL ( \
Py_TPFLAGS_HAVE_GETCHARBUFFER | \
Py_TPFLAGS_HAVE_SEQUENCE_IN | \
@@ -247,6 +252,8 @@
#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)
#define PyType_FastSubclass(t,f) PyType_HasFeature(t,f)
+#define _PyPy_Type_FastSubclass(t,f) (((t)->tp_pypy_flags & (f)) != 0)
+
#define PyType_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)
diff --git a/pypy/module/cpyext/include/sliceobject.h b/pypy/module/cpyext/include/sliceobject.h
--- a/pypy/module/cpyext/include/sliceobject.h
+++ b/pypy/module/cpyext/include/sliceobject.h
@@ -17,6 +17,8 @@
PyObject *step;
} PySliceObject;
+#define PySlice_Check(op) ((op)->ob_type == &PySlice_Type)
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -45,6 +45,18 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
+def w_kwargs_from_args(space, __args__):
+ w_kwargs = None
+ if __args__.keywords:
+ # CCC: we should probably have a @jit.look_inside_iff if the
+ # keyword count is constant, as we do in Arguments.unpack
+ w_kwargs = space.newdict()
+ for i in range(len(__args__.keywords)):
+ key = __args__.keywords[i]
+ w_obj = __args__.keywords_w[i]
+ space.setitem(w_kwargs, space.newtext(key), w_obj)
+ return w_kwargs
+
class W_PyCFunctionObject(W_Root):
_immutable_fields_ = ["flags"]
@@ -103,15 +115,7 @@
def call_keywords(self, space, w_self, __args__):
func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
py_args = tuple_from_args_w(space, __args__.arguments_w)
- w_kwargs = None
- if __args__.keywords:
- # CCC: we should probably have a @jit.look_inside_iff if the
- # keyword count is constant, as we do in Arguments.unpack
- w_kwargs = space.newdict()
- for i in range(len(__args__.keywords)):
- key = __args__.keywords[i]
- w_obj = __args__.keywords_w[i]
- space.setitem(w_kwargs, space.newtext(key), w_obj)
+ w_kwargs = w_kwargs_from_args(space, __args__)
try:
return generic_cpy_call(space, func, w_self, py_args, w_kwargs)
finally:
@@ -213,14 +217,15 @@
(self.name, self.w_objclass.getname(self.space)))
+class W_PyCWrapperObject(W_Root):
+ """
+ Abstract class; for concrete subclasses, see slotdefs.py
+ """
+ _immutable_fields_ = ['offset[*]']
-class W_PyCWrapperObject(W_Root):
- def __init__(self, space, pto, method_name, wrapper_func,
- wrapper_func_kwds, doc, func, offset=None):
+ def __init__(self, space, pto, method_name, doc, func, offset):
self.space = space
self.method_name = method_name
- self.wrapper_func = wrapper_func
- self.wrapper_func_kwds = wrapper_func_kwds
self.doc = doc
self.func = func
self.offset = offset
@@ -229,10 +234,17 @@
assert isinstance(w_type, W_TypeObject)
self.w_objclass = w_type
- def call(self, space, w_self, w_args, w_kw):
+ def descr_call(self, space, w_self, __args__):
+ return self.call(space, w_self, __args__)
+
+ def call(self, space, w_self, __args__):
+ raise NotImplementedError
+
+ @jit.unroll_safe
+ def get_func_to_call(self):
func_to_call = self.func
if self.offset:
- pto = as_pyobj(space, self.w_objclass)
+ pto = as_pyobj(self.space, self.w_objclass)
# make ptr the equivalent of this, using the offsets
#func_to_call = rffi.cast(rffi.VOIDP, ptr.c_tp_as_number.c_nb_multiply)
if pto:
@@ -246,31 +258,33 @@
assert False, "failed to convert w_type %s to PyObject" % str(
self.w_objclass)
assert func_to_call
- if self.wrapper_func is None:
- assert self.wrapper_func_kwds is not None
- return self.wrapper_func_kwds(space, w_self, w_args, func_to_call,
- w_kw)
- if space.is_true(w_kw):
- raise oefmt(space.w_TypeError,
+ return func_to_call
+
+ def check_args(self, __args__, arity):
+ length = len(__args__.arguments_w)
+ if length != arity:
+ raise oefmt(self.space.w_TypeError, "expected %d arguments, got %d",
+ arity, length)
+ if __args__.keywords:
+ raise oefmt(self.space.w_TypeError,
"wrapper %s doesn't take any keyword arguments",
self.method_name)
- return self.wrapper_func(space, w_self, w_args, func_to_call)
+
+ def check_argsv(self, __args__, min, max):
+ length = len(__args__.arguments_w)
+ if not min <= length <= max:
+ raise oefmt(self.space.w_TypeError, "expected %d-%d arguments, got %d",
+ min, max, length)
+ if __args__.keywords:
+ raise oefmt(self.space.w_TypeError,
+ "wrapper %s doesn't take any keyword arguments",
+ self.method_name)
def descr_method_repr(self):
return self.space.newtext("<slot wrapper '%s' of '%s' objects>" %
(self.method_name,
self.w_objclass.name))
- at jit.dont_look_inside
-def cwrapper_descr_call(space, w_self, __args__):
- self = space.interp_w(W_PyCWrapperObject, w_self)
- args_w, kw_w = __args__.unpack()
- w_args = space.newtuple(args_w[1:])
- w_self = args_w[0]
- w_kw = space.newdict()
- for key, w_obj in kw_w.items():
- space.setitem(w_kw, space.newtext(key), w_obj)
- return self.call(space, w_self, w_args, w_kw)
def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
asking_for_bound = (space.is_none(w_cls) or
@@ -323,7 +337,7 @@
W_PyCWrapperObject.typedef = TypeDef(
'wrapper_descriptor',
- __call__ = interp2app(cwrapper_descr_call),
+ __call__ = interp2app(W_PyCWrapperObject.descr_call),
__get__ = interp2app(cmethod_descr_get),
__name__ = interp_attrproperty('method_name', cls=W_PyCWrapperObject,
wrapfn="newtext_or_none"),
diff --git a/pypy/module/cpyext/parse/cpyext_object.h b/pypy/module/cpyext/parse/cpyext_object.h
--- a/pypy/module/cpyext/parse/cpyext_object.h
+++ b/pypy/module/cpyext/parse/cpyext_object.h
@@ -311,6 +311,10 @@
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
+ /* PyPy specific extra fields: make sure that they are ALWAYS at the end,
+ for compatibility with CPython */
+ long tp_pypy_flags;
+
} PyTypeObject;
typedef struct _heaptypeobject {
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -31,9 +31,10 @@
@cpython_api([], PyObject, result_borrowed=True)
def PyErr_Occurred(space):
state = space.fromcache(State)
- if state.operror is None:
+ operror = state.get_exception()
+ if operror is None:
return None
- return state.operror.w_type # borrowed ref
+ return operror.w_type # borrowed ref
@cpython_api([], lltype.Void)
def PyErr_Clear(space):
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -5,7 +5,8 @@
from pypy.objspace.std.listobject import (
ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
from pypy.module.cpyext.api import (
- cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP)
+ cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP,
+ generic_cpy_call)
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
from pypy.module.cpyext.pyobject import as_pyobj, incref
from rpython.rtyper.lltypesystem import rffi, lltype
@@ -145,21 +146,26 @@
# XXX we should call Py*_GET_ITEM() instead of Py*_GetItem()
# from here, but we cannot because we are also called from
# PySequence_GetItem()
+ py_obj = as_pyobj(space, w_obj)
if isinstance(w_obj, tupleobject.W_TupleObject):
from pypy.module.cpyext.tupleobject import PyTuple_GetItem
- py_obj = as_pyobj(space, w_obj)
py_res = PyTuple_GetItem(space, py_obj, i)
incref(space, py_res)
keepalive_until_here(w_obj)
return py_res
if isinstance(w_obj, W_ListObject):
from pypy.module.cpyext.listobject import PyList_GetItem
- py_obj = as_pyobj(space, w_obj)
py_res = PyList_GetItem(space, py_obj, i)
incref(space, py_res)
keepalive_until_here(w_obj)
return py_res
- return make_ref(space, space.getitem(w_obj, space.newint(i)))
+
+ as_sequence = py_obj.c_ob_type.c_tp_as_sequence
+ if not as_sequence or not as_sequence.c_sq_item:
+ raise oefmt(space.w_TypeError,
+ "'%T' object does not support indexing", w_obj)
+ ret = generic_cpy_call(space, as_sequence.c_sq_item, w_obj, i)
+ return make_ref(space, ret)
@cpython_api([PyObject, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_GetItem(space, w_obj, i):
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -47,7 +47,6 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
-PySlice_Check, PySlice_CheckExact = build_type_checkers("Slice")
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PySlice_New(space, w_start, w_stop, w_step):
@@ -75,9 +74,8 @@
normal slices.
Returns 0 on success and -1 on error with exception set."""
- if not PySlice_Check(space, w_slice):
+ if not isinstance(w_slice, W_SliceObject):
PyErr_BadInternalCall(space)
- assert isinstance(w_slice, W_SliceObject)
start_p[0], stop_p[0], step_p[0], slicelength_p[0] = \
w_slice.indices4(space, length)
return 0
@@ -97,9 +95,8 @@
objects in versions of Python prior to 2.3, you would probably do well to
incorporate the source of PySlice_GetIndicesEx(), suitably renamed,
in the source of your extension."""
- if not PySlice_Check(space, w_slice):
+ if not isinstance(w_slice, W_SliceObject):
PyErr_BadInternalCall(space)
- assert isinstance(w_slice, W_SliceObject)
start_p[0], stop_p[0], step_p[0] = \
w_slice.indices3(space, length)
return 0
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
@@ -19,6 +19,8 @@
from pypy.module.cpyext.state import State
from pypy.module.cpyext import userslot
from pypy.module.cpyext.buffer import CBuffer, CPyBuffer, fq
+from pypy.module.cpyext.methodobject import (W_PyCWrapperObject, tuple_from_args_w,
+ w_kwargs_from_args)
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.argument import Arguments
from rpython.rlib.unroll import unrolling_iterable
@@ -38,29 +40,6 @@
Py_GT = 4
Py_GE = 5
-
-def check_num_args(space, w_ob, n):
- from pypy.module.cpyext.tupleobject import PyTuple_CheckExact
- if not PyTuple_CheckExact(space, w_ob):
- raise oefmt(space.w_SystemError,
- "PyArg_UnpackTuple() argument list is not a tuple")
- if n == space.len_w(w_ob):
- return
- raise oefmt(space.w_TypeError,
- "expected %d arguments, got %d",
- n, space.len_w(w_ob))
-
-def check_num_argsv(space, w_ob, low, high):
- from pypy.module.cpyext.tupleobject import PyTuple_CheckExact
- if not PyTuple_CheckExact(space, w_ob):
- raise oefmt(space.w_SystemError,
- "PyArg_UnpackTuple() argument list is not a tuple")
- if low <=space.len_w(w_ob) <= high:
- return
- raise oefmt(space.w_TypeError,
- "expected %d-%d arguments, got %d",
- low, high, space.len_w(w_ob))
-
@not_rpython
def llslot(space, func):
return func.api_func.get_llhelper(space)
@@ -71,337 +50,413 @@
get_llhelper = v_func.value.api_func.get_llhelper
return ctx.appcall(get_llhelper, v_space)
+# NOTE: the following wrap_* are subclasses of W_PyCWrapperObject, even if
+# they don't follow the usual W_* naming convention for subclasses of W_Root:
+# we do this because we automatically generate most of the slots from the
+# CPython code copy&pasted inside slotdefs_str, and thus we need to keep the
+# same names as they are used in C.
-def wrap_init(space, w_self, w_args, func, w_kwargs):
- func_init = rffi.cast(initproc, func)
- res = generic_cpy_call(space, func_init, w_self, w_args, w_kwargs)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return None
+class wrap_init(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_init = rffi.cast(initproc, func)
+ py_args = tuple_from_args_w(space, __args__.arguments_w)
+ w_kwargs = w_kwargs_from_args(space, __args__)
+ res = generic_cpy_call(space, func_init, w_self, py_args, w_kwargs)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return None
-def wrap_unaryfunc(space, w_self, w_args, func):
- func_unary = rffi.cast(unaryfunc, func)
- check_num_args(space, w_args, 0)
- return generic_cpy_call(space, func_unary, w_self)
+class wrap_unaryfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 0)
+ func = self.get_func_to_call()
+ func_unary = rffi.cast(unaryfunc, func)
+ return generic_cpy_call(space, func_unary, w_self)
-def wrap_binaryfunc(space, w_self, w_args, func):
- func_binary = rffi.cast(binaryfunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- return generic_cpy_call(space, func_binary, w_self, args_w[0])
+class wrap_binaryfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_binary = rffi.cast(binaryfunc, func)
+ w_x = __args__.arguments_w[0]
+ return generic_cpy_call(space, func_binary, w_self, w_x)
def _get_ob_type(space, w_obj):
# please ensure that w_obj stays alive
ob_type = as_pyobj(space, space.type(w_obj))
return rffi.cast(PyTypeObjectPtr, ob_type)
-def wrap_binaryfunc_l(space, w_self, w_args, func):
- func_binary = rffi.cast(binaryfunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- ob_type = _get_ob_type(space, w_self)
- if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
- not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
- return space.w_NotImplemented
- return generic_cpy_call(space, func_binary, w_self, args_w[0])
+class wrap_binaryfunc_l(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_binary = rffi.cast(binaryfunc, func)
+ w_value = __args__.arguments_w[0]
+ ob_type = _get_ob_type(space, w_self)
+ if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+ not space.issubtype_w(space.type(w_value), space.type(w_self))):
+ return space.w_NotImplemented
+ return generic_cpy_call(space, func_binary, w_self, w_value)
-def wrap_binaryfunc_r(space, w_self, w_args, func):
- func_binary = rffi.cast(binaryfunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- ob_type = _get_ob_type(space, w_self)
- if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
- not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
- return space.w_NotImplemented
- return generic_cpy_call(space, func_binary, args_w[0], w_self)
+class wrap_binaryfunc_r(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_binary = rffi.cast(binaryfunc, func)
+ w_value = __args__.arguments_w[0]
+ ob_type = _get_ob_type(space, w_self)
+ if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+ not space.issubtype_w(space.type(w_value), space.type(w_self))):
+ return space.w_NotImplemented
+ return generic_cpy_call(space, func_binary, w_value, w_self)
-def wrap_ternaryfunc(space, w_self, w_args, func):
- # The third argument is optional
- func_ternary = rffi.cast(ternaryfunc, func)
- check_num_argsv(space, w_args, 1, 2)
- args_w = space.fixedview(w_args)
- arg3 = space.w_None
- if len(args_w) > 1:
- arg3 = args_w[1]
- return generic_cpy_call(space, func_ternary, w_self, args_w[0], arg3)
+class wrap_ternaryfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ # The third argument is optional
+ self.check_argsv(__args__, 1, 2)
+ func = self.get_func_to_call()
+ func_ternary = rffi.cast(ternaryfunc, func)
+ w_arg0 = __args__.arguments_w[0]
+ if len(__args__.arguments_w) == 2:
+ w_arg1 = __args__.arguments_w[1]
+ else:
+ w_arg1 = space.w_None
+ return generic_cpy_call(space, func_ternary, w_self, w_arg0, w_arg1)
-def wrap_ternaryfunc_r(space, w_self, w_args, func):
- # The third argument is optional
- func_ternary = rffi.cast(ternaryfunc, func)
- check_num_argsv(space, w_args, 1, 2)
- args_w = space.fixedview(w_args)
- ob_type = _get_ob_type(space, w_self)
- if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
- not space.issubtype_w(space.type(args_w[0]), space.type(w_self))):
- return space.w_NotImplemented
- arg3 = space.w_None
- if len(args_w) > 1:
- arg3 = args_w[1]
- return generic_cpy_call(space, func_ternary, args_w[0], w_self, arg3)
+class wrap_ternaryfunc_r(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ # The third argument is optional
+ self.check_argsv(__args__, 1, 2)
+ func = self.get_func_to_call()
+ func_ternary = rffi.cast(ternaryfunc, func)
+ w_arg0 = __args__.arguments_w[0]
+ if len(__args__.arguments_w) == 2:
+ w_arg1 = __args__.arguments_w[1]
+ else:
+ w_arg1 = space.w_None
+ ob_type = _get_ob_type(space, w_self)
+ if (not ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+ not space.issubtype_w(space.type(w_arg0), space.type(w_self))):
+ return space.w_NotImplemented
+ return generic_cpy_call(space, func_ternary, w_arg0, w_self, w_arg1)
+class wrap_inquirypred(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 0)
+ func = self.get_func_to_call()
+ func_inquiry = rffi.cast(inquiry, func)
+ res = generic_cpy_call(space, func_inquiry, w_self)
+ res = rffi.cast(lltype.Signed, res)
+ if res == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.newbool(bool(res))
-def wrap_inquirypred(space, w_self, w_args, func):
- func_inquiry = rffi.cast(inquiry, func)
- check_num_args(space, w_args, 0)
- res = generic_cpy_call(space, func_inquiry, w_self)
- res = rffi.cast(lltype.Signed, res)
- if res == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.newbool(bool(res))
+class wrap_getattr(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(getattrfunc, func)
+ w_name = __args__.arguments_w[0]
+ name_ptr = rffi.str2charp(space.text_w(w_name))
+ try:
+ return generic_cpy_call(space, func_target, w_self, name_ptr)
+ finally:
+ rffi.free_charp(name_ptr)
-def wrap_getattr(space, w_self, w_args, func):
- func_target = rffi.cast(getattrfunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- name_ptr = rffi.str2charp(space.text_w(args_w[0]))
- try:
- return generic_cpy_call(space, func_target, w_self, name_ptr)
- finally:
- rffi.free_charp(name_ptr)
+class wrap_getattro(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(getattrofunc, func)
+ w_name = __args__.arguments_w[0]
+ return generic_cpy_call(space, func_target, w_self, w_name)
-def wrap_getattro(space, w_self, w_args, func):
- func_target = rffi.cast(getattrofunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- return generic_cpy_call(space, func_target, w_self, args_w[0])
+class wrap_setattr(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 2)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(setattrofunc, func)
+ w_name = __args__.arguments_w[0]
+ w_value = __args__.arguments_w[1]
+ # XXX "Carlo Verre hack"?
+ res = generic_cpy_call(space, func_target, w_self, w_name, w_value)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_setattr(space, w_self, w_args, func):
- func_target = rffi.cast(setattrofunc, func)
- check_num_args(space, w_args, 2)
- w_name, w_value = space.fixedview(w_args)
- # XXX "Carlo Verre hack"?
- res = generic_cpy_call(space, func_target, w_self, w_name, w_value)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_delattr(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(setattrofunc, func)
+ w_name = __args__.arguments_w[0]
+ # XXX "Carlo Verre hack"?
+ res = generic_cpy_call(space, func_target, w_self, w_name, None)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_delattr(space, w_self, w_args, func):
- func_target = rffi.cast(setattrofunc, func)
- check_num_args(space, w_args, 1)
- w_name, = space.fixedview(w_args)
- # XXX "Carlo Verre hack"?
- res = generic_cpy_call(space, func_target, w_self, w_name, None)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_descr_get(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_target = rffi.cast(descrgetfunc, func)
+ length = len(__args__.arguments_w)
+ if length == 1:
+ w_obj = __args__.arguments_w[0]
+ w_type = None
+ elif length == 2:
+ w_obj = __args__.arguments_w[0]
+ w_type = __args__.arguments_w[1]
+ else:
+ raise oefmt(space.w_TypeError,
+ "expected 1 or 2 arguments, got %d", len(__args__.arguments_w))
+ if w_obj is space.w_None:
+ w_obj = None
+ if w_type is space.w_None:
+ w_type = None
+ if w_obj is None and w_type is None:
+ raise oefmt(space.w_TypeError, "__get__(None, None) is invalid")
+ return generic_cpy_call(space, func_target, w_self, w_obj, w_type)
-def wrap_descr_get(space, w_self, w_args, func):
- func_target = rffi.cast(descrgetfunc, func)
- args_w = space.fixedview(w_args)
- if len(args_w) == 1:
- w_obj, = args_w
- w_type = None
- elif len(args_w) == 2:
- w_obj, w_type = args_w
- else:
- raise oefmt(space.w_TypeError,
- "expected 1 or 2 arguments, got %d", len(args_w))
- if w_obj is space.w_None:
- w_obj = None
- if w_type is space.w_None:
- w_type = None
- if w_obj is None and w_type is None:
- raise oefmt(space.w_TypeError, "__get__(None, None) is invalid")
- return generic_cpy_call(space, func_target, w_self, w_obj, w_type)
+class wrap_descr_set(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 2)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(descrsetfunc, func)
+ w_obj = __args__.arguments_w[0]
+ w_value = __args__.arguments_w[1]
+ res = generic_cpy_call(space, func_target, w_self, w_obj, w_value)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_descr_set(space, w_self, w_args, func):
- func_target = rffi.cast(descrsetfunc, func)
- check_num_args(space, w_args, 2)
- w_obj, w_value = space.fixedview(w_args)
- res = generic_cpy_call(space, func_target, w_self, w_obj, w_value)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_descr_delete(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(descrsetfunc, func)
+ w_obj = __args__.arguments_w[0]
+ res = generic_cpy_call(space, func_target, w_self, w_obj, None)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_descr_delete(space, w_self, w_args, func):
- func_target = rffi.cast(descrsetfunc, func)
- check_num_args(space, w_args, 1)
- w_obj, = space.fixedview(w_args)
- res = generic_cpy_call(space, func_target, w_self, w_obj, None)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_call(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ternaryfunc, func)
+ py_args = tuple_from_args_w(space, __args__.arguments_w)
+ w_kwargs = w_kwargs_from_args(space, __args__)
+ return generic_cpy_call(space, func_target, w_self, py_args, w_kwargs)
-def wrap_call(space, w_self, w_args, func, w_kwds):
- func_target = rffi.cast(ternaryfunc, func)
- return generic_cpy_call(space, func_target, w_self, w_args, w_kwds)
+class wrap_ssizessizeobjargproc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 3)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ssizessizeobjargproc, func)
+ i = space.int_w(space.index(__args__.arguments_w[0]))
+ j = space.int_w(space.index(__args__.arguments_w[1]))
+ w_y = __args__.arguments_w[2]
+ res = generic_cpy_call(space, func_target, w_self, i, j, w_y)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_ssizessizeobjargproc(space, w_self, w_args, func):
- func_target = rffi.cast(ssizessizeobjargproc, func)
- check_num_args(space, w_args, 3)
- args_w = space.fixedview(w_args)
- i = space.int_w(space.index(args_w[0]))
- j = space.int_w(space.index(args_w[1]))
- w_y = args_w[2]
- res = generic_cpy_call(space, func_target, w_self, i, j, w_y)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_lenfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 0)
+ func = self.get_func_to_call()
+ func_len = rffi.cast(lenfunc, func)
+ res = generic_cpy_call(space, func_len, w_self)
+ if widen(res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.newint(res)
-def wrap_lenfunc(space, w_self, w_args, func):
- func_len = rffi.cast(lenfunc, func)
- check_num_args(space, w_args, 0)
- res = generic_cpy_call(space, func_len, w_self)
- if widen(res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.newint(res)
+class wrap_sq_item(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ssizeargfunc, func)
+ w_index = __args__.arguments_w[0]
+ index = space.int_w(space.index(w_index))
+ return generic_cpy_call(space, func_target, w_self, index)
-def wrap_sq_item(space, w_self, w_args, func):
- func_target = rffi.cast(ssizeargfunc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- index = space.int_w(space.index(args_w[0]))
- return generic_cpy_call(space, func_target, w_self, index)
+class wrap_sq_setitem(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 2)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ssizeobjargproc, func)
+ w_index = __args__.arguments_w[0]
+ w_value = __args__.arguments_w[1]
+ index = space.int_w(space.index(w_index))
+ res = generic_cpy_call(space, func_target, w_self, index, w_value)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
-def wrap_sq_setitem(space, w_self, w_args, func):
- func_target = rffi.cast(ssizeobjargproc, func)
- check_num_args(space, w_args, 2)
- args_w = space.fixedview(w_args)
- index = space.int_w(space.index(args_w[0]))
- res = generic_cpy_call(space, func_target, w_self, index, args_w[1])
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
-
-def wrap_sq_delitem(space, w_self, w_args, func):
- func_target = rffi.cast(ssizeobjargproc, func)
- check_num_args(space, w_args, 1)
- args_w = space.fixedview(w_args)
- index = space.int_w(space.index(args_w[0]))
- null = rffi.cast(PyObject, 0)
- res = generic_cpy_call(space, func_target, w_self, index, null)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
+class wrap_sq_delitem(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ssizeobjargproc, func)
+ w_index = __args__.arguments_w[0]
+ index = space.int_w(space.index(w_index))
+ null = rffi.cast(PyObject, 0)
+ res = generic_cpy_call(space, func_target, w_self, index, null)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
# Warning, confusing function name (like CPython). Used only for sq_contains.
-def wrap_objobjproc(space, w_self, w_args, func):
- func_target = rffi.cast(objobjproc, func)
- check_num_args(space, w_args, 1)
- w_value, = space.fixedview(w_args)
- res = generic_cpy_call(space, func_target, w_self, w_value)
- res = rffi.cast(lltype.Signed, res)
- if res == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.newbool(bool(res))
+class wrap_objobjproc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(objobjproc, func)
+ w_value = __args__.arguments_w[0]
+ res = generic_cpy_call(space, func_target, w_self, w_value)
+ res = rffi.cast(lltype.Signed, res)
+ if res == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.newbool(bool(res))
-def wrap_objobjargproc(space, w_self, w_args, func):
- func_target = rffi.cast(objobjargproc, func)
- check_num_args(space, w_args, 2)
- w_key, w_value = space.fixedview(w_args)
- res = generic_cpy_call(space, func_target, w_self, w_key, w_value)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.w_None
+class wrap_objobjargproc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 2)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(objobjargproc, func)
+ w_key = __args__.arguments_w[0]
+ w_value = __args__.arguments_w[1]
+ res = generic_cpy_call(space, func_target, w_self, w_key, w_value)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.w_None
-def wrap_delitem(space, w_self, w_args, func):
- func_target = rffi.cast(objobjargproc, func)
- check_num_args(space, w_args, 1)
- w_key, = space.fixedview(w_args)
- null = rffi.cast(PyObject, 0)
- res = generic_cpy_call(space, func_target, w_self, w_key, null)
- if rffi.cast(lltype.Signed, res) == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.w_None
+class wrap_delitem(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(objobjargproc, func)
+ w_key = __args__.arguments_w[0]
+ null = rffi.cast(PyObject, 0)
+ res = generic_cpy_call(space, func_target, w_self, w_key, null)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.w_None
-def wrap_ssizessizeargfunc(space, w_self, w_args, func):
- func_target = rffi.cast(ssizessizeargfunc, func)
- check_num_args(space, w_args, 2)
- args_w = space.fixedview(w_args)
- start = space.int_w(args_w[0])
- end = space.int_w(args_w[1])
- return generic_cpy_call(space, func_target, w_self, start, end)
+class wrap_ssizessizeargfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 2)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(ssizessizeargfunc, func)
+ start = space.int_w(__args__.arguments_w[0])
+ end = space.int_w(__args__.arguments_w[1])
+ return generic_cpy_call(space, func_target, w_self, start, end)
-def wrap_next(space, w_self, w_args, func):
- from pypy.module.cpyext.api import generic_cpy_call_expect_null
- func_target = rffi.cast(iternextfunc, func)
- check_num_args(space, w_args, 0)
- w_res = generic_cpy_call_expect_null(space, func_target, w_self)
- if not w_res and not PyErr_Occurred(space):
- raise OperationError(space.w_StopIteration, space.w_None)
- return w_res
+class wrap_next(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ from pypy.module.cpyext.api import generic_cpy_call_expect_null
+ self.check_args(__args__, 0)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(iternextfunc, func)
+ w_res = generic_cpy_call_expect_null(space, func_target, w_self)
+ if not w_res and not PyErr_Occurred(space):
+ raise OperationError(space.w_StopIteration, space.w_None)
+ return w_res
-def wrap_hashfunc(space, w_self, w_args, func):
- func_target = rffi.cast(hashfunc, func)
- check_num_args(space, w_args, 0)
- res = generic_cpy_call(space, func_target, w_self)
- if res == -1:
- space.fromcache(State).check_and_raise_exception(always=True)
- return space.newint(res)
+class wrap_hashfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 0)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(hashfunc, func)
+ res = generic_cpy_call(space, func_target, w_self)
+ if res == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.newint(res)
-def wrap_getreadbuffer(space, w_self, w_args, func):
- func_target = rffi.cast(readbufferproc, func)
- py_type = _get_ob_type(space, w_self)
- rbp = rffi.cast(rffi.VOIDP, 0)
- if py_type.c_tp_as_buffer:
- rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
- with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
- index = rffi.cast(Py_ssize_t, 0)
- size = generic_cpy_call(space, func_target, w_self, index, ptr)
- if size < 0:
- space.fromcache(State).check_and_raise_exception(always=True)
- view = CPyBuffer(space, ptr[0], size, w_self,
- releasebufferproc=rbp)
- fq.register_finalizer(view)
- return space.newbuffer(CBuffer(view))
+class wrap_getreadbuffer(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_target = rffi.cast(readbufferproc, func)
+ py_type = _get_ob_type(space, w_self)
+ rbp = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+ with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
+ index = rffi.cast(Py_ssize_t, 0)
+ size = generic_cpy_call(space, func_target, w_self, index, ptr)
+ if size < 0:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ view = CPyBuffer(space, ptr[0], size, w_self,
+ releasebufferproc=rbp)
+ fq.register_finalizer(view)
+ return space.newbuffer(CBuffer(view))
-def wrap_getwritebuffer(space, w_self, w_args, func):
- func_target = rffi.cast(readbufferproc, func)
- py_type = _get_ob_type(space, w_self)
- rbp = rffi.cast(rffi.VOIDP, 0)
- if py_type.c_tp_as_buffer:
- rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
- with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
- index = rffi.cast(Py_ssize_t, 0)
- size = generic_cpy_call(space, func_target, w_self, index, ptr)
- if size < 0:
- space.fromcache(State).check_and_raise_exception(always=True)
- view = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
- releasebufferproc=rbp)
- fq.register_finalizer(view)
- return space.newbuffer(CBuffer(view))
+class wrap_getwritebuffer(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_target = rffi.cast(readbufferproc, func)
+ py_type = _get_ob_type(space, w_self)
+ rbp = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+ with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
+ index = rffi.cast(Py_ssize_t, 0)
+ size = generic_cpy_call(space, func_target, w_self, index, ptr)
+ if size < 0:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ view = CPyBuffer(space, ptr[0], size, w_self, readonly=False,
+ releasebufferproc=rbp)
+ fq.register_finalizer(view)
+ return space.newbuffer(CBuffer(view))
-def wrap_getbuffer(space, w_self, w_args, func):
- func_target = rffi.cast(getbufferproc, func)
- py_type = _get_ob_type(space, w_self)
- rbp = rffi.cast(rffi.VOIDP, 0)
- if py_type.c_tp_as_buffer:
- rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
- with lltype.scoped_alloc(Py_buffer) as pybuf:
- _flags = 0
- if space.len_w(w_args) > 0:
- _flags = space.int_w(space.listview(w_args)[0])
- flags = rffi.cast(rffi.INT_real,_flags)
- size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
- if widen(size) < 0:
- space.fromcache(State).check_and_raise_exception(always=True)
- ptr = pybuf.c_buf
- size = pybuf.c_len
- ndim = widen(pybuf.c_ndim)
- shape = None
- if pybuf.c_shape:
- shape = [pybuf.c_shape[i] for i in range(ndim)]
- strides = None
- if pybuf.c_strides:
- strides = [pybuf.c_strides[i] for i in range(ndim)]
- if pybuf.c_format:
- format = rffi.charp2str(pybuf.c_format)
- else:
- format = 'B'
- # the CPython docs mandates that you do an incref whenever you call
- # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't
- # leak we release the buffer:
- # https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
- buf = CPyBuffer(space, ptr, size, w_self, format=format,
- ndim=ndim, shape=shape, strides=strides,
- itemsize=pybuf.c_itemsize,
- readonly=widen(pybuf.c_readonly),
- needs_decref=True,
- releasebufferproc = rbp)
- fq.register_finalizer(buf)
- return buf.wrap(space)
+
+class wrap_getbuffer(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ func = self.get_func_to_call()
+ func_target = rffi.cast(getbufferproc, func)
+ py_type = _get_ob_type(space, w_self)
+ rbp = rffi.cast(rffi.VOIDP, 0)
+ if py_type.c_tp_as_buffer:
+ rbp = rffi.cast(rffi.VOIDP, py_type.c_tp_as_buffer.c_bf_releasebuffer)
+ with lltype.scoped_alloc(Py_buffer) as pybuf:
+ _flags = 0
+ if len(__args__.arguments_w) > 0:
+ _flags = space.int_w(__args__.arguments_w[0])
+ flags = rffi.cast(rffi.INT_real,_flags)
+ size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
+ if widen(size) < 0:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ ptr = pybuf.c_buf
+ size = pybuf.c_len
+ ndim = widen(pybuf.c_ndim)
+ shape = None
+ if pybuf.c_shape:
+ shape = [pybuf.c_shape[i] for i in range(ndim)]
+ strides = None
+ if pybuf.c_strides:
+ strides = [pybuf.c_strides[i] for i in range(ndim)]
+ if pybuf.c_format:
+ format = rffi.charp2str(pybuf.c_format)
+ else:
+ format = 'B'
+ # the CPython docs mandates that you do an incref whenever you call
+ # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't
+ # leak we release the buffer:
+ # https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer
+ buf = CPyBuffer(space, ptr, size, w_self, format=format,
+ ndim=ndim, shape=shape, strides=strides,
+ itemsize=pybuf.c_itemsize,
+ readonly=widen(pybuf.c_readonly),
+ needs_decref=True,
+ releasebufferproc = rbp)
+ fq.register_finalizer(buf)
+ return buf.wrap(space)
def get_richcmp_func(OP_CONST):
- def inner(space, w_self, w_args, func):
- func_target = rffi.cast(richcmpfunc, func)
- check_num_args(space, w_args, 1)
- w_other, = space.fixedview(w_args)
- return generic_cpy_call(space, func_target,
- w_self, w_other, rffi.cast(rffi.INT_real, OP_CONST))
- return inner
+ class wrap_richcmp(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(richcmpfunc, func)
+ w_other = __args__.arguments_w[0]
+ return generic_cpy_call(space, func_target,
+ w_self, w_other, rffi.cast(rffi.INT_real, OP_CONST))
+ return wrap_richcmp
richcmp_eq = get_richcmp_func(Py_EQ)
richcmp_ne = get_richcmp_func(Py_NE)
@@ -410,17 +465,19 @@
richcmp_gt = get_richcmp_func(Py_GT)
richcmp_ge = get_richcmp_func(Py_GE)
-def wrap_cmpfunc(space, w_self, w_args, func):
- func_target = rffi.cast(cmpfunc, func)
- check_num_args(space, w_args, 1)
- w_other, = space.fixedview(w_args)
+class wrap_cmpfunc(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ self.check_args(__args__, 1)
+ func = self.get_func_to_call()
+ func_target = rffi.cast(cmpfunc, func)
+ w_other = __args__.arguments_w[0]
- if not space.issubtype_w(space.type(w_self), space.type(w_other)):
- raise oefmt(space.w_TypeError,
- "%T.__cmp__(x,y) requires y to be a '%T', not a '%T'",
- w_self, w_self, w_other)
+ if not space.issubtype_w(space.type(w_self), space.type(w_other)):
+ raise oefmt(space.w_TypeError,
+ "%T.__cmp__(x,y) requires y to be a '%T', not a '%T'",
+ w_self, w_self, w_other)
- return space.newint(generic_cpy_call(space, func_target, w_self, w_other))
+ return space.newint(generic_cpy_call(space, func_target, w_self, w_other))
SLOT_FACTORIES = {}
def slot_factory(tp_name):
@@ -804,9 +861,10 @@
missing_wrappers = ['wrap_indexargfunc', 'wrap_delslice', 'wrap_coercefunc']
for name in missing_wrappers:
assert name not in globals()
- def missing_wrapper(space, w_self, w_args, func):
- print "cpyext: missing slot wrapper " + name
- raise NotImplementedError("Slot wrapper " + name)
+ class missing_wrapper(W_PyCWrapperObject):
+ def call(self, space, w_self, __args__):
+ print "cpyext: missing slot wrapper " + name
+ raise NotImplementedError("Slot wrapper " + name)
missing_wrapper.__name__ = name
globals()[name] = missing_wrapper
@@ -836,13 +894,12 @@
PyWrapperFlag_KEYWORDS = 1
class TypeSlot:
- def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc):
+ def __init__(self, method_name, slot_name, function, wrapper, doc):
self.method_name = method_name
self.slot_name = slot_name
self.slot_names = tuple(("c_" + slot_name).split("."))
self.slot_func = function
- self.wrapper_func = wrapper1
- self.wrapper_func_kwds = wrapper2
+ self.wrapper_class = wrapper
self.doc = doc
# adapted from typeobject.c
@@ -863,13 +920,7 @@
function = getattr(userslot, FUNCTION or '!missing', None)
assert FLAGS == 0 or FLAGS == PyWrapperFlag_KEYWORDS
- if FLAGS:
- wrapper1 = None
- wrapper2 = wrapper
- else:
- wrapper1 = wrapper
- wrapper2 = None
- return TypeSlot(NAME, SLOT, function, wrapper1, wrapper2, DOC)
+ return TypeSlot(NAME, SLOT, function, wrapper, DOC)
def TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC):
return FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, 0)
@@ -1158,7 +1209,7 @@
x.slot_func.api_func if x.slot_func else None) for x in slotdefs])
slotdefs_for_wrappers = unrolling_iterable(
- [(x.method_name, x.slot_names, x.wrapper_func, x.wrapper_func_kwds, x.doc)
+ [(x.method_name, x.slot_names, x.wrapper_class, x.doc)
for x in slotdefs])
if __name__ == "__main__":
diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -2,11 +2,18 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter import executioncontext
+from pypy.interpreter.executioncontext import ExecutionContext
from rpython.rtyper.annlowlevel import llhelper
from rpython.rlib.rdynload import DLLHANDLE
from rpython.rlib import rawrefcount
import sys
+
+# Keep track of exceptions raised in cpyext for a particular execution
+# context.
+ExecutionContext.cpyext_operror = None
+
+
class State:
def __init__(self, space):
self.space = space
@@ -18,7 +25,8 @@
def reset(self):
from pypy.module.cpyext.modsupport import PyMethodDef
- self.operror = None
+ ec = self.space.getexecutioncontext()
+ ec.cpyext_operror = None
self.new_method_def = lltype.nullptr(PyMethodDef)
# When importing a package, use this to keep track
@@ -37,17 +45,24 @@
def set_exception(self, operror):
self.clear_exception()
- self.operror = operror
+ ec = self.space.getexecutioncontext()
+ ec.cpyext_operror = operror
def clear_exception(self):
"""Clear the current exception state, and return the operror."""
- operror = self.operror
- self.operror = None
+ ec = self.space.getexecutioncontext()
+ operror = ec.cpyext_operror
+ ec.cpyext_operror = None
return operror
+ def get_exception(self):
+ ec = self.space.getexecutioncontext()
+ return ec.cpyext_operror
+
@specialize.arg(1)
def check_and_raise_exception(self, always=False):
- operror = self.operror
+ ec = self.space.getexecutioncontext()
+ operror = ec.cpyext_operror
if operror:
self.clear_exception()
raise operror
diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c
--- a/pypy/module/cpyext/test/array.c
+++ b/pypy/module/cpyext/test/array.c
@@ -2202,6 +2202,16 @@
Py_RETURN_NONE;
};
+static PyObject *
+getitem(PyObject* self, PyObject * args) {
+ PyObject * obj;
+ int i;
+ if (!PyArg_ParseTuple(args, "Oi", &obj, &i)) {
+ return NULL;
+ }
+ return PySequence_ITEM(obj, i);
+}
+
PyDoc_STRVAR(module_doc,
"This module defines an object type which can efficiently represent\n\
an array of basic values: characters, integers, floating point\n\
@@ -2491,6 +2501,7 @@
{"get_releasebuffer_cnt", (PyCFunction)get_releasebuffer_cnt, METH_NOARGS, NULL},
{"create_and_release_buffer", (PyCFunction)create_and_release_buffer, METH_O, NULL},
{"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL},
+ {"getitem", (PyCFunction)getitem, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/pypy/module/cpyext/test/test_api.py b/pypy/module/cpyext/test/test_api.py
--- a/pypy/module/cpyext/test/test_api.py
+++ b/pypy/module/cpyext/test/test_api.py
@@ -39,7 +39,7 @@
raise Exception("%s is not callable" % (f,))
f(*args)
state = space.fromcache(State)
- operror = state.operror
+ operror = state.get_exception()
if not operror:
raise Exception("DID NOT RAISE")
if getattr(space, 'w_' + expected_exc.__name__) is not operror.w_type:
diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -76,7 +76,9 @@
else:
expected = '\x01\0\0\0' '\x02\0\0\0' '\x03\0\0\0' '\x04\0\0\0'
assert str(buf) == expected
- assert str(buffer('') + arr) == expected
+ assert str(buffer('a') + arr) == "a" + expected
+ # python2 special cases empty-buffer + obj
+ assert str(buffer('') + arr) == "array('i', [1, 2, 3, 4])"
def test_releasebuffer(self):
module = self.import_module(name='array')
@@ -172,3 +174,15 @@
fd = BytesIO()
# only test that it works
fd.write(a)
+
+ def test_getitem_via_PySequence_GetItem(self):
+ module = self.import_module(name='array')
+ a = module.array('i', range(10))
+ # call via tp_as_mapping.mp_subscript
+ assert 5 == a[-5]
+ # PySequence_ITEM used to call space.getitem() which
+ # prefers tp_as_mapping.mp_subscript over tp_as_sequence.sq_item
+ # Now fixed so this test raises (array_item does not add len(a),
+ # array_subscr does)
+ raises(IndexError, module.getitem, a, -5)
+
diff --git a/pypy/module/cpyext/test/test_boolobject.py b/pypy/module/cpyext/test/test_boolobject.py
--- a/pypy/module/cpyext/test/test_boolobject.py
+++ b/pypy/module/cpyext/test/test_boolobject.py
@@ -1,7 +1,6 @@
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.boolobject import PyBool_Check, PyBool_FromLong
-from pypy.module.cpyext.floatobject import PyFloat_FromDouble
+from pypy.module.cpyext.boolobject import PyBool_FromLong
class TestBoolObject(BaseApiTest):
def test_fromlong(self, space):
@@ -12,12 +11,6 @@
else:
assert obj is space.w_False
- def test_check(self, space):
- assert PyBool_Check(space, space.w_True)
- assert PyBool_Check(space, space.w_False)
- assert not PyBool_Check(space, space.w_None)
- assert not PyBool_Check(space, PyFloat_FromDouble(space, 1.0))
-
class AppTestBoolMacros(AppTestCpythonExtensionBase):
def test_macros(self):
module = self.import_extension('foo', [
@@ -42,4 +35,14 @@
assert module.to_int(False) == 0
assert module.to_int(True) == 1
-
+ def test_check(self):
+ module = self.import_extension('foo', [
+ ("type_check", "METH_O",
+ '''
+ return PyLong_FromLong(PyBool_Check(args));
+ ''')])
+ assert module.type_check(True)
+ assert module.type_check(False)
+ assert not module.type_check(None)
+ assert not module.type_check(1.0)
+
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -636,7 +636,8 @@
Py_ssize_t refcnt_after;
Py_INCREF(true_obj);
Py_INCREF(true_obj);
- PyBool_Check(true_obj);
+ if (!PyBool_Check(true_obj))
+ Py_RETURN_NONE;
refcnt_after = true_obj->ob_refcnt;
Py_DECREF(true_obj);
Py_DECREF(true_obj);
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
@@ -102,9 +102,11 @@
"""
PyObject* pyobj = PyFloat_FromDouble(1.0);
PyFloatObject* pfo = (PyFloatObject*)pyobj;
- int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) &&
- PyFloat_Check(pfo) && PyFloat_CheckExact(pfo);
+ int res = (PyFloat_Check(pyobj) +
+ PyFloat_CheckExact(pyobj) * 10 +
+ PyFloat_Check(pfo) * 100 +
+ PyFloat_CheckExact(pfo) * 1000);
Py_DecRef(pyobj);
return PyLong_FromLong(res);"""),
])
- assert module.test() == 1
+ assert module.test() == 1111
diff --git a/pypy/module/cpyext/test/test_number.py b/pypy/module/cpyext/test/test_number.py
--- a/pypy/module/cpyext/test/test_number.py
+++ b/pypy/module/cpyext/test/test_number.py
@@ -11,7 +11,6 @@
PyNumber_Index, PyNumber_Coerce, PyNumber_CoerceEx, PyNumber_Add,
PyNumber_Multiply, PyNumber_InPlaceMultiply, PyNumber_Absolute,
PyNumber_Power, PyNumber_InPlacePower)
-from pypy.module.cpyext.floatobject import PyFloat_Check
from pypy.module.cpyext.intobject import PyInt_CheckExact
from pypy.module.cpyext.longobject import PyLong_CheckExact
from pypy.module.cpyext.object import PyObject_Size
@@ -86,7 +85,7 @@
w_res = from_ref(space, ppl[0])
- assert PyFloat_Check(space, w_res)
+ assert space.isinstance_w(w_res, space.w_float)
assert space.unwrap(w_res) == 123.
decref(space, pl)
decref(space, pf)
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
@@ -52,7 +52,8 @@
api.PyErr_SetObject(space.w_ValueError, space.wrap("a value"))
assert api.PyErr_Occurred() is space.w_ValueError
state = space.fromcache(State)
- assert space.eq_w(state.operror.get_w_value(space),
+ operror = state.get_exception()
+ assert space.eq_w(operror.get_w_value(space),
space.wrap("a value"))
api.PyErr_Clear()
@@ -60,12 +61,14 @@
def test_SetNone(self, space, api):
api.PyErr_SetNone(space.w_KeyError)
state = space.fromcache(State)
- assert space.eq_w(state.operror.w_type, space.w_KeyError)
- assert space.eq_w(state.operror.get_w_value(space), space.w_None)
+ operror = state.get_exception()
+ assert space.eq_w(operror.w_type, space.w_KeyError)
+ assert space.eq_w(operror.get_w_value(space), space.w_None)
api.PyErr_Clear()
api.PyErr_NoMemory()
- assert space.eq_w(state.operror.w_type, space.w_MemoryError)
+ operror = state.get_exception()
+ assert space.eq_w(operror.w_type, space.w_MemoryError)
api.PyErr_Clear()
def test_Warning(self, space, api, capfd):
@@ -437,3 +440,59 @@
'''),
])
raises(SystemError, module.oops)
+
+ def test_error_thread_race(self):
+ # Check race condition: thread 0 returns from cpyext with error set,
+ # after thread 1 has set an error but before it returns.
+ module = self.import_extension('foo', [
+ ("emit_error", "METH_VARARGS",
+ '''
+ PyThreadState *save = NULL;
+ PyGILState_STATE gilsave;
+
+ /* NB. synchronization due to GIL */
+ static volatile int flag = 0;
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i", &id))
+ return NULL;
+
+ /* Proceed in thread 1 first */
+ save = PyEval_SaveThread();
+ while (id == 0 && flag == 0);
+ gilsave = PyGILState_Ensure();
+
+ PyErr_Format(PyExc_ValueError, "%d", id);
+
+ /* Proceed in thread 0 first */
+ if (id == 1) flag = 1;
+ PyGILState_Release(gilsave);
+ while (id == 1 && flag == 1);
+ PyEval_RestoreThread(save);
+
+ if (id == 0) flag = 0;
+ return NULL;
+ '''
+ ),
+ ])
+
+ import threading
+
+ failures = []
+
+ def worker(arg):
+ try:
+ module.emit_error(arg)
+ failures.append(True)
+ except Exception as exc:
+ if str(exc) != str(arg):
+ failures.append(exc)
+
+ threads = [threading.Thread(target=worker, args=(j,))
+ for j in (0, 1)]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+
+ assert not failures
diff --git a/pypy/module/cpyext/test/test_sliceobject.py b/pypy/module/cpyext/test/test_sliceobject.py
--- a/pypy/module/cpyext/test/test_sliceobject.py
+++ b/pypy/module/cpyext/test/test_sliceobject.py
@@ -2,14 +2,8 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.api import Py_ssize_t, Py_ssize_tP
-from pypy.module.cpyext.sliceobject import PySlice_Check
class TestSliceObject(BaseApiTest):
- def test_slice(self, space):
- w_i = space.wrap(10)
- w_slice = space.newslice(w_i, w_i, w_i)
- assert PySlice_Check(space, w_slice)
- assert not PySlice_Check(space, w_i)
def test_GetIndicesEx(self, space, api):
w = space.wrap
@@ -79,3 +73,14 @@
"""),
])
assert module.get_ellipsis() is Ellipsis
+
+ def test_typecheck(self):
+ module = self.import_extension('foo', [
+ ("check", "METH_O",
+ """
+ PySliceObject *slice = (PySliceObject *)args;
+ return PyLong_FromLong(PySlice_Check(slice));
+ """),
+ ])
+ s = slice(10, 20, 30)
+ assert module.check(s)
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
@@ -1,3 +1,4 @@
+import pytest
from pypy.interpreter import gateway
from rpython.rtyper.lltypesystem import rffi
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
@@ -6,6 +7,7 @@
from pypy.module.cpyext.pyobject import make_ref, from_ref, decref, as_pyobj
from pypy.module.cpyext.typeobject import PyTypeObjectPtr
+
class AppTestTypeObject(AppTestCpythonExtensionBase):
def setup_class(cls):
@@ -136,8 +138,10 @@
module = self.import_module(name='foo')
descr = module.fooType.copy
assert type(descr).__name__ == 'method_descriptor'
- assert str(descr) == "<method 'copy' of 'foo.foo' objects>"
- assert repr(descr) == "<method 'copy' of 'foo.foo' objects>"
+ assert str(descr) in ("<method 'copy' of 'foo.foo' objects>",
+ "<method 'copy' of 'foo' objects>")
+ assert repr(descr) in ("<method 'copy' of 'foo.foo' objects>",
+ "<method 'copy' of 'foo' objects>")
raises(TypeError, descr, None)
def test_cython_fake_classmethod(self):
@@ -250,7 +254,7 @@
import re
assert re.sre_compile._sre is module
s = u"Foo " * 1000 + u"Bar"
- prog = re.compile(ur"Foo.*Bar")
+ prog = re.compile(u"Foo.*Bar")
assert prog.match(s)
m = re.search(u"xyz", u"xyzxyz")
assert m
@@ -319,7 +323,7 @@
def test_tp_dict(self):
foo = self.import_module("foo")
module = self.import_extension('test', [
- ("read_tp_dict", "METH_O",
+ ("read_tp_dict", "METH_O",
'''
PyObject *method;
if (!args->ob_type->tp_dict)
@@ -420,7 +424,7 @@
return NULL;
Py_DECREF(a1);
PyType_Modified(type);
- value = PyObject_GetAttrString((PyObject*)type, "a");
+ value = PyObject_GetAttrString((PyObject *)type, "a");
Py_DECREF(value);
if (PyDict_SetItemString(type->tp_dict, "a",
@@ -428,7 +432,7 @@
return NULL;
Py_DECREF(a2);
PyType_Modified(type);
- value = PyObject_GetAttrString((PyObject*)type, "a");
+ value = PyObject_GetAttrString((PyObject *)type, "a");
return value;
'''
)
@@ -529,7 +533,7 @@
py_type = rffi.cast(PyTypeObjectPtr, ref)
w_dict = from_ref(space, py_type.c_tp_dict)
- w_name = space.wrap('a')
+ w_name = space.newtext('a')
space.setitem(w_dict, w_name, space.wrap(1))
assert space.int_w(space.getattr(w_class, w_name)) == 1
space.delitem(w_dict, w_name)
@@ -611,16 +615,21 @@
module = self.import_extension('foo', [
("test_tp_getattro", "METH_VARARGS",
'''
+ #if PY_MAJOR_VERSION > 2
+ #define PyString_FromString PyUnicode_FromString
+ #define PyIntObject PyLongObject
+ #define PyInt_AsLong PyLong_AsLong
+ #endif
PyObject *name, *obj = PyTuple_GET_ITEM(args, 0);
- PyIntObject *attr, *value = (PyIntObject*) PyTuple_GET_ITEM(args, 1);
+ PyObject *attr, *value = PyTuple_GET_ITEM(args, 1);
if (!obj->ob_type->tp_getattro)
{
PyErr_SetString(PyExc_ValueError, "missing tp_getattro");
return NULL;
}
name = PyString_FromString("attr1");
- attr = (PyIntObject*) obj->ob_type->tp_getattro(obj, name);
- if (attr->ob_ival != value->ob_ival)
+ attr = obj->ob_type->tp_getattro(obj, name);
+ if (PyInt_AsLong(attr) != PyInt_AsLong(value))
{
PyErr_SetString(PyExc_ValueError,
"tp_getattro returned wrong value");
@@ -629,7 +638,7 @@
Py_DECREF(name);
Py_DECREF(attr);
name = PyString_FromString("attr2");
- attr = (PyIntObject*) obj->ob_type->tp_getattro(obj, name);
+ attr = obj->ob_type->tp_getattro(obj, name);
if (attr == NULL && PyErr_ExceptionMatches(PyExc_AttributeError))
{
PyErr_Clear();
@@ -652,6 +661,9 @@
module = self.import_extension('foo', [
("get_foo", "METH_O",
'''
+ #if PY_MAJOR_VERSION > 2
+ #define PyString_FromString PyUnicode_FromString
More information about the pypy-commit
mailing list