[pypy-commit] pypy dynamic-specialized-tuple: Merged default in.
alex_gaynor
noreply at buildbot.pypy.org
Sat Apr 7 20:47:47 CEST 2012
Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: dynamic-specialized-tuple
Changeset: r54236:48aba87044b4
Date: 2012-04-07 14:47 -0400
http://bitbucket.org/pypy/pypy/changeset/48aba87044b4/
Log: Merged default in.
diff --git a/dotviewer/graphparse.py b/dotviewer/graphparse.py
--- a/dotviewer/graphparse.py
+++ b/dotviewer/graphparse.py
@@ -93,6 +93,7 @@
return result
def parse_plain(graph_id, plaincontent, links={}, fixedfont=False):
+ plaincontent = plaincontent.replace('\r\n', '\n') # fix Windows EOL
lines = plaincontent.splitlines(True)
for i in range(len(lines)-2, -1, -1):
if lines[i].endswith('\\\n'): # line ending in '\'
diff --git a/lib_pypy/datetime.py b/lib_pypy/datetime.py
--- a/lib_pypy/datetime.py
+++ b/lib_pypy/datetime.py
@@ -968,8 +968,7 @@
self._checkOverflow(t.year)
result = date(t.year, t.month, t.day)
return result
- raise TypeError
- # XXX Should be 'return NotImplemented', but there's a bug in 2.2...
+ return NotImplemented # note that this doesn't work on CPython 2.2
__radd__ = __add__
diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst
--- a/pypy/doc/project-ideas.rst
+++ b/pypy/doc/project-ideas.rst
@@ -149,6 +149,22 @@
exported. This would give us a one-size-fits-all generic .so file to be
imported by any application that wants to load .so files :-)
+Optimising cpyext (CPython C-API compatibility layer)
+-----------------------------------------------------
+
+A lot of work has gone into PyPy's implementation of CPython's C-API over
+the last years to let it reach a practical level of compatibility, so that
+C extensions for CPython work on PyPy without major rewrites. However,
+there are still many edges and corner cases where it misbehaves, and it has
+not received any substantial optimisation so far.
+
+The objective of this project is to fix bugs in cpyext and to optimise
+several performance critical parts of it, such as the reference counting
+support and other heavily used C-API functions. The net result would be to
+have CPython extensions run much faster on PyPy than they currently do, or
+to make them work at all if they currently don't. A part of this work would
+be to get cpyext into a shape where it supports running Cython generated
+extensions.
.. _`issue tracker`: http://bugs.pypy.org
.. _`mailing list`: http://mail.python.org/mailman/listinfo/pypy-dev
diff --git a/pypy/doc/stackless.rst b/pypy/doc/stackless.rst
--- a/pypy/doc/stackless.rst
+++ b/pypy/doc/stackless.rst
@@ -199,17 +199,11 @@
The following features (present in some past Stackless version of PyPy)
are for the time being not supported any more:
-* Tasklets and channels (currently ``stackless.py`` seems to import,
- but you have tasklets on top of coroutines on top of greenlets on
- top of continulets on top of stacklets, and it's probably not too
- hard to cut two of these levels by adapting ``stackless.py`` to
- use directly continulets)
-
* Coroutines (could be rewritten at app-level)
-* Pickling and unpickling continulets (*)
-
-* Continuing execution of a continulet in a different thread (*)
+* Continuing execution of a continulet in a different thread
+ (but if it is "simple enough", you can pickle it and unpickle it
+ in the other thread).
* Automatic unlimited stack (must be emulated__ so far)
@@ -217,15 +211,6 @@
.. __: `recursion depth limit`_
-(*) Pickling, as well as changing threads, could be implemented by using
-a "soft" stack switching mode again. We would get either "hard" or
-"soft" switches, similarly to Stackless Python 3rd version: you get a
-"hard" switch (like now) when the C stack contains non-trivial C frames
-to save, and a "soft" switch (like previously) when it contains only
-simple calls from Python to Python. Soft-switched continulets would
-also consume a bit less RAM, and the switch might be a bit faster too
-(unsure about that; what is the Stackless Python experience?).
-
Recursion depth limit
+++++++++++++++++++++
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -296,6 +296,7 @@
self.check_signal_action = None # changed by the signal module
self.user_del_action = UserDelAction(self)
self.frame_trace_action = FrameTraceAction(self)
+ self._code_of_sys_exc_info = None
from pypy.interpreter.pycode import cpython_magic, default_magic
self.our_magic = default_magic
@@ -467,9 +468,9 @@
if name not in modules:
modules.append(name)
- # a bit of custom logic: time2 or rctime take precedence over time
+ # a bit of custom logic: rctime take precedence over time
# XXX this could probably be done as a "requires" in the config
- if ('time2' in modules or 'rctime' in modules) and 'time' in modules:
+ if 'rctime' in modules and 'time' in modules:
modules.remove('time')
if not self.config.objspace.nofaking:
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -154,6 +154,7 @@
#operationerr.print_detailed_traceback(self.space)
def _convert_exc(self, operr):
+ # Only for the flow object space
return operr
def sys_exc_info(self): # attn: the result is not the wrapped sys.exc_info() !!!
@@ -166,6 +167,11 @@
frame = self.getnextframe_nohidden(frame)
return None
+ def set_sys_exc_info(self, operror):
+ frame = self.gettopframe_nohidden()
+ if frame: # else, the exception goes nowhere and is lost
+ frame.last_exception = operror
+
def settrace(self, w_func):
"""Set the global trace function."""
if self.space.is_w(w_func, self.space.w_None):
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -113,6 +113,12 @@
from pypy.interpreter.pycode import PyCode
code = self.getcode() # hook for the jit
+ #
+ if (jit.we_are_jitted() and code is self.space._code_of_sys_exc_info
+ and nargs == 0):
+ from pypy.module.sys.vm import exc_info_direct
+ return exc_info_direct(self.space, frame)
+ #
fast_natural_arity = code.fast_natural_arity
if nargs == fast_natural_arity:
if nargs == 0:
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -874,6 +874,12 @@
fn.add_to_table()
if gateway.as_classmethod:
fn = ClassMethod(space.wrap(fn))
+ #
+ from pypy.module.sys.vm import exc_info
+ if code._bltin is exc_info:
+ assert space._code_of_sys_exc_info is None
+ space._code_of_sys_exc_info = code
+ #
return fn
diff --git a/pypy/interpreter/pyparser/parsestring.py b/pypy/interpreter/pyparser/parsestring.py
--- a/pypy/interpreter/pyparser/parsestring.py
+++ b/pypy/interpreter/pyparser/parsestring.py
@@ -2,34 +2,39 @@
from pypy.interpreter import unicodehelper
from pypy.rlib.rstring import StringBuilder
-def parsestr(space, encoding, s, unicode_literals=False):
- # compiler.transformer.Transformer.decode_literal depends on what
- # might seem like minor details of this function -- changes here
- # must be reflected there.
+def parsestr(space, encoding, s, unicode_literal=False):
+ """Parses a string or unicode literal, and return a wrapped value.
+
+ If encoding=iso8859-1, the source string is also in this encoding.
+ If encoding=None, the source string is ascii only.
+ In other cases, the source string is in utf-8 encoding.
+
+ When a bytes string is returned, it will be encoded with the
+ original encoding.
+
+ Yes, it's very inefficient.
+ Yes, CPython has very similar code.
+ """
# we use ps as "pointer to s"
# q is the virtual last char index of the string
ps = 0
quote = s[ps]
rawmode = False
- unicode = unicode_literals
# string decoration handling
- o = ord(quote)
- isalpha = (o>=97 and o<=122) or (o>=65 and o<=90)
- if isalpha or quote == '_':
- if quote == 'b' or quote == 'B':
- ps += 1
- quote = s[ps]
- unicode = False
- elif quote == 'u' or quote == 'U':
- ps += 1
- quote = s[ps]
- unicode = True
- if quote == 'r' or quote == 'R':
- ps += 1
- quote = s[ps]
- rawmode = True
+ if quote == 'b' or quote == 'B':
+ ps += 1
+ quote = s[ps]
+ unicode_literal = False
+ elif quote == 'u' or quote == 'U':
+ ps += 1
+ quote = s[ps]
+ unicode_literal = True
+ if quote == 'r' or quote == 'R':
+ ps += 1
+ quote = s[ps]
+ rawmode = True
if quote != "'" and quote != '"':
raise_app_valueerror(space,
'Internal error: parser passed unquoted literal')
@@ -46,21 +51,28 @@
'unmatched triple quotes in literal')
q -= 2
- if unicode: # XXX Py_UnicodeFlag is ignored for now
+ if unicode_literal: # XXX Py_UnicodeFlag is ignored for now
if encoding is None or encoding == "iso-8859-1":
+ # 'unicode_escape' expects latin-1 bytes, string is ready.
buf = s
bufp = ps
bufq = q
u = None
else:
- # "\XX" may become "\u005c\uHHLL" (12 bytes)
+ # String is utf8-encoded, but 'unicode_escape' expects
+ # latin-1; So multibyte sequences must be escaped.
lis = [] # using a list to assemble the value
end = q
+ # Worst case: "\XX" may become "\u005c\uHHLL" (12 bytes)
while ps < end:
if s[ps] == '\\':
lis.append(s[ps])
ps += 1
if ord(s[ps]) & 0x80:
+ # A multibyte sequence will follow, it will be
+ # escaped like \u1234. To avoid confusion with
+ # the backslash we just wrote, we emit "\u005c"
+ # instead.
lis.append("u005c")
if ord(s[ps]) & 0x80: # XXX inefficient
w, ps = decode_utf8(space, s, ps, end, "utf-16-be")
@@ -86,13 +98,11 @@
need_encoding = (encoding is not None and
encoding != "utf-8" and encoding != "iso-8859-1")
- # XXX add strchr like interface to rtyper
assert 0 <= ps <= q
substr = s[ps : q]
if rawmode or '\\' not in s[ps:]:
if need_encoding:
w_u = space.wrap(unicodehelper.PyUnicode_DecodeUTF8(space, substr))
- #w_v = space.wrap(space.unwrap(w_u).encode(encoding)) this works
w_v = unicodehelper.PyUnicode_AsEncodedString(space, w_u, space.wrap(encoding))
return w_v
else:
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -321,7 +321,7 @@
def user_setup(self, space, w_subtype):
self.w__dict__ = space.newdict(
- instance=True, classofinstance=w_subtype)
+ instance=True)
base_user_setup(self, space, w_subtype)
def setclass(self, space, w_subtype):
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -1796,6 +1796,7 @@
if specialize_as_constant:
def specialize_call(self, hop):
llvalue = func(hop.args_s[0].const)
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.typeOf(llvalue), llvalue)
else:
# specialize as direct_call
@@ -1812,6 +1813,7 @@
sm = ootype._static_meth(FUNCTYPE, _name=func.__name__, _callable=func)
cfunc = hop.inputconst(FUNCTYPE, sm)
args_v = hop.inputargs(*hop.args_r)
+ hop.exception_is_here()
return hop.genop('direct_call', [cfunc] + args_v, hop.r_result)
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -432,7 +432,8 @@
raise _ssl_seterror(self.space, self, length)
try:
# this is actually an immutable bytes sequence
- return self.space.wrap(rffi.charp2str(buf_ptr[0]))
+ return self.space.wrap(rffi.charpsize2str(buf_ptr[0],
+ length))
finally:
libssl_OPENSSL_free(buf_ptr[0])
else:
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -11,7 +11,7 @@
from pypy.objspace.std.register_all import register_all
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.objectmodel import specialize
+from pypy.rlib.objectmodel import specialize, keepalive_until_here
from pypy.rpython.lltypesystem import lltype, rffi
@@ -145,18 +145,24 @@
unroll_typecodes = unrolling_iterable(types.keys())
class ArrayBuffer(RWBuffer):
- def __init__(self, data, bytes):
- self.data = data
- self.len = bytes
+ def __init__(self, array):
+ self.array = array
def getlength(self):
- return self.len
+ return self.array.len * self.array.itemsize
def getitem(self, index):
- return self.data[index]
+ array = self.array
+ data = array._charbuf_start()
+ char = data[index]
+ array._charbuf_stop()
+ return char
def setitem(self, index, char):
- self.data[index] = char
+ array = self.array
+ data = array._charbuf_start()
+ data[index] = char
+ array._charbuf_stop()
def make_array(mytype):
@@ -278,9 +284,10 @@
oldlen = self.len
new = len(s) / mytype.bytes
self.setlen(oldlen + new)
- cbuf = self.charbuf()
+ cbuf = self._charbuf_start()
for i in range(len(s)):
cbuf[oldlen * mytype.bytes + i] = s[i]
+ self._charbuf_stop()
def fromlist(self, w_lst):
s = self.len
@@ -310,8 +317,11 @@
else:
self.fromsequence(w_iterable)
- def charbuf(self):
- return rffi.cast(rffi.CCHARP, self.buffer)
+ def _charbuf_start(self):
+ return rffi.cast(rffi.CCHARP, self.buffer)
+
+ def _charbuf_stop(self):
+ keepalive_until_here(self)
def w_getitem(self, space, idx):
item = self.buffer[idx]
@@ -530,8 +540,10 @@
self.fromstring(space.str_w(w_s))
def array_tostring__Array(space, self):
- cbuf = self.charbuf()
- return self.space.wrap(rffi.charpsize2str(cbuf, self.len * mytype.bytes))
+ cbuf = self._charbuf_start()
+ s = rffi.charpsize2str(cbuf, self.len * mytype.bytes)
+ self._charbuf_stop()
+ return self.space.wrap(s)
def array_fromfile__Array_ANY_ANY(space, self, w_f, w_n):
if not isinstance(w_f, W_File):
@@ -613,8 +625,7 @@
# Misc methods
def buffer__Array(space, self):
- b = ArrayBuffer(self.charbuf(), self.len * mytype.bytes)
- return space.wrap(b)
+ return space.wrap(ArrayBuffer(self))
def array_buffer_info__Array(space, self):
w_ptr = space.wrap(rffi.cast(lltype.Unsigned, self.buffer))
@@ -649,7 +660,7 @@
raise OperationError(space.w_RuntimeError, space.wrap(msg))
if self.len == 0:
return
- bytes = self.charbuf()
+ bytes = self._charbuf_start()
tmp = [bytes[0]] * mytype.bytes
for start in range(0, self.len * mytype.bytes, mytype.bytes):
stop = start + mytype.bytes - 1
@@ -657,6 +668,7 @@
tmp[i] = bytes[start + i]
for i in range(mytype.bytes):
bytes[stop - i] = tmp[i]
+ self._charbuf_stop()
def repr__Array(space, self):
if self.len == 0:
diff --git a/pypy/module/array/test/test_array.py b/pypy/module/array/test/test_array.py
--- a/pypy/module/array/test/test_array.py
+++ b/pypy/module/array/test/test_array.py
@@ -433,7 +433,25 @@
a = self.array('h', 'Hi')
buf = buffer(a)
assert buf[1] == 'i'
- #raises(TypeError, buf.__setitem__, 1, 'o')
+
+ def test_buffer_write(self):
+ a = self.array('c', 'hello')
+ buf = buffer(a)
+ print repr(buf)
+ try:
+ buf[3] = 'L'
+ except TypeError:
+ skip("buffer(array) returns a read-only buffer on CPython")
+ assert a.tostring() == 'helLo'
+
+ def test_buffer_keepalive(self):
+ buf = buffer(self.array('c', 'text'))
+ assert buf[2] == 'x'
+ #
+ a = self.array('c', 'foobarbaz')
+ buf = buffer(a)
+ a.fromstring('some extra text')
+ assert buf[:] == 'foobarbazsome extra text'
def test_list_methods(self):
assert repr(self.array('i')) == "array('i')"
diff --git a/pypy/module/cpyext/bufferobject.py b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -2,8 +2,10 @@
from pypy.module.cpyext.api import (
cpython_api, Py_ssize_t, cpython_struct, bootstrap_function,
PyObjectFields, PyObject)
-from pypy.module.cpyext.pyobject import make_typedescr, Py_DecRef
+from pypy.module.cpyext.pyobject import make_typedescr, Py_DecRef, make_ref
from pypy.interpreter.buffer import Buffer, StringBuffer, SubBuffer
+from pypy.interpreter.error import OperationError
+from pypy.module.array.interp_array import ArrayBuffer
PyBufferObjectStruct = lltype.ForwardReference()
@@ -41,26 +43,38 @@
py_buf.c_b_offset = w_obj.offset
w_obj = w_obj.buffer
+ # If w_obj already allocated a fixed buffer, use it, and keep a
+ # reference to w_obj.
+ # Otherwise, b_base stays NULL, and we own the b_ptr.
+
if isinstance(w_obj, StringBuffer):
- py_buf.c_b_base = rffi.cast(PyObject, 0) # space.wrap(w_obj.value)
- py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, rffi.str2charp(w_obj.as_str()))
+ py_buf.c_b_base = lltype.nullptr(PyObject.TO)
+ py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, rffi.str2charp(w_obj.value))
+ py_buf.c_b_size = w_obj.getlength()
+ elif isinstance(w_obj, ArrayBuffer):
+ w_base = w_obj.array
+ py_buf.c_b_base = make_ref(space, w_base)
+ py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, w_obj.array._charbuf_start())
py_buf.c_b_size = w_obj.getlength()
else:
- raise Exception("Fail fail fail fail fail")
+ raise OperationError(space.w_NotImplementedError, space.wrap(
+ "buffer flavor not supported"))
def buffer_realize(space, py_obj):
"""
Creates the buffer in the PyPy interpreter from a cpyext representation.
"""
- raise Exception("realize fail fail fail")
-
+ raise OperationError(space.w_NotImplementedError, space.wrap(
+ "Don't know how to realize a buffer"))
@cpython_api([PyObject], lltype.Void, external=False)
def buffer_dealloc(space, py_obj):
py_buf = rffi.cast(PyBufferObject, py_obj)
- Py_DecRef(space, py_buf.c_b_base)
- rffi.free_charp(rffi.cast(rffi.CCHARP, py_buf.c_b_ptr))
+ if py_buf.c_b_base:
+ Py_DecRef(space, py_buf.c_b_base)
+ else:
+ rffi.free_charp(rffi.cast(rffi.CCHARP, py_buf.c_b_ptr))
from pypy.module.cpyext.object import PyObject_dealloc
PyObject_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h
--- a/pypy/module/cpyext/include/patchlevel.h
+++ b/pypy/module/cpyext/include/patchlevel.h
@@ -29,7 +29,7 @@
#define PY_VERSION "2.7.2"
/* PyPy version as a string */
-#define PYPY_VERSION "1.8.1"
+#define PYPY_VERSION "1.9.1"
/* Subversion Revision number of this file (not of the repository).
* Empty since Mercurial migration. */
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
@@ -2,6 +2,7 @@
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError
+from pypy.interpreter import pytraceback
from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL, CONST_STRING
from pypy.module.exceptions.interp_exceptions import W_RuntimeWarning
from pypy.module.cpyext.pyobject import (
@@ -315,3 +316,65 @@
It may be called without holding the interpreter lock."""
space.check_signal_action.set_interrupt()
+ at cpython_api([PyObjectP, PyObjectP, PyObjectP], lltype.Void)
+def PyErr_GetExcInfo(space, ptype, pvalue, ptraceback):
+ """---Cython extension---
+
+ Retrieve the exception info, as known from ``sys.exc_info()``. This
+ refers to an exception that was already caught, not to an exception
+ that was freshly raised. Returns new references for the three
+ objects, any of which may be *NULL*. Does not modify the exception
+ info state.
+
+ .. note::
+
+ This function is not normally used by code that wants to handle
+ exceptions. Rather, it can be used when code needs to save and
+ restore the exception state temporarily. Use
+ :c:func:`PyErr_SetExcInfo` to restore or clear the exception
+ state.
+ """
+ ec = space.getexecutioncontext()
+ operror = ec.sys_exc_info()
+ if operror:
+ ptype[0] = make_ref(space, operror.w_type)
+ pvalue[0] = make_ref(space, operror.get_w_value(space))
+ ptraceback[0] = make_ref(space, space.wrap(operror.get_traceback()))
+ else:
+ ptype[0] = lltype.nullptr(PyObject.TO)
+ pvalue[0] = lltype.nullptr(PyObject.TO)
+ ptraceback[0] = lltype.nullptr(PyObject.TO)
+
+ at cpython_api([PyObject, PyObject, PyObject], lltype.Void)
+def PyErr_SetExcInfo(space, w_type, w_value, w_traceback):
+ """---Cython extension---
+
+ Set the exception info, as known from ``sys.exc_info()``. This refers
+ to an exception that was already caught, not to an exception that was
+ freshly raised. This function steals the references of the arguments.
+ To clear the exception state, pass *NULL* for all three arguments.
+ For general rules about the three arguments, see :c:func:`PyErr_Restore`.
+
+ .. note::
+
+ This function is not normally used by code that wants to handle
+ exceptions. Rather, it can be used when code needs to save and
+ restore the exception state temporarily. Use
+ :c:func:`PyErr_GetExcInfo` to read the exception state.
+ """
+ if w_value is None or space.is_w(w_value, space.w_None):
+ operror = None
+ else:
+ tb = None
+ if w_traceback is not None:
+ try:
+ tb = pytraceback.check_traceback(space, w_traceback, '?')
+ except OperationError: # catch and ignore bogus objects
+ pass
+ operror = OperationError(w_type, w_value, tb)
+ #
+ ec = space.getexecutioncontext()
+ ec.set_sys_exc_info(operror)
+ Py_DecRef(space, w_type)
+ Py_DecRef(space, w_value)
+ Py_DecRef(space, w_traceback)
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
@@ -167,14 +167,16 @@
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)
- if rffi.cast(lltype.Signed, res) == -1:
+ res = rffi.cast(lltype.Signed, res)
+ if res == -1:
space.fromcache(State).check_and_raise_exception(always=True)
- return space.wrap(res)
+ return space.wrap(bool(res))
def wrap_objobjargproc(space, w_self, w_args, func):
func_target = rffi.cast(objobjargproc, func)
@@ -183,7 +185,7 @@
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.wrap(res)
+ return space.w_None
def wrap_delitem(space, w_self, w_args, func):
func_target = rffi.cast(objobjargproc, func)
diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -176,6 +176,8 @@
{NULL} /* Sentinel */
};
+PyDoc_STRVAR(foo_doc, "foo is for testing.");
+
static PyTypeObject footype = {
PyVarObject_HEAD_INIT(NULL, 0)
"foo.foo", /*tp_name*/
@@ -198,7 +200,7 @@
(setattrofunc)foo_setattro, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
- 0, /*tp_doc*/
+ foo_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
diff --git a/pypy/module/cpyext/test/test_bufferobject.py b/pypy/module/cpyext/test/test_bufferobject.py
--- a/pypy/module/cpyext/test/test_bufferobject.py
+++ b/pypy/module/cpyext/test/test_bufferobject.py
@@ -48,3 +48,17 @@
])
b = module.buffer_new()
raises(AttributeError, getattr, b, 'x')
+
+ def test_array_buffer(self):
+ module = self.import_extension('foo', [
+ ("roundtrip", "METH_O",
+ """
+ PyBufferObject *buf = (PyBufferObject *)args;
+ return PyString_FromStringAndSize(buf->b_ptr, buf->b_size);
+ """),
+ ])
+ import array
+ a = array.array('c', 'text')
+ b = buffer(a)
+ assert module.roundtrip(b) == 'text'
+
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
@@ -218,3 +218,51 @@
assert e.filename == "blyf"
assert e.errno == errno.EBADF
assert e.strerror == os.strerror(errno.EBADF)
+
+ def test_GetSetExcInfo(self):
+ import sys
+ module = self.import_extension('foo', [
+ ("getset_exc_info", "METH_VARARGS",
+ r'''
+ PyObject *type, *val, *tb;
+ PyObject *new_type, *new_val, *new_tb;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "OOO", &new_type, &new_val, &new_tb))
+ return NULL;
+
+ PyErr_GetExcInfo(&type, &val, &tb);
+
+ Py_INCREF(new_type);
+ Py_INCREF(new_val);
+ Py_INCREF(new_tb);
+ PyErr_SetExcInfo(new_type, new_val, new_tb);
+
+ result = Py_BuildValue("OOO",
+ type ? type : Py_None,
+ val ? val : Py_None,
+ tb ? tb : Py_None);
+ Py_XDECREF(type);
+ Py_XDECREF(val);
+ Py_XDECREF(tb);
+ return result;
+ '''
+ ),
+ ])
+ try:
+ raise ValueError(5)
+ except ValueError, old_exc:
+ new_exc = TypeError("TEST")
+ orig_sys_exc_info = sys.exc_info()
+ orig_exc_info = module.getset_exc_info(new_exc.__class__,
+ new_exc, None)
+ new_sys_exc_info = sys.exc_info()
+ new_exc_info = module.getset_exc_info(*orig_exc_info)
+ reset_sys_exc_info = sys.exc_info()
+
+ assert orig_exc_info[0] is old_exc.__class__
+ assert orig_exc_info[1] is old_exc
+ assert orig_exc_info == orig_sys_exc_info
+ assert orig_exc_info == reset_sys_exc_info
+ assert new_exc_info == (new_exc.__class__, new_exc, None)
+ assert new_exc_info == new_sys_exc_info
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
@@ -20,6 +20,7 @@
assert type(obj) is module.fooType
print "type of obj has type", type(type(obj))
print "type of type of obj has type", type(type(type(obj)))
+ assert module.fooType.__doc__ == "foo is for testing."
def test_typeobject_method_descriptor(self):
module = self.import_module(name='foo')
@@ -414,8 +415,11 @@
static int
mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value)
{
- PyErr_SetNone(PyExc_ZeroDivisionError);
- return -1;
+ if (PyInt_Check(key)) {
+ PyErr_SetNone(PyExc_ZeroDivisionError);
+ return -1;
+ }
+ return 0;
}
PyMappingMethods tp_as_mapping;
static PyTypeObject Foo_Type = {
@@ -425,6 +429,36 @@
''')
obj = module.new_obj()
raises(ZeroDivisionError, obj.__setitem__, 5, None)
+ res = obj.__setitem__('foo', None)
+ assert res is None
+
+ def test_sq_contains(self):
+ module = self.import_extension('foo', [
+ ("new_obj", "METH_NOARGS",
+ '''
+ PyObject *obj;
+ Foo_Type.tp_as_sequence = &tp_as_sequence;
+ tp_as_sequence.sq_contains = sq_contains;
+ if (PyType_Ready(&Foo_Type) < 0) return NULL;
+ obj = PyObject_New(PyObject, &Foo_Type);
+ return obj;
+ '''
+ )],
+ '''
+ static int
+ sq_contains(PyObject *self, PyObject *value)
+ {
+ return 42;
+ }
+ PySequenceMethods tp_as_sequence;
+ static PyTypeObject Foo_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "foo.foo",
+ };
+ ''')
+ obj = module.new_obj()
+ res = "foo" in obj
+ assert res is True
def test_tp_iter(self):
module = self.import_extension('foo', [
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
@@ -307,6 +307,8 @@
if not space.is_true(space.issubtype(self, space.w_type)):
self.flag_cpytype = True
self.flag_heaptype = False
+ if pto.c_tp_doc:
+ self.w_doc = space.wrap(rffi.charp2str(pto.c_tp_doc))
@bootstrap_function
def init_typeobject(space):
@@ -624,7 +626,6 @@
Creates an interpreter type from a PyTypeObject structure.
"""
# missing:
- # setting __doc__ if not defined and tp_doc defined
# inheriting tp_as_* slots
# unsupported:
# tp_mro, tp_subclasses
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -29,6 +29,7 @@
'flatiter': 'interp_numarray.W_FlatIterator',
'isna': 'interp_numarray.isna',
'concatenate': 'interp_numarray.concatenate',
+ 'repeat': 'interp_numarray.repeat',
'set_string_function': 'appbridge.set_string_function',
@@ -99,9 +100,12 @@
("exp2", "exp2"),
("expm1", "expm1"),
("fabs", "fabs"),
+ ("fmax", "fmax"),
+ ("fmin", "fmin"),
("fmod", "fmod"),
("floor", "floor"),
("ceil", "ceil"),
+ ("trunc", "trunc"),
("greater", "greater"),
("greater_equal", "greater_equal"),
("less", "less"),
@@ -122,12 +126,16 @@
("sinh", "sinh"),
("subtract", "subtract"),
('sqrt', 'sqrt'),
+ ('square', 'square'),
("tan", "tan"),
("tanh", "tanh"),
('bitwise_and', 'bitwise_and'),
('bitwise_or', 'bitwise_or'),
('bitwise_xor', 'bitwise_xor'),
('bitwise_not', 'invert'),
+ ('left_shift', 'left_shift'),
+ ('right_shift', 'right_shift'),
+ ('invert', 'invert'),
('isnan', 'isnan'),
('isinf', 'isinf'),
('isneginf', 'isneginf'),
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -673,6 +673,10 @@
def compute_first_step(self, sig, frame):
pass
+ @unwrap_spec(repeats=int)
+ def descr_repeat(self, space, repeats, w_axis=None):
+ return repeat(space, self, repeats, w_axis)
+
def convert_to_array(space, w_obj):
if isinstance(w_obj, BaseArray):
return w_obj
@@ -1261,6 +1265,31 @@
return convert_to_array(space, w_obj2).descr_dot(space, w_arr)
return w_arr.descr_dot(space, w_obj2)
+ at unwrap_spec(repeats=int)
+def repeat(space, w_arr, repeats, w_axis=None):
+ arr = convert_to_array(space, w_arr)
+ if space.is_w(w_axis, space.w_None):
+ arr = arr.descr_flatten(space).get_concrete()
+ orig_size = arr.shape[0]
+ shape = [arr.shape[0] * repeats]
+ res = W_NDimArray(shape, arr.find_dtype())
+ for i in range(repeats):
+ Chunks([Chunk(i, shape[0] - repeats + i, repeats,
+ orig_size)]).apply(res).setslice(space, arr)
+ else:
+ arr = arr.get_concrete()
+ axis = space.int_w(w_axis)
+ shape = arr.shape[:]
+ chunks = [Chunk(0, i, 1, i) for i in shape]
+ orig_size = shape[axis]
+ shape[axis] *= repeats
+ res = W_NDimArray(shape, arr.find_dtype())
+ for i in range(repeats):
+ chunks[axis] = Chunk(i, shape[axis] - repeats + i, repeats,
+ orig_size)
+ Chunks(chunks).apply(res).setslice(space, arr)
+ return res
+
@unwrap_spec(axis=int)
def concatenate(space, w_args, axis=0):
args_w = space.listview(w_args)
@@ -1386,6 +1415,7 @@
tolist = interp2app(BaseArray.descr_tolist),
take = interp2app(BaseArray.descr_take),
compress = interp2app(BaseArray.descr_compress),
+ repeat = interp2app(BaseArray.descr_repeat),
)
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -3,9 +3,11 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
from pypy.module.micronumpy import interp_boxes, interp_dtype, support, loop
+from pypy.rlib import jit
from pypy.rlib.rarithmetic import LONG_BIT
from pypy.tool.sourcetools import func_with_new_name
+
class W_Ufunc(Wrappable):
_attrs_ = ["name", "promote_to_float", "promote_bools", "identity"]
_immutable_fields_ = ["promote_to_float", "promote_bools", "name"]
@@ -28,7 +30,7 @@
return self.identity
def descr_call(self, space, __args__):
- from interp_numarray import BaseArray
+ from interp_numarray import BaseArray
args_w, kwds_w = __args__.unpack()
# it occurs to me that we don't support any datatypes that
# require casting, change it later when we do
@@ -179,7 +181,7 @@
elif out.shape != shape:
raise operationerrfmt(space.w_ValueError,
'output parameter shape mismatch, expecting [%s]' +
- ' , got [%s]',
+ ' , got [%s]',
",".join([str(x) for x in shape]),
",".join([str(x) for x in out.shape]),
)
@@ -204,7 +206,7 @@
else:
arr = ReduceArray(self.func, self.name, self.identity, obj, dtype)
val = loop.compute(arr)
- return val
+ return val
def do_axis_reduce(self, obj, dtype, axis, result):
from pypy.module.micronumpy.interp_numarray import AxisReduce
@@ -253,7 +255,7 @@
if isinstance(w_obj, Scalar):
arr = self.func(calc_dtype, w_obj.value.convert_to(calc_dtype))
if isinstance(out,Scalar):
- out.value=arr
+ out.value = arr
elif isinstance(out, BaseArray):
out.fill(space, arr)
else:
@@ -265,7 +267,7 @@
if not broadcast_shape or broadcast_shape != out.shape:
raise operationerrfmt(space.w_ValueError,
'output parameter shape mismatch, could not broadcast [%s]' +
- ' to [%s]',
+ ' to [%s]',
",".join([str(x) for x in w_obj.shape]),
",".join([str(x) for x in out.shape]),
)
@@ -292,10 +294,11 @@
self.func = func
self.comparison_func = comparison_func
+ @jit.unroll_safe
def call(self, space, args_w):
from pypy.module.micronumpy.interp_numarray import (Call2,
convert_to_array, Scalar, shape_agreement, BaseArray)
- if len(args_w)>2:
+ if len(args_w) > 2:
[w_lhs, w_rhs, w_out] = args_w
else:
[w_lhs, w_rhs] = args_w
@@ -326,7 +329,7 @@
w_rhs.value.convert_to(calc_dtype)
)
if isinstance(out,Scalar):
- out.value=arr
+ out.value = arr
elif isinstance(out, BaseArray):
out.fill(space, arr)
else:
@@ -337,7 +340,7 @@
if out and out.shape != shape_agreement(space, new_shape, out.shape):
raise operationerrfmt(space.w_ValueError,
'output parameter shape mismatch, could not broadcast [%s]' +
- ' to [%s]',
+ ' to [%s]',
",".join([str(x) for x in new_shape]),
",".join([str(x) for x in out.shape]),
)
@@ -347,7 +350,6 @@
w_lhs.add_invalidates(w_res)
w_rhs.add_invalidates(w_res)
if out:
- #out.add_invalidates(w_res) #causes a recursion loop
w_res.get_concrete()
return w_res
@@ -539,14 +541,18 @@
("reciprocal", "reciprocal", 1),
("fabs", "fabs", 1, {"promote_to_float": True}),
+ ("fmax", "fmax", 2, {"promote_to_float": True}),
+ ("fmin", "fmin", 2, {"promote_to_float": True}),
("fmod", "fmod", 2, {"promote_to_float": True}),
("floor", "floor", 1, {"promote_to_float": True}),
("ceil", "ceil", 1, {"promote_to_float": True}),
+ ("trunc", "trunc", 1, {"promote_to_float": True}),
("exp", "exp", 1, {"promote_to_float": True}),
("exp2", "exp2", 1, {"promote_to_float": True}),
("expm1", "expm1", 1, {"promote_to_float": True}),
('sqrt', 'sqrt', 1, {'promote_to_float': True}),
+ ('square', 'square', 1, {'promote_to_float': True}),
("sin", "sin", 1, {"promote_to_float": True}),
("cos", "cos", 1, {"promote_to_float": True}),
diff --git a/pypy/module/micronumpy/signature.py b/pypy/module/micronumpy/signature.py
--- a/pypy/module/micronumpy/signature.py
+++ b/pypy/module/micronumpy/signature.py
@@ -107,6 +107,10 @@
arr.compute_first_step(self, f)
return f
+ def debug_repr(self):
+ # should be overridden, but in case it isn't, provide a default
+ return str(self)
+
class ConcreteSignature(Signature):
_immutable_fields_ = ['dtype']
@@ -207,7 +211,7 @@
def _create_iter(self, iterlist, arraylist, arr, transforms):
from pypy.module.micronumpy.interp_numarray import VirtualSlice
assert isinstance(arr, VirtualSlice)
- transforms = transforms + [ViewTransform(arr.chunks)]
+ transforms = [ViewTransform(arr.chunks)] + transforms
self.child._create_iter(iterlist, arraylist, arr.child, transforms)
def eval(self, frame, arr):
@@ -215,6 +219,9 @@
assert isinstance(arr, VirtualSlice)
return self.child.eval(frame, arr.child)
+ def debug_repr(self):
+ return 'VirtualSlice(%s)' % self.child.debug_repr()
+
class Call1(Signature):
_immutable_fields_ = ['unfunc', 'name', 'child', 'res', 'dtype']
@@ -270,7 +277,7 @@
from pypy.module.micronumpy.interp_numarray import Call1
assert isinstance(arr, Call1)
- vtransforms = transforms + [BroadcastTransform(arr.values.shape)]
+ vtransforms = [BroadcastTransform(arr.values.shape)] + transforms
self.child._create_iter(iterlist, arraylist, arr.values, vtransforms)
self.res._create_iter(iterlist, arraylist, arr.res, transforms)
@@ -348,7 +355,7 @@
from pypy.module.micronumpy.interp_numarray import ResultArray
assert isinstance(arr, ResultArray)
- rtransforms = transforms + [BroadcastTransform(arr.left.shape)]
+ rtransforms = [BroadcastTransform(arr.left.shape)] + transforms
self.left._create_iter(iterlist, arraylist, arr.left, transforms)
self.right._create_iter(iterlist, arraylist, arr.right, rtransforms)
@@ -375,7 +382,7 @@
from pypy.module.micronumpy.interp_numarray import Call2
assert isinstance(arr, Call2)
- ltransforms = transforms + [BroadcastTransform(arr.shape)]
+ ltransforms = [BroadcastTransform(arr.shape)] + transforms
self.left._create_iter(iterlist, arraylist, arr.left, ltransforms)
self.right._create_iter(iterlist, arraylist, arr.right, transforms)
@@ -388,7 +395,7 @@
from pypy.module.micronumpy.interp_numarray import Call2
assert isinstance(arr, Call2)
- rtransforms = transforms + [BroadcastTransform(arr.shape)]
+ rtransforms = [BroadcastTransform(arr.shape)] + transforms
self.left._create_iter(iterlist, arraylist, arr.left, transforms)
self.right._create_iter(iterlist, arraylist, arr.right, rtransforms)
@@ -401,8 +408,8 @@
from pypy.module.micronumpy.interp_numarray import Call2
assert isinstance(arr, Call2)
- rtransforms = transforms + [BroadcastTransform(arr.shape)]
- ltransforms = transforms + [BroadcastTransform(arr.shape)]
+ rtransforms = [BroadcastTransform(arr.shape)] + transforms
+ ltransforms = [BroadcastTransform(arr.shape)] + transforms
self.left._create_iter(iterlist, arraylist, arr.left, ltransforms)
self.right._create_iter(iterlist, arraylist, arr.right, rtransforms)
@@ -424,7 +431,7 @@
frame.cur_value = self.binfunc(self.calc_dtype, frame.cur_value, rval)
def debug_repr(self):
- return 'ReduceSig(%s)' % (self.name, self.right.debug_repr())
+ return 'ReduceSig(%s, %s)' % (self.name, self.right.debug_repr())
class SliceloopSignature(Call2):
def eval(self, frame, arr):
@@ -448,7 +455,7 @@
from pypy.module.micronumpy.interp_numarray import SliceArray
assert isinstance(arr, SliceArray)
- rtransforms = transforms + [BroadcastTransform(arr.shape)]
+ rtransforms = [BroadcastTransform(arr.shape)] + transforms
self.left._create_iter(iterlist, arraylist, arr.left, transforms)
self.right._create_iter(iterlist, arraylist, arr.right, rtransforms)
diff --git a/pypy/module/micronumpy/strides.py b/pypy/module/micronumpy/strides.py
--- a/pypy/module/micronumpy/strides.py
+++ b/pypy/module/micronumpy/strides.py
@@ -1,6 +1,7 @@
from pypy.rlib import jit
from pypy.interpreter.error import OperationError
+ at jit.look_inside_iff(lambda chunks: jit.isconstant(len(chunks)))
def enumerate_chunks(chunks):
result = []
i = -1
@@ -85,9 +86,9 @@
space.isinstance_w(w_item_or_slice, space.w_slice)):
raise OperationError(space.w_IndexError,
space.wrap('unsupported iterator index'))
-
+
start, stop, step, lngth = space.decode_index4(w_item_or_slice, size)
-
+
coords = [0] * len(shape)
i = start
if order == 'C':
diff --git a/pypy/module/micronumpy/support.py b/pypy/module/micronumpy/support.py
--- a/pypy/module/micronumpy/support.py
+++ b/pypy/module/micronumpy/support.py
@@ -1,5 +1,9 @@
+from pypy.rlib import jit
+
+
+ at jit.unroll_safe
def product(s):
i = 1
for x in s:
i *= x
- return i
\ No newline at end of file
+ return i
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -395,11 +395,19 @@
assert a[3] == 0.
def test_newaxis(self):
- from _numpypy import array
+ import math
+ from _numpypy import array, cos, zeros
from numpypy.core.numeric import newaxis
a = array(range(5))
b = array([range(5)])
assert (a[newaxis] == b).all()
+ a = array(range(3))
+ b = array([1, 3])
+ expected = zeros((3, 2))
+ for x in range(3):
+ for y in range(2):
+ expected[x, y] = math.cos(a[x]) * math.cos(b[y])
+ assert ((cos(a)[:,newaxis] * cos(b).T) == expected).all()
def test_newaxis_slice(self):
from _numpypy import array
@@ -1338,6 +1346,10 @@
dims_disagree = raises(ValueError, concatenate, (a1, b1), axis=0)
assert str(dims_disagree.value) == \
"array dimensions must agree except for axis being concatenated"
+ a = array([1, 2, 3, 4, 5, 6])
+ a = (a + a)[::2]
+ b = concatenate((a[:3], a[-3:]))
+ assert (b == [2, 6, 10, 2, 6, 10]).all()
def test_std(self):
from _numpypy import array
@@ -1387,6 +1399,16 @@
assert (ones(1) + ones(1)).nbytes == 8
assert array(3.0).nbytes == 8
+ def test_repeat(self):
+ from _numpypy import repeat, array
+ assert (repeat([[1, 2], [3, 4]], 3) == [1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 4, 4, 4]).all()
+ assert (repeat([[1, 2], [3, 4]], 2, axis=0) == [[1, 2], [1, 2], [3, 4],
+ [3, 4]]).all()
+ assert (repeat([[1, 2], [3, 4]], 2, axis=1) == [[1, 1, 2, 2], [3, 3,
+ 4, 4]]).all()
+ assert (array([1, 2]).repeat(2) == array([1, 1, 2, 2])).all()
+
class AppTestMultiDim(BaseNumpyAppTest):
def test_init(self):
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -135,6 +135,38 @@
assert fabs(float('-inf')) == float('inf')
assert isnan(fabs(float('nan')))
+ def test_fmax(self):
+ from _numpypy import fmax
+ import math
+
+ nnan, nan, inf, ninf = float('-nan'), float('nan'), float('inf'), float('-inf')
+
+ a = [ninf, -5, 0, 5, inf]
+ assert (fmax(a, [ninf]*5) == a).all()
+ assert (fmax(a, [inf]*5) == [inf]*5).all()
+ assert (fmax(a, [1]*5) == [1, 1, 1, 5, inf]).all()
+ assert math.isnan(fmax(nan, 0))
+ assert math.isnan(fmax(0, nan))
+ assert math.isnan(fmax(nan, nan))
+ # The numpy docs specify that the FIRST NaN should be used if both are NaN
+ assert math.copysign(1.0, fmax(nnan, nan)) == -1.0
+
+ def test_fmin(self):
+ from _numpypy import fmin
+ import math
+
+ nnan, nan, inf, ninf = float('-nan'), float('nan'), float('inf'), float('-inf')
+
+ a = [ninf, -5, 0, 5, inf]
+ assert (fmin(a, [ninf]*5) == [ninf]*5).all()
+ assert (fmin(a, [inf]*5) == a).all()
+ assert (fmin(a, [1]*5) == [ninf, -5, 0, 1, 1]).all()
+ assert math.isnan(fmin(nan, 0))
+ assert math.isnan(fmin(0, nan))
+ assert math.isnan(fmin(nan, nan))
+ # The numpy docs specify that the FIRST NaN should be used if both are NaN
+ assert math.copysign(1.0, fmin(nnan, nan)) == -1.0
+
def test_fmod(self):
from _numpypy import fmod
import math
@@ -221,24 +253,17 @@
for i in range(3):
assert c[i] == a[i] - b[i]
- def test_floorceil(self):
- from _numpypy import array, floor, ceil
+ def test_floorceiltrunc(self):
+ from _numpypy import array, floor, ceil, trunc
import math
- reference = [-2.0, -2.0, -1.0, 0.0, 1.0, 1.0, 0]
- a = array([-1.4, -1.5, -1.0, 0.0, 1.0, 1.4, 0.5])
- b = floor(a)
- for i in range(5):
- assert b[i] == reference[i]
- reference = [-1.0, -1.0, -1.0, 0.0, 1.0, 2.0, 1.0]
- a = array([-1.4, -1.5, -1.0, 0.0, 1.0, 1.4, 0.5])
- b = ceil(a)
- assert (reference == b).all()
- inf = float("inf")
- data = [1.5, 2.9999, -1.999, inf]
- results = [math.floor(x) for x in data]
- assert (floor(data) == results).all()
- results = [math.ceil(x) for x in data]
- assert (ceil(data) == results).all()
+ ninf, inf = float("-inf"), float("inf")
+ a = array([ninf, -1.4, -1.5, -1.0, 0.0, 1.0, 1.4, 0.5, inf])
+ assert ([ninf, -2.0, -2.0, -1.0, 0.0, 1.0, 1.0, 0.0, inf] == floor(a)).all()
+ assert ([ninf, -1.0, -1.0, -1.0, 0.0, 1.0, 2.0, 1.0, inf] == ceil(a)).all()
+ assert ([ninf, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, inf] == trunc(a)).all()
+ assert all([math.isnan(f(float("nan"))) for f in floor, ceil, trunc])
+ assert all([math.copysign(1, f(float("nan"))) == 1 for f in floor, ceil, trunc])
+ assert all([math.copysign(1, f(float("-nan"))) == -1 for f in floor, ceil, trunc])
def test_copysign(self):
from _numpypy import array, copysign
@@ -455,6 +480,19 @@
assert math.isnan(sqrt(-1))
assert math.isnan(sqrt(nan))
+ def test_square(self):
+ import math
+ from _numpypy import square
+
+ nan, inf, ninf = float("nan"), float("inf"), float("-inf")
+
+ assert math.isnan(square(nan))
+ assert math.isinf(square(inf))
+ assert math.isinf(square(ninf))
+ assert square(ninf) > 0
+ assert [square(x) for x in range(-5, 5)] == [x*x for x in range(-5, 5)]
+ assert math.isinf(square(1e300))
+
def test_radians(self):
import math
from _numpypy import radians, array
@@ -546,10 +584,18 @@
raises(TypeError, 'array([1.0]) & 1')
def test_unary_bitops(self):
- from _numpypy import bitwise_not, array
+ from _numpypy import bitwise_not, invert, array
a = array([1, 2, 3, 4])
assert (~a == [-2, -3, -4, -5]).all()
assert (bitwise_not(a) == ~a).all()
+ assert (invert(a) == ~a).all()
+
+ def test_shift(self):
+ from _numpypy import left_shift, right_shift
+ import sys
+
+ assert (left_shift([5, 1], [2, 31]) == [20, 2**31]).all()
+ assert (right_shift(10, range(5)) == [10, 5, 2, 1, 0]).all()
def test_comparisons(self):
import operator
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -631,6 +631,22 @@
return math.fabs(v)
@simple_binary_op
+ def fmax(self, v1, v2):
+ if math.isnan(v1):
+ return v1
+ elif math.isnan(v2):
+ return v2
+ return max(v1, v2)
+
+ @simple_binary_op
+ def fmin(self, v1, v2):
+ if math.isnan(v1):
+ return v1
+ elif math.isnan(v2):
+ return v2
+ return min(v1, v2)
+
+ @simple_binary_op
def fmod(self, v1, v2):
try:
return math.fmod(v1, v2)
@@ -652,6 +668,13 @@
return math.ceil(v)
@simple_unary_op
+ def trunc(self, v):
+ if v < 0:
+ return math.ceil(v)
+ else:
+ return math.floor(v)
+
+ @simple_unary_op
def exp(self, v):
try:
return math.exp(v)
@@ -741,6 +764,10 @@
except ValueError:
return rfloat.NAN
+ @simple_unary_op
+ def square(self, v):
+ return v*v
+
@raw_unary_op
def isnan(self, v):
return rfloat.isnan(v)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -212,7 +212,7 @@
i19 = int_add(i12, 1)
setfield_gc(p9, i19, descr=<FieldS .*W_AbstractSeqIterObject.inst_index .*>)
guard_nonnull_class(p17, 146982464, descr=...)
- i21 = getfield_gc(p17, descr=<FieldS .*W_ArrayTypei.inst_len .*>)
+ i21 = getfield_gc(p17, descr=<FieldS .*W_Array.*.inst_len .*>)
i23 = int_lt(0, i21)
guard_true(i23, descr=...)
i24 = getfield_gc(p17, descr=<FieldU .*W_ArrayTypei.inst_buffer .*>)
@@ -351,3 +351,23 @@
# the following assertion fails if the loop was cancelled due
# to "abort: vable escape"
assert len(log.loops_by_id("eval")) == 1
+
+ def test_sys_exc_info(self):
+ def main():
+ i = 1
+ lst = [i]
+ while i < 1000:
+ try:
+ return lst[i]
+ except:
+ e = sys.exc_info()[1] # ID: exc_info
+ if not isinstance(e, IndexError):
+ raise
+ i += 1
+ return 42
+
+ log = self.run(main)
+ assert log.result == 42
+ # the following assertion fails if the loop was cancelled due
+ # to "abort: vable escape"
+ assert len(log.loops_by_id("exc_info")) == 1
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -198,3 +198,37 @@
i49 = call(ConstClass(_ll_2_str_eq_nonnull__rpy_stringPtr_rpy_stringPtr), p35, ConstPtr(ptr48), descr=<Calli [48] rr EF=0 OS=28>)
guard_value(i49, 1, descr=...)
''')
+
+ def test_remove_duplicate_method_calls(self):
+ def main(n):
+ lst = []
+ for i in range(n):
+ s = 'Hello %d' % i
+ t = s.lower() # ID: callone
+ u = s.lower() # ID: calltwo
+ lst.append(t)
+ lst.append(u)
+ return len(','.join(lst))
+ log = self.run(main, [1000])
+ assert log.result == main(1000)
+ loops = log.loops_by_filename(self.filepath)
+ loop, = loops
+ loop.match_by_id('callone', '''
+ p114 = call(ConstClass(ll_lower__rpy_stringPtr), p113, descr=<Callr . r EF=3>)
+ guard_no_exception(descr=...)
+ ''')
+ loop.match_by_id('calltwo', '') # nothing
+
+ def test_move_method_call_out_of_loop(self):
+ def main(n):
+ lst = []
+ s = 'Hello %d' % n
+ for i in range(n):
+ t = s.lower() # ID: callone
+ lst.append(t)
+ return len(','.join(lst))
+ log = self.run(main, [1000])
+ assert log.result == main(1000)
+ loops = log.loops_by_filename(self.filepath)
+ loop, = loops
+ loop.match_by_id('callone', '') # nothing
diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py
--- a/pypy/module/sys/test/test_sysmodule.py
+++ b/pypy/module/sys/test/test_sysmodule.py
@@ -595,3 +595,124 @@
assert len(frames) == 1
_, other_frame = frames.popitem()
assert other_frame.f_code.co_name in ('other_thread', '?')
+
+
+class AppTestSysExcInfoDirect:
+
+ def setup_method(self, meth):
+ self.checking = not option.runappdirect
+ if self.checking:
+ self.seen = []
+ from pypy.module.sys import vm
+ def exc_info_with_tb(*args):
+ self.seen.append("n") # not optimized
+ return self.old[0](*args)
+ def exc_info_without_tb(*args):
+ self.seen.append("y") # optimized
+ return self.old[1](*args)
+ self.old = [vm.exc_info_with_tb, vm.exc_info_without_tb]
+ vm.exc_info_with_tb = exc_info_with_tb
+ vm.exc_info_without_tb = exc_info_without_tb
+ #
+ from pypy.rlib import jit
+ self.old2 = [jit.we_are_jitted]
+ jit.we_are_jitted = lambda: True
+
+ def teardown_method(self, meth):
+ if self.checking:
+ from pypy.module.sys import vm
+ from pypy.rlib import jit
+ vm.exc_info_with_tb = self.old[0]
+ vm.exc_info_without_tb = self.old[1]
+ jit.we_are_jitted = self.old2[0]
+ #
+ assert ''.join(self.seen) == meth.expected
+
+ def test_returns_none(self):
+ import sys
+ assert sys.exc_info() == (None, None, None)
+ assert sys.exc_info()[0] is None
+ assert sys.exc_info()[1] is None
+ assert sys.exc_info()[2] is None
+ assert sys.exc_info()[:2] == (None, None)
+ assert sys.exc_info()[:3] == (None, None, None)
+ assert sys.exc_info()[0:2] == (None, None)
+ assert sys.exc_info()[2:4] == (None,)
+ test_returns_none.expected = 'nnnnnnnn'
+
+ def test_returns_subscr(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ assert sys.exc_info()[0] is KeyError # y
+ assert sys.exc_info()[1] is e # y
+ assert sys.exc_info()[2] is not None # n
+ assert sys.exc_info()[-3] is KeyError # y
+ assert sys.exc_info()[-2] is e # y
+ assert sys.exc_info()[-1] is not None # n
+ test_returns_subscr.expected = 'yynyyn'
+
+ def test_returns_slice_2(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ foo = sys.exc_info() # n
+ assert sys.exc_info()[:0] == () # y
+ assert sys.exc_info()[:1] == foo[:1] # y
+ assert sys.exc_info()[:2] == foo[:2] # y
+ assert sys.exc_info()[:3] == foo # n
+ assert sys.exc_info()[:4] == foo # n
+ assert sys.exc_info()[:-1] == foo[:2] # y
+ assert sys.exc_info()[:-2] == foo[:1] # y
+ assert sys.exc_info()[:-3] == () # y
+ test_returns_slice_2.expected = 'nyyynnyyy'
+
+ def test_returns_slice_3(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ foo = sys.exc_info() # n
+ assert sys.exc_info()[2:2] == () # y
+ assert sys.exc_info()[0:1] == foo[:1] # y
+ assert sys.exc_info()[1:2] == foo[1:2] # y
+ assert sys.exc_info()[0:3] == foo # n
+ assert sys.exc_info()[2:4] == foo[2:] # n
+ assert sys.exc_info()[0:-1] == foo[:2] # y
+ assert sys.exc_info()[0:-2] == foo[:1] # y
+ assert sys.exc_info()[5:-3] == () # y
+ test_returns_slice_3.expected = 'nyyynnyyy'
+
+ def test_strange_invocation(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ a = []; k = {}
+ assert sys.exc_info(*a)[:0] == ()
+ assert sys.exc_info(**k)[:0] == ()
+ test_strange_invocation.expected = 'nn'
+
+ def test_call_in_subfunction(self):
+ import sys
+ def g():
+ # this case is not optimized, because we need to search the
+ # frame chain. it's probably not worth the complications
+ return sys.exc_info()[1]
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ assert g() is e
+ test_call_in_subfunction.expected = 'n'
+
+
+class AppTestSysExcInfoDirectCallMethod(AppTestSysExcInfoDirect):
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.opcodes.CALL_METHOD": True})
diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py
--- a/pypy/module/sys/version.py
+++ b/pypy/module/sys/version.py
@@ -10,7 +10,7 @@
CPYTHON_VERSION = (2, 7, 2, "final", 42) #XXX # sync patchlevel.h
CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h
-PYPY_VERSION = (1, 8, 1, "dev", 0) #XXX # sync patchlevel.h
+PYPY_VERSION = (1, 9, 1, "dev", 0) #XXX # sync patchlevel.h
if platform.name == 'msvc':
COMPILER_INFO = 'MSC v.%d 32 bit' % (platform.version * 10 + 600)
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -89,6 +89,9 @@
"""Return the (type, value, traceback) of the most recent exception
caught by an except clause in the current stack frame or in an older stack
frame."""
+ return exc_info_with_tb(space) # indirection for the tests
+
+def exc_info_with_tb(space):
operror = space.getexecutioncontext().sys_exc_info()
if operror is None:
return space.newtuple([space.w_None,space.w_None,space.w_None])
@@ -96,6 +99,59 @@
return space.newtuple([operror.w_type, operror.get_w_value(space),
space.wrap(operror.get_traceback())])
+def exc_info_without_tb(space, frame):
+ operror = frame.last_exception
+ return space.newtuple([operror.w_type, operror.get_w_value(space),
+ space.w_None])
+
+def exc_info_direct(space, frame):
+ from pypy.tool import stdlib_opcode
+ # In order to make the JIT happy, we try to return (exc, val, None)
+ # instead of (exc, val, tb). We can do that only if we recognize
+ # the following pattern in the bytecode:
+ # CALL_FUNCTION/CALL_METHOD <-- invoking me
+ # LOAD_CONST 0, 1, -2 or -3
+ # BINARY_SUBSCR
+ # or:
+ # CALL_FUNCTION/CALL_METHOD
+ # LOAD_CONST <=2
+ # SLICE_2
+ # or:
+ # CALL_FUNCTION/CALL_METHOD
+ # LOAD_CONST any integer
+ # LOAD_CONST <=2
+ # SLICE_3
+ need_all_three_args = True
+ co = frame.getcode().co_code
+ p = frame.last_instr
+ if (ord(co[p]) == stdlib_opcode.CALL_FUNCTION or
+ ord(co[p]) == stdlib_opcode.CALL_METHOD):
+ if ord(co[p+3]) == stdlib_opcode.LOAD_CONST:
+ lo = ord(co[p+4])
+ hi = ord(co[p+5])
+ w_constant = frame.getconstant_w((hi * 256) | lo)
+ if space.isinstance_w(w_constant, space.w_int):
+ constant = space.int_w(w_constant)
+ if ord(co[p+6]) == stdlib_opcode.BINARY_SUBSCR:
+ if -3 <= constant <= 1 and constant != -1:
+ need_all_three_args = False
+ elif ord(co[p+6]) == stdlib_opcode.SLICE+2:
+ if constant <= 2:
+ need_all_three_args = False
+ elif (ord(co[p+6]) == stdlib_opcode.LOAD_CONST and
+ ord(co[p+9]) == stdlib_opcode.SLICE+3):
+ lo = ord(co[p+7])
+ hi = ord(co[p+8])
+ w_constant = frame.getconstant_w((hi * 256) | lo)
+ if space.isinstance_w(w_constant, space.w_int):
+ if space.int_w(w_constant) <= 2:
+ need_all_three_args = False
+ #
+ if need_all_three_args or frame.last_exception is None or frame.hide():
+ return exc_info_with_tb(space)
+ else:
+ return exc_info_without_tb(space, frame)
+
def exc_clear(space):
"""Clear global information on the current exception. Subsequent calls
to exc_info() will return (None,None,None) until another exception is
diff --git a/pypy/module/test_lib_pypy/test_datetime.py b/pypy/module/test_lib_pypy/test_datetime.py
--- a/pypy/module/test_lib_pypy/test_datetime.py
+++ b/pypy/module/test_lib_pypy/test_datetime.py
@@ -44,3 +44,9 @@
assert type(dt.microsecond) is int
copy.copy(dt)
+
+def test_radd():
+ class X(object):
+ def __radd__(self, other):
+ return "radd"
+ assert datetime.date(10, 10, 10) + X() == "radd"
diff --git a/pypy/module/thread/test/test_ll_thread.py b/pypy/module/thread/test/test_ll_thread.py
--- a/pypy/module/thread/test/test_ll_thread.py
+++ b/pypy/module/thread/test/test_ll_thread.py
@@ -66,7 +66,6 @@
def test_gc_locking(self):
import time
from pypy.rlib.objectmodel import invoke_around_extcall
- from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.debug import ll_assert
class State:
@@ -129,8 +128,6 @@
state.finished = 0
# the next line installs before_extcall() and after_extcall()
# to be called automatically around external function calls.
- # When not translated it does not work around time.sleep(),
- # so we have to call them manually for this test.
invoke_around_extcall(before_extcall, after_extcall)
g(10, 1)
@@ -142,13 +139,9 @@
willing_to_wait_more -= 1
done = len(state.answers) == expected
- if not we_are_translated(): before_extcall()
time.sleep(0.01)
- if not we_are_translated(): after_extcall()
- if not we_are_translated(): before_extcall()
time.sleep(0.1)
- if not we_are_translated(): after_extcall()
return len(state.answers)
@@ -160,12 +153,11 @@
answers = fn()
assert answers == expected
-class TestRunDirectly(AbstractThreadTests):
- def getcompiled(self, f, argtypes):
- return f
-
- def test_start_new_thread(self):
- py.test.skip("deadlocks occasionally -- why???")
+#class TestRunDirectly(AbstractThreadTests):
+# def getcompiled(self, f, argtypes):
+# return f
+# These are disabled because they crash occasionally for bad reasons
+# related to the fact that ll2ctypes is not at all thread-safe
class TestUsingBoehm(AbstractThreadTests):
gcpolicy = 'boehm'
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -43,6 +43,13 @@
return w_eq
type_eq._annspecialcase_ = 'specialize:memo'
+def list_iter(space):
+ "Utility that returns the app-level descriptor list.__iter__."
+ w_src, w_iter = space.lookup_in_type_where(space.w_list,
+ '__iter__')
+ return w_iter
+list_iter._annspecialcase_ = 'specialize:memo'
+
def raiseattrerror(space, w_obj, name, w_descr=None):
w_type = space.type(w_obj)
typename = w_type.getname(space)
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -86,6 +86,7 @@
return s_None
def specialize_call(self, hop):
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Void, None)
# ____________________________________________________________
@@ -109,7 +110,7 @@
"NOT_RPYTHON"
raise NotImplementedError
- def newdict(self, module=False, instance=False, classofinstance=None,
+ def newdict(self, module=False, instance=False,
strdict=False):
return w_some_obj()
diff --git a/pypy/objspace/std/celldict.py b/pypy/objspace/std/celldict.py
--- a/pypy/objspace/std/celldict.py
+++ b/pypy/objspace/std/celldict.py
@@ -163,7 +163,8 @@
class ModuleDictIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
+ IteratorImplementation.__init__(
+ self, space, strategy, dictimplementation)
dict_w = strategy.unerase(dictimplementation.dstorage)
self.iterator = dict_w.iteritems()
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -33,8 +33,7 @@
@staticmethod
def allocate_and_init_instance(space, w_type=None, module=False,
- instance=False, classofinstance=None,
- strdict=False):
+ instance=False, strdict=False):
if space.config.objspace.std.withcelldict and module:
from pypy.objspace.std.celldict import ModuleDictStrategy
@@ -247,7 +246,7 @@
return 0
def iter(self, w_dict):
- return EmptyIteratorImplementation(self.space, w_dict)
+ return EmptyIteratorImplementation(self.space, self, w_dict)
def clear(self, w_dict):
return
@@ -263,8 +262,9 @@
# Iterator Implementation base classes
class IteratorImplementation(object):
- def __init__(self, space, implementation):
+ def __init__(self, space, strategy, implementation):
self.space = space
+ self.strategy = strategy
self.dictimplementation = implementation
self.len = implementation.length()
self.pos = 0
@@ -280,7 +280,20 @@
if self.pos < self.len:
result = self.next_entry()
self.pos += 1
- return result
+ if self.strategy is self.dictimplementation.strategy:
+ return result # common case
+ else:
+ # waaa, obscure case: the strategy changed, but not the
+ # length of the dict. The (key, value) pair in 'result'
+ # might be out-of-date. We try to explicitly look up
+ # the key in the dict.
+ w_key = result[0]
+ w_value = self.dictimplementation.getitem(w_key)
+ if w_value is None:
+ self.len = -1 # Make this error state sticky
+ raise OperationError(self.space.w_RuntimeError,
+ self.space.wrap("dictionary changed during iteration"))
+ return (w_key, w_value)
# no more entries
self.dictimplementation = None
return None, None
@@ -489,7 +502,7 @@
_mixin_ = True
def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
+ IteratorImplementation.__init__(self, space, strategy, dictimplementation)
self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
def next_entry(self):
@@ -503,7 +516,7 @@
_mixin_ = True
def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
+ IteratorImplementation.__init__(self, space, strategy, dictimplementation)
self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
def next_entry(self):
@@ -549,10 +562,7 @@
def listview_int(self, w_dict):
return self.unerase(w_dict.dstorage).keys()
- def w_keys(self, w_dict):
- # XXX there is no space.newlist_int yet
- space = self.space
- return space.call_function(space.w_list, w_dict)
+ # XXX there is no space.newlist_int yet to implement w_keys more efficiently
class IntIteratorImplementation(_WrappedIteratorMixin, IteratorImplementation):
pass
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -20,7 +20,17 @@
def getitem(self, w_dict, w_key):
space = self.space
w_lookup_type = space.type(w_key)
- if space.is_w(w_lookup_type, space.w_str):
+ if (space.is_w(w_lookup_type, space.w_str) or # Most common path first
+ space.abstract_issubclass_w(w_lookup_type, space.w_str)):
+ return self.getitem_str(w_dict, space.str_w(w_key))
+ elif space.abstract_issubclass_w(w_lookup_type, space.w_unicode):
+ try:
+ w_key = space.str(w_key)
+ except OperationError, e:
+ if not e.match(space, space.w_UnicodeEncodeError):
+ raise
+ # non-ascii unicode is never equal to a byte string
+ return None
return self.getitem_str(w_dict, space.str_w(w_key))
else:
return None
@@ -98,7 +108,8 @@
class DictProxyIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
+ IteratorImplementation.__init__(
+ self, space, strategy, dictimplementation)
w_type = strategy.unerase(dictimplementation.dstorage)
self.iterator = w_type.dict_w.iteritems()
diff --git a/pypy/objspace/std/identitydict.py b/pypy/objspace/std/identitydict.py
--- a/pypy/objspace/std/identitydict.py
+++ b/pypy/objspace/std/identitydict.py
@@ -1,5 +1,5 @@
## ----------------------------------------------------------------------------
-## dict strategy (see dict_multiobject.py)
+## dict strategy (see dictmultiobject.py)
from pypy.rlib import rerased
from pypy.rlib.debug import mark_dict_non_null
@@ -80,8 +80,8 @@
def iter(self, w_dict):
return IdentityDictIteratorImplementation(self.space, self, w_dict)
- def keys(self, w_dict):
- return self.unerase(w_dict.dstorage).keys()
+ def w_keys(self, w_dict):
+ return self.space.newlist(self.unerase(w_dict.dstorage).keys())
class IdentityDictIteratorImplementation(_UnwrappedIteratorMixin, IteratorImplementation):
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -703,7 +703,8 @@
class MapDictIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
+ IteratorImplementation.__init__(
+ self, space, strategy, dictimplementation)
w_obj = strategy.unerase(dictimplementation.dstorage)
self.w_obj = w_obj
self.orig_map = self.curr_map = w_obj._get_mapdict_map()
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
@@ -313,11 +313,10 @@
def newlist_str(self, list_s):
return W_ListObject.newlist_str(self, list_s)
- def newdict(self, module=False, instance=False, classofinstance=None,
+ def newdict(self, module=False, instance=False,
strdict=False):
return W_DictMultiObject.allocate_and_init_instance(
self, module=module, instance=instance,
- classofinstance=classofinstance,
strdict=strdict)
def newset(self):
@@ -439,6 +438,8 @@
t = w_obj.getitems()
elif isinstance(w_obj, W_AbstractTupleObject):
t = w_obj.getitems_copy(self)
+ elif isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
+ t = w_obj.getitems()
else:
return ObjSpace.unpackiterable(self, w_obj, expected_length)
if expected_length != -1 and len(t) != expected_length:
@@ -456,6 +457,8 @@
return w_obj.listview_str()
if isinstance(w_obj, W_StringObject):
return w_obj.listview_str()
+ if isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
+ return w_obj.getitems_str()
return None
def listview_int(self, w_obj):
@@ -465,8 +468,14 @@
return w_obj.listview_int()
if type(w_obj) is W_SetObject or type(w_obj) is W_FrozensetObject:
return w_obj.listview_int()
+ if isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
+ return w_obj.getitems_int()
return None
+ def _uses_list_iter(self, w_obj):
+ from pypy.objspace.descroperation import list_iter
+ return self.lookup(w_obj, '__iter__') is list_iter(self)
+
def sliceindices(self, w_slice, w_length):
if isinstance(w_slice, W_SliceObject):
a, b, c = w_slice.indices3(self, self.int_w(w_length))
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -359,7 +359,7 @@
w_set.sstorage = w_other.get_storage_copy()
def iter(self, w_set):
- return EmptyIteratorImplementation(self.space, w_set)
+ return EmptyIteratorImplementation(self.space, self, w_set)
def popitem(self, w_set):
raise OperationError(self.space.w_KeyError,
@@ -784,8 +784,9 @@
d_obj[w_item] = None
class IteratorImplementation(object):
- def __init__(self, space, implementation):
+ def __init__(self, space, strategy, implementation):
self.space = space
+ self.strategy = strategy
self.setimplementation = implementation
self.len = implementation.length()
self.pos = 0
@@ -801,7 +802,17 @@
if self.pos < self.len:
result = self.next_entry()
self.pos += 1
- return result
+ if self.strategy is self.setimplementation.strategy:
+ return result # common case
+ else:
+ # waaa, obscure case: the strategy changed, but not the
+ # length of the set. The 'result' might be out-of-date.
+ # We try to explicitly look it up in the set.
+ if not self.setimplementation.has_key(result):
+ self.len = -1 # Make this error state sticky
+ raise OperationError(self.space.w_RuntimeError,
+ self.space.wrap("dictionary changed during iteration"))
+ return result
# no more entries
self.setimplementation = None
return None
@@ -823,7 +834,7 @@
class StringIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, w_set):
- IteratorImplementation.__init__(self, space, w_set)
+ IteratorImplementation.__init__(self, space, strategy, w_set)
d = strategy.unerase(w_set.sstorage)
self.iterator = d.iterkeys()
@@ -835,9 +846,9 @@
class IntegerIteratorImplementation(IteratorImplementation):
#XXX same implementation in dictmultiobject on dictstrategy-branch
- def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
- d = strategy.unerase(dictimplementation.sstorage)
+ def __init__(self, space, strategy, w_set):
+ IteratorImplementation.__init__(self, space, strategy, w_set)
+ d = strategy.unerase(w_set.sstorage)
self.iterator = d.iterkeys()
def next_entry(self):
@@ -848,9 +859,9 @@
return None
class RDictIteratorImplementation(IteratorImplementation):
- def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
- d = strategy.unerase(dictimplementation.sstorage)
+ def __init__(self, space, strategy, w_set):
+ IteratorImplementation.__init__(self, space, strategy, w_set)
+ d = strategy.unerase(w_set.sstorage)
self.iterator = d.iterkeys()
def next_entry(self):
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -804,6 +804,33 @@
assert "IntDictStrategy" in self.get_strategy(d)
assert d[1L] == "hi"
+ def test_iter_dict_length_change(self):
+ d = {1: 2, 3: 4, 5: 6}
+ it = d.iteritems()
+ d[7] = 8
+ # 'd' is now length 4
+ raises(RuntimeError, it.next)
+
+ def test_iter_dict_strategy_only_change_1(self):
+ d = {1: 2, 3: 4, 5: 6}
+ it = d.iteritems()
+ class Foo(object):
+ def __eq__(self, other):
+ return False
+ assert d.get(Foo()) is None # this changes the strategy of 'd'
+ lst = list(it) # but iterating still works
+ assert sorted(lst) == [(1, 2), (3, 4), (5, 6)]
+
+ def test_iter_dict_strategy_only_change_2(self):
+ d = {1: 2, 3: 4, 5: 6}
+ it = d.iteritems()
+ d['foo'] = 'bar'
+ del d[1]
+ # 'd' is still length 3, but its strategy changed. we are
+ # getting a RuntimeError because iterating over the old storage
+ # gives us (1, 2), but 1 is not in the dict any longer.
+ raises(RuntimeError, list, it)
+
class FakeString(str):
hash_count = 0
@@ -858,10 +885,9 @@
def newtuple(self, l):
return tuple(l)
- def newdict(self, module=False, instance=False, classofinstance=None):
+ def newdict(self, module=False, instance=False):
return W_DictMultiObject.allocate_and_init_instance(
- self, module=module, instance=instance,
- classofinstance=classofinstance)
+ self, module=module, instance=instance)
def finditem_str(self, w_dict, s):
return w_dict.getitem_str(s) # assume it's a multidict
@@ -941,6 +967,20 @@
assert type(self.impl.strategy) is self.StrategyClass
#assert self.impl.r_dict_content is None
+ def test_popitem(self):
+ self.fill_impl()
+ assert self.impl.length() == 2
+ a, b = self.impl.popitem()
+ assert self.impl.length() == 1
+ if a == self.string:
+ assert b == 1000
+ assert self.impl.getitem(self.string2) == 2000
+ else:
+ assert a == self.string2
+ assert b == 2000
+ assert self.impl.getitem_str(self.string) == 1000
+ self.check_not_devolved()
+
def test_setitem(self):
self.impl.setitem(self.string, 1000)
assert self.impl.length() == 1
diff --git a/pypy/objspace/std/test/test_dictproxy.py b/pypy/objspace/std/test/test_dictproxy.py
--- a/pypy/objspace/std/test/test_dictproxy.py
+++ b/pypy/objspace/std/test/test_dictproxy.py
@@ -25,6 +25,16 @@
key, value = NotEmpty.__dict__.popitem()
assert (key == 'a' and value == 1) or (key == 'b' and value == 4)
+ def test_dictproxy_getitem(self):
+ class NotEmpty(object):
+ a = 1
+ assert 'a' in NotEmpty.__dict__
+ class substr(str): pass
+ assert substr('a') in NotEmpty.__dict__
+ assert u'a' in NotEmpty.__dict__
+ assert NotEmpty.__dict__[u'a'] == 1
+ assert u'\xe9' not in NotEmpty.__dict__
+
def test_dictproxyeq(self):
class a(object):
pass
diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py
--- a/pypy/objspace/std/test/test_listobject.py
+++ b/pypy/objspace/std/test/test_listobject.py
@@ -1186,14 +1186,23 @@
# of dicts, because the OrderedDict in the stdlib relies on this.
# we extend the use case to lists and sets, i.e. all types that have
# strategies, to avoid surprizes depending on the strategy.
- for base, arg in [(list, []), (list, [5]), (list, ['x']),
- (set, []), (set, [5]), (set, ['x']),
- (dict, []), (dict, [(5,6)]), (dict, [('x',7)])]:
+ class X: pass
+ for base, arg in [
+ (list, []), (list, [5]), (list, ['x']), (list, [X]),
+ (set, []), (set, [5]), (set, ['x']), (set, [X]),
+ (dict, []), (dict, [(5,6)]), (dict, [('x',7)]), (dict, [(X,8)]),
+ ]:
print base, arg
class SubClass(base):
def __iter__(self):
return iter("foobar")
assert list(SubClass(arg)) == ['f', 'o', 'o', 'b', 'a', 'r']
+ class Sub2(base):
+ pass
+ assert list(Sub2(arg)) == list(base(arg))
+ s = set()
+ s.update(Sub2(arg))
+ assert s == set(base(arg))
class AppTestForRangeLists(AppTestW_ListObject):
diff --git a/pypy/objspace/std/test/test_setobject.py b/pypy/objspace/std/test/test_setobject.py
--- a/pypy/objspace/std/test/test_setobject.py
+++ b/pypy/objspace/std/test/test_setobject.py
@@ -907,3 +907,30 @@
return [5, 3, 4][i]
s = set([10,3,2]).intersection(Obj())
assert list(s) == [3]
+
+ def test_iter_set_length_change(self):
+ s = set([1, 3, 5])
+ it = iter(s)
+ s.add(7)
+ # 's' is now length 4
+ raises(RuntimeError, it.next)
+
+ def test_iter_set_strategy_only_change_1(self):
+ s = set([1, 3, 5])
+ it = iter(s)
+ class Foo(object):
+ def __eq__(self, other):
+ return False
+ assert Foo() not in s # this changes the strategy of 'd'
+ lst = list(s) # but iterating still works
+ assert sorted(lst) == [1, 3, 5]
+
+ def test_iter_set_strategy_only_change_2(self):
+ s = set([1, 3, 5])
+ it = iter(s)
+ s.add('foo')
+ s.remove(1)
+ # 's' is still length 3, but its strategy changed. we are
+ # getting a RuntimeError because iterating over the old storage
+ # gives us 1, but 1 is not in the set any longer.
+ raises(RuntimeError, list, it)
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -382,7 +382,7 @@
pass
def specialize_call(self, hop):
- pass
+ hop.exception_cannot_occur()
vref_None = non_virtual_ref(None)
diff --git a/pypy/rlib/jit_hooks.py b/pypy/rlib/jit_hooks.py
--- a/pypy/rlib/jit_hooks.py
+++ b/pypy/rlib/jit_hooks.py
@@ -22,6 +22,7 @@
c_name = hop.inputconst(lltype.Void, 'access_helper')
args_v = [hop.inputarg(arg, arg=i)
for i, arg in enumerate(hop.args_r)]
+ hop.exception_cannot_occur()
return hop.genop('jit_marker', [c_name, c_func] + args_v,
resulttype=hop.r_result)
return helper
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -215,6 +215,7 @@
def specialize_call(self, hop):
from pypy.rpython.lltypesystem import lltype
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Bool, hop.s_result.const)
# ____________________________________________________________
@@ -397,6 +398,7 @@
r_obj, = hop.args_r
v_obj, = hop.inputargs(r_obj)
ll_fn = r_obj.get_ll_hash_function()
+ hop.exception_is_here()
return hop.gendirectcall(ll_fn, v_obj)
class Entry(ExtRegistryEntry):
@@ -419,6 +421,7 @@
from pypy.rpython.error import TyperError
raise TyperError("compute_identity_hash() cannot be applied to"
" %r" % (vobj.concretetype,))
+ hop.exception_cannot_occur()
return hop.genop('gc_identityhash', [vobj], resulttype=lltype.Signed)
class Entry(ExtRegistryEntry):
@@ -441,6 +444,7 @@
from pypy.rpython.error import TyperError
raise TyperError("compute_unique_id() cannot be applied to"
" %r" % (vobj.concretetype,))
+ hop.exception_cannot_occur()
return hop.genop('gc_id', [vobj], resulttype=lltype.Signed)
class Entry(ExtRegistryEntry):
@@ -452,6 +456,7 @@
def specialize_call(self, hop):
vobj, = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
if hop.rtyper.type_system.name == 'lltypesystem':
from pypy.rpython.lltypesystem import lltype
if isinstance(vobj.concretetype, lltype.Ptr):
diff --git a/pypy/rlib/rbigint.py b/pypy/rlib/rbigint.py
--- a/pypy/rlib/rbigint.py
+++ b/pypy/rlib/rbigint.py
@@ -85,7 +85,7 @@
s_DIGIT = self.bookkeeper.valueoftype(type(NULLDIGIT))
assert s_DIGIT.contains(s_list.listdef.listitem.s_value)
def specialize_call(self, hop):
- pass
+ hop.exception_cannot_occur()
class rbigint(object):
diff --git a/pypy/rlib/rerased.py b/pypy/rlib/rerased.py
--- a/pypy/rlib/rerased.py
+++ b/pypy/rlib/rerased.py
@@ -100,6 +100,7 @@
def specialize_call(self, hop):
bk = hop.rtyper.annotator.bookkeeper
s_obj = identity.get_input_annotation(bk)
+ hop.exception_cannot_occur()
return hop.r_result.rtype_erase(hop, s_obj)
class Entry(ExtRegistryEntry):
@@ -110,6 +111,7 @@
return identity.leave_tunnel(self.bookkeeper)
def specialize_call(self, hop):
+ hop.exception_cannot_occur()
if hop.r_result.lowleveltype is lltype.Void:
return hop.inputconst(lltype.Void, None)
[v] = hop.inputargs(hop.args_r[0])
@@ -214,6 +216,7 @@
return hop.genop('cast_opaque_ptr', [v], resulttype=hop.r_result)
def rtype_unerase_int(self, hop, v):
+ hop.exception_cannot_occur()
return hop.gendirectcall(ll_unerase_int, v)
def rtype_erase_int(self, hop):
@@ -264,6 +267,7 @@
def rtype_unerase_int(self, hop, v):
c_one = hop.inputconst(lltype.Signed, 1)
+ hop.exception_cannot_occur()
v2 = hop.genop('oounbox_int', [v], resulttype=hop.r_result)
return hop.genop('int_rshift', [v2, c_one], resulttype=lltype.Signed)
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -382,6 +382,7 @@
def compute_result_annotation(self):
return s_list_of_gcrefs()
def specialize_call(self, hop):
+ hop.exception_cannot_occur()
return hop.genop('gc_get_rpy_roots', [], resulttype = hop.r_result)
class Entry(ExtRegistryEntry):
@@ -392,6 +393,7 @@
return s_list_of_gcrefs()
def specialize_call(self, hop):
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('gc_get_rpy_referents', vlist,
resulttype = hop.r_result)
@@ -402,6 +404,7 @@
return annmodel.SomeInteger()
def specialize_call(self, hop):
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('gc_get_rpy_memory_usage', vlist,
resulttype = hop.r_result)
@@ -412,6 +415,7 @@
return annmodel.SomeInteger()
def specialize_call(self, hop):
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('gc_get_rpy_type_index', vlist,
resulttype = hop.r_result)
@@ -430,6 +434,7 @@
return annmodel.SomeBool()
def specialize_call(self, hop):
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('gc_is_rpy_instance', vlist,
resulttype = hop.r_result)
@@ -449,6 +454,7 @@
classrepr = getclassrepr(hop.rtyper, classdef)
vtable = classrepr.getvtable()
assert lltype.typeOf(vtable) == rclass.CLASSTYPE
+ hop.exception_cannot_occur()
return Constant(vtable, concretetype=rclass.CLASSTYPE)
class Entry(ExtRegistryEntry):
diff --git a/pypy/rlib/rsre/rsre_re.py b/pypy/rlib/rsre/rsre_re.py
--- a/pypy/rlib/rsre/rsre_re.py
+++ b/pypy/rlib/rsre/rsre_re.py
@@ -172,8 +172,9 @@
self._ctx = ctx
def span(self, groupnum=0):
- if not isinstance(groupnum, (int, long)):
- groupnum = self.re.groupindex[groupnum]
+# if not isinstance(groupnum, (int, long)):
+# groupnum = self.re.groupindex[groupnum]
+
return self._ctx.span(groupnum)
def start(self, groupnum=0):
@@ -182,19 +183,25 @@
def end(self, groupnum=0):
return self.span(groupnum)[1]
- def group(self, *groups):
- groups = groups or (0,)
- result = []
- for group in groups:
- frm, to = self.span(group)
- if 0 <= frm <= to:
- result.append(self._ctx._string[frm:to])
- else:
- result.append(None)
- if len(result) > 1:
- return tuple(result)
+ def group(self, group=0):
+ frm, to = self.span(group)
+ if 0 <= frm <= to:
+ return self._ctx._string[frm:to]
else:
- return result[0]
+ return None
+
+# def group(self, *groups):
+# groups = groups or (0,)
+# result = []
+# for group in groups:
+# frm, to = self.span(group)
+# if 0 <= frm <= to:
+# result.append(self._ctx._string[frm:to])
+# else:
+# result.append(None)
+# if len(result) > 1:
+# return tuple(result)
+
def groups(self, default=None):
fmarks = self._ctx.flatten_marks()
diff --git a/pypy/rlib/rsre/test/test_re.py b/pypy/rlib/rsre/test/test_re.py
--- a/pypy/rlib/rsre/test/test_re.py
+++ b/pypy/rlib/rsre/test/test_re.py
@@ -204,7 +204,7 @@
assert re.match('(a)', 'a').groups() == ('a',)
assert re.match(r'(a)', 'a').group(0) == 'a'
assert re.match(r'(a)', 'a').group(1) == 'a'
- assert re.match(r'(a)', 'a').group(1, 1) == ('a', 'a')
+ #assert re.match(r'(a)', 'a').group(1, 1) == ('a', 'a')
pat = re.compile('((a)|(b))(c)?')
assert pat.match('a').groups() == ('a', 'a', None, None)
@@ -218,13 +218,13 @@
assert m.group(0) == 'a'
assert m.group(0) == 'a'
assert m.group(1) == 'a'
- assert m.group(1, 1) == ('a', 'a')
+ #assert m.group(1, 1) == ('a', 'a')
pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?')
- assert pat.match('a').group(1, 2, 3) == ('a', None, None)
- assert pat.match('b').group('a1', 'b2', 'c3') == (
- (None, 'b', None))
- assert pat.match('ac').group(1, 'b2', 3) == ('a', None, 'c')
+ #assert pat.match('a').group(1, 2, 3) == ('a', None, None)
+ #assert pat.match('b').group('a1', 'b2', 'c3') == (
+ # (None, 'b', None))
+ #assert pat.match('ac').group(1, 'b2', 3) == ('a', None, 'c')
def test_bug_923(self):
# Issue923: grouping inside optional lookahead problem
diff --git a/pypy/rlib/rsre/test/test_zinterp.py b/pypy/rlib/rsre/test/test_zinterp.py
--- a/pypy/rlib/rsre/test/test_zinterp.py
+++ b/pypy/rlib/rsre/test/test_zinterp.py
@@ -1,7 +1,8 @@
# minimal test: just checks that (parts of) rsre can be translated
-from pypy.rpython.test.test_llinterp import gengraph
+from pypy.rpython.test.test_llinterp import gengraph, interpret
from pypy.rlib.rsre import rsre_core
+from pypy.rlib.rsre.rsre_re import compile
def main(n):
assert n >= 0
@@ -19,3 +20,18 @@
def test_gengraph():
t, typer, graph = gengraph(main, [int])
+
+m = compile("(a|b)aaaaa")
+
+def test_match():
+ def f(i):
+ if i:
+ s = "aaaaaa"
+ else:
+ s = "caaaaa"
+ g = m.match(s)
+ if g is None:
+ return 3
+ return int("aaaaaa" == g.group(0))
+ assert interpret(f, [3]) == 1
+ assert interpret(f, [0]) == 3
diff --git a/pypy/rlib/rstring.py b/pypy/rlib/rstring.py
--- a/pypy/rlib/rstring.py
+++ b/pypy/rlib/rstring.py
@@ -245,5 +245,5 @@
raise ValueError("Value is not no_nul")
def specialize_call(self, hop):
- pass
+ hop.exception_cannot_occur()
diff --git a/pypy/rpython/annlowlevel.py b/pypy/rpython/annlowlevel.py
--- a/pypy/rpython/annlowlevel.py
+++ b/pypy/rpython/annlowlevel.py
@@ -543,11 +543,11 @@
else:
assert False
+ hop.exception_cannot_occur()
if isinstance(hop.args_r[1], rpbc.NoneFrozenPBCRepr):
return hop.inputconst(PTR, null)
v_arg = hop.inputarg(hop.args_r[1], arg=1)
assert isinstance(v_arg.concretetype, T)
- hop.exception_cannot_occur()
return hop.genop(opname, [v_arg], resulttype = PTR)
diff --git a/pypy/rpython/controllerentry.py b/pypy/rpython/controllerentry.py
--- a/pypy/rpython/controllerentry.py
+++ b/pypy/rpython/controllerentry.py
@@ -201,6 +201,7 @@
def specialize_call(self, hop):
from pypy.rpython.lltypesystem import lltype
assert hop.s_result.is_constant()
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Bool, hop.s_result.const)
# ____________________________________________________________
diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -130,6 +130,7 @@
def specialize_call(self, hop):
from pypy.rpython.lltypesystem import lltype
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Void, None)
def enum_ops_without_sideeffects(raising_is_ok=False):
diff --git a/pypy/rpython/lltypesystem/rbuiltin.py b/pypy/rpython/lltypesystem/rbuiltin.py
--- a/pypy/rpython/lltypesystem/rbuiltin.py
+++ b/pypy/rpython/lltypesystem/rbuiltin.py
@@ -9,6 +9,7 @@
from pypy.rpython.rbool import bool_repr
def rtype_builtin_isinstance(hop):
+ hop.exception_cannot_occur()
if hop.s_result.is_constant():
return hop.inputconst(lltype.Bool, hop.s_result.const)
if hop.args_r[0] == pyobj_repr or hop.args_r[1] == pyobj_repr:
@@ -33,6 +34,7 @@
return my_instantiate()
def rtype_instantiate(hop):
+ hop.exception_cannot_occur()
s_class = hop.args_s[0]
assert isinstance(s_class, annmodel.SomePBC)
if len(s_class.descriptions) != 1:
@@ -46,6 +48,7 @@
return rclass.rtype_new_instance(hop.rtyper, classdef, hop.llops)
def rtype_builtin_hasattr(hop):
+ hop.exception_cannot_occur()
if hop.s_result.is_constant():
return hop.inputconst(lltype.Bool, hop.s_result.const)
if hop.args_r[0] == pyobj_repr:
@@ -56,6 +59,7 @@
raise TyperError("hasattr is only suported on a constant or on PyObject")
def rtype_builtin___import__(hop):
+ xxx # should not be used any more
args_v = hop.inputargs(*[pyobj_repr for ign in hop.args_r])
c = hop.inputconst(pyobj_repr, __import__)
return hop.genop('simple_call', [c] + args_v, resulttype = pyobj_repr)
diff --git a/pypy/rpython/lltypesystem/rclass.py b/pypy/rpython/lltypesystem/rclass.py
--- a/pypy/rpython/lltypesystem/rclass.py
+++ b/pypy/rpython/lltypesystem/rclass.py
@@ -746,4 +746,5 @@
assert isinstance(TYPE, GcStruct)
assert lltype._castdepth(TYPE, OBJECT) > 0
hop.rtyper.set_type_for_typeptr(vtable, TYPE)
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Void, None)
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -765,7 +765,11 @@
def _ll_stringslice(s1, start, stop):
lgt = stop - start
assert start >= 0
- assert lgt >= 0
+ # If start > stop, return a empty string. This can happen if the start
+ # is greater than the length of the string. Use < instead of <= to avoid
+ # creating another path for the JIT when start == stop.
+ if lgt < 0:
+ return s1.empty()
newstr = s1.malloc(lgt)
s1.copy_contents(s1, newstr, start, 0, lgt)
return newstr
diff --git a/pypy/rpython/lltypesystem/rtuple.py b/pypy/rpython/lltypesystem/rtuple.py
--- a/pypy/rpython/lltypesystem/rtuple.py
+++ b/pypy/rpython/lltypesystem/rtuple.py
@@ -55,6 +55,7 @@
vtup = hop.inputarg(self, 0)
LIST = hop.r_result.lowleveltype.TO
cno = inputconst(Signed, nitems)
+ hop.exception_is_here()
vlist = hop.gendirectcall(LIST.ll_newlist, cno)
v_func = hop.inputconst(Void, rlist.dum_nocheck)
for index in range(nitems):
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -1426,23 +1426,25 @@
self._visit_young_rawmalloced_object(obj)
return
#
- # If 'obj' was already forwarded, change it to its forwarding address.
- if self.is_forwarded(obj):
+ size_gc_header = self.gcheaderbuilder.size_gc_header
+ if self.header(obj).tid & GCFLAG_HAS_SHADOW == 0:
+ #
+ # Common case: 'obj' was not already forwarded (otherwise
+ # tid == -42, containing all flags), and it doesn't have the
+ # HAS_SHADOW flag either. We must move it out of the nursery,
+ # into a new nonmovable location.
+ totalsize = size_gc_header + self.get_size(obj)
+ newhdr = self._malloc_out_of_nursery(totalsize)
+ #
+ elif self.is_forwarded(obj):
+ #
+ # 'obj' was already forwarded. Change the original reference
+ # to point to its forwarding address, and we're done.
root.address[0] = self.get_forwarding_address(obj)
return
- #
- # First visit to 'obj': we must move it out of the nursery.
- size_gc_header = self.gcheaderbuilder.size_gc_header
- size = self.get_size(obj)
- totalsize = size_gc_header + size
- #
- if self.header(obj).tid & GCFLAG_HAS_SHADOW == 0:
- #
- # Common case: allocate a new nonmovable location for it.
- newhdr = self._malloc_out_of_nursery(totalsize)
#
else:
- # The object has already a shadow.
+ # First visit to an object that has already a shadow.
newobj = self.nursery_objects_shadows.get(obj)
ll_assert(newobj != NULL, "GCFLAG_HAS_SHADOW but no shadow found")
newhdr = newobj - size_gc_header
@@ -1450,6 +1452,8 @@
# Remove the flag GCFLAG_HAS_SHADOW, so that it doesn't get
# copied to the shadow itself.
self.header(obj).tid &= ~GCFLAG_HAS_SHADOW
+ #
+ totalsize = size_gc_header + self.get_size(obj)
#
# Copy it. Note that references to other objects in the
# nursery are kept unchanged in this step.
diff --git a/pypy/rpython/module/ll_os_stat.py b/pypy/rpython/module/ll_os_stat.py
--- a/pypy/rpython/module/ll_os_stat.py
+++ b/pypy/rpython/module/ll_os_stat.py
@@ -455,6 +455,6 @@
return intmask(time), intmask(nsec)
def time_t_to_FILE_TIME(time, filetime):
- ft = (rffi.r_longlong(time) + secs_between_epochs) * 10000000
+ ft = rffi.r_longlong((time + secs_between_epochs) * 10000000)
filetime.c_dwHighDateTime = rffi.r_uint(ft >> 32)
filetime.c_dwLowDateTime = rffi.r_uint(ft) # masking off high bits
diff --git a/pypy/rpython/module/r_os_stat.py b/pypy/rpython/module/r_os_stat.py
--- a/pypy/rpython/module/r_os_stat.py
+++ b/pypy/rpython/module/r_os_stat.py
@@ -65,4 +65,5 @@
r_StatResult = hop.rtyper.getrepr(ll_os_stat.s_StatResult)
[v_result] = hop.inputargs(r_StatResult.r_tuple)
# no-op conversion from r_StatResult.r_tuple to r_StatResult
+ hop.exception_cannot_occur()
return v_result
diff --git a/pypy/rpython/ootypesystem/ooregistry.py b/pypy/rpython/ootypesystem/ooregistry.py
--- a/pypy/rpython/ootypesystem/ooregistry.py
+++ b/pypy/rpython/ootypesystem/ooregistry.py
@@ -22,6 +22,7 @@
annmodel.SomeOOInstance,
annmodel.SomeString))
vlist = hop.inputargs(hop.args_r[0], ootype.Signed)
+ hop.exception_cannot_occur()
return hop.genop('oostring', vlist, resulttype = ootype.String)
class Entry_oounicode(ExtRegistryEntry):
@@ -38,6 +39,7 @@
assert isinstance(hop.args_s[0], (annmodel.SomeUnicodeCodePoint,
annmodel.SomeOOInstance))
vlist = hop.inputargs(hop.args_r[0], ootype.Signed)
+ hop.exception_cannot_occur()
return hop.genop('oounicode', vlist, resulttype = ootype.Unicode)
diff --git a/pypy/rpython/ootypesystem/rbuiltin.py b/pypy/rpython/ootypesystem/rbuiltin.py
--- a/pypy/rpython/ootypesystem/rbuiltin.py
+++ b/pypy/rpython/ootypesystem/rbuiltin.py
@@ -7,12 +7,14 @@
from pypy.rpython.error import TyperError
def rtype_new(hop):
+ hop.exception_cannot_occur()
assert hop.args_s[0].is_constant()
vlist = hop.inputargs(ootype.Void)
return hop.genop('new', vlist,
resulttype = hop.r_result.lowleveltype)
def rtype_oonewarray(hop):
+ hop.exception_cannot_occur()
assert hop.args_s[0].is_constant()
vlist = hop.inputarg(ootype.Void, arg=0)
vlength = hop.inputarg(ootype.Signed, arg=1)
@@ -20,23 +22,27 @@
resulttype = hop.r_result.lowleveltype)
def rtype_null(hop):
+ hop.exception_cannot_occur()
assert hop.args_s[0].is_constant()
TYPE = hop.args_s[0].const
nullvalue = ootype.null(TYPE)
return hop.inputconst(TYPE, nullvalue)
def rtype_classof(hop):
+ hop.exception_cannot_occur()
assert isinstance(hop.args_s[0], annmodel.SomeOOInstance)
vlist = hop.inputargs(hop.args_r[0])
return hop.genop('classof', vlist,
resulttype = ootype.Class)
def rtype_subclassof(hop):
+ hop.exception_cannot_occur()
vlist = hop.inputargs(rootype.ooclass_repr, rootype.ooclass_repr)
return hop.genop('subclassof', vlist,
resulttype = ootype.Bool)
def rtype_instanceof(hop):
+ hop.exception_cannot_occur()
INSTANCE = hop.args_v[1].value
v_inst = hop.inputarg(hop.args_r[0], arg=0)
c_cls = hop.inputconst(ootype.Void, INSTANCE)
@@ -44,23 +50,27 @@
resulttype=ootype.Bool)
def rtype_runtimenew(hop):
+ hop.exception_cannot_occur()
vlist = hop.inputargs(rootype.ooclass_repr)
return hop.genop('runtimenew', vlist,
resulttype = hop.r_result.lowleveltype)
def rtype_ooupcast(hop):
+ hop.exception_cannot_occur()
assert isinstance(hop.args_s[0].const, ootype.Instance)
assert isinstance(hop.args_s[1], annmodel.SomeOOInstance)
v_inst = hop.inputarg(hop.args_r[1], arg=1)
return hop.genop('ooupcast', [v_inst], resulttype = hop.r_result.lowleveltype)
def rtype_oodowncast(hop):
+ hop.exception_cannot_occur()
assert isinstance(hop.args_s[0].const, ootype.Instance)
assert isinstance(hop.args_s[1], annmodel.SomeOOInstance)
v_inst = hop.inputarg(hop.args_r[1], arg=1)
return hop.genop('oodowncast', [v_inst], resulttype = hop.r_result.lowleveltype)
def rtype_cast_to_object(hop):
+ hop.exception_cannot_occur()
assert isinstance(hop.args_s[0], annmodel.SomeOOStaticMeth) or \
isinstance(hop.args_s[0], annmodel.SomeOOClass) or \
isinstance(hop.args_s[0].ootype, ootype.OOType)
@@ -68,12 +78,14 @@
return hop.genop('cast_to_object', [v_inst], resulttype = hop.r_result.lowleveltype)
def rtype_cast_from_object(hop):
+ hop.exception_cannot_occur()
assert isinstance(hop.args_s[0].const, ootype.OOType)
assert isinstance(hop.args_s[1], annmodel.SomeOOObject)
v_inst = hop.inputarg(hop.args_r[1], arg=1)
return hop.genop('cast_from_object', [v_inst], resulttype = hop.r_result.lowleveltype)
def rtype_builtin_isinstance(hop):
+ hop.exception_cannot_occur()
if hop.s_result.is_constant():
return hop.inputconst(ootype.Bool, hop.s_result.const)
@@ -99,6 +111,7 @@
return ootype.subclassof(c1, class_)
def rtype_instantiate(hop):
+ hop.exception_cannot_occur()
if hop.args_s[0].is_constant():
## INSTANCE = hop.s_result.rtyper_makerepr(hop.rtyper).lowleveltype
## v_instance = hop.inputconst(ootype.Void, INSTANCE)
diff --git a/pypy/rpython/ootypesystem/rstr.py b/pypy/rpython/ootypesystem/rstr.py
--- a/pypy/rpython/ootypesystem/rstr.py
+++ b/pypy/rpython/ootypesystem/rstr.py
@@ -222,6 +222,10 @@
length = s.ll_strlen()
if stop > length:
stop = length
+ # If start > stop, return a empty string. This can happen if the start
+ # is greater than the length of the string.
+ if start > stop:
+ start = stop
return s.ll_substring(start, stop-start)
def ll_stringslice_minusone(s):
diff --git a/pypy/rpython/ootypesystem/rtuple.py b/pypy/rpython/ootypesystem/rtuple.py
--- a/pypy/rpython/ootypesystem/rtuple.py
+++ b/pypy/rpython/ootypesystem/rtuple.py
@@ -39,6 +39,7 @@
RESULT = hop.r_result.lowleveltype
c_resulttype = inputconst(ootype.Void, RESULT)
c_length = inputconst(ootype.Signed, len(self.items_r))
+ hop.exception_is_here()
if isinstance(RESULT, ootype.Array):
v_list = hop.genop('oonewarray', [c_resulttype, c_length], resulttype=RESULT)
else:
diff --git a/pypy/rpython/rbool.py b/pypy/rpython/rbool.py
--- a/pypy/rpython/rbool.py
+++ b/pypy/rpython/rbool.py
@@ -34,6 +34,7 @@
def rtype_float(_, hop):
vlist = hop.inputargs(Float)
+ hop.exception_cannot_occur()
return vlist[0]
#
diff --git a/pypy/rpython/rbuiltin.py b/pypy/rpython/rbuiltin.py
--- a/pypy/rpython/rbuiltin.py
+++ b/pypy/rpython/rbuiltin.py
@@ -111,25 +111,32 @@
raise TyperError("don't know about built-in function %r" % (
self.builtinfunc,))
+ def _call(self, hop2, **kwds_i):
+ bltintyper = self.findbltintyper(hop2.rtyper)
+ hop2.llops._called_exception_is_here_or_cannot_occur = False
+ v_result = bltintyper(hop2, **kwds_i)
+ if not hop2.llops._called_exception_is_here_or_cannot_occur:
+ raise TyperError("missing hop.exception_cannot_occur() or "
+ "hop.exception_is_here() in %s" % bltintyper)
+ return v_result
+
def rtype_simple_call(self, hop):
- bltintyper = self.findbltintyper(hop.rtyper)
hop2 = hop.copy()
hop2.r_s_popfirstarg()
- return bltintyper(hop2)
+ return self._call(hop2)
def rtype_call_args(self, hop):
# calling a built-in function with keyword arguments:
# mostly for rpython.objectmodel.hint()
hop, kwds_i = call_args_expand(hop)
- bltintyper = self.findbltintyper(hop.rtyper)
hop2 = hop.copy()
hop2.r_s_popfirstarg()
hop2.r_s_popfirstarg()
# the RPython-level keyword args are passed with an 'i_' prefix and
# the corresponding value is an *index* in the hop2 arguments,
# to be used with hop.inputarg(arg=..)
- return bltintyper(hop2, **kwds_i)
+ return self._call(hop2, **kwds_i)
class BuiltinMethodRepr(Repr):
@@ -198,6 +205,7 @@
# ____________________________________________________________
def rtype_builtin_bool(hop):
+ # not called any more?
assert hop.nb_args == 1
return hop.args_r[0].rtype_is_true(hop)
@@ -241,6 +249,7 @@
def rtype_builtin_min(hop):
v1, v2 = hop.inputargs(hop.r_result, hop.r_result)
+ hop.exception_cannot_occur()
return hop.gendirectcall(ll_min, v1, v2)
def ll_min(i1, i2):
@@ -250,6 +259,7 @@
def rtype_builtin_max(hop):
v1, v2 = hop.inputargs(hop.r_result, hop.r_result)
+ hop.exception_cannot_occur()
return hop.gendirectcall(ll_max, v1, v2)
def ll_max(i1, i2):
@@ -264,6 +274,7 @@
pass
def rtype_OSError__init__(hop):
+ hop.exception_cannot_occur()
if hop.nb_args == 2:
raise TyperError("OSError() should not be called with "
"a single argument")
@@ -274,6 +285,7 @@
r_self.setfield(v_self, 'errno', v_errno, hop.llops)
def rtype_WindowsError__init__(hop):
+ hop.exception_cannot_occur()
if hop.nb_args == 2:
raise TyperError("WindowsError() should not be called with "
"a single argument")
@@ -442,6 +454,7 @@
assert hop.args_s[0].is_constant()
TGT = hop.args_s[0].const
v_type, v_value = hop.inputargs(lltype.Void, hop.args_r[1])
+ hop.exception_cannot_occur()
return gen_cast(hop.llops, TGT, v_value)
_cast_to_Signed = {
@@ -523,11 +536,13 @@
def rtype_identity_hash(hop):
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('gc_identityhash', vlist, resulttype=lltype.Signed)
def rtype_runtime_type_info(hop):
assert isinstance(hop.args_r[0], rptr.PtrRepr)
vlist = hop.inputargs(hop.args_r[0])
+ hop.exception_cannot_occur()
return hop.genop('runtime_type_info', vlist,
resulttype = hop.r_result.lowleveltype)
@@ -558,6 +573,7 @@
def rtype_raw_malloc(hop):
v_size, = hop.inputargs(lltype.Signed)
+ hop.exception_cannot_occur()
return hop.genop('raw_malloc', [v_size], resulttype=llmemory.Address)
def rtype_raw_malloc_usage(hop):
@@ -586,6 +602,7 @@
if s_addr.is_null_address():
raise TyperError("raw_memclear(x, n) where x is the constant NULL")
v_list = hop.inputargs(llmemory.Address, lltype.Signed)
+ hop.exception_cannot_occur()
return hop.genop('raw_memclear', v_list)
BUILTIN_TYPER[llmemory.raw_malloc] = rtype_raw_malloc
@@ -596,6 +613,7 @@
def rtype_offsetof(hop):
TYPE, field = hop.inputargs(lltype.Void, lltype.Void)
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Signed,
llmemory.offsetof(TYPE.value, field.value))
@@ -605,6 +623,7 @@
# non-gc objects
def rtype_free_non_gc_object(hop):
+ hop.exception_cannot_occur()
vinst, = hop.inputargs(hop.args_r[0])
flavor = hop.args_r[0].gcflavor
assert flavor != 'gc'
@@ -617,6 +636,7 @@
# keepalive_until_here
def rtype_keepalive_until_here(hop):
+ hop.exception_cannot_occur()
for v in hop.args_v:
hop.genop('keepalive', [v], resulttype=lltype.Void)
return hop.inputconst(lltype.Void, None)
diff --git a/pypy/rpython/rfloat.py b/pypy/rpython/rfloat.py
--- a/pypy/rpython/rfloat.py
+++ b/pypy/rpython/rfloat.py
@@ -136,7 +136,10 @@
hop.exception_cannot_occur()
return hop.genop('cast_float_to_int', vlist, resulttype=Signed)
- rtype_float = rtype_pos
+ def rtype_float(_, hop):
+ vlist = hop.inputargs(Float)
+ hop.exception_cannot_occur()
+ return vlist[0]
# version picked by specialisation based on which
# type system rtyping is using, from <type_system>.ll_str module
diff --git a/pypy/rpython/rint.py b/pypy/rpython/rint.py
--- a/pypy/rpython/rint.py
+++ b/pypy/rpython/rint.py
@@ -310,6 +310,8 @@
if hop.has_implicit_exception(ValueError):
hop.exception_is_here()
hop.gendirectcall(ll_check_chr, vlist[0])
+ else:
+ hop.exception_cannot_occur()
return hop.genop('cast_int_to_char', vlist, resulttype=Char)
def rtype_unichr(_, hop):
@@ -317,6 +319,8 @@
if hop.has_implicit_exception(ValueError):
hop.exception_is_here()
hop.gendirectcall(ll_check_unichr, vlist[0])
+ else:
+ hop.exception_cannot_occur()
return hop.genop('cast_int_to_unichar', vlist, resulttype=UniChar)
def rtype_is_true(self, hop):
diff --git a/pypy/rpython/rlist.py b/pypy/rpython/rlist.py
--- a/pypy/rpython/rlist.py
+++ b/pypy/rpython/rlist.py
@@ -115,6 +115,7 @@
def rtype_bltn_list(self, hop):
v_lst = hop.inputarg(self, 0)
cRESLIST = hop.inputconst(Void, hop.r_result.LIST)
+ hop.exception_is_here()
return hop.gendirectcall(ll_copy, cRESLIST, v_lst)
def rtype_len(self, hop):
diff --git a/pypy/rpython/rrange.py b/pypy/rpython/rrange.py
--- a/pypy/rpython/rrange.py
+++ b/pypy/rpython/rrange.py
@@ -107,8 +107,10 @@
if isinstance(hop.r_result, AbstractRangeRepr):
if hop.r_result.step != 0:
c_rng = hop.inputconst(Void, hop.r_result.RANGE)
+ hop.exception_is_here()
return hop.gendirectcall(hop.r_result.ll_newrange, c_rng, vstart, vstop)
else:
+ hop.exception_is_here()
return hop.gendirectcall(hop.r_result.ll_newrangest, vstart, vstop, vstep)
else:
# cannot build a RANGE object, needs a real list
@@ -117,6 +119,7 @@
if isinstance(ITEMTYPE, Ptr):
ITEMTYPE = ITEMTYPE.TO
cLIST = hop.inputconst(Void, ITEMTYPE)
+ hop.exception_is_here()
return hop.gendirectcall(ll_range2list, cLIST, vstart, vstop, vstep)
rtype_builtin_xrange = rtype_builtin_range
@@ -212,4 +215,5 @@
[v_index, v_item])
def rtype_builtin_enumerate(hop):
+ hop.exception_cannot_occur()
return hop.r_result.r_baseiter.newiter(hop)
diff --git a/pypy/rpython/rstr.py b/pypy/rpython/rstr.py
--- a/pypy/rpython/rstr.py
+++ b/pypy/rpython/rstr.py
@@ -288,6 +288,8 @@
def rtype_unicode(self, hop):
if hop.args_s[0].is_constant():
+ # convertion errors occur during annotation, so cannot any more:
+ hop.exception_cannot_occur()
return hop.inputconst(hop.r_result, hop.s_result.const)
repr = hop.args_r[0].repr
v_str = hop.inputarg(repr, 0)
diff --git a/pypy/rpython/rtyper.py b/pypy/rpython/rtyper.py
--- a/pypy/rpython/rtyper.py
+++ b/pypy/rpython/rtyper.py
@@ -846,6 +846,7 @@
return result
def exception_is_here(self):
+ self.llops._called_exception_is_here_or_cannot_occur = True
if self.llops.llop_raising_exceptions is not None:
raise TyperError("cannot catch an exception at more than one llop")
if not self.exceptionlinks:
@@ -861,6 +862,7 @@
self.llops.llop_raising_exceptions = len(self.llops)
def exception_cannot_occur(self):
+ self.llops._called_exception_is_here_or_cannot_occur = True
if self.llops.llop_raising_exceptions is not None:
raise TyperError("cannot catch an exception at more than one llop")
if not self.exceptionlinks:
diff --git a/pypy/rpython/test/test_extregistry.py b/pypy/rpython/test/test_extregistry.py
--- a/pypy/rpython/test/test_extregistry.py
+++ b/pypy/rpython/test/test_extregistry.py
@@ -114,6 +114,7 @@
_about_ = dummy_func
s_result_annotation = annmodel.SomeInteger()
def specialize_call(self, hop):
+ hop.exception_cannot_occur()
return hop.inputconst(lltype.Signed, 42)
def func():
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -1085,6 +1085,7 @@
return annmodel.SomeInteger()
def specialize_call(self, hop):
[v_instance] = hop.inputargs(*hop.args_r)
+ hop.exception_is_here()
return hop.gendirectcall(ll_my_gethash, v_instance)
def f(n):
diff --git a/pypy/rpython/test/test_rstr.py b/pypy/rpython/test/test_rstr.py
--- a/pypy/rpython/test/test_rstr.py
+++ b/pypy/rpython/test/test_rstr.py
@@ -477,7 +477,11 @@
s1 = s[:3]
s2 = s[3:]
s3 = s[3:10]
- return s1+s2 == s and s2+s1 == const('lohel') and s1+s3 == s
+ s4 = s[42:44]
+ return (s1+s2 == s and
+ s2+s1 == const('lohel') and
+ s1+s3 == s and
+ s4 == const(''))
res = self.interpret(fn, [0])
assert res
diff --git a/pypy/tool/clean_old_branches.py b/pypy/tool/clean_old_branches.py
--- a/pypy/tool/clean_old_branches.py
+++ b/pypy/tool/clean_old_branches.py
@@ -38,7 +38,7 @@
closed_heads.reverse()
for head, branch in closed_heads:
- print '\t', branch
+ print '\t', head, '\t', branch
print
print 'The branches listed above will be merged to "closed-branches".'
print 'You need to run this script in a clean working copy where you'
diff --git a/pypy/translator/c/gcc/trackgcroot.py b/pypy/translator/c/gcc/trackgcroot.py
--- a/pypy/translator/c/gcc/trackgcroot.py
+++ b/pypy/translator/c/gcc/trackgcroot.py
@@ -847,6 +847,10 @@
if sources:
target, = sources
+ if target.endswith('@PLT'):
+ # In -fPIC mode, all functions calls have this suffix
+ target = target[:-4]
+
if target in self.FUNCTIONS_NOT_RETURNING:
return [InsnStop(target)]
if self.format == 'mingw32' and target == '__alloca':
@@ -1137,7 +1141,7 @@
r_jump_rel_label = re.compile(r"\tj\w+\s+"+"(\d+)f"+"\s*$")
r_unaryinsn_star= re.compile(r"\t[a-z]\w*\s+[*]("+OPERAND+")\s*$")
- r_jmptable_item = re.compile(r"\t.quad\t"+LABEL+"(-\"[A-Za-z0-9$]+\")?\s*$")
+ r_jmptable_item = re.compile(r"\t.(?:quad|long)\t"+LABEL+"(-\"[A-Za-z0-9$]+\"|-"+LABEL+")?\s*$")
r_jmptable_end = re.compile(r"\t.text|\t.section\s+.text|\t\.align|"+LABEL)
r_gcroot_marker = re.compile(r"\t/[*] GCROOT ("+LOCALVARFP+") [*]/")
diff --git a/pypy/translator/c/test/test_extfunc.py b/pypy/translator/c/test/test_extfunc.py
--- a/pypy/translator/c/test/test_extfunc.py
+++ b/pypy/translator/c/test/test_extfunc.py
@@ -919,4 +919,5 @@
t, cbuilder = self.compile(does_stuff)
data = cbuilder.cmdexec('')
res = os.nice(0) + 3
+ if res > 19: res = 19 # xxx Linux specific, probably
assert data.startswith('os.nice returned %d\n' % res)
diff --git a/pypy/translator/cli/dotnet.py b/pypy/translator/cli/dotnet.py
--- a/pypy/translator/cli/dotnet.py
+++ b/pypy/translator/cli/dotnet.py
@@ -459,6 +459,7 @@
def specialize_call(self, hop):
+ hop.exception_cannot_occur()
assert hop.args_s[1].is_constant()
TYPE = hop.args_s[1].const
v_obj = hop.inputarg(hop.args_r[0], arg=0)
@@ -507,6 +508,7 @@
def specialize_call(self, hop):
v_obj, = hop.inputargs(*hop.args_r)
+ hop.exception_cannot_occur()
return hop.genop('same_as', [v_obj], hop.r_result.lowleveltype)
def new_array(type, length):
@@ -608,6 +610,7 @@
def specialize_call(self, hop):
v_type, = hop.inputargs(*hop.args_r)
+ hop.exception_cannot_occur()
return hop.genop('cli_typeof', [v_type], hop.r_result.lowleveltype)
@@ -626,6 +629,7 @@
v_obj, = hop.inputargs(*hop.args_r)
methodname = hop.args_r[0].methodname
c_methodname = hop.inputconst(ootype.Void, methodname)
+ hop.exception_cannot_occur()
return hop.genop('cli_eventhandler', [v_obj, c_methodname], hop.r_result.lowleveltype)
@@ -647,6 +651,7 @@
def specialize_call(self, hop):
assert isinstance(hop.args_s[0], annmodel.SomeOOInstance)
v_inst = hop.inputarg(hop.args_r[0], arg=0)
+ hop.exception_cannot_occur()
return hop.genop('oodowncast', [v_inst], resulttype = hop.r_result.lowleveltype)
@@ -668,6 +673,7 @@
def specialize_call(self, hop):
assert isinstance(hop.args_s[0], annmodel.SomeOOInstance)
v_inst = hop.inputarg(hop.args_r[0], arg=0)
+ hop.exception_cannot_occur()
return hop.genop('ooupcast', [v_inst], resulttype = hop.r_result.lowleveltype)
@@ -701,6 +707,7 @@
def specialize_call(self, hop):
v_obj = hop.inputarg(hop.args_r[0], arg=0)
+ hop.exception_cannot_occur()
return hop.genop('oodowncast', [v_obj], hop.r_result.lowleveltype)
More information about the pypy-commit
mailing list