Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r91355:64deec640157 Date: 2017-05-20 21:58 +0100 http://bitbucket.org/pypy/pypy/changeset/64deec640157/
Log: hg merge default diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py --- a/lib_pypy/_ctypes/function.py +++ b/lib_pypy/_ctypes/function.py @@ -1,4 +1,3 @@ - from _ctypes.basics import _CData, _CDataMeta, cdata_from_address from _ctypes.primitive import SimpleType, _SimpleCData from _ctypes.basics import ArgumentError, keepalive_key @@ -9,13 +8,16 @@ import sys import traceback -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f + +try: + from __pypy__ import builtinify +except ImportError: + builtinify = lambda f: f # XXX this file needs huge refactoring I fear -PARAMFLAG_FIN = 0x1 -PARAMFLAG_FOUT = 0x2 +PARAMFLAG_FIN = 0x1 +PARAMFLAG_FOUT = 0x2 PARAMFLAG_FLCID = 0x4 PARAMFLAG_COMBINED = PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID @@ -24,7 +26,7 @@ PARAMFLAG_FIN, PARAMFLAG_FIN | PARAMFLAG_FOUT, PARAMFLAG_FIN | PARAMFLAG_FLCID - ) +) WIN64 = sys.platform == 'win32' and sys.maxsize == 2**63 - 1 @@ -35,6 +37,7 @@ from _ctypes import COMError return COMError(errcode, None, None) + @builtinify def call_function(func, args): "Only for debugging so far: So that we can call CFunction instances" @@ -93,14 +96,9 @@ "item %d in _argtypes_ has no from_param method" % ( i + 1,)) self._argtypes_ = list(argtypes) - self._check_argtypes_for_fastpath() + argtypes = property(_getargtypes, _setargtypes) - def _check_argtypes_for_fastpath(self): - if all([hasattr(argtype, '_ffiargshape_') for argtype in self._argtypes_]): - fastpath_cls = make_fastpath_subclass(self.__class__) - fastpath_cls.enable_fastpath_maybe(self) - def _getparamflags(self): return self._paramflags @@ -125,27 +123,26 @@ raise TypeError( "paramflags must be a sequence of (int [,string [,value]]) " "tuples" - ) + ) if not isinstance(flag, int): raise TypeError( "paramflags must be a sequence of (int [,string [,value]]) " "tuples" - ) + ) _flag = flag & PARAMFLAG_COMBINED if _flag == PARAMFLAG_FOUT: typ = self._argtypes_[idx] if getattr(typ, '_ffiargshape_', None) not in ('P', 'z', 'Z'): raise TypeError( "'out' parameter %d must be a pointer type, not %s" - % (idx+1, type(typ).__name__) - ) + % (idx + 1, type(typ).__name__) + ) elif _flag not in VALID_PARAMFLAGS: raise TypeError("paramflag value %d not supported" % flag) self._paramflags = paramflags paramflags = property(_getparamflags, _setparamflags) - def _getrestype(self): return self._restype_ @@ -155,7 +152,7 @@ from ctypes import c_int restype = c_int if not (isinstance(restype, _CDataMeta) or restype is None or - callable(restype)): + callable(restype)): raise TypeError("restype must be a type, a callable, or None") self._restype_ = restype @@ -167,15 +164,18 @@ def _geterrcheck(self): return getattr(self, '_errcheck_', None) + def _seterrcheck(self, errcheck): if not callable(errcheck): raise TypeError("The errcheck attribute must be callable") self._errcheck_ = errcheck + def _delerrcheck(self): try: del self._errcheck_ except AttributeError: pass + errcheck = property(_geterrcheck, _seterrcheck, _delerrcheck) def _ffishapes(self, args, restype): @@ -187,7 +187,7 @@ raise TypeError("invalid result type for callback function") restype = restype._ffiargshape_ else: - restype = 'O' # void + restype = 'O' # void return argtypes, restype def _set_address(self, address): @@ -200,7 +200,7 @@ def __init__(self, *args): self.name = None - self._objects = {keepalive_key(0):self} + self._objects = {keepalive_key(0): self} self._needs_free = True # Empty function object -- this is needed for casts @@ -221,10 +221,8 @@ if self._argtypes_ is None: self._argtypes_ = [] self._ptr = self._getfuncptr_fromaddress(self._argtypes_, restype) - self._check_argtypes_for_fastpath() return - # A callback into python if callable(argument) and not argsl: self.callable = argument @@ -258,7 +256,7 @@ if (sys.platform == 'win32' and isinstance(argument, int) and argsl): ffiargs, ffires = self._ffishapes(self._argtypes_, self._restype_) - self._com_index = argument + 0x1000 + self._com_index = argument + 0x1000 self.name = argsl.pop(0) if argsl: self.paramflags = argsl.pop(0) @@ -280,6 +278,7 @@ except SystemExit as e: handle_system_exit(e) raise + return f def __call__(self, *args, **kwargs): @@ -328,7 +327,7 @@ # really slow". Now we don't worry that much about slowness # of ctypes, and it's strange to get warnings for perfectly- # legal code. - #warnings.warn('C function without declared arguments called', + # warnings.warn('C function without declared arguments called', # RuntimeWarning, stacklevel=2) argtypes = [] @@ -337,7 +336,7 @@ if not args: raise ValueError( "native COM method call without 'this' parameter" - ) + ) thisvalue = args[0] thisarg = cast(thisvalue, POINTER(POINTER(c_void_p))) keepalives, newargs, argtypes, outargs, errcheckargs = ( @@ -366,7 +365,6 @@ return tuple(outargs) def _call_funcptr(self, funcptr, *newargs): - if self._flags_ & _rawffi.FUNCFLAG_USE_ERRNO: tmp = _rawffi.get_errno() _rawffi.set_errno(get_errno()) @@ -431,7 +429,7 @@ ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes] ffires = restype.get_ffi_argtype() return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires, self._flags_) - + cdll = self.dll._handle try: ffi_argtypes = [argtype.get_ffi_argtype() for argtype in argtypes] @@ -450,7 +448,7 @@ # funcname -> _funcname@<n> # where n is 0, 4, 8, 12, ..., 128 for i in range(33): - mangled_name = "_%s@%d" % (self.name, i*4) + mangled_name = "_%s@%d" % (self.name, i * 4) try: return cdll.getfunc(mangled_name, ffi_argtypes, ffi_restype, @@ -492,7 +490,7 @@ for argtype, arg in zip(argtypes, args): param = argtype.from_param(arg) _type_ = getattr(argtype, '_type_', None) - if _type_ == 'P': # special-case for c_void_p + if _type_ == 'P': # special-case for c_void_p param = param._get_buffer_value() elif self._is_primitive(argtype): param = param.value @@ -668,69 +666,11 @@ self._needs_free = False -def make_fastpath_subclass(CFuncPtr): - if CFuncPtr._is_fastpath: - return CFuncPtr - # - try: - return make_fastpath_subclass.memo[CFuncPtr] - except KeyError: - pass - - class CFuncPtrFast(CFuncPtr): - - _is_fastpath = True - _slowpath_allowed = True # set to False by tests - - @classmethod - def enable_fastpath_maybe(cls, obj): - if (obj.callable is None and - obj._com_index is None): - obj.__class__ = cls - - def __rollback(self): - assert self._slowpath_allowed - self.__class__ = CFuncPtr - - # disable the fast path if we reset argtypes - def _setargtypes(self, argtypes): - self.__rollback() - self._setargtypes(argtypes) - argtypes = property(CFuncPtr._getargtypes, _setargtypes) - - def _setcallable(self, func): - self.__rollback() - self.callable = func - callable = property(lambda x: None, _setcallable) - - def _setcom_index(self, idx): - self.__rollback() - self._com_index = idx - _com_index = property(lambda x: None, _setcom_index) - - def __call__(self, *args): - thisarg = None - argtypes = self._argtypes_ - restype = self._restype_ - funcptr = self._getfuncptr(argtypes, restype, thisarg) - try: - result = self._call_funcptr(funcptr, *args) - result, _ = self._do_errcheck(result, args) - except (TypeError, ArgumentError, UnicodeDecodeError): - assert self._slowpath_allowed - return CFuncPtr.__call__(self, *args) - return result - - make_fastpath_subclass.memo[CFuncPtr] = CFuncPtrFast - return CFuncPtrFast -make_fastpath_subclass.memo = {} - - def handle_system_exit(e): # issue #1194: if we get SystemExit here, then exit the interpreter. # Highly obscure imho but some people seem to depend on it. if sys.flags.inspect: - return # Don't exit if -i flag was given. + return # Don't exit if -i flag was given. else: code = e.code if isinstance(code, int): diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -46,8 +46,9 @@ from rpython.jit.backend import detect_cpu try: if detect_cpu.autodetect().startswith('x86'): - working_modules.add('_vmprof') - working_modules.add('faulthandler') + if not sys.platform.startswith('openbsd'): + working_modules.add('_vmprof') + working_modules.add('faulthandler') except detect_cpu.ProcessorAutodetectError: pass @@ -222,9 +223,6 @@ BoolOption("withsmalllong", "use a version of 'long' in a C long long", default=False), - BoolOption("withstrbuf", "use strings optimized for addition (ver 2)", - default=False), - BoolOption("withspecialisedtuple", "use specialised tuples", default=False), 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 @@ -39,3 +39,20 @@ Internal refactoring of buffers and memoryviews. Memoryviews will now be accepted in a few more places, e.g. in compile(). + +.. branch: sthalik/fix-signed-integer-sizes-1494493539409 + +.. branch: cpyext-obj-stealing + +Redo much of the refcount semantics in PyList_{SG}etItem to closer match +CPython and ensure the same PyObject stored in the list can be later +retrieved + +.. branch: cpyext-recursionlimit + +Implement Py_EnterRecursiveCall and associated functions + +.. branch: pypy_ctypes_nosegfault_nofastpath + +Remove faulty fastpath from ctypes + 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 @@ -690,7 +690,8 @@ }.items(): register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl) - for cpyname in '''PyMethodObject PyListObject PyLongObject'''.split(): + for cpyname in '''PyMethodObject PyListObject PyLongObject + PyBaseExceptionObject'''.split(): FORWARD_DECLS.append('typedef struct { PyObject_HEAD } %s' % (cpyname, )) build_exported_objects() @@ -1613,7 +1614,6 @@ @specialize.memo() def make_generic_cpy_call(FT, expect_null, convert_result): - from pypy.module.cpyext.pyobject import make_ref, from_ref from pypy.module.cpyext.pyobject import is_pyobj, as_pyobj from pypy.module.cpyext.pyobject import get_w_obj_and_decref from pypy.module.cpyext.pyerrors import PyErr_Occurred diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -257,7 +257,8 @@ if w_dict is None: return 0 - + if not space.isinstance_w(w_dict, space.w_dict): + return 0 pos = ppos[0] py_obj = as_pyobj(space, w_dict) py_dict = rffi.cast(PyDictObject, py_obj) @@ -268,6 +269,9 @@ py_dict.c__tmpkeys = create_ref(space, w_keys) Py_IncRef(space, py_dict.c__tmpkeys) else: + if not py_dict.c__tmpkeys: + # pos should have been 0, cannot fail so return 0 + return 0; w_keys = from_ref(space, py_dict.c__tmpkeys) ppos[0] += 1 if pos >= space.len_w(w_keys): diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py --- a/pypy/module/cpyext/eval.py +++ b/pypy/module/cpyext/eval.py @@ -1,6 +1,8 @@ from pypy.interpreter.error import oefmt from pypy.interpreter.astcompiler import consts from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib.objectmodel import we_are_translated +from rpython.rlib.rarithmetic import widen from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP, cpython_struct, is_valid_fp) @@ -227,4 +229,51 @@ cf.c_cf_flags = rffi.cast(rffi.INT, flags) return result +@cpython_api([], rffi.INT_real, error=CANNOT_FAIL) +def Py_GetRecursionLimit(space): + from pypy.module.sys.vm import getrecursionlimit + return space.int_w(getrecursionlimit(space)) +@cpython_api([rffi.INT_real], lltype.Void, error=CANNOT_FAIL) +def Py_SetRecursionLimit(space, limit): + from pypy.module.sys.vm import setrecursionlimit + setrecursionlimit(space, widen(limit)) + +limit = 0 # for testing + +@cpython_api([rffi.CCHARP], rffi.INT_real, error=1) +def Py_EnterRecursiveCall(space, where): + """Marks a point where a recursive C-level call is about to be performed. + + If USE_STACKCHECK is defined, this function checks if the the OS + stack overflowed using PyOS_CheckStack(). In this is the case, it + sets a MemoryError and returns a nonzero value. + + The function then checks if the recursion limit is reached. If this is the + case, a RuntimeError is set and a nonzero value is returned. + Otherwise, zero is returned. + + where should be a string such as " in instance check" to be + concatenated to the RuntimeError message caused by the recursion depth + limit.""" + if not we_are_translated(): + # XXX hack since the stack checks only work translated + global limit + limit += 1 + if limit > 10: + raise oefmt(space.w_RuntimeError, + "maximum recursion depth exceeded%s", rffi.charp2str(where)) + return 0 + from rpython.rlib.rstack import stack_almost_full + if stack_almost_full(): + raise oefmt(space.w_RuntimeError, + "maximum recursion depth exceeded%s", rffi.charp2str(where)) + return 0 + +@cpython_api([], lltype.Void) +def Py_LeaveRecursiveCall(space): + """Ends a Py_EnterRecursiveCall(). Must be called once for each + successful invocation of Py_EnterRecursiveCall().""" + # A NOP in PyPy + if not we_are_translated(): + limit = 0 diff --git a/pypy/module/cpyext/include/listobject.h b/pypy/module/cpyext/include/listobject.h --- a/pypy/module/cpyext/include/listobject.h +++ b/pypy/module/cpyext/include/listobject.h @@ -1,1 +1,1 @@ -#define PyList_GET_ITEM(o, i) PyList_GetItem((PyObject*)(o), (i)) +/* empty */ diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -1,9 +1,10 @@ +from rpython.rlib.objectmodel import always_inline from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import (cpython_api, CANNOT_FAIL, Py_ssize_t, build_type_checkers) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall -from pypy.module.cpyext.pyobject import Py_DecRef, PyObject, make_ref +from pypy.module.cpyext.pyobject import decref, incref, PyObject, make_ref from pypy.objspace.std.listobject import W_ListObject from pypy.interpreter.error import oefmt @@ -19,59 +20,68 @@ PySequence_SetItem() or expose the object to Python code before setting all items to a real object with PyList_SetItem(). """ - return space.newlist([None] * len) + w_list = space.newlist([None] * len) + #w_list.convert_to_cpy_strategy(space) + return w_list -@cpython_api([rffi.VOIDP, Py_ssize_t, PyObject], PyObject, error=CANNOT_FAIL, - result_borrowed=True) -def PyList_SET_ITEM(space, w_list, index, w_item): - """Macro form of PyList_SetItem() without error checking. This is normally +@always_inline +def get_list_storage(space, w_list): + from pypy.module.cpyext.sequence import CPyListStrategy + assert isinstance(w_list, W_ListObject) + w_list.convert_to_cpy_strategy(space) + return CPyListStrategy.unerase(w_list.lstorage) + +@cpython_api([rffi.VOIDP, Py_ssize_t, PyObject], lltype.Void, error=CANNOT_FAIL) +def PyList_SET_ITEM(space, w_list, index, py_item): + """Form of PyList_SetItem() without error checking. This is normally only used to fill in new lists where there is no previous content. - This function "steals" a reference to item, and, unlike PyList_SetItem(), - does not discard a reference to any item that it being replaced; any - reference in list at position i will be leaked. + "steals" a reference to item, and, unlike PyList_SetItem(), does not + discard a reference to any item that it being replaced; any reference in + list at position i will be leaked. """ - assert isinstance(w_list, W_ListObject) + storage = get_list_storage(space, w_list) assert 0 <= index < w_list.length() - # Deliberately leak, so that it can be safely decref'd. - make_ref(space, w_list.getitem(index)) - Py_DecRef(space, w_item) - w_list.setitem(index, w_item) - return w_item - + storage._elems[index] = py_item @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1) -def PyList_SetItem(space, w_list, index, w_item): +def PyList_SetItem(space, w_list, index, py_item): """Set the item at index index in list to item. Return 0 on success or -1 on failure. - + This function "steals" a reference to item and discards a reference to an item already in the list at the affected position. """ - Py_DecRef(space, w_item) if not isinstance(w_list, W_ListObject): + decref(space, w_item) PyErr_BadInternalCall(space) if index < 0 or index >= w_list.length(): + decref(space, w_item) raise oefmt(space.w_IndexError, "list assignment index out of range") - w_list.setitem(index, w_item) + storage = get_list_storage(space, w_list) + py_old = storage._elems[index] + storage._elems[index] = py_item + decref(w_list.space, py_old) return 0 -@cpython_api([PyObject, Py_ssize_t], PyObject, result_borrowed=True) +@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True) +def PyList_GET_ITEM(space, w_list, index): + storage = get_list_storage(space, w_list) + assert 0 <= index < w_list.length() + return storage._elems[index] # borrowed ref + +@cpython_api([PyObject, Py_ssize_t], PyObject, result_is_ll=True) def PyList_GetItem(space, w_list, index): """Return the object at position pos in the list pointed to by p. The position must be positive, indexing from the end of the list is not supported. If pos is out of bounds, return NULL and set an IndexError exception.""" - from pypy.module.cpyext.sequence import CPyListStrategy if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) if index < 0 or index >= w_list.length(): raise oefmt(space.w_IndexError, "list index out of range") - cpy_strategy = space.fromcache(CPyListStrategy) - if w_list.strategy is not cpy_strategy: - w_list.ensure_object_strategy() # make sure we can return a borrowed obj - w_res = w_list.getitem(index) - return w_res # borrowed ref + storage = get_list_storage(space, w_list) + return storage._elems[index] # borrowed ref @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -30,8 +30,7 @@ return subtype_dealloc.api_func def allocate(self, space, w_type, itemcount=0): - # similar to PyType_GenericAlloc? - # except that it's not related to any pypy object. + # typically called from PyType_GenericAlloc via typedescr.allocate # this returns a PyObject with ob_refcnt == 1. pytype = as_pyobj(space, w_type) @@ -250,6 +249,8 @@ w_obj = rawrefcount.to_obj(W_Root, pyobj) return w_obj is not None and w_obj is not w_marker_deallocating +def w_obj_has_pyobj(w_obj): + return bool(rawrefcount.from_obj(PyObject, w_obj)) def is_pyobj(x): if x is None or isinstance(x, W_Root): @@ -275,13 +276,14 @@ """ if is_pyobj(obj): pyobj = rffi.cast(PyObject, obj) + at_least = 1 else: pyobj = as_pyobj(space, obj, w_userdata) + at_least = rawrefcount.REFCNT_FROM_PYPY if pyobj: - assert pyobj.c_ob_refcnt > 0 + assert pyobj.c_ob_refcnt >= at_least pyobj.c_ob_refcnt += 1 - if not is_pyobj(obj): - keepalive_until_here(obj) + keepalive_until_here(obj) return pyobj @@ -315,9 +317,14 @@ obj = rffi.cast(PyObject, obj) if obj: assert obj.c_ob_refcnt > 0 + assert obj.c_ob_pypy_link == 0 or obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY obj.c_ob_refcnt -= 1 if obj.c_ob_refcnt == 0: _Py_Dealloc(space, obj) + #else: + # w_obj = rawrefcount.to_obj(W_Root, ref) + # if w_obj is not None: + # assert obj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY else: get_w_obj_and_decref(space, obj) 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 @@ -256,8 +256,9 @@ def setitem(self, w_list, index, w_obj): storage = self.unerase(w_list.lstorage) index = self._check_index(index, storage._length) - decref(w_list.space, storage._elems[index]) + py_old = storage._elems[index] storage._elems[index] = make_ref(w_list.space, w_obj) + decref(w_list.space, py_old) def length(self, w_list): storage = self.unerase(w_list.lstorage) 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 @@ -14,7 +14,7 @@ ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc, cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc, getbufferproc, releasebufferproc, ssizessizeobjargproc) -from pypy.module.cpyext.pyobject import make_ref, decref, as_pyobj +from pypy.module.cpyext.pyobject import make_ref, decref, as_pyobj, from_ref from pypy.module.cpyext.pyerrors import PyErr_Occurred from pypy.module.cpyext.memoryobject import fill_Py_buffer from pypy.module.cpyext.state import State @@ -256,7 +256,7 @@ check_num_args(space, w_args, 1) args_w = space.fixedview(w_args) index = space.int_w(space.index(args_w[0])) - null = lltype.nullptr(PyObject.TO) + 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) @@ -285,7 +285,8 @@ func_target = rffi.cast(objobjargproc, func) check_num_args(space, w_args, 1) w_key, = space.fixedview(w_args) - res = generic_cpy_call(space, func_target, w_self, w_key, None) + 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 @@ -611,6 +612,8 @@ handled = True for tp_name, attr in [('tp_hash', '__hash__'), + ('tp_as_sequence.c_sq_length', '__len__'), + ('tp_as_mapping.c_mp_length', '__len__'), ]: if name == tp_name: slot_fn = w_type.getdictvalue(space, attr) @@ -636,7 +639,8 @@ ('tp_as_number.c_nb_xor', '__xor__'), ('tp_as_number.c_nb_or', '__or__'), ('tp_as_sequence.c_sq_concat', '__add__'), - ('tp_as_sequence.c_sq_inplace_concat', '__iadd__') + ('tp_as_sequence.c_sq_inplace_concat', '__iadd__'), + ('tp_as_mapping.c_mp_subscript', '__getitem__'), ]: if name == tp_name: slot_fn = w_type.getdictvalue(space, attr) @@ -650,7 +654,7 @@ handled = True # binary-with-Py_ssize_t-type - for tp_name, attr in [('tp_as_sequence.c_sq_item', '__getitem'), + for tp_name, attr in [('tp_as_sequence.c_sq_item', '__getitem__'), ('tp_as_sequence.c_sq_repeat', '__mul__'), ('tp_as_sequence.c_sq_repeat', '__mul__'), ('tp_as_sequence.c_sq_inplace_repeat', '__imul__'), @@ -679,7 +683,48 @@ def slot_func(space, w_self, w_arg1, w_arg2): return space.call_function(slot_fn, w_self, w_arg1, w_arg2) handled = True + # ternary-with-void returning-Py_size_t-type + for tp_name, attr in [('tp_as_mapping.c_mp_ass_subscript', '__setitem__'), + ]: + if name == tp_name: + slot_ass = w_type.getdictvalue(space, attr) + if slot_ass is None: + return + slot_del = w_type.getdictvalue(space, '__delitem__') + if slot_del is None: + return + @slot_function([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, w_arg1, arg2): + if arg2: + w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) + space.call_function(slot_ass, w_self, w_arg1, w_arg2) + else: + space.call_function(slot_del, w_self, w_arg1) + return 0 + handled = True + # ternary-Py_size_t-void returning-Py_size_t-type + for tp_name, attr in [('tp_as_sequence.c_sq_ass_item', '__setitem__'), + ]: + if name == tp_name: + slot_ass = w_type.getdictvalue(space, attr) + if slot_ass is None: + return + slot_del = w_type.getdictvalue(space, '__delitem__') + if slot_del is None: + return + + @slot_function([PyObject, lltype.Signed, PyObject], rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_func(space, w_self, arg1, arg2): + if arg2: + w_arg2 = from_ref(space, rffi.cast(PyObject, arg2)) + space.call_function(slot_ass, w_self, space.newint(arg1), w_arg2) + else: + space.call_function(slot_del, w_self, space.newint(arg1)) + return 0 + handled = True if handled: pass elif name == 'tp_setattro': diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py --- a/pypy/module/cpyext/stubs.py +++ b/pypy/module/cpyext/stubs.py @@ -483,29 +483,6 @@ 0 on success, -1 on failure.""" raise NotImplementedError -@cpython_api([rffi.CCHARP], rffi.INT_real, error=-1) -def Py_EnterRecursiveCall(space, where): - """Marks a point where a recursive C-level call is about to be performed. - - If USE_STACKCHECK is defined, this function checks if the the OS - stack overflowed using PyOS_CheckStack(). In this is the case, it - sets a MemoryError and returns a nonzero value. - - The function then checks if the recursion limit is reached. If this is the - case, a RuntimeError is set and a nonzero value is returned. - Otherwise, zero is returned. - - where should be a string such as " in instance check" to be - concatenated to the RuntimeError message caused by the recursion depth - limit.""" - raise NotImplementedError - -@cpython_api([], lltype.Void) -def Py_LeaveRecursiveCall(space): - """Ends a Py_EnterRecursiveCall(). Must be called once for each - successful invocation of Py_EnterRecursiveCall().""" - raise NotImplementedError - @cpython_api([PyObject], rffi.INT_real, error=-1) def Py_ReprEnter(space, object): """Called at the beginning of the tp_repr implementation to 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 @@ -2330,6 +2330,7 @@ for (nn = 0; nn < n; nn++) { v = PyList_GetItem(obj1, nn); + Py_INCREF(v); PyList_SetItem(ret, nn+ii*n, v); } return ret; @@ -2345,6 +2346,7 @@ for (nn = 0; nn < n; nn++) { v = PyList_GetItem(obj2, nn); + Py_INCREF(v); PyList_SetItem(ret, nn+ii*n, v); } return ret; diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -260,4 +260,60 @@ ]) d = module.get_type_dict(1) assert d['real'].__get__(1, 1) == 1 - + def test_advanced(self): + module = self.import_extension('foo', [ + ("dict_len", "METH_O", + ''' + int ret = args->ob_type->tp_as_mapping->mp_length(args); + return PyLong_FromLong(ret); + '''), + ("dict_setitem", "METH_VARARGS", + ''' + int ret; + PyObject * dict = PyTuple_GetItem(args, 0); + if (PyTuple_Size(args) < 3 || !dict || + !dict->ob_type->tp_as_mapping || + !dict->ob_type->tp_as_mapping->mp_ass_subscript) + return PyLong_FromLong(-1); + ret = dict->ob_type->tp_as_mapping->mp_ass_subscript( + dict, PyTuple_GetItem(args, 1), + PyTuple_GetItem(args, 2)); + return PyLong_FromLong(ret); + '''), + ("dict_delitem", "METH_VARARGS", + ''' + int ret; + PyObject * dict = PyTuple_GetItem(args, 0); + if (PyTuple_Size(args) < 2 || !dict || + !dict->ob_type->tp_as_mapping || + !dict->ob_type->tp_as_mapping->mp_ass_subscript) + return PyLong_FromLong(-1); + ret = dict->ob_type->tp_as_mapping->mp_ass_subscript( + dict, PyTuple_GetItem(args, 1), NULL); + return PyLong_FromLong(ret); + '''), + ("dict_next", "METH_VARARGS", + ''' + PyObject *key, *value; + PyObject *arg = NULL; + Py_ssize_t pos = 0; + int ret = 0; + if ((PyArg_ParseTuple(args, "|O", &arg))) { + if (arg && PyDict_Check(arg)) { + while (PyDict_Next(arg, &pos, &key, &value)) + ret ++; + /* test no crash if pos is not reset to 0*/ + while (PyDict_Next(arg, &pos, &key, &value)) + ret ++; + } + } + return PyLong_FromLong(ret); + '''), + ]) + d = {'a': 1, 'b':2} + assert module.dict_len(d) == 2 + assert module.dict_setitem(d, 'a', 'c') == 0 + assert d['a'] == 'c' + assert module.dict_delitem(d, 'a') == 0 + r = module.dict_next({'a': 1, 'b': 2}) + assert r == 2 diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py --- a/pypy/module/cpyext/test/test_eval.py +++ b/pypy/module/cpyext/test/test_eval.py @@ -339,3 +339,30 @@ def nested_flags(): return module.get_flags()""", ns) assert ns['nested_flags']() == (0, 0) + + def test_recursive_function(self): + module = self.import_extension('foo', [ + ("call_recursive", "METH_NOARGS", + """ + int res = 0; + int recurse(void) { + if (Py_EnterRecursiveCall(" while calling recurse")) + return -1; + res ++; + return recurse(); + } + int oldlimit = Py_GetRecursionLimit(); + Py_SetRecursionLimit(200); + res = recurse(); + Py_SetRecursionLimit(oldlimit); + if (PyErr_Occurred()) + return NULL; + return PyLong_FromLong(res); + """),], prologue= ''' int recurse(void); ''' + ) + try: + res = module.call_recursive() + except RuntimeError as e: + assert 'while calling recurse' in str(e) + else: + assert False, "expected RuntimeError" diff --git a/pypy/module/cpyext/test/test_listobject.py b/pypy/module/cpyext/test/test_listobject.py --- a/pypy/module/cpyext/test/test_listobject.py +++ b/pypy/module/cpyext/test/test_listobject.py @@ -38,10 +38,12 @@ assert api.PyList_Insert(w_l, 0, space.wrap(1)) == 0 assert api.PyList_Size(w_l) == 3 assert api.PyList_Insert(w_l, 99, space.wrap(2)) == 0 - assert space.unwrap(api.PyList_GetItem(w_l, 3)) == 2 + assert api.PyObject_Compare(api.PyList_GetItem(w_l, 3), + space.wrap(2)) == 0 # insert at index -1: next-to-last assert api.PyList_Insert(w_l, -1, space.wrap(3)) == 0 - assert space.unwrap(api.PyList_GetItem(w_l, 3)) == 3 + assert api.PyObject_Compare(api.PyList_GET_ITEM(w_l, 3), + space.wrap(3)) == 0 def test_sort(self, space, api): l = space.newlist([space.wrap(1), space.wrap(0), space.wrap(7000)]) @@ -152,29 +154,35 @@ def test_list_macros(self): """The PyList_* macros cast, and calls expecting that build.""" module = self.import_extension('foo', [ - ("test_macro_invocations", "METH_NOARGS", + ("test_macro_invocations", "METH_O", """ PyObject* o = PyList_New(2); PyListObject* l = (PyListObject*)o; + Py_INCREF(args); + Py_INCREF(args); + PyList_SET_ITEM(o, 0, args); + PyList_SET_ITEM(l, 1, args); - Py_INCREF(o); - PyList_SET_ITEM(o, 0, o); - Py_INCREF(o); - PyList_SET_ITEM(l, 1, o); + if(PyList_GET_ITEM(o, 0) != PyList_GET_ITEM(l, 1)) + { + PyErr_SetString(PyExc_AssertionError, "PyList_GET_ITEM error"); + return NULL; + } - PyList_GET_ITEM(o, 0); - PyList_GET_ITEM(l, 1); - - PyList_GET_SIZE(o); - PyList_GET_SIZE(l); + if(PyList_GET_SIZE(o) != PyList_GET_SIZE(l)) + { + PyErr_SetString(PyExc_AssertionError, "PyList_GET_SIZE error"); + return NULL; + } return o; """ ) ]) - x = module.test_macro_invocations() - assert x[0] is x[1] is x + s = 'abcdef' + x = module.test_macro_invocations(s) + assert x[0] is x[1] is s def test_get_item_macro(self): module = self.import_extension('foo', [ @@ -183,39 +191,96 @@ PyObject* o, *o2, *o3; o = PyList_New(1); - o2 = PyLong_FromLong(0); + o2 = PyBytes_FromString("test_get_item0"); + Py_INCREF(o2); PyList_SET_ITEM(o, 0, o2); - o2 = NULL; o3 = PyList_GET_ITEM(o, 0); Py_INCREF(o3); - Py_CLEAR(o); + Py_DECREF(o); + Py_DECREF(o2); return o3; """)]) - assert module.test_get_item() == 0 + assert module.test_get_item() == b'test_get_item0' - def test_set_item_macro(self): + def test_item_refcounts(self): """PyList_SET_ITEM leaks a reference to the target.""" module = self.import_extension('foo', [ - ("test_refcount_diff_after_setitem", "METH_NOARGS", + ("test_refcount_diff", "METH_VARARGS", """ - PyObject* o = PyList_New(0); - PyObject* o2 = PyList_New(0); - Py_ssize_t refcount, new_refcount; + /* test that the refcount differences for functions + * are correct. diff1 - expected refcount diff for i1, + * diff2 - expected refcount diff for i2 + */ + #define CHECKCOUNT(diff1, diff2, action) \ + new_count1 = Py_REFCNT(i1); \ + new_count2 = Py_REFCNT(i2); \ + diff = new_count1 - old_count1; \ + if (diff != diff1) {\ + sprintf(errbuffer, action \ + " i1 expected diff of %ld got %ld", (long)diff1, (long)diff); \ + PyErr_SetString(PyExc_AssertionError, errbuffer); \ + return NULL; } \ + diff = new_count2 - old_count2; \ + if (diff != diff2) {\ + sprintf(errbuffer, action \ + " i2 expected diff of %ld got %ld", (long)diff2, (long)diff); \ + PyErr_SetString(PyExc_AssertionError, errbuffer); \ + return NULL; } \ + old_count1 = new_count1; \ + old_count2 = new_count2; - PyList_Append(o, o2); // does not steal o2 + PyObject* tmp, *o = PyList_New(0); + char errbuffer[1024]; + PyObject* i1 = PyTuple_GetItem(args, 0); + PyObject* i2 = PyTuple_GetItem(args, 1); + Py_ssize_t old_count1, new_count1; + Py_ssize_t old_count2, new_count2; + Py_ssize_t diff; + int ret; - refcount = Py_REFCNT(o2); + old_count1 = Py_REFCNT(i1); + old_count2 = Py_REFCNT(i2); - // Steal a reference to o2, but leak the old reference to o2. - // The net result should be no change in refcount. - PyList_SET_ITEM(o, 0, o2); + ret = PyList_Append(o, i1); + if (ret != 0) + return NULL; + /* check the result of Append(), and also force the list + to use the CPyListStrategy now */ + if (PyList_GET_ITEM(o, 0) != i1) + { + PyErr_SetString(PyExc_AssertionError, "Append() error?"); + return NULL; + } + CHECKCOUNT(1, 0, "PyList_Append"); - new_refcount = Py_REFCNT(o2); + Py_INCREF(i2); /* for PyList_SET_ITEM */ + CHECKCOUNT(0, 1, "Py_INCREF"); - Py_CLEAR(o); - Py_DECREF(o2); // append incref'd. - // Py_CLEAR(o2); // naive implementation would fail here. - return PyLong_FromSsize_t(new_refcount - refcount); + PyList_SET_ITEM(o, 0, i2); + CHECKCOUNT(0, 0, "PyList_SET_ITEM"); + + tmp = PyList_GET_ITEM(o, 0); + if (tmp != i2) + { + PyErr_SetString(PyExc_AssertionError, "SetItem() error?"); + return NULL; + } + CHECKCOUNT(0, 0, "PyList_GET_ITEM"); + + PyList_SetItem(o, 0, i1); + CHECKCOUNT(0, -1, "PyList_Set_Item"); + + PyList_GetItem(o, 0); + CHECKCOUNT(0, 0, "PyList_Get_Item"); + + Py_DECREF(o); + #ifndef PYPY_VERSION + { + // PyPy deletes only at teardown + CHECKCOUNT(-1, 0, "Py_DECREF(o)"); + } + #endif + return PyLong_FromSsize_t(0); """)]) - assert module.test_refcount_diff_after_setitem() == 0 + assert module.test_refcount_diff(["first"], ["second"]) == 0 diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -23,7 +23,7 @@ W_PyCMethodObject, W_PyCFunctionObject) from pypy.module.cpyext.modsupport import convert_method_defs from pypy.module.cpyext.pyobject import ( - PyObject, make_ref, create_ref, from_ref, get_typedescr, make_typedescr, + PyObject, make_ref, from_ref, get_typedescr, make_typedescr, track_reference, Py_DecRef, as_pyobj) from pypy.module.cpyext.slotdefs import ( slotdefs_for_tp_slots, slotdefs_for_wrappers, get_slot_tp_function, diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -730,7 +730,7 @@ if sys.platform == 'win32': make_conversion_functions('MBCS', 'mbcs') -@cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP, rffi.INTP], PyObject) +@cpython_api([rffi.CCHARP, Py_ssize_t, CONST_STRING, rffi.INTP], PyObject) def PyUnicode_DecodeUTF16(space, s, size, llerrors, pbyteorder): """Decode length bytes from a UTF-16 encoded buffer string and return the corresponding Unicode object. errors (if non-NULL) defines the error diff --git a/pypy/module/test_lib_pypy/README.txt b/pypy/module/test_lib_pypy/README.txt new file mode 100644 --- /dev/null +++ b/pypy/module/test_lib_pypy/README.txt @@ -0,0 +1,4 @@ +This directory contains app-level tests are supposed to be run *after* +translation. So you run them by saying: + +pypy pytest.py <testfile.py> diff --git a/pypy/module/test_lib_pypy/ctypes_tests/README b/pypy/module/test_lib_pypy/ctypes_tests/README new file mode 100644 --- /dev/null +++ b/pypy/module/test_lib_pypy/ctypes_tests/README @@ -0,0 +1,8 @@ +-------Ctypes tests------ + +Unlike the other tests in the PyPy sources, these tests are assumed to run after the translation is complete. +Therefore, using the resulting binary, you can then call, for example: + +/path/to/your_modified_pypy_binary pytest.py pypy/module/test_lib_pypy/ctypes_tests/test_libc.py + +This also applies to any other test in ctypes_tests. \ No newline at end of file diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py b/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py deleted file mode 100644 --- a/pypy/module/test_lib_pypy/ctypes_tests/test_fastpath.py +++ /dev/null @@ -1,128 +0,0 @@ -from ctypes import CDLL, POINTER, pointer, c_byte, c_int, c_char_p, CFUNCTYPE, c_void_p, c_size_t -import sys -import py -from support import BaseCTypesTestChecker - -class MyCDLL(CDLL): - def __getattr__(self, attr): - fn = self[attr] # this way it's not cached as an attribute - fn._slowpath_allowed = False - return fn - -def setup_module(mod): - import conftest - _ctypes_test = str(conftest.sofile) - mod.dll = MyCDLL(_ctypes_test) # slowpath not allowed - mod.dll2 = CDLL(_ctypes_test) # slowpath allowed - - -class TestFastpath(BaseCTypesTestChecker): - - def test_fastpath_forbidden(self): - def myfunc(): - pass - # - tf_b = dll.tf_b - tf_b.restype = c_byte - # - # so far, it's still using the slowpath - assert not tf_b._is_fastpath - tf_b.callable = myfunc - tf_b.argtypes = (c_byte,) - # errcheck prevented the fastpath to kick in - assert not tf_b._is_fastpath - # - del tf_b.callable - tf_b.argtypes = (c_byte,) # try to re-enable the fastpath - assert tf_b._is_fastpath - # - assert not tf_b._slowpath_allowed - py.test.raises(AssertionError, "tf_b.callable = myfunc") - py.test.raises(AssertionError, "tf_b('aaa')") # force a TypeError - - def test_simple_args(self): - tf_b = dll.tf_b - tf_b.restype = c_byte - tf_b.argtypes = (c_byte,) - assert tf_b(-126) == -42 - - def test_from_cfunctype(self): - from _ctypes import _memmove_addr - functype = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t) - my_memmove = functype(_memmove_addr) - assert my_memmove._is_fastpath - - def test_undeclared_restype(self): - # make sure we get a fresh function - try: - del dll.tf_i - except AttributeError: - pass - tf_i = dll.tf_i - assert not tf_i._is_fastpath - tf_i.argtypes = (c_int,) - assert tf_i._is_fastpath - assert tf_i(12) == 4 - - def test_pointer_args(self): - f = dll._testfunc_p_p - f.restype = POINTER(c_int) - f.argtypes = [POINTER(c_int)] - v = c_int(42) - result = f(pointer(v)) - assert type(result) == POINTER(c_int) - assert result.contents.value == 42 - - def test_simple_pointer_args(self): - f = dll.my_strchr - f.argtypes = [c_char_p, c_int] - f.restype = c_char_p - mystr = c_char_p("abcd") - result = f(mystr, ord("b")) - assert result == "bcd" - - def test_strings(self): - f = dll.my_strchr - f.argtypes = [c_char_p, c_int] - f.restype = c_char_p - result = f("abcd", ord("b")) - assert result == "bcd" - - def test_errcheck(self): - def errcheck(result, func, args): - return 'hello' - tf_b = dll.tf_b - tf_b.restype = c_byte - tf_b.argtypes = (c_byte,) - tf_b.errcheck = errcheck - assert tf_b(-126) == 'hello' - - def test_array_to_ptr(self): - ARRAY = c_int * 8 - func = dll._testfunc_ai8 - func.restype = POINTER(c_int) - func.argtypes = [ARRAY] - array = ARRAY(1, 2, 3, 4, 5, 6, 7, 8) - ptr = func(array) - assert ptr[0] == 1 - assert ptr[7] == 8 - - -class TestFallbackToSlowpath(BaseCTypesTestChecker): - - def test_argtypes_is_None(self): - tf_b = dll2.tf_b - tf_b.restype = c_byte - tf_b.argtypes = (c_char_p,) # this is intentionally wrong - tf_b.argtypes = None # kill the fast path - assert not tf_b._is_fastpath - assert tf_b(-126) == -42 - - def test_callable_is_None(self): - tf_b = dll2.tf_b - tf_b.restype = c_byte - tf_b.argtypes = (c_byte,) - tf_b.callable = lambda x: x+1 - assert not tf_b._is_fastpath - assert tf_b(-126) == -125 - tf_b.callable = None diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py @@ -478,7 +478,7 @@ raises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT())) def test_argument_conversion_and_checks(self): - py.test.skip("XXX currently broken on PyPy, sorry") + #This test is designed to check for segfaults if the wrong type of argument is passed as parameter strlen = dll.my_strchr strlen.argtypes = [c_char_p, c_int] strlen.restype = c_char_p diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -580,55 +580,31 @@ return space.newint(x) def descr_eq(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value == w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value == w_other._value) def descr_ne(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value != w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value != w_other._value) def descr_lt(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value < w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value < w_other._value) def descr_le(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value <= w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value <= w_other._value) def descr_gt(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value > w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value > w_other._value) def descr_ge(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - if isinstance(w_other, W_StringBufferObject): - return space.newbool(self._value >= w_other.force()) if not isinstance(w_other, W_BytesObject): return space.w_NotImplemented return space.newbool(self._value >= w_other._value) @@ -637,18 +613,6 @@ _StringMethods_descr_add = descr_add def descr_add(self, space, w_other): - if space.config.objspace.std.withstrbuf: - from pypy.objspace.std.strbufobject import W_StringBufferObject - try: - other = self._op_val(space, w_other) - except OperationError as e: - if e.match(space, space.w_TypeError): - return space.w_NotImplemented - raise - builder = StringBuilder() - builder.append(self._value) - builder.append(other) - return W_StringBufferObject(builder) return self._StringMethods_descr_add(space, w_other) _StringMethods_descr_join = descr_join diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -229,15 +229,13 @@ return list(items) def switch_to_object_strategy(self): + object_strategy = self.space.fromcache(ObjectListStrategy) + if self.strategy is object_strategy: + return list_w = self.getitems() - object_strategy = self.space.fromcache(ObjectListStrategy) self.strategy = object_strategy object_strategy.init_from_list_w(self, list_w) - def ensure_object_strategy(self): # for cpyext - if self.strategy is not self.space.fromcache(ObjectListStrategy): - self.switch_to_object_strategy() - def _temporarily_as_objects(self): if self.strategy is self.space.fromcache(ObjectListStrategy): return self diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -18,7 +18,7 @@ # Object imports from pypy.objspace.std.boolobject import W_BoolObject from pypy.objspace.std.bytearrayobject import W_BytearrayObject -from pypy.objspace.std.bytesobject import W_AbstractBytesObject, W_BytesObject +from pypy.objspace.std.bytesobject import W_BytesObject from pypy.objspace.std.complexobject import W_ComplexObject from pypy.objspace.std.dictmultiobject import W_DictMultiObject, W_DictObject from pypy.objspace.std.floatobject import W_FloatObject @@ -82,9 +82,6 @@ W_TypeObject.typedef: W_TypeObject, W_UnicodeObject.typedef: W_UnicodeObject, } - if self.config.objspace.std.withstrbuf: - builtin_type_classes[W_BytesObject.typedef] = W_AbstractBytesObject - self.builtin_types = {} self._interplevel_classes = {} for typedef, cls in builtin_type_classes.items(): diff --git a/pypy/objspace/std/strbufobject.py b/pypy/objspace/std/strbufobject.py deleted file mode 100644 --- a/pypy/objspace/std/strbufobject.py +++ /dev/null @@ -1,96 +0,0 @@ -import inspect - -import py - -from pypy.objspace.std.bytesobject import W_AbstractBytesObject, W_BytesObject -from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.buffer import SimpleView, StringBuffer -from pypy.interpreter.error import OperationError -from rpython.rlib.rstring import StringBuilder - - -class W_StringBufferObject(W_AbstractBytesObject): - w_str = None - - def __init__(self, builder): - self.builder = builder # StringBuilder - self.length = builder.getlength() - - def force(self): - if self.w_str is None: - s = self.builder.build() - if self.length < len(s): - s = s[:self.length] - self.w_str = W_BytesObject(s) - return s - else: - return self.w_str._value - - def __repr__(self): - """ representation for debugging purposes """ - return "%s(%r[:%d])" % ( - self.__class__.__name__, self.builder, self.length) - - def unwrap(self, space): - return self.force() - - def bytes_w(self, space): - return self.force() - - def buffer_w(self, space, flags): - return SimpleView(StringBuffer(self.force())) - - def descr_len(self, space): - return space.newint(self.length) - - def descr_add(self, space, w_other): - try: - other = W_BytesObject._op_val(space, w_other) - except OperationError as e: - if e.match(space, space.w_TypeError): - return space.w_NotImplemented - raise - if self.builder.getlength() != self.length: - builder = StringBuilder() - builder.append(self.force()) - else: - builder = self.builder - builder.append(other) - return W_StringBufferObject(builder) - - def descr_str(self, space): - # you cannot get subclasses of W_StringBufferObject here - assert type(self) is W_StringBufferObject - return self - - -delegation_dict = {} -for key, value in W_BytesObject.typedef.rawdict.iteritems(): - if not isinstance(value, interp2app): - continue - if key in ('__len__', '__add__', '__str__'): - continue - - func = value._code._bltin - args = inspect.getargs(func.func_code) - if args.varargs or args.keywords: - raise TypeError("Varargs and keywords not supported in unwrap_spec") - argspec = ', '.join([arg for arg in args.args[1:]]) - func_code = py.code.Source(""" - def f(self, %(args)s): - self.force() - return self.w_str.%(func_name)s(%(args)s) - """ % {'args': argspec, 'func_name': func.func_name}) - d = {} - exec func_code.compile() in d - f = d['f'] - f.func_defaults = func.func_defaults - f.__module__ = func.__module__ - # necessary for unique identifiers for pickling - f.func_name = func.func_name - unwrap_spec_ = getattr(func, 'unwrap_spec', None) - if unwrap_spec_ is not None: - f = unwrap_spec(**unwrap_spec_)(f) - setattr(W_StringBufferObject, func.func_name, f) - -W_StringBufferObject.typedef = W_BytesObject.typedef diff --git a/pypy/objspace/std/test/test_stdobjspace.py b/pypy/objspace/std/test/test_stdobjspace.py --- a/pypy/objspace/std/test/test_stdobjspace.py +++ b/pypy/objspace/std/test/test_stdobjspace.py @@ -62,13 +62,6 @@ cls = space._get_interplevel_cls(w_sequenceiterator) assert cls is W_AbstractSeqIterObject - def test_withstrbuf_fastpath_isinstance(self): - from pypy.objspace.std.bytesobject import W_AbstractBytesObject - - space = gettestobjspace(withstrbuf=True) - cls = space._get_interplevel_cls(space.w_bytes) - assert cls is W_AbstractBytesObject - def test_wrap_various_unsigned_types(self): import sys from rpython.rlib.rarithmetic import r_uint diff --git a/pypy/objspace/std/test/test_strbufobject.py b/pypy/objspace/std/test/test_strbufobject.py deleted file mode 100644 --- a/pypy/objspace/std/test/test_strbufobject.py +++ /dev/null @@ -1,84 +0,0 @@ -import py - -from pypy.objspace.std.test import test_bytesobject - -class AppTestStringObject(test_bytesobject.AppTestBytesObject): - spaceconfig = {"objspace.std.withstrbuf": True} - - def test_basic(self): - import __pypy__ - # cannot do "Hello, " + "World!" because cpy2.5 optimises this - # away on AST level - s = b"Hello, ".__add__(b"World!") - assert type(s) is bytes - assert 'W_StringBufferObject' in __pypy__.internal_repr(s) - - def test_add_twice(self): - x = b"a".__add__(b"b") - y = x + b"c" - c = x + b"d" - assert y == b"abc" - assert c == b"abd" - - def test_add(self): - import __pypy__ - all = b"" - for i in range(20): - all += str(i).encode() - assert 'W_StringBufferObject' in __pypy__.internal_repr(all) - assert all == b"012345678910111213141516171819" - - def test_hash(self): - import __pypy__ - def join(s): return s[:len(s) // 2] + s[len(s) // 2:] - t = b'a' * 101 - s = join(t) - assert 'W_StringBufferObject' in __pypy__.internal_repr(s) - assert hash(s) == hash(t) - - def test_len(self): - s = b"a".__add__(b"b") - r = b"c".__add__(b"d") - t = s + r - assert len(s) == 2 - assert len(r) == 2 - assert len(t) == 4 - - def test_buffer(self): - s = b'a'.__add__(b'b') - assert memoryview(s) == b'ab' - - def test_add_strbuf(self): - # make three strbuf objects - s = b'a'.__add__(b'b') - t = b'x'.__add__(b'c') - u = b'y'.__add__(b'd') - - # add two different strbufs to the same string - v = s + t - w = s + u - - # check that insanity hasn't resulted. - assert v == b"abxc" - assert w == b"abyd" - - def test_more_adding_fun(self): - s = b'a'.__add__(b'b') # s is a strbuf now - t = s + b'c' - u = s + b'd' - v = s + b'e' - assert v == b'abe' - assert u == b'abd' - assert t == b'abc' - - def test_buh_even_more(self): - a = b'a'.__add__(b'b') - b = a + b'c' - c = b'0'.__add__(b'1') - x = c + a - assert x == b'01ab' - - def test_add_non_string(self): - a = b'a' - a += b'b' - raises(TypeError, "a += 5") diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -239,7 +239,7 @@ 'signal.h', 'sys/utsname.h', _ptyh] if sys.platform.startswith('linux'): includes.append('sys/sysmacros.h') - if sys.platform.startswith('freebsd'): + if sys.platform.startswith('freebsd') or sys.platform.startswith('openbsd'): includes.append('sys/ttycom.h') libraries = ['util'] eci = ExternalCompilationInfo( diff --git a/rpython/rlib/rvmprof/src/shared/machine.c b/rpython/rlib/rvmprof/src/shared/machine.c --- a/rpython/rlib/rvmprof/src/shared/machine.c +++ b/rpython/rlib/rvmprof/src/shared/machine.c @@ -4,6 +4,7 @@ #include <stdio.h> #ifdef VMPROF_UNIX +#include <string.h> #include <unistd.h> #include <fcntl.h> #endif diff --git a/rpython/rlib/streamio.py b/rpython/rlib/streamio.py --- a/rpython/rlib/streamio.py +++ b/rpython/rlib/streamio.py @@ -902,18 +902,30 @@ self.do_read = base.read self.do_write = base.write self.do_flush = base.flush_buffers - self.lfbuffer = "" + self.readahead_count = 0 # either 0 or 1 def read(self, n=-1): - data = self.lfbuffer + self.do_read(n) - self.lfbuffer = "" + """If n >= 1, this should read between 1 and n bytes.""" + if n <= 0: + if n < 0: + return self.readall() + else: + return "" + + data = self.do_read(n - self.readahead_count) + if self.readahead_count > 0: + data = self.readahead_char + data + self.readahead_count = 0 + if data.endswith("\r"): c = self.do_read(1) - if c and c[0] == '\n': - data = data + '\n' - self.lfbuffer = c[1:] - else: - self.lfbuffer = c + if len(c) >= 1: + assert len(c) == 1 + if c[0] == '\n': + data = data + '\n' + else: + self.readahead_char = c[0] + self.readahead_count = 1 result = [] offset = 0 @@ -936,21 +948,21 @@ def tell(self): pos = self.base.tell() - return pos - len(self.lfbuffer) + return pos - self.readahead_count def seek(self, offset, whence): if whence == 1: - offset -= len(self.lfbuffer) # correct for already-read-ahead character + offset -= self.readahead_count # correct for already-read-ahead character self.base.seek(offset, whence) - self.lfbuffer = "" + self.readahead_count = 0 def flush_buffers(self): - if self.lfbuffer: + if self.readahead_count > 0: try: - self.base.seek(-len(self.lfbuffer), 1) + self.base.seek(-self.readahead_count, 1) except (MyNotImplementedError, OSError): return - self.lfbuffer = "" + self.readahead_count = 0 self.do_flush() def write(self, data): diff --git a/rpython/rlib/test/test_streamio.py b/rpython/rlib/test/test_streamio.py --- a/rpython/rlib/test/test_streamio.py +++ b/rpython/rlib/test/test_streamio.py @@ -657,6 +657,23 @@ assert line == '' self.interpret(f, []) + def test_read1(self): + s_input = "abc\r\nabc\nd\r\nef\r\ngha\rbc\rdef\n\r\n\r" + s_output = "abc\nabc\nd\nef\ngha\rbc\rdef\n\n\r" + assert s_output == s_input.replace('\r\n', '\n') + packets = list(s_input) + expected = list(s_output) + crlf = streamio.TextCRLFFilter(TSource(packets)) + def f(): + blocks = [] + while True: + block = crlf.read(1) + if not block: + break + blocks.append(block) + assert blocks == expected + self.interpret(f, []) + class TestTextCRLFFilterLLInterp(BaseTestTextCRLFFilter): pass _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit