[pypy-commit] pypy default: merge
fijal
pypy.commits at gmail.com
Mon Aug 7 08:24:06 EDT 2017
Author: fijal
Branch:
Changeset: r92106:bddc7d672228
Date: 2017-08-07 14:23 +0200
http://bitbucket.org/pypy/pypy/changeset/bddc7d672228/
Log: merge
diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py
--- a/lib_pypy/_ctypes/structure.py
+++ b/lib_pypy/_ctypes/structure.py
@@ -40,6 +40,22 @@
else:
rawfields.append((f[0], f[1]._ffishape_))
+ # hack for duplicate field names
+ already_seen = set()
+ names1 = names
+ names = []
+ for f in names1:
+ if f not in already_seen:
+ names.append(f)
+ already_seen.add(f)
+ already_seen = set()
+ for i in reversed(range(len(rawfields))):
+ if rawfields[i][0] in already_seen:
+ rawfields[i] = (('$DUP%d$%s' % (i, rawfields[i][0]),)
+ + rawfields[i][1:])
+ already_seen.add(rawfields[i][0])
+ # /hack
+
_set_shape(self, rawfields, self._is_union)
fields = {}
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
@@ -60,3 +60,16 @@
Small improvement to optimize list accesses with constant indexes better by
throwing away information about them less eagerly.
+
+
+.. branch: getarrayitem-into-bridges:
+
+More information is retained into a bridge: knowledge about the content of
+arrays (at fixed indices) is stored in guards (and thus available at the
+beginning of bridges). Also, some better feeding of information about known
+fields of constant objects into bridges.
+
+.. branch: cpyext-leakchecking
+
+Add support for leakfinder in cpyext tests (disabled for now, due to too many
+failures).
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1,4 +1,5 @@
import sys
+import py
from rpython.rlib.cache import Cache
from rpython.tool.uid import HUGEVAL_BYTES
@@ -1338,8 +1339,22 @@
self.setitem(w_globals, w_key, self.builtin)
return statement.exec_code(self, w_globals, w_locals)
+ @not_rpython
+ def appdef(self, source):
+ '''Create interp-level function object from app-level source.
+
+ The source should be in the same format as for space.appexec():
+ """(foo, bar): return 'baz'"""
+ '''
+ source = source.lstrip()
+ assert source.startswith('('), "incorrect header in:\n%s" % (source,)
+ source = py.code.Source("def anonymous%s\n" % source)
+ w_glob = self.newdict(module=True)
+ self.exec_(str(source), w_glob, w_glob)
+ return self.getitem(w_glob, self.newtext('anonymous'))
+
@specialize.arg(2)
- def appexec(self, posargs_w, source):
+ def appexec(self, posargs_w, source, cache=True):
""" return value from executing given source at applevel.
The source must look like
'''(x, y):
@@ -1347,7 +1362,11 @@
return result
'''
"""
- w_func = self.fromcache(AppExecCache).getorbuild(source)
+ if cache:
+ w_func = self.fromcache(AppExecCache).getorbuild(source)
+ else:
+ # NB: since appdef() is not-RPython, using cache=False also is.
+ w_func = self.appdef(source)
args = Arguments(self, list(posargs_w))
return self.call_args(w_func, args)
@@ -1926,15 +1945,7 @@
class AppExecCache(SpaceCache):
@not_rpython
def build(cache, source):
- space = cache.space
- # XXX will change once we have our own compiler
- import py
- source = source.lstrip()
- assert source.startswith('('), "incorrect header in:\n%s" % (source,)
- source = py.code.Source("def anonymous%s\n" % source)
- w_glob = space.newdict(module=True)
- space.exec_(str(source), w_glob, w_glob)
- return space.getitem(w_glob, space.newtext('anonymous'))
+ return cache.space.appdef(source)
# Table describing the regular part of the interface of object spaces,
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
--- a/pypy/module/cpyext/buffer.py
+++ b/pypy/module/cpyext/buffer.py
@@ -73,22 +73,27 @@
if self.needs_decref:
if self.releasebufferproc:
func_target = rffi.cast(releasebufferproc, self.releasebufferproc)
- with lltype.scoped_alloc(Py_buffer) as pybuf:
- pybuf.c_buf = self.ptr
- pybuf.c_len = self.size
- pybuf.c_ndim = cts.cast('int', self.ndim)
- pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape)
- pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides)
- for i in range(self.ndim):
- pybuf.c_shape[i] = self.shape[i]
- pybuf.c_strides[i] = self.strides[i]
- if self.format:
- pybuf.c_format = rffi.str2charp(self.format)
- else:
- pybuf.c_format = rffi.str2charp("B")
+ size = rffi.sizeof(cts.gettype('Py_buffer'))
+ pybuf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True)
+ pybuf = cts.cast('Py_buffer*', pybuf)
+ pybuf.c_buf = self.ptr
+ pybuf.c_len = self.size
+ pybuf.c_ndim = cts.cast('int', self.ndim)
+ pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape)
+ pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides)
+ for i in range(self.ndim):
+ pybuf.c_shape[i] = self.shape[i]
+ pybuf.c_strides[i] = self.strides[i]
+ fmt = rffi.str2charp(self.format if self.format else "B")
+ try:
+ pybuf.c_format = fmt
generic_cpy_call(self.space, func_target, self.pyobj, pybuf)
+ finally:
+ lltype.free(fmt, flavor='raw')
+ lltype.free(pybuf, flavor='raw')
decref(self.space, self.pyobj)
self.pyobj = lltype.nullptr(PyObject.TO)
+ self.w_obj = None
else:
#do not call twice
return
@@ -167,6 +172,8 @@
sizep[0] = size
return 0
+DEFAULT_FMT = rffi.str2charp("B")
+
@cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
lltype.Signed, lltype.Signed], rffi.INT, error=-1)
def PyBuffer_FillInfo(space, view, obj, buf, length, readonly, flags):
@@ -187,7 +194,8 @@
rffi.setintfield(view, 'c_ndim', 1)
view.c_format = lltype.nullptr(rffi.CCHARP.TO)
if (flags & PyBUF_FORMAT) == PyBUF_FORMAT:
- view.c_format = rffi.str2charp("B")
+ # NB: this needs to be a static string, because nothing frees it
+ view.c_format = DEFAULT_FMT
view.c_shape = lltype.nullptr(Py_ssize_tP.TO)
if (flags & PyBUF_ND) == PyBUF_ND:
view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
diff --git a/pypy/module/cpyext/memoryobject.py b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -72,7 +72,7 @@
readonly=widen(view.c_readonly))
# Ensure view.c_buf is released upon object finalization
fq.register_finalizer(buf)
- # Allow subclassing W_MemeoryView
+ # Allow subclassing W_MemoryView
w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
w_obj = space.allocate_instance(W_MemoryView, w_type)
w_obj.__init__(buf)
@@ -177,11 +177,9 @@
return (_IsCContiguous(view) or _IsFortranContiguous(view))
return 0
- at cpython_api([PyObject], PyObject, result_is_ll=True)
+ at cpython_api([PyObject], PyObject)
def PyMemoryView_FromObject(space, w_obj):
- w_memview = space.call_method(space.builtin, "memoryview", w_obj)
- py_memview = make_ref(space, w_memview, w_obj)
- return py_memview
+ return space.call_method(space.builtin, "memoryview", w_obj)
@cpython_api([Py_bufferP], PyObject, result_is_ll=True)
def PyMemoryView_FromBuffer(space, view):
@@ -193,6 +191,7 @@
# copy view into obj.c_view, without creating a new view.c_obj
typedescr = get_typedescr(W_MemoryView.typedef)
py_obj = typedescr.allocate(space, space.w_memoryview)
+
py_mem = rffi.cast(PyMemoryViewObject, py_obj)
mview = py_mem.c_view
mview.c_buf = view.c_buf
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
@@ -29,7 +29,7 @@
from pypy.module.cpyext.typeobject import subtype_dealloc
return subtype_dealloc.api_func
- def allocate(self, space, w_type, itemcount=0):
+ def allocate(self, space, w_type, itemcount=0, immortal=False):
# typically called from PyType_GenericAlloc via typedescr.allocate
# this returns a PyObject with ob_refcnt == 1.
@@ -50,7 +50,7 @@
assert size >= rffi.sizeof(PyObject.TO)
buf = lltype.malloc(rffi.VOIDP.TO, size,
flavor='raw', zero=True,
- add_memory_pressure=True)
+ add_memory_pressure=True, immortal=immortal)
pyobj = rffi.cast(PyObject, buf)
if pytype.c_tp_itemsize:
pyvarobj = rffi.cast(PyVarObject, pyobj)
@@ -102,7 +102,7 @@
basestruct = tp_basestruct
if tp_alloc:
- def allocate(self, space, w_type, itemcount=0):
+ def allocate(self, space, w_type, itemcount=0, immortal=False):
return tp_alloc(space, w_type, itemcount)
if tp_dealloc:
@@ -151,7 +151,7 @@
class InvalidPointerException(Exception):
pass
-def create_ref(space, w_obj, w_userdata=None):
+def create_ref(space, w_obj, w_userdata=None, immortal=False):
"""
Allocates a PyObject, and fills its fields with info from the given
interpreter object.
@@ -163,7 +163,7 @@
itemcount = space.len_w(w_obj) # PyBytesObject and subclasses
else:
itemcount = 0
- py_obj = typedescr.allocate(space, w_type, itemcount=itemcount)
+ py_obj = typedescr.allocate(space, w_type, itemcount=itemcount, immortal=immortal)
track_reference(space, py_obj, w_obj)
#
# py_obj.c_ob_refcnt should be exactly REFCNT_FROM_PYPY + 1 here,
@@ -227,7 +227,7 @@
assert isinstance(w_type, W_TypeObject)
return get_typedescr(w_type.layout.typedef).realize(space, ref)
-def as_pyobj(space, w_obj, w_userdata=None):
+def as_pyobj(space, w_obj, w_userdata=None, immortal=False):
"""
Returns a 'PyObject *' representing the given intepreter object.
This doesn't give a new reference, but the returned 'PyObject *'
@@ -239,7 +239,7 @@
assert not is_pyobj(w_obj)
py_obj = rawrefcount.from_obj(PyObject, w_obj)
if not py_obj:
- py_obj = create_ref(space, w_obj, w_userdata)
+ py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal)
return py_obj
else:
return lltype.nullptr(PyObject.TO)
@@ -270,7 +270,7 @@
return hop.inputconst(lltype.Bool, hop.s_result.const)
@specialize.ll()
-def make_ref(space, obj, w_userdata=None):
+def make_ref(space, obj, w_userdata=None, immortal=False):
"""Increment the reference counter of the PyObject and return it.
Can be called with either a PyObject or a W_Root.
"""
@@ -278,7 +278,7 @@
pyobj = rffi.cast(PyObject, obj)
at_least = 1
else:
- pyobj = as_pyobj(space, obj, w_userdata)
+ pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal)
at_least = rawrefcount.REFCNT_FROM_PYPY
if pyobj:
assert pyobj.c_ob_refcnt >= at_least
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
@@ -48,7 +48,7 @@
m as the message text. If the conversion otherwise, fails, reraise the
original exception"""
if isinstance(w_obj, W_ListObject):
- # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
+ # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
w_obj.convert_to_cpy_strategy(space)
return w_obj
try:
@@ -313,7 +313,7 @@
self)
w_clone.switch_to_object_strategy()
return w_clone
-
+
def copy_into(self, w_list, w_other):
w_list.switch_to_object_strategy()
w_list.strategy.copy_into(w_list, w_other)
@@ -378,7 +378,7 @@
def is_empty_strategy(self):
return False
-
+
PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True}))
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
@@ -1864,10 +1864,12 @@
if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1)
{
int ii, nn;
+ PyObject *ret;
int n = PyList_Size(obj1);
PyObject *v = getarrayitem(obj2, 0);
int i = ((PyIntObject*)v)->ob_ival;
- PyObject * ret = PyList_New(n*i);
+ Py_DECREF(v);
+ ret = PyList_New(n*i);
for (ii = 0; ii < i; ii++)
for (nn = 0; nn < n; nn++)
{
@@ -1880,10 +1882,12 @@
else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1)
{
int ii, nn;
+ PyObject *ret;
int n = PyList_Size(obj2);
PyObject *v = getarrayitem(obj1, 0);
int i = ((PyIntObject*)v)->ob_ival;
- PyObject * ret = PyList_New(n*i);
+ Py_DECREF(v);
+ ret = PyList_New(n*i);
for (ii = 0; ii < i; ii++)
for (nn = 0; nn < n; nn++)
{
@@ -1916,34 +1920,44 @@
if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1)
{
int nn;
+ PyObject *ret;
int n = PyList_Size(obj1);
PyObject *v = getarrayitem(obj2, 0);
int i = ((PyIntObject*)v)->ob_ival;
- PyObject * ret = PyList_New(n);
+ Py_DECREF(v);
+ ret = PyList_New(n);
for (nn = 0; nn < n; nn++)
{
v = PyList_GetItem(obj1, nn);
if (PyInt_Check(v))
PyList_SetItem(ret, nn, PyLong_FromLong(i * ((PyIntObject*)v)->ob_ival));
else
+ {
+ Py_INCREF(v);
PyList_SetItem(ret, nn, v);
+ }
}
return ret;
}
else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1)
{
int nn;
+ PyObject *ret;
int n = PyList_Size(obj2);
PyObject *v = getarrayitem(obj1, 0);
int i = ((PyIntObject*)v)->ob_ival;
- PyObject * ret = PyList_New(n);
+ Py_DECREF(v);
+ ret = PyList_New(n);
for (nn = 0; nn < n; nn++)
{
v = PyList_GetItem(obj2, nn);
if (PyInt_Check(v))
PyList_SetItem(ret, nn, PyLong_FromLong(i * ((PyIntObject*)v)->ob_ival));
else
+ {
+ Py_INCREF(v);
PyList_SetItem(ret, nn, v);
+ }
}
return ret;
}
@@ -2458,6 +2472,15 @@
Py_RETURN_NONE;
}
+static PyObject *
+same_dealloc(PyObject *self, PyObject *args)
+{
+ PyObject *obj1, *obj2;
+ if (!PyArg_ParseTuple(args, "OO", &obj1, &obj2)) {
+ return NULL;
+ }
+ return PyLong_FromLong(obj1->ob_type->tp_dealloc == obj2->ob_type->tp_dealloc);
+}
/*********************** Install Module **************************/
@@ -2467,6 +2490,7 @@
{"readbuffer_as_string", (PyCFunction)readbuffer_as_string, METH_VARARGS, NULL},
{"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},
{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
@@ -6,7 +6,8 @@
from pypy.module.cpyext.api import (
slot_function, cpython_api, copy_header_files, INTERPLEVEL_API,
Py_ssize_t, Py_ssize_tP, PyObject, cts)
-from pypy.module.cpyext.test.test_cpyext import freeze_refcnts, LeakCheckingTest
+from pypy.module.cpyext.test.test_cpyext import (
+ freeze_refcnts, LeakCheckingTest)
from pypy.interpreter.error import OperationError
from rpython.rlib import rawrefcount
import os
@@ -21,17 +22,7 @@
class BaseApiTest(LeakCheckingTest):
def setup_class(cls):
space = cls.space
- # warm up reference counts:
- # - the posix module allocates a HCRYPTPROV on Windows
- # - writing to stdout and stderr allocates a file lock
- space.getbuiltinmodule("cpyext")
- space.getbuiltinmodule(os.name)
- space.call_function(space.getattr(space.sys.get("stderr"),
- space.wrap("write")),
- space.wrap(""))
- space.call_function(space.getattr(space.sys.get("stdout"),
- space.wrap("write")),
- space.wrap(""))
+ cls.preload_builtins(space)
class CAPI:
def __getattr__(self, name):
@@ -39,9 +30,6 @@
cls.api = CAPI()
CAPI.__dict__.update(INTERPLEVEL_API)
- print 'DONT_FREE_ANY_MORE'
- rawrefcount._dont_free_any_more()
-
def raises(self, space, api, expected_exc, f, *args):
if not callable(f):
raise Exception("%s is not callable" % (f,))
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
@@ -1,3 +1,4 @@
+import pytest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.conftest import option
@@ -111,6 +112,19 @@
res = [1, 2, 3] * arr
assert res == [2, 4, 6]
+ @pytest.mark.xfail
+ def test_subclass_dealloc(self):
+ module = self.import_module(name='array')
+ class Sub(module.array):
+ pass
+
+ arr = Sub('i', [2])
+ module.readbuffer_as_string(arr)
+ class A(object):
+ pass
+ assert not module.same_dealloc(arr, module.array('i', [2]))
+ assert module.same_dealloc(arr, A())
+
def test_subclass(self):
import struct
module = self.import_module(name='array')
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
@@ -4,8 +4,11 @@
from pypy.tool.cpyext.extbuild import SystemCompilationInfo, HERE
from pypy.interpreter.gateway import unwrap_spec, interp2app
+from pypy.interpreter.error import OperationError
from rpython.rtyper.lltypesystem import lltype
from pypy.module.cpyext import api
+from pypy.module.cpyext.api import cts
+from pypy.module.cpyext.pyobject import from_ref
from pypy.module.cpyext.state import State
from rpython.tool import leakfinder
from rpython.rlib import rawrefcount
@@ -73,6 +76,58 @@
def freeze_refcnts(self):
rawrefcount._dont_free_any_more()
+def preload(space, name):
+ from pypy.module.cpyext.pyobject import make_ref
+ if '.' not in name:
+ w_obj = space.builtin.getdictvalue(space, name)
+ else:
+ module, localname = name.rsplit('.', 1)
+ code = "(): import {module}; return {module}.{localname}"
+ code = code.format(**locals())
+ w_obj = space.appexec([], code)
+ make_ref(space, w_obj)
+
+def preload_expr(space, expr):
+ from pypy.module.cpyext.pyobject import make_ref
+ code = "(): return {}".format(expr)
+ w_obj = space.appexec([], code)
+ make_ref(space, w_obj)
+
+def is_interned_string(space, w_obj):
+ try:
+ s = space.str_w(w_obj)
+ except OperationError:
+ return False
+ return space.is_interned_str(s)
+
+def is_allowed_to_leak(space, obj):
+ from pypy.module.cpyext.methodobject import W_PyCFunctionObject
+ try:
+ w_obj = from_ref(space, cts.cast('PyObject*', obj._as_ptr()))
+ except:
+ return False
+ if isinstance(w_obj, W_PyCFunctionObject):
+ return True
+ # It's OK to "leak" some interned strings: if the pyobj is created by
+ # the test, but the w_obj is referred to from elsewhere.
+ return is_interned_string(space, w_obj)
+
+def _get_w_obj(space, c_obj):
+ return from_ref(space, cts.cast('PyObject*', c_obj._as_ptr()))
+
+class CpyextLeak(leakfinder.MallocMismatch):
+ def __str__(self):
+ lines = [leakfinder.MallocMismatch.__str__(self), '']
+ lines.append(
+ "These objects are attached to the following W_Root objects:")
+ for c_obj in self.args[0]:
+ try:
+ lines.append(" %s" % (_get_w_obj(self.args[1], c_obj),))
+ except:
+ pass
+ return '\n'.join(lines)
+
+
class LeakCheckingTest(object):
"""Base class for all cpyext tests."""
spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array',
@@ -80,11 +135,35 @@
'micronumpy', 'mmap'
])
+ @classmethod
+ def preload_builtins(cls, space):
+ """
+ Eagerly create pyobjs for various builtins so they don't look like
+ leaks.
+ """
+ for name in [
+ 'buffer', 'mmap.mmap',
+ 'types.FunctionType', 'types.CodeType',
+ 'types.TracebackType', 'types.FrameType']:
+ preload(space, name)
+ for expr in ['type(str.join)']:
+ preload_expr(space, expr)
+
def cleanup(self):
self.space.getexecutioncontext().cleanup_cpyext_state()
rawrefcount._collect()
self.space.user_del_action._run_finalizers()
- leakfinder.stop_tracking_allocations(check=False)
+ try:
+ # set check=True to actually enable leakfinder
+ leakfinder.stop_tracking_allocations(check=False)
+ except leakfinder.MallocMismatch as e:
+ result = e.args[0]
+ filtered_result = {}
+ for obj, value in result.iteritems():
+ if not is_allowed_to_leak(self.space, obj):
+ filtered_result[obj] = value
+ if filtered_result:
+ raise CpyextLeak(filtered_result, self.space)
assert not self.space.finalizer_queue.next_dead()
@@ -131,6 +210,7 @@
def debug_collect(space):
rawrefcount._collect()
+
class AppTestCpythonExtensionBase(LeakCheckingTest):
def setup_class(cls):
@@ -140,13 +220,8 @@
cls.w_runappdirect = space.wrap(cls.runappdirect)
if not cls.runappdirect:
cls.sys_info = get_cpyext_info(space)
- space.getbuiltinmodule("cpyext")
- # 'import os' to warm up reference counts
- w_import = space.builtin.getdictvalue(space, '__import__')
- space.call_function(w_import, space.wrap("os"))
- #state = cls.space.fromcache(RefcountState) ZZZ
- #state.non_heaptypes_w[:] = []
cls.w_debug_collect = space.wrap(interp2app(debug_collect))
+ cls.preload_builtins(space)
else:
def w_import_module(self, name, init=None, body='', filename=None,
include_dirs=None, PY_SSIZE_T_CLEAN=False):
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
@@ -111,70 +111,14 @@
PyDict_Update(space, w_d, w_d2)
assert space.unwrap(w_d) == dict(a='b') # unchanged
- def test_iter(self, space):
- w_dict = space.sys.getdict(space)
- py_dict = make_ref(space, w_dict)
-
- ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
- ppos[0] = 0
- pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
- pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-
- try:
- w_copy = space.newdict()
- while PyDict_Next(space, w_dict, ppos, pkey, pvalue):
- w_key = from_ref(space, pkey[0])
- w_value = from_ref(space, pvalue[0])
- space.setitem(w_copy, w_key, w_value)
- finally:
- lltype.free(ppos, flavor='raw')
- lltype.free(pkey, flavor='raw')
- lltype.free(pvalue, flavor='raw')
-
- decref(space, py_dict) # release borrowed references
-
- assert space.eq_w(space.len(w_copy), space.len(w_dict))
- assert space.eq_w(w_copy, w_dict)
-
- def test_iterkeys(self, space):
- w_dict = space.sys.getdict(space)
- py_dict = make_ref(space, w_dict)
-
- ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
- pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
- pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-
- keys_w = []
- values_w = []
- try:
- ppos[0] = 0
- while PyDict_Next(space, w_dict, ppos, pkey, None):
- w_key = from_ref(space, pkey[0])
- keys_w.append(w_key)
- ppos[0] = 0
- while PyDict_Next(space, w_dict, ppos, None, pvalue):
- w_value = from_ref(space, pvalue[0])
- values_w.append(w_value)
- finally:
- lltype.free(ppos, flavor='raw')
- lltype.free(pkey, flavor='raw')
- lltype.free(pvalue, flavor='raw')
-
- decref(space, py_dict) # release borrowed references
-
- assert space.eq_w(space.newlist(keys_w),
- space.call_method(w_dict, "keys"))
- assert space.eq_w(space.newlist(values_w),
- space.call_method(w_dict, "values"))
-
def test_dictproxy(self, space):
- w_dict = space.sys.get('modules')
+ w_dict = space.appexec([], """(): return {1: 2, 3: 4}""")
w_proxy = PyDictProxy_New(space, w_dict)
- assert space.contains_w(w_proxy, space.wrap('sys'))
+ assert space.contains_w(w_proxy, space.newint(1))
raises(OperationError, space.setitem,
- w_proxy, space.wrap('sys'), space.w_None)
+ w_proxy, space.newint(1), space.w_None)
raises(OperationError, space.delitem,
- w_proxy, space.wrap('sys'))
+ w_proxy, space.newint(1))
raises(OperationError, space.call_method, w_proxy, 'clear')
assert PyDictProxy_Check(space, w_proxy)
@@ -243,6 +187,59 @@
d = {"a": 1}
raises(AttributeError, module.update, d, [("c", 2)])
+ def test_iter(self):
+ module = self.import_extension('foo', [
+ ("copy", "METH_O",
+ '''
+ Py_ssize_t pos = 0;
+ PyObject *key, *value;
+ PyObject* copy = PyDict_New();
+ while (PyDict_Next(args, &pos, &key, &value))
+ {
+ if (PyDict_SetItem(copy, key, value) < 0)
+ {
+ Py_DecRef(copy);
+ return NULL;
+ }
+ }
+ return copy;
+ ''')])
+ d = {1: 'xyz', 3: 'abcd'}
+ copy = module.copy(d)
+ assert len(copy) == len(d)
+ assert copy == d
+
+ def test_iterkeys(self):
+ module = self.import_extension('foo', [
+ ("keys_and_values", "METH_O",
+ '''
+ Py_ssize_t pos = 0;
+ PyObject *key, *value, *values;
+ PyObject* keys = PyList_New(0);
+ while (PyDict_Next(args, &pos, &key, NULL))
+ {
+ if (PyList_Append(keys, key) < 0)
+ {
+ Py_DecRef(keys);
+ return NULL;
+ }
+ }
+ pos = 0;
+ values = PyList_New(0);
+ while (PyDict_Next(args, &pos, NULL, &value))
+ {
+ if (PyList_Append(values, value) < 0)
+ {
+ Py_DecRef(keys);
+ Py_DecRef(values);
+ return NULL;
+ }
+ }
+ return Py_BuildValue("(NN)", keys, values);
+ ''')])
+ d = {1: 'xyz', 3: 'abcd'}
+ assert module.keys_and_values(d) == (d.keys(), d.values())
+
def test_typedict2(self):
module = self.import_extension('foo', [
("get_type_dict", "METH_O",
@@ -255,6 +252,7 @@
])
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",
@@ -266,7 +264,7 @@
'''
int ret;
PyObject * dict = PyTuple_GetItem(args, 0);
- if (PyTuple_Size(args) < 3 || !dict ||
+ 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);
@@ -279,7 +277,7 @@
'''
int ret;
PyObject * dict = PyTuple_GetItem(args, 0);
- if (PyTuple_Size(args) < 2 || !dict ||
+ 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);
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
@@ -279,6 +279,7 @@
assert module.call_method("text") == 2
def test_CompileString_and_Exec(self):
+ import sys
module = self.import_extension('foo', [
("compile_string", "METH_NOARGS",
"""
@@ -313,6 +314,9 @@
print mod.__dict__
assert mod.f(42) == 47
+ # Clean-up
+ del sys.modules['cpyext_test_modname']
+
def test_merge_compiler_flags(self):
module = self.import_extension('foo', [
("get_flags", "METH_NOARGS",
@@ -357,4 +361,4 @@
except RuntimeError as e:
assert 'while calling recurse' in str(e)
else:
- assert False, "expected RuntimeError"
+ assert False, "expected RuntimeError"
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
@@ -104,6 +104,7 @@
PyFloatObject* pfo = (PyFloatObject*)pyobj;
int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) &&
PyFloat_Check(pfo) && PyFloat_CheckExact(pfo);
+ Py_DecRef(pyobj);
return PyLong_FromLong(res);"""),
])
assert module.test() == 1
diff --git a/pypy/module/cpyext/test/test_funcobject.py b/pypy/module/cpyext/test/test_funcobject.py
--- a/pypy/module/cpyext/test/test_funcobject.py
+++ b/pypy/module/cpyext/test/test_funcobject.py
@@ -46,7 +46,7 @@
w_function = space.appexec([], """():
def func(x, y, z): return x
return func
- """)
+ """, cache=False)
w_code = PyFunction_GetCode(space, w_function)
assert w_code.co_name == "func"
@@ -63,7 +63,7 @@
w_code = space.appexec([], """():
def func(%s): %s
return func.__code__
- """ % (signature, body))
+ """ % (signature, body), cache=False)
ref = make_ref(space, w_code)
co_flags = rffi.cast(PyCodeObject, ref).c_co_flags
decref(space, ref)
diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py
--- a/pypy/module/cpyext/test/test_longobject.py
+++ b/pypy/module/cpyext/test/test_longobject.py
@@ -313,6 +313,7 @@
ret = obj->ob_type->tp_as_number->nb_power(obj, one, one);
else
ret = PyLong_FromLong(-1);
+ Py_DECREF(one);
Py_DECREF(obj);
return ret;
"""),
@@ -340,4 +341,3 @@
assert module.has_pow() == 0
assert module.has_hex() == '0x2aL'
assert module.has_oct() == '052L'
-
diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -4,7 +4,7 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from rpython.rlib.buffer import StringBuffer
-from pypy.module.cpyext.pyobject import from_ref
+from pypy.module.cpyext.pyobject import make_ref, from_ref
from pypy.module.cpyext.memoryobject import PyMemoryViewObject
only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names"
@@ -12,9 +12,9 @@
class TestMemoryViewObject(BaseApiTest):
def test_frombuffer(self, space, api):
w_buf = space.newbuffer(StringBuffer("hello"))
+ w_memoryview = api.PyMemoryView_FromObject(w_buf)
c_memoryview = rffi.cast(
- PyMemoryViewObject, api.PyMemoryView_FromObject(w_buf))
- w_memoryview = from_ref(space, c_memoryview)
+ PyMemoryViewObject, make_ref(space, w_memoryview))
view = c_memoryview.c_view
assert view.c_ndim == 1
f = rffi.charp2str(view.c_format)
@@ -32,6 +32,7 @@
assert space.eq_w(space.getattr(w_mv, w_f),
space.getattr(w_memoryview, w_f))
api.Py_DecRef(ref)
+ api.Py_DecRef(w_memoryview)
class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
def test_fillWithObject(self):
@@ -62,7 +63,6 @@
""")])
result = module.fillinfo()
assert b"hello, world." == result
- del result
class AppTestBufferProtocol(AppTestCpythonExtensionBase):
def test_fromobject(self):
@@ -172,8 +172,6 @@
# in <bound method ConcreteArray.__del__ ...> ignored
def test_releasebuffer(self):
- if not self.runappdirect:
- skip("Fails due to ll2ctypes nonsense")
module = self.import_extension('foo', [
("create_test", "METH_NOARGS",
"""
diff --git a/pypy/module/cpyext/test/test_traceback.py b/pypy/module/cpyext/test/test_traceback.py
--- a/pypy/module/cpyext/test/test_traceback.py
+++ b/pypy/module/cpyext/test/test_traceback.py
@@ -3,17 +3,19 @@
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
from pypy.module.cpyext.pytraceback import PyTracebackObject
from pypy.interpreter.pytraceback import PyTraceback
-from pypy.interpreter.pyframe import PyFrame
+from pypy.interpreter.baseobjspace import AppExecCache
class TestPyTracebackObject(BaseApiTest):
def test_traceback(self, space, api):
- w_traceback = space.appexec([], """():
+ src = """():
import sys
try:
1/0
except:
return sys.exc_info()[2]
- """)
+ """
+ w_traceback = space.appexec([], src)
+
py_obj = make_ref(space, w_traceback)
py_traceback = rffi.cast(PyTracebackObject, py_obj)
assert (from_ref(space, rffi.cast(PyObject, py_traceback.c_ob_type)) is
@@ -38,3 +40,5 @@
assert lltype.normalizeptr(py_traceback) is None
api.Py_DecRef(py_obj)
+ # hack to allow the code object to be freed
+ del space.fromcache(AppExecCache).content[src]
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -24,6 +24,7 @@
def test_tuple_realize_refuses_nulls(self, space, api):
py_tuple = api.PyTuple_New(1)
py.test.raises(FatalError, from_ref, space, py_tuple)
+ api.Py_DecRef(py_tuple)
def test_tuple_resize(self, space, api):
w_42 = space.wrap(42)
@@ -70,6 +71,7 @@
w_tuple = from_ref(space, py_tuple)
assert space.eq_w(w_tuple, space.newtuple([space.wrap(42),
space.wrap(43)]))
+ api.Py_DecRef(py_tuple)
def test_getslice(self, space, api):
w_tuple = space.newtuple([space.wrap(i) for i in range(10)])
@@ -174,6 +176,7 @@
res = PyTuple_SetItem(tuple, 0, one);
if (res != 0)
{
+ Py_DECREF(one);
Py_DECREF(tuple);
return NULL;
}
@@ -187,14 +190,13 @@
/* Do something that uses the tuple, but does not incref */
t2 = PyTuple_GetSlice(tuple, 0, 1);
Py_DECREF(t2);
- Py_INCREF(one);
res = PyTuple_SetItem(tuple, 0, one);
- Py_DECREF(tuple);
if (res != 0)
{
- Py_DECREF(one);
+ Py_DECREF(tuple);
return NULL;
}
+ Py_DECREF(tuple);
Py_INCREF(Py_None);
return Py_None;
"""),
@@ -205,4 +207,3 @@
raises(SystemError, module.set_after_use, s)
else:
module.set_after_use(s)
-
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -143,6 +143,7 @@
old_ref = tupleobj.c_ob_item[index]
if pyobj_has_w_obj(ref):
# similar but not quite equal to ref.c_ob_refcnt != 1 on CPython
+ decref(space, py_obj)
raise oefmt(space.w_SystemError, "PyTuple_SetItem called on tuple after"
" use of tuple")
tupleobj.c_ob_item[index] = py_obj # consumes a reference
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
@@ -195,6 +195,8 @@
py_getsetdef = make_GetSet(space, w_obj)
assert space.isinstance_w(w_userdata, space.w_type)
w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata)
+ # now w_obj.getset is py_getsetdef, which was freshly allocated
+ # XXX how is this ever released?
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_GetSetPropertyEx)
py_getsetdescr.c_d_getset = w_obj.getset
@@ -922,7 +924,9 @@
bases_w = []
else:
bases_w = [from_ref(space, base_pyo)]
- pto.c_tp_bases = make_ref(space, space.newtuple(bases_w))
+ is_heaptype = bool(pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE)
+ pto.c_tp_bases = make_ref(space, space.newtuple(bases_w),
+ immortal=not is_heaptype)
def finish_type_2(space, pto, w_obj):
"""
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py b/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py
@@ -562,3 +562,13 @@
x = X()
assert x.x == 0
+
+ def test_duplicate_names(self):
+ class S(Structure):
+ _fields_ = [('a', c_int),
+ ('b', c_int),
+ ('a', c_byte)]
+ s = S(260, -123)
+ assert sizeof(s) == 3 * sizeof(c_int)
+ assert s.a == 4 # 256 + 4
+ assert s.b == -123
diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py
--- a/pypy/objspace/std/specialisedtupleobject.py
+++ b/pypy/objspace/std/specialisedtupleobject.py
@@ -74,15 +74,15 @@
elif typetuple[i] == int:
# mimic cpythons behavior of a hash value of -2 for -1
y = value
- if y == -1:
- y = -2
+ y -= (y == -1) # No explicit condition, to avoid JIT bridges
elif typetuple[i] == float:
# get the correct hash for float which is an
# integer & other less frequent cases
from pypy.objspace.std.floatobject import _hash_float
y = _hash_float(space, value)
+ y -= (y == -1)
else:
- y = compute_hash(value)
+ assert 0, "unreachable"
x = (x ^ y) * mult
z -= 1
mult += 82520 + z + z
diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py b/pypy/objspace/std/test/test_specialisedtupleobject.py
--- a/pypy/objspace/std/test/test_specialisedtupleobject.py
+++ b/pypy/objspace/std/test/test_specialisedtupleobject.py
@@ -37,6 +37,8 @@
self.space.eq(self.space.hash(N_w_tuple),
self.space.hash(S_w_tuple)))
+ hash_test([-1, -1])
+ hash_test([-1.0, -1.0])
hash_test([1, 2])
hash_test([1.5, 2.8])
hash_test([1.0, 2.0])
diff --git a/rpython/jit/metainterp/logger.py b/rpython/jit/metainterp/logger.py
--- a/rpython/jit/metainterp/logger.py
+++ b/rpython/jit/metainterp/logger.py
@@ -13,10 +13,11 @@
self.guard_number = guard_number
def log_loop_from_trace(self, trace, memo):
+ debug_start("jit-log-noopt")
if not have_debug_prints():
+ debug_stop("jit-log-noopt")
return
inputargs, ops = self._unpack_trace(trace)
- debug_start("jit-log-noopt")
debug_print("# Traced loop or bridge with", len(ops), "ops")
logops = self._log_operations(inputargs, ops, None, memo)
debug_stop("jit-log-noopt")
diff --git a/rpython/jit/metainterp/optimizeopt/bridgeopt.py b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
--- a/rpython/jit/metainterp/optimizeopt/bridgeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
@@ -84,7 +84,6 @@
# heap knowledge: we store triples of known heap fields in non-virtual
# structs
- # XXX could be extended to arrays
if optimizer.optheap:
triples_struct, triples_array = optimizer.optheap.serialize_optheap(available_boxes)
# can only encode descrs that have a known index into
diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -2208,7 +2208,7 @@
return _ptr(Ptr(T), o, solid)
@analyzer_for(malloc)
-def ann_malloc(s_T, s_n=None, s_flavor=None, s_zero=None,
+def ann_malloc(s_T, s_n=None, s_flavor=None, s_immortal=None, s_zero=None,
s_track_allocation=None, s_add_memory_pressure=None,
s_nonmovable=None):
assert (s_n is None or s_n.knowntype == int
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -347,19 +347,20 @@
# annotation of low-level types
@typer_for(lltype.malloc)
-def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None,
- i_add_memory_pressure=None, i_nonmovable=None):
+def rtype_malloc(hop, i_flavor=None, i_immortal=None, i_zero=None,
+ i_track_allocation=None, i_add_memory_pressure=None, i_nonmovable=None):
assert hop.args_s[0].is_constant()
vlist = [hop.inputarg(lltype.Void, arg=0)]
opname = 'malloc'
kwds_v = parse_kwds(
hop,
(i_flavor, lltype.Void),
+ (i_immortal, None),
(i_zero, None),
(i_track_allocation, None),
(i_add_memory_pressure, None),
(i_nonmovable, None))
- (v_flavor, v_zero, v_track_allocation,
+ (v_flavor, v_immortal, v_zero, v_track_allocation,
v_add_memory_pressure, v_nonmovable) = kwds_v
flags = {'flavor': 'gc'}
if v_flavor is not None:
More information about the pypy-commit
mailing list