Author: Carl Friedrich Bolz-Tereick <cfb...@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-...@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-...@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)) -@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit