Author: Manuel Jacob <m...@manueljacob.de> Branch: improve-docs Changeset: r74027:0019c149a635 Date: 2014-10-21 11:59 +0200 http://bitbucket.org/pypy/pypy/changeset/0019c149a635/
Log: hg merge default diff too long, truncating to 2000 out of 17847 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -3,8 +3,8 @@ Except when otherwise stated (look for LICENSE files in directories or information at the beginning of each file) all software and documentation in -the 'rpython', 'pypy', 'ctype_configure', 'dotviewer', 'demo', and 'lib_pypy' -directories is licensed as follows: +the 'rpython', 'pypy', 'ctype_configure', 'dotviewer', 'demo', 'lib_pypy', +'py', and '_pytest' directories is licensed as follows: The MIT License @@ -367,3 +367,43 @@ Detailed license information is contained in the NOTICE file in the directory. + +Licenses and Acknowledgements for Incorporated Software +======================================================= + +This section is an incomplete, but growing list of licenses and +acknowledgements for third-party software incorporated in the PyPy +distribution. + +License for 'Tcl/Tk' +-------------------- + +This copy of PyPy contains library code that may, when used, result in +the Tcl/Tk library to be loaded. PyPy also includes code that may be +regarded as being a copy of some parts of the Tcl/Tk header files. +You may see a copy of the License for Tcl/Tk in the file +`lib_pypy/_tkinter/license.terms` included here. + +License for 'bzip2' +------------------- + +This copy of PyPy may be linked (dynamically or statically) with the +bzip2 library. You may see a copy of the License for bzip2/libbzip2 at + + http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html + +License for 'openssl' +--------------------- + +This copy of PyPy may be linked (dynamically or statically) with the +openssl library. You may see a copy of the License for OpenSSL at + + https://www.openssl.org/source/license.html + +License for 'gdbm' +------------------ + +The gdbm module includes code from gdbm.h, which is distributed under +the terms of the GPL license version 2 or any later version. Thus the +gdbm module, provided in the file lib_pypy/gdbm.py, is redistributed +under the terms of the GPL license as well. diff --git a/lib-python/2.7/test/test_select.py b/lib-python/2.7/test/test_select.py --- a/lib-python/2.7/test/test_select.py +++ b/lib-python/2.7/test/test_select.py @@ -57,7 +57,17 @@ del a[-1] return sys.__stdout__.fileno() a[:] = [F()] * 10 - self.assertEqual(select.select([], a, []), ([], a[:5], [])) + result = select.select([], a, []) + # CPython: 'a' ends up with 5 items, because each fileno() + # removes an item and at the middle the iteration stops. + # PyPy: 'a' ends up empty, because the iteration is done on + # a copy of the original list: fileno() is called 10 times. + if test_support.check_impl_detail(cpython=True): + self.assertEqual(len(result[1]), 5) + self.assertEqual(len(a), 5) + if test_support.check_impl_detail(pypy=True): + self.assertEqual(len(result[1]), 10) + self.assertEqual(len(a), 0) def test_main(): test_support.run_unittest(SelectTestCase) diff --git a/lib_pypy/_curses.py b/lib_pypy/_curses.py --- a/lib_pypy/_curses.py +++ b/lib_pypy/_curses.py @@ -286,6 +286,13 @@ lib = ffi.verify(""" +#ifdef __APPLE__ +/* the following define is necessary for OS X 10.6+; without it, the + Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python + can't get at the WINDOW flags field. */ +#define NCURSES_OPAQUE 0 +#endif + #include <ncurses.h> #include <panel.h> #include <term.h> diff --git a/lib_pypy/datetime.py b/lib_pypy/datetime.py --- a/lib_pypy/datetime.py +++ b/lib_pypy/datetime.py @@ -1242,7 +1242,7 @@ (other._hour, other._minute, other._second, other._microsecond)) if myoff is None or otoff is None: - raise TypeError("cannot compare naive and aware times") + raise TypeError("can't compare offset-naive and offset-aware times") myhhmm = self._hour * 60 + self._minute - myoff othhmm = other._hour * 60 + other._minute - otoff return _cmp((myhhmm, self._second, self._microsecond), @@ -1838,7 +1838,7 @@ other._hour, other._minute, other._second, other._microsecond)) if myoff is None or otoff is None: - raise TypeError("cannot compare naive and aware datetimes") + raise TypeError("can't compare offset-naive and offset-aware datetimes") # XXX What follows could be done more efficiently... diff = self - other # this will take offsets into account if diff.days < 0: @@ -1885,7 +1885,7 @@ if myoff == otoff: return base if myoff is None or otoff is None: - raise TypeError("cannot mix naive and timezone-aware time") + raise TypeError("can't subtract offset-naive and offset-aware datetimes") return base + timedelta(minutes = otoff-myoff) def __hash__(self): diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst --- a/pypy/doc/cppyy.rst +++ b/pypy/doc/cppyy.rst @@ -80,7 +80,7 @@ the selection of scientific software) will also work for a build with the builtin backend. -.. _download: http://cern.ch/wlav/reflex-2013-08-14.tar.bz2 +.. _download: http://cern.ch/wlav/reflex-2014-10-20.tar.bz2 .. _ROOT: http://root.cern.ch/ Besides Reflex, you probably need a version of `gccxml`_ installed, which is @@ -95,8 +95,8 @@ To install the standalone version of Reflex, after download:: - $ tar jxf reflex-2013-08-14.tar.bz2 - $ cd reflex-2013-08-14 + $ tar jxf reflex-2014-10-20.tar.bz2 + $ cd reflex-2014-10-20 $ ./build/autogen $ ./configure <usual set of options such as --prefix> $ make && make install @@ -796,7 +796,7 @@ also means that you can't actually find out whether it is in use, other than by running a micro-benchmark or a JIT test). -.. _provided: http://cern.ch/wlav/reflex-2013-04-23.tar.bz2 +.. _provided: http://cern.ch/wlav/reflex-2014-10-20.tar.bz2 .. _genreflex-methptrgetter.patch: https://bitbucket.org/pypy/pypy/src/default/pypy/module/cppyy/genreflex-methptrgetter.patch CPython diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -38,14 +38,16 @@ no JIT: windows, linux, os/x sandbox: linux, os/x +* repackage and upload source tar.bz2 to bitbucket and to cobra, as some packagers + prefer a clearly labeled source package * write release announcement pypy/doc/release-x.y(.z).txt the release announcement should contain a direct link to the download page * update pypy.org (under extradoc/pypy.org), rebuild and commit * post announcement on morepypy.blogspot.com -* send announcements to pypy-dev, python-list, +* send announcements to twitter.com, pypy-dev, python-list, python-announce, python-dev ... * add a tag on the pypy/jitviewer repo that corresponds to pypy release * add a tag on the codespeed web site that corresponds to pypy release - +* revise versioning at https://readthedocs.org/projects/pypy diff --git a/pypy/doc/release-2.4.0.rst b/pypy/doc/release-2.4.0.rst --- a/pypy/doc/release-2.4.0.rst +++ b/pypy/doc/release-2.4.0.rst @@ -5,7 +5,7 @@ We're pleased to announce PyPy 2.4, which contains significant performance enhancements and bug fixes. -You can already download the PyPy 2.4-beta1 pre-release here: +You can download the PyPy 2.4.0 release here: http://pypy.org/download.html @@ -63,6 +63,8 @@ PyPy now uses Python 2.7.8 standard library. +We fixed a memory leak in IO in the sandbox_ code + We welcomed more than 12 new contributors, and conducted two Google Summer of Code projects, as well as other student projects not directly related to Summer of Code. @@ -103,8 +105,9 @@ * Many issues were resolved_ since the 2.3.1 release on June 8 -.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.3.1.html +.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.4.0.html .. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved +.. _sandbox: http://doc.pypy.org/en/latest/sandbox.html We have further improvements on the way: rpython file handling, numpy linalg compatibility, as well diff --git a/pypy/doc/release-pypy3-2.4.0.rst b/pypy/doc/release-pypy3-2.4.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-pypy3-2.4.0.rst @@ -0,0 +1,126 @@ +================================================= +PyPy3 2.4 - Snow White +================================================= + +We're pleased to announce PyPy3 2.4, which contains significant performance +enhancements and bug fixes. + +You can download the PyPy3 2.4.0 release here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project, and for those who donate to our three sub-projects. +We've shown quite a bit of progress, but we're slowly running out of funds. +Please consider donating more, or even better convince your employer to donate, +so we can finish those projects! The three sub-projects are: + +* `Py3k`_ (supporting Python 3.x): This is a Python 3.2.5 compatible + version we call PyPy3 2.4, and we are working toward a Python 3.3 + compatible version + +* `STM`_ (software transactional memory): We have released a first working version, + and continue to try out new promising paths of achieving a fast multithreaded Python + +* `NumPy`_ which requires installation of our fork of upstream numpy, + available `on bitbucket`_ + +.. _`Py3k`: http://pypy.org/py3donate.html +.. _`STM`: http://pypy.org/tmdonate2.html +.. _`NumPy`: http://pypy.org/numpydonate.html +.. _`on bitbucket`: https://www.bitbucket.org/pypy/numpy + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7 or 3.2.5. It's fast (`pypy 2.4 and cpython 2.7.x`_ performance +comparison) due to its integrated tracing JIT compiler. + +This release supports **x86** machines on most common operating systems +(Linux 32/64, Mac OS X 64, Windows, and OpenBSD), +as well as newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux. + +While we support 32 bit python on Windows, work on the native Windows 64 +bit python is still stalling, we would welcome a volunteer +to `handle that`_. + +.. _`pypy 2.4 and cpython 2.7.x`: http://speed.pypy.org +.. _`handle that`: http://doc.pypy.org/en/latest/windows.html#what-is-missing-for-a-full-64-bit-translation + +PyPy3 Highlights +================ + +Issues reported with our previous release were fixed after reports from users on +our new issue tracker at https://bitbucket.org/pypy/pypy/issues or on IRC at +#pypy. Here is a summary of the user-facing PyPy3 specific changes: + +* Better Windows compatibility, e.g. the nt module functions _getfinalpathname + & _getfileinformation are now supported (the former is required for the + popular pathlib library for example) + +* Various fsencode PEP 383 related fixes to the posix module (readlink, uname, + ttyname and ctermid) and improved locale handling + +* Switched default binary name os POSIX distributions to 'pypy3' (which + symlinks to to 'pypy3.2') + +* Fixed a couple different crashes related to parsing Python 3 source code + +Further Highlights (shared w/ PyPy2) +==================================== + +Benchmarks improved after internal enhancements in string and +bytearray handling, and a major rewrite of the GIL handling. This means +that external calls are now a lot faster, especially the CFFI ones. It also +means better performance in a lot of corner cases with handling strings or +bytearrays. The main bugfix is handling of many socket objects in your +program which in the long run used to "leak" memory. + +We fixed a memory leak in IO in the sandbox_ code + +We welcomed more than 12 new contributors, and conducted two Google +Summer of Code projects, as well as other student projects not +directly related to Summer of Code. + +* Reduced internal copying of bytearray operations + +* Tweak the internal structure of StringBuilder to speed up large string + handling, which becomes advantageous on large programs at the cost of slightly + slower small *benchmark* type programs. + +* Boost performance of thread-local variables in both unjitted and jitted code, + this mostly affects errno handling on linux, which makes external calls + faster. + +* Move to a mixed polling and mutex GIL model that make mutlithreaded jitted + code run *much* faster + +* Optimize errno handling in linux (x86 and x86-64 only) + +* Remove ctypes pythonapi and ctypes.PyDLL, which never worked on PyPy + +* Classes in the ast module are now distinct from structures used by + the compiler, which simplifies and speeds up translation of our + source code to the PyPy binary interpreter + +* Win32 now links statically to zlib, expat, bzip, and openssl-1.0.1i. + No more missing DLLs + +* Many issues were resolved_ since the 2.3.1 release in June + +.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.4.0.html +.. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved +.. _sandbox: http://doc.pypy.org/en/latest/sandbox.html + +We have further improvements on the way: rpython file handling, +numpy linalg compatibility, as well +as improved GC and many smaller improvements. + +Please try it out and let us know what you think. We especially welcome +success stories, we know you are using PyPy, please tell us about it! + +Cheers + +The PyPy Team + diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -6,3 +6,19 @@ .. this is a revision shortly after release-2.4.x .. startrev: 7026746cbb1b +.. branch: win32-fixes5 +Fix c code generation for msvc so empty "{ }" are avoided in unions, +Avoid re-opening files created with NamedTemporaryFile, +Allocate by 4-byte chunks in rffi_platform, +Skip testing objdump if it does not exist, +and other small adjustments in own tests + +.. branch: rtyper-stuff +Small internal refactorings in the rtyper. + +.. branch: var-in-Some +Store annotations on the Variable objects, rather than in a big dict. +Introduce a new framework for double-dispatched annotation implementations. + +.. branch: ClassRepr +Refactor ClassRepr and make normalizecalls independent of the rtyper. diff --git a/pypy/doc/whatsnew-pypy3-2.4.0.rst b/pypy/doc/whatsnew-pypy3-2.4.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy3-2.4.0.rst @@ -0,0 +1,6 @@ +========================= +What's new in PyPy3 2.4.0 +========================= + +.. this is a revision shortly after pypy3-release-2.4.x +.. startrev: 12b940544622 diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -251,6 +251,8 @@ enable_translationmodules(config) config.translation.suggest(check_str_without_nul=True) + if sys.platform.startswith('linux'): + config.translation.suggest(shared=True) if config.translation.thread: config.objspace.usemodules.thread = True diff --git a/pypy/interpreter/astcompiler/ast.py b/pypy/interpreter/astcompiler/ast.py --- a/pypy/interpreter/astcompiler/ast.py +++ b/pypy/interpreter/astcompiler/ast.py @@ -22,9 +22,11 @@ def get_field(space, w_node, name, optional): w_obj = w_node.getdictvalue(space, name) - if w_obj is None and not optional: - raise oefmt(space.w_TypeError, + if w_obj is None: + if not optional: + raise oefmt(space.w_TypeError, "required field \"%s\" missing from %T", name, w_node) + w_obj = space.w_None return w_obj diff --git a/pypy/interpreter/astcompiler/tools/asdl_py.py b/pypy/interpreter/astcompiler/tools/asdl_py.py --- a/pypy/interpreter/astcompiler/tools/asdl_py.py +++ b/pypy/interpreter/astcompiler/tools/asdl_py.py @@ -405,9 +405,11 @@ def get_field(space, w_node, name, optional): w_obj = w_node.getdictvalue(space, name) - if w_obj is None and not optional: - raise oefmt(space.w_TypeError, + if w_obj is None: + if not optional: + raise oefmt(space.w_TypeError, "required field \"%s\" missing from %T", name, w_node) + w_obj = space.w_None return w_obj diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -38,18 +38,15 @@ def cpython_code_signature(code): "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." argcount = code.co_argcount + varnames = code.co_varnames assert argcount >= 0 # annotator hint - argnames = list(code.co_varnames[:argcount]) + argnames = list(varnames[:argcount]) if code.co_flags & CO_VARARGS: - varargname = code.co_varnames[argcount] + varargname = varnames[argcount] argcount += 1 else: varargname = None - if code.co_flags & CO_VARKEYWORDS: - kwargname = code.co_varnames[argcount] - argcount += 1 - else: - kwargname = None + kwargname = varnames[argcount] if code.co_flags & CO_VARKEYWORDS else None return Signature(argnames, varargname, kwargname) 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 @@ -83,12 +83,6 @@ v = PyString_DecodeEscape(space, substr, 'strict', enc) return space.wrap(v) -def hexbyte(val): - result = "%x" % val - if len(result) == 1: - result = "0" + result - return result - def decode_unicode_utf8(space, s, ps, q): # ****The Python 2.7 version, producing UTF-32 escapes**** # String is utf8-encoded, but 'unicode_escape' expects @@ -108,15 +102,14 @@ # instead. lis.append("u005c") if ord(s[ps]) & 0x80: # XXX inefficient - w, ps = decode_utf8(space, s, ps, end, "utf-32-be") - rn = len(w) - assert rn % 4 == 0 - for i in range(0, rn, 4): - lis.append('\\U') - lis.append(hexbyte(ord(w[i]))) - lis.append(hexbyte(ord(w[i+1]))) - lis.append(hexbyte(ord(w[i+2]))) - lis.append(hexbyte(ord(w[i+3]))) + w, ps = decode_utf8(space, s, ps, end) + for c in w: + # The equivalent of %08x, which is not supported by RPython. + # 7 zeroes are enough for the unicode range, and the + # result still fits in 32-bit. + hexa = hex(ord(c) + 0x10000000) + lis.append('\\U0') + lis.append(hexa[3:]) # Skip 0x and the leading 1 else: lis.append(s[ps]) ps += 1 @@ -136,7 +129,7 @@ # note that the C code has a label here. # the logic is the same. if recode_encoding and ord(s[ps]) & 0x80: - w, ps = decode_utf8(space, s, ps, end, recode_encoding) + w, ps = decode_utf8_recode(space, s, ps, end, recode_encoding) # Append bytes to output buffer. builder.append(w) else: @@ -222,14 +215,18 @@ ch >= 'A' and ch <= 'F') -def decode_utf8(space, s, ps, end, encoding): +def decode_utf8(space, s, ps, end): assert ps >= 0 pt = ps # while (s < end && *s != '\\') s++; */ /* inefficient for u".." while ps < end and ord(s[ps]) & 0x80: ps += 1 - w_u = space.wrap(unicodehelper.decode_utf8(space, s[pt:ps])) - w_v = unicodehelper.encode(space, w_u, encoding) + u = unicodehelper.decode_utf8(space, s[pt:ps]) + return u, ps + +def decode_utf8_recode(space, s, ps, end, recode_encoding): + u, ps = decode_utf8(space, s, ps, end) + w_v = unicodehelper.encode(space, space.wrap(u), recode_encoding) v = space.str_w(w_v) return v, ps diff --git a/pypy/interpreter/pyparser/test/test_parsestring.py b/pypy/interpreter/pyparser/test/test_parsestring.py --- a/pypy/interpreter/pyparser/test/test_parsestring.py +++ b/pypy/interpreter/pyparser/test/test_parsestring.py @@ -73,11 +73,11 @@ def test_simple_enc_roundtrip(self): space = self.space - s = "'\x81'" + s = "'\x81\\t'" s = s.decode("koi8-u").encode("utf8") w_ret = parsestring.parsestr(self.space, 'koi8-u', s) ret = space.unwrap(w_ret) - assert ret == eval("# -*- coding: koi8-u -*-\n'\x81'") + assert ret == eval("# -*- coding: koi8-u -*-\n'\x81\\t'") def test_multiline_unicode_strings_with_backslash(self): space = self.space diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -5,6 +5,7 @@ @specialize.memo() def decode_error_handler(space): + # Fast version of the "strict" errors handler. def raise_unicode_exception_decode(errors, encoding, msg, s, startingpos, endingpos): raise OperationError(space.w_UnicodeDecodeError, @@ -17,6 +18,7 @@ @specialize.memo() def encode_error_handler(space): + # Fast version of the "strict" errors handler. def raise_unicode_exception_encode(errors, encoding, msg, u, startingpos, endingpos): raise OperationError(space.w_UnicodeEncodeError, diff --git a/pypy/module/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py --- a/pypy/module/_ast/test/test_ast.py +++ b/pypy/module/_ast/test/test_ast.py @@ -425,3 +425,8 @@ str_node2 = copy.deepcopy(str_node) dict_res = str_node2.__dict__ assert dict_res == {'n':2, 'lineno':2} + + def test_bug_null_in_objspace_type(self): + import ast + code = ast.Expression(lineno=1, col_offset=1, body=ast.ListComp(lineno=1, col_offset=1, elt=ast.Call(lineno=1, col_offset=1, func=ast.Name(lineno=1, col_offset=1, id='str', ctx=ast.Load(lineno=1, col_offset=1)), args=[ast.Name(lineno=1, col_offset=1, id='x', ctx=ast.Load(lineno=1, col_offset=1))], keywords=[]), generators=[ast.comprehension(lineno=1, col_offset=1, target=ast.Name(lineno=1, col_offset=1, id='x', ctx=ast.Store(lineno=1, col_offset=1)), iter=ast.List(lineno=1, col_offset=1, elts=[ast.Num(lineno=1, col_offset=1, n=23)], ctx=ast.Load(lineno=1, col_offset=1, )), ifs=[])])) + compile(code, '<template>', 'eval') diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py --- a/pypy/module/_cffi_backend/ctypefunc.py +++ b/pypy/module/_cffi_backend/ctypefunc.py @@ -8,6 +8,7 @@ from rpython.rlib.jit_libffi import (CIF_DESCRIPTION, CIF_DESCRIPTION_P, FFI_TYPE, FFI_TYPE_P, FFI_TYPE_PP, SIZE_OF_FFI_ARG) from rpython.rlib.objectmodel import we_are_translated, instantiate +from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from pypy.interpreter.error import OperationError, oefmt @@ -160,6 +161,7 @@ raw_cdata = rffi.cast(rffi.CCHARPP, data)[0] lltype.free(raw_cdata, flavor='raw') lltype.free(buffer, flavor='raw') + keepalive_until_here(args_w) return w_res def get_mustfree_flag(data): diff --git a/pypy/module/_socket/__init__.py b/pypy/module/_socket/__init__.py --- a/pypy/module/_socket/__init__.py +++ b/pypy/module/_socket/__init__.py @@ -17,8 +17,6 @@ def startup(self, space): from rpython.rlib.rsocket import rsocket_startup rsocket_startup() - from pypy.module._socket.interp_func import State - space.fromcache(State).startup(space) def buildloaders(cls): from rpython.rlib import rsocket diff --git a/pypy/module/_socket/interp_func.py b/pypy/module/_socket/interp_func.py --- a/pypy/module/_socket/interp_func.py +++ b/pypy/module/_socket/interp_func.py @@ -1,5 +1,6 @@ from rpython.rlib import rsocket from rpython.rlib.rsocket import SocketError, INVALID_SOCKET +from rpython.rlib.rarithmetic import intmask from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import unwrap_spec, WrappedDefault @@ -46,9 +47,8 @@ Return the true host name, a list of aliases, and a list of IP addresses, for a host. The host argument is a string giving a host name or IP number. """ - lock = space.fromcache(State).netdb_lock try: - res = rsocket.gethostbyname_ex(host, lock) + res = rsocket.gethostbyname_ex(host) except SocketError, e: raise converted_error(space, e) return common_wrapgethost(space, res) @@ -60,9 +60,8 @@ Return the true host name, a list of aliases, and a list of IP addresses, for a host. The host argument is a string giving a host name or IP number. """ - lock = space.fromcache(State).netdb_lock try: - res = rsocket.gethostbyaddr(host, lock) + res = rsocket.gethostbyaddr(host) except SocketError, e: raise converted_error(space, e) return common_wrapgethost(space, res) @@ -174,7 +173,7 @@ Convert a 16-bit integer from network to host byte order. """ - return space.wrap(rsocket.ntohs(x)) + return space.wrap(rsocket.ntohs(intmask(x))) @unwrap_spec(x="c_uint") def ntohl(space, x): @@ -190,7 +189,7 @@ Convert a 16-bit integer from host to network byte order. """ - return space.wrap(rsocket.htons(x)) + return space.wrap(rsocket.htons(intmask(x))) @unwrap_spec(x="c_uint") def htonl(space, x): @@ -319,10 +318,3 @@ raise OperationError(space.w_ValueError, space.wrap('Timeout value out of range')) rsocket.setdefaulttimeout(timeout) - -class State(object): - def __init__(self, space): - self.netdb_lock = None - - def startup(self, space): - self.netdb_lock = space.allocate_lock() diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -109,10 +109,11 @@ # XXX Hack to seperate rpython and pypy def make_ushort_port(space, port): + assert isinstance(port, int) if port < 0 or port > 0xffff: raise OperationError(space.w_OverflowError, space.wrap( "port must be 0-65535.")) - return rffi.cast(rffi.USHORT, port) + return port def make_unsigned_flowinfo(space, flowinfo): if flowinfo < 0 or flowinfo > 0xfffff: @@ -401,8 +402,10 @@ The value argument can either be an integer or a string. """ try: - optval = space.int_w(w_optval) - except: + optval = space.c_int_w(w_optval) + except OperationError, e: + if e.async(space): + raise optval = space.str_w(w_optval) try: self.sock.setsockopt(level, optname, optval) diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py --- a/pypy/module/_socket/test/test_sock_app.py +++ b/pypy/module/_socket/test/test_sock_app.py @@ -498,6 +498,13 @@ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) reuse = s.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) assert reuse == 0 + # + raises(TypeError, s.setsockopt, socket.SOL_SOCKET, + socket.SO_REUSEADDR, 2 ** 31) + raises(TypeError, s.setsockopt, socket.SOL_SOCKET, + socket.SO_REUSEADDR, 2 ** 32 + 1) + assert s.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 0 + # s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) reuse = s.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) assert reuse != 0 diff --git a/pypy/module/_winreg/interp_winreg.py b/pypy/module/_winreg/interp_winreg.py --- a/pypy/module/_winreg/interp_winreg.py +++ b/pypy/module/_winreg/interp_winreg.py @@ -266,10 +266,16 @@ buf = None if typ == rwinreg.REG_DWORD: - if space.isinstance_w(w_value, space.w_int): + if space.is_none(w_value) or ( + space.isinstance_w(w_value, space.w_int) or + space.isinstance_w(w_value, space.w_long)): + if space.is_none(w_value): + value = r_uint(0) + else: + value = space.c_uint_w(w_value) buflen = rffi.sizeof(rwin32.DWORD) buf1 = lltype.malloc(rffi.CArray(rwin32.DWORD), 1, flavor='raw') - buf1[0] = space.uint_w(w_value) + buf1[0] = value buf = rffi.cast(rffi.CCHARP, buf1) elif typ == rwinreg.REG_SZ or typ == rwinreg.REG_EXPAND_SZ: diff --git a/pypy/module/_winreg/test/test_winreg.py b/pypy/module/_winreg/test/test_winreg.py --- a/pypy/module/_winreg/test/test_winreg.py +++ b/pypy/module/_winreg/test/test_winreg.py @@ -40,7 +40,7 @@ cls.w_tmpfilename = space.wrap(str(udir.join('winreg-temp'))) test_data = [ - ("Int Value", 45, _winreg.REG_DWORD), + ("Int Value", 0xFEDCBA98, _winreg.REG_DWORD), ("Str Value", "A string Value", _winreg.REG_SZ), ("Unicode Value", u"A unicode Value", _winreg.REG_SZ), ("Str Expand", "The path is %path%", _winreg.REG_EXPAND_SZ), @@ -137,9 +137,11 @@ assert 0, "Did not raise" def test_SetValueEx(self): - from _winreg import CreateKey, SetValueEx, REG_BINARY + from _winreg import CreateKey, SetValueEx, REG_BINARY, REG_DWORD key = CreateKey(self.root_key, self.test_key_name) sub_key = CreateKey(key, "sub_key") + SetValueEx(sub_key, 'Int Value', 0, REG_DWORD, None) + SetValueEx(sub_key, 'Int Value', 0, REG_DWORD, 45) for name, value, type in self.test_data: SetValueEx(sub_key, name, 0, type, value) exc = raises(TypeError, SetValueEx, sub_key, 'test_name', None, diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -116,6 +116,8 @@ validate_fd(fileno(fp)) return _feof(fp) +def is_valid_fp(fp): + return is_valid_fd(fileno(fp)) constant_names = """ Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GETCHARBUFFER diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py --- a/pypy/module/cpyext/eval.py +++ b/pypy/module/cpyext/eval.py @@ -3,7 +3,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP, - cpython_struct) + cpython_struct, is_valid_fp) from pypy.module.cpyext.pyobject import PyObject, borrow_from from pypy.module.cpyext.pyerrors import PyErr_SetFromErrno from pypy.module.cpyext.funcobject import PyCodeObject @@ -154,6 +154,10 @@ source = "" filename = rffi.charp2str(filename) buf = lltype.malloc(rffi.CCHARP.TO, BUF_SIZE, flavor='raw') + if not is_valid_fp(fp): + lltype.free(buf, flavor='raw') + PyErr_SetFromErrno(space, space.w_IOError) + return None try: while True: count = fread(buf, 1, BUF_SIZE, fp) diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py --- a/pypy/module/cpyext/test/test_eval.py +++ b/pypy/module/cpyext/test/test_eval.py @@ -89,12 +89,12 @@ rffi.free_charp(buf) assert 0 == run("42 * 43") - + assert -1 == run("4..3 * 43") - + assert api.PyErr_Occurred() api.PyErr_Clear() - + def test_run_string(self, space, api): def run(code, start, w_globals, w_locals): buf = rffi.str2charp(code) 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 @@ -20,6 +20,7 @@ 'concatenate': 'arrayops.concatenate', 'count_nonzero': 'arrayops.count_nonzero', 'dot': 'arrayops.dot', + 'result_type': 'arrayops.result_type', 'where': 'arrayops.where', 'set_string_function': 'appbridge.set_string_function', diff --git a/pypy/module/micronumpy/app_numpy.py b/pypy/module/micronumpy/app_numpy.py --- a/pypy/module/micronumpy/app_numpy.py +++ b/pypy/module/micronumpy/app_numpy.py @@ -16,9 +16,9 @@ dtype = test.dtype length = math.ceil((float(stop) - start) / step) length = int(length) - arr = _numpypy.multiarray.zeros(length, dtype=dtype) + arr = _numpypy.multiarray.empty(length, dtype=dtype) i = start - for j in range(arr.size): + for j in xrange(arr.size): arr[j] = i i += step return arr diff --git a/pypy/module/micronumpy/arrayops.py b/pypy/module/micronumpy/arrayops.py --- a/pypy/module/micronumpy/arrayops.py +++ b/pypy/module/micronumpy/arrayops.py @@ -1,3 +1,4 @@ +from rpython.rlib import jit from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import unwrap_spec from pypy.module.micronumpy import loop, descriptor, ufuncs, support, \ @@ -6,6 +7,7 @@ from pypy.module.micronumpy.converters import clipmode_converter from pypy.module.micronumpy.strides import Chunk, Chunks, shape_agreement, \ shape_agreement_multiple +from .boxes import W_GenericBox def where(space, w_arr, w_x=None, w_y=None): @@ -283,3 +285,28 @@ else: loop.diagonal_array(space, arr, out, offset, axis1, axis2, shape) return out + + +@jit.unroll_safe +def result_type(space, __args__): + args_w, kw_w = __args__.unpack() + if kw_w: + raise oefmt(space.w_TypeError, "result_type() takes no keyword arguments") + if not args_w: + raise oefmt(space.w_ValueError, "at least one array or dtype is required") + result = None + for w_arg in args_w: + if isinstance(w_arg, W_NDimArray): + dtype = w_arg.get_dtype() + elif isinstance(w_arg, W_GenericBox) or ( + space.isinstance_w(w_arg, space.w_int) or + space.isinstance_w(w_arg, space.w_float) or + space.isinstance_w(w_arg, space.w_complex) or + space.isinstance_w(w_arg, space.w_long) or + space.isinstance_w(w_arg, space.w_bool)): + dtype = ufuncs.find_dtype_for_scalar(space, w_arg) + else: + dtype = space.interp_w(descriptor.W_Dtype, + space.call_function(space.gettypefor(descriptor.W_Dtype), w_arg)) + result = ufuncs.find_binop_result_dtype(space, result, dtype) + return result diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py --- a/pypy/module/micronumpy/compile.py +++ b/pypy/module/micronumpy/compile.py @@ -36,7 +36,7 @@ SINGLE_ARG_FUNCTIONS = ["sum", "prod", "max", "min", "all", "any", "unegative", "flat", "tostring","count_nonzero", "argsort"] -TWO_ARG_FUNCTIONS = ["dot", 'take'] +TWO_ARG_FUNCTIONS = ["dot", 'take', 'searchsorted'] TWO_ARG_FUNCTIONS_OR_NONE = ['view', 'astype'] THREE_ARG_FUNCTIONS = ['where'] @@ -109,6 +109,9 @@ if stop < 0: stop += size + 1 if step < 0: + start, stop = stop, start + start -= 1 + stop -= 1 lgt = (stop - start + 1) / step + 1 else: lgt = (stop - start - 1) / step + 1 @@ -475,7 +478,6 @@ class SliceConstant(Node): def __init__(self, start, stop, step): - # no negative support for now self.start = start self.stop = stop self.step = step @@ -582,6 +584,9 @@ w_res = arr.descr_dot(interp.space, arg) elif self.name == 'take': w_res = arr.descr_take(interp.space, arg) + elif self.name == "searchsorted": + w_res = arr.descr_searchsorted(interp.space, arg, + interp.space.wrap('left')) else: assert False # unreachable code elif self.name in THREE_ARG_FUNCTIONS: diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -19,6 +19,7 @@ 'strides[*]', 'backstrides[*]', 'order'] start = 0 parent = None + flags = 0 # JIT hints that length of all those arrays is a constant @@ -357,11 +358,11 @@ self.dtype = dtype def argsort(self, space, w_axis): - from pypy.module.micronumpy.sort import argsort_array + from .selection import argsort_array return argsort_array(self, space, w_axis) def sort(self, space, w_axis, w_order): - from pypy.module.micronumpy.sort import sort_array + from .selection import sort_array return sort_array(self, space, w_axis, w_order) def base(self): diff --git a/pypy/module/micronumpy/constants.py b/pypy/module/micronumpy/constants.py --- a/pypy/module/micronumpy/constants.py +++ b/pypy/module/micronumpy/constants.py @@ -65,6 +65,9 @@ FLOATINGLTR = 'f' COMPLEXLTR = 'c' +SEARCHLEFT = 0 +SEARCHRIGHT = 1 + ANYORDER = -1 CORDER = 0 FORTRANORDER = 1 @@ -74,6 +77,9 @@ WRAP = 1 RAISE = 2 +ARRAY_C_CONTIGUOUS = 0x0001 +ARRAY_F_CONTIGUOUS = 0x0002 + LITTLE = '<' BIG = '>' NATIVE = '=' diff --git a/pypy/module/micronumpy/converters.py b/pypy/module/micronumpy/converters.py --- a/pypy/module/micronumpy/converters.py +++ b/pypy/module/micronumpy/converters.py @@ -1,4 +1,4 @@ -from pypy.interpreter.error import OperationError +from pypy.interpreter.error import OperationError, oefmt from pypy.module.micronumpy import constants as NPY @@ -41,6 +41,23 @@ space.wrap("clipmode not understood")) +def searchside_converter(space, w_obj): + try: + s = space.str_w(w_obj) + except OperationError: + s = None + if not s: + raise oefmt(space.w_ValueError, + "expected nonempty string for keyword 'side'") + if s[0] == 'l' or s[0] == 'L': + return NPY.SEARCHLEFT + elif s[0] == 'r' or s[0] == 'R': + return NPY.SEARCHRIGHT + else: + raise oefmt(space.w_ValueError, + "'%s' is an invalid value for keyword 'side'", s) + + def order_converter(space, w_order, default): if space.is_none(w_order): return default diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py --- a/pypy/module/micronumpy/ctors.py +++ b/pypy/module/micronumpy/ctors.py @@ -3,7 +3,7 @@ from rpython.rlib.buffer import SubBuffer from rpython.rlib.rstring import strip_spaces from rpython.rtyper.lltypesystem import lltype, rffi -from pypy.module.micronumpy import descriptor, loop +from pypy.module.micronumpy import descriptor, loop, support from pypy.module.micronumpy.base import ( W_NDimArray, convert_to_array, W_NumpyObject) from pypy.module.micronumpy.converters import shape_converter @@ -134,6 +134,15 @@ if dtype.is_str_or_unicode() and dtype.elsize < 1: dtype = descriptor.variable_dtype(space, dtype.char + '1') shape = shape_converter(space, w_shape, dtype) + for dim in shape: + if dim < 0: + raise OperationError(space.w_ValueError, space.wrap( + "negative dimensions are not allowed")) + try: + support.product(shape) + except OverflowError: + raise OperationError(space.w_ValueError, space.wrap( + "array is too big.")) return W_NDimArray.from_shape(space, shape, dtype=dtype, zero=zero) def empty(space, w_shape, w_dtype=None, w_order=None): diff --git a/pypy/module/micronumpy/flagsobj.py b/pypy/module/micronumpy/flagsobj.py --- a/pypy/module/micronumpy/flagsobj.py +++ b/pypy/module/micronumpy/flagsobj.py @@ -2,6 +2,46 @@ from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import interp2app from pypy.interpreter.typedef import TypeDef, GetSetProperty +from pypy.module.micronumpy import constants as NPY + + +def enable_flags(arr, flags): + arr.flags |= flags + + +def clear_flags(arr, flags): + arr.flags &= ~flags + + +def _update_contiguous_flags(arr): + shape = arr.shape + strides = arr.strides + + is_c_contig = True + sd = arr.dtype.elsize + for i in range(len(shape) - 1, -1, -1): + dim = shape[i] + if strides[i] != sd: + is_c_contig = False + break + if dim == 0: + break + sd *= dim + if is_c_contig: + enable_flags(arr, NPY.ARRAY_C_CONTIGUOUS) + else: + clear_flags(arr, NPY.ARRAY_C_CONTIGUOUS) + + sd = arr.dtype.elsize + for i in range(len(shape)): + dim = shape[i] + if strides[i] != sd: + clear_flags(arr, NPY.ARRAY_F_CONTIGUOUS) + return + if dim == 0: + break + sd *= dim + enable_flags(arr, NPY.ARRAY_F_CONTIGUOUS) class W_FlagsObject(W_Root): diff --git a/pypy/module/micronumpy/flatiter.py b/pypy/module/micronumpy/flatiter.py --- a/pypy/module/micronumpy/flatiter.py +++ b/pypy/module/micronumpy/flatiter.py @@ -1,7 +1,10 @@ from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.gateway import interp2app +from pypy.interpreter.typedef import TypeDef, GetSetProperty from pypy.module.micronumpy import loop -from pypy.module.micronumpy.base import W_NDimArray, convert_to_array +from pypy.module.micronumpy.base import convert_to_array from pypy.module.micronumpy.concrete import BaseConcreteArray +from .ndarray import W_NDimArray class FakeArrayImplementation(BaseConcreteArray): @@ -27,12 +30,22 @@ class W_FlatIterator(W_NDimArray): def __init__(self, arr): self.base = arr + self.iter, self.state = arr.create_iter() # this is needed to support W_NDimArray interface self.implementation = FakeArrayImplementation(self.base) - self.reset() - def reset(self): - self.iter, self.state = self.base.create_iter() + def descr_base(self, space): + return space.wrap(self.base) + + def descr_index(self, space): + return space.wrap(self.state.index) + + def descr_coords(self, space): + self.state = self.iter.update(self.state) + return space.newtuple([space.wrap(c) for c in self.state.indices]) + + def descr_iter(self): + return self def descr_len(self, space): return space.wrap(self.iter.size) @@ -44,40 +57,59 @@ self.state = self.iter.next(self.state) return w_res - def descr_index(self, space): - return space.wrap(self.state.index) - - def descr_coords(self, space): - return space.newtuple([space.wrap(c) for c in self.state.indices]) - def descr_getitem(self, space, w_idx): if not (space.isinstance_w(w_idx, space.w_int) or space.isinstance_w(w_idx, space.w_slice)): raise oefmt(space.w_IndexError, 'unsupported iterator index') - self.reset() - base = self.base - start, stop, step, length = space.decode_index4(w_idx, base.get_size()) - base_iter, base_state = base.create_iter() - base_state = base_iter.next_skip_x(base_state, start) - if length == 1: - return base_iter.getitem(base_state) - res = W_NDimArray.from_shape(space, [length], base.get_dtype(), - base.get_order(), w_instance=base) - return loop.flatiter_getitem(res, base_iter, base_state, step) + try: + start, stop, step, length = space.decode_index4(w_idx, self.iter.size) + state = self.iter.goto(start) + if length == 1: + return self.iter.getitem(state) + base = self.base + res = W_NDimArray.from_shape(space, [length], base.get_dtype(), + base.get_order(), w_instance=base) + return loop.flatiter_getitem(res, self.iter, state, step) + finally: + self.state = self.iter.reset(self.state) def descr_setitem(self, space, w_idx, w_value): if not (space.isinstance_w(w_idx, space.w_int) or space.isinstance_w(w_idx, space.w_slice)): raise oefmt(space.w_IndexError, 'unsupported iterator index') - base = self.base - start, stop, step, length = space.decode_index4(w_idx, base.get_size()) - arr = convert_to_array(space, w_value) - loop.flatiter_setitem(space, self.base, arr, start, step, length) + start, stop, step, length = space.decode_index4(w_idx, self.iter.size) + try: + state = self.iter.goto(start) + dtype = self.base.get_dtype() + if length == 1: + try: + val = dtype.coerce(space, w_value) + except OperationError: + raise oefmt(space.w_ValueError, "Error setting single item of array.") + self.iter.setitem(state, val) + return + arr = convert_to_array(space, w_value) + loop.flatiter_setitem(space, dtype, arr, self.iter, state, step, length) + finally: + self.state = self.iter.reset(self.state) - def descr_iter(self): - return self - def descr_base(self, space): - return space.wrap(self.base) +W_FlatIterator.typedef = TypeDef("numpy.flatiter", + base = GetSetProperty(W_FlatIterator.descr_base), + index = GetSetProperty(W_FlatIterator.descr_index), + coords = GetSetProperty(W_FlatIterator.descr_coords), -# typedef is in interp_ndarray, so we see the additional arguments + __iter__ = interp2app(W_FlatIterator.descr_iter), + __len__ = interp2app(W_FlatIterator.descr_len), + next = interp2app(W_FlatIterator.descr_next), + + __getitem__ = interp2app(W_FlatIterator.descr_getitem), + __setitem__ = interp2app(W_FlatIterator.descr_setitem), + + __eq__ = interp2app(W_FlatIterator.descr_eq), + __ne__ = interp2app(W_FlatIterator.descr_ne), + __lt__ = interp2app(W_FlatIterator.descr_lt), + __le__ = interp2app(W_FlatIterator.descr_le), + __gt__ = interp2app(W_FlatIterator.descr_gt), + __ge__ = interp2app(W_FlatIterator.descr_ge), +) diff --git a/pypy/module/micronumpy/iterators.py b/pypy/module/micronumpy/iterators.py --- a/pypy/module/micronumpy/iterators.py +++ b/pypy/module/micronumpy/iterators.py @@ -35,14 +35,11 @@ [x.strides[i] * (x.shape[i] - 1) for i in range(len(x.shape))] we can go faster. All the calculations happen in next() - -next_skip_x(steps) tries to do the iteration for a number of steps at once, -but then we cannot guarantee that we only overflow one single shape -dimension, perhaps we could overflow times in one big step. """ from rpython.rlib import jit -from pypy.module.micronumpy import support +from pypy.module.micronumpy import support, constants as NPY from pypy.module.micronumpy.base import W_NDimArray +from pypy.module.micronumpy.flagsobj import _update_contiguous_flags class PureShapeIter(object): @@ -80,7 +77,7 @@ class IterState(object): - _immutable_fields_ = ['iterator', 'index', 'indices[*]', 'offset'] + _immutable_fields_ = ['iterator', 'index', 'indices', 'offset'] def __init__(self, iterator, index, indices, offset): self.iterator = iterator @@ -90,11 +87,18 @@ class ArrayIter(object): - _immutable_fields_ = ['array', 'size', 'ndim_m1', 'shape_m1[*]', - 'strides[*]', 'backstrides[*]'] + _immutable_fields_ = ['contiguous', 'array', 'size', 'ndim_m1', 'shape_m1[*]', + 'strides[*]', 'backstrides[*]', 'factors[*]', + 'track_index'] + + track_index = True def __init__(self, array, size, shape, strides, backstrides): assert len(shape) == len(strides) == len(backstrides) + _update_contiguous_flags(array) + self.contiguous = (array.flags & NPY.ARRAY_C_CONTIGUOUS and + array.shape == shape and array.strides == strides) + self.array = array self.size = size self.ndim_m1 = len(shape) - 1 @@ -102,52 +106,79 @@ self.strides = strides self.backstrides = backstrides - def reset(self): - return IterState(self, 0, [0] * len(self.shape_m1), self.array.start) + ndim = len(shape) + factors = [0] * ndim + for i in xrange(ndim): + if i == 0: + factors[ndim-1] = 1 + else: + factors[ndim-i-1] = factors[ndim-i] * shape[ndim-i] + self.factors = factors + + @jit.unroll_safe + def reset(self, state=None): + if state is None: + indices = [0] * len(self.shape_m1) + else: + assert state.iterator is self + indices = state.indices + for i in xrange(self.ndim_m1, -1, -1): + indices[i] = 0 + return IterState(self, 0, indices, self.array.start) @jit.unroll_safe def next(self, state): assert state.iterator is self - index = state.index + 1 + index = state.index + if self.track_index: + index += 1 indices = state.indices offset = state.offset - for i in xrange(self.ndim_m1, -1, -1): - idx = indices[i] - if idx < self.shape_m1[i]: - indices[i] = idx + 1 - offset += self.strides[i] - break - else: - indices[i] = 0 - offset -= self.backstrides[i] + if self.contiguous: + offset += self.array.dtype.elsize + else: + for i in xrange(self.ndim_m1, -1, -1): + idx = indices[i] + if idx < self.shape_m1[i]: + indices[i] = idx + 1 + offset += self.strides[i] + break + else: + indices[i] = 0 + offset -= self.backstrides[i] return IterState(self, index, indices, offset) @jit.unroll_safe - def next_skip_x(self, state, step): + def goto(self, index): + offset = self.array.start + if self.contiguous: + offset += index * self.array.dtype.elsize + else: + current = index + for i in xrange(len(self.shape_m1)): + offset += (current / self.factors[i]) * self.strides[i] + current %= self.factors[i] + return IterState(self, index, None, offset) + + @jit.unroll_safe + def update(self, state): assert state.iterator is self - assert step >= 0 - if step == 0: + assert self.track_index + if not self.contiguous: return state - index = state.index + step + current = state.index indices = state.indices - offset = state.offset - for i in xrange(self.ndim_m1, -1, -1): - idx = indices[i] - if idx < (self.shape_m1[i] + 1) - step: - indices[i] = idx + step - offset += self.strides[i] * step - break + for i in xrange(len(self.shape_m1)): + if self.factors[i] != 0: + indices[i] = current / self.factors[i] + current %= self.factors[i] else: - rem_step = (idx + step) // (self.shape_m1[i] + 1) - cur_step = step - rem_step * (self.shape_m1[i] + 1) - indices[i] = idx + cur_step - offset += self.strides[i] * cur_step - step = rem_step - assert step > 0 - return IterState(self, index, indices, offset) + indices[i] = 0 + return IterState(self, state.index, indices, state.offset) def done(self, state): assert state.iterator is self + assert self.track_index return state.index >= self.size def getitem(self, state): diff --git a/pypy/module/micronumpy/loop.py b/pypy/module/micronumpy/loop.py --- a/pypy/module/micronumpy/loop.py +++ b/pypy/module/micronumpy/loop.py @@ -48,6 +48,7 @@ left_iter, left_state = w_lhs.create_iter(shape) right_iter, right_state = w_rhs.create_iter(shape) out_iter, out_state = out.create_iter(shape) + left_iter.track_index = right_iter.track_index = False shapelen = len(shape) while not out_iter.done(out_state): call2_driver.jit_merge_point(shapelen=shapelen, func=func, @@ -71,6 +72,7 @@ out = W_NDimArray.from_shape(space, shape, res_dtype, w_instance=w_obj) obj_iter, obj_state = w_obj.create_iter(shape) out_iter, out_state = out.create_iter(shape) + obj_iter.track_index = False shapelen = len(shape) while not out_iter.done(out_state): call1_driver.jit_merge_point(shapelen=shapelen, func=func, @@ -182,6 +184,9 @@ iter, state = y_iter, y_state else: iter, state = x_iter, x_state + out_iter.track_index = x_iter.track_index = False + arr_iter.track_index = y_iter.track_index = False + iter.track_index = True shapelen = len(shape) while not iter.done(state): where_driver.jit_merge_point(shapelen=shapelen, dtype=dtype, @@ -229,6 +234,7 @@ dtype=dtype) assert not arr_iter.done(arr_state) w_val = arr_iter.getitem(arr_state).convert_to(space, dtype) + out_state = out_iter.update(out_state) if out_state.indices[axis] == 0: if identity is not None: w_val = func(dtype, identity, w_val) @@ -298,6 +304,7 @@ assert left_shape[-1] == right_shape[right_critical_dim] assert result.get_dtype() == dtype outi, outs = result.create_iter() + outi.track_index = False lefti = AllButAxisIter(left_impl, len(left_shape) - 1) righti = AllButAxisIter(right_impl, right_critical_dim) lefts = lefti.reset() @@ -322,7 +329,7 @@ outi.setitem(outs, oval) outs = outi.next(outs) rights = righti.next(rights) - rights = righti.reset() + rights = righti.reset(rights) lefts = lefti.next(lefts) return result @@ -360,6 +367,7 @@ while not arr_iter.done(arr_state): nonzero_driver.jit_merge_point(shapelen=shapelen, dims=dims, dtype=dtype) if arr_iter.getitem_bool(arr_state): + arr_state = arr_iter.update(arr_state) for d in dims: res_iter.setitem(res_state, box(arr_state.indices[d])) res_state = res_iter.next(res_state) @@ -435,7 +443,7 @@ while not ri.done(rs): flatiter_getitem_driver.jit_merge_point(dtype=dtype) ri.setitem(rs, base_iter.getitem(base_state)) - base_state = base_iter.next_skip_x(base_state, step) + base_state = base_iter.goto(base_state.index + step) rs = ri.next(rs) return res @@ -443,11 +451,8 @@ greens = ['dtype'], reds = 'auto') -def flatiter_setitem(space, arr, val, start, step, length): - dtype = arr.get_dtype() - arr_iter, arr_state = arr.create_iter() +def flatiter_setitem(space, dtype, val, arr_iter, arr_state, step, length): val_iter, val_state = val.create_iter() - arr_state = arr_iter.next_skip_x(arr_state, start) while length > 0: flatiter_setitem_driver.jit_merge_point(dtype=dtype) val = val_iter.getitem(val_state) @@ -456,9 +461,10 @@ else: val = val.convert_to(space, dtype) arr_iter.setitem(arr_state, val) - # need to repeat i_nput values until all assignments are done - arr_state = arr_iter.next_skip_x(arr_state, step) + arr_state = arr_iter.goto(arr_state.index + step) val_state = val_iter.next(val_state) + if val_iter.done(val_state): + val_state = val_iter.reset(val_state) length -= 1 fromstring_driver = jit.JitDriver(name = 'numpy_fromstring', @@ -694,3 +700,43 @@ out_iter.setitem(out_state, arr.getitem_index(space, indexes)) iter.next() out_state = out_iter.next(out_state) + +def _new_binsearch(side, op_name): + binsearch_driver = jit.JitDriver(name='numpy_binsearch_' + side, + greens=['dtype'], + reds='auto') + + def binsearch(space, arr, key, ret): + assert len(arr.get_shape()) == 1 + dtype = key.get_dtype() + op = getattr(dtype.itemtype, op_name) + key_iter, key_state = key.create_iter() + ret_iter, ret_state = ret.create_iter() + ret_iter.track_index = False + size = arr.get_size() + min_idx = 0 + max_idx = size + last_key_val = key_iter.getitem(key_state) + while not key_iter.done(key_state): + key_val = key_iter.getitem(key_state) + if dtype.itemtype.lt(last_key_val, key_val): + max_idx = size + else: + min_idx = 0 + max_idx = max_idx + 1 if max_idx < size else size + last_key_val = key_val + while min_idx < max_idx: + binsearch_driver.jit_merge_point(dtype=dtype) + mid_idx = min_idx + ((max_idx - min_idx) >> 1) + mid_val = arr.getitem(space, [mid_idx]).convert_to(space, dtype) + if op(mid_val, key_val): + min_idx = mid_idx + 1 + else: + max_idx = mid_idx + ret_iter.setitem(ret_state, ret.get_dtype().box(min_idx)) + ret_state = ret_iter.next(ret_state) + key_state = key_iter.next(key_state) + return binsearch + +binsearch_left = _new_binsearch('left', 'lt') +binsearch_right = _new_binsearch('right', 'le') diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -16,9 +16,8 @@ ArrayArgumentException, wrap_impl from pypy.module.micronumpy.concrete import BaseConcreteArray from pypy.module.micronumpy.converters import multi_axis_converter, \ - order_converter, shape_converter + order_converter, shape_converter, searchside_converter from pypy.module.micronumpy.flagsobj import W_FlagsObject -from pypy.module.micronumpy.flatiter import W_FlatIterator from pypy.module.micronumpy.strides import get_shape_from_iterable, \ shape_agreement, shape_agreement_multiple @@ -407,8 +406,19 @@ -------- numpy.swapaxes : equivalent function """ - if self.is_scalar(): + if axis1 == axis2: return self + n = len(self.get_shape()) + if n <= 1: + return self + if axis1 < 0: + axis1 += n + if axis2 < 0: + axis2 += n + if axis1 < 0 or axis1 >= n: + raise oefmt(space.w_ValueError, "bad axis1 argument to swapaxes") + if axis2 < 0 or axis2 >= n: + raise oefmt(space.w_ValueError, "bad axis2 argument to swapaxes") return self.implementation.swapaxes(space, self, axis1, axis2) def descr_nonzero(self, space): @@ -464,10 +474,13 @@ return repeat(space, self, repeats, w_axis) def descr_set_flatiter(self, space, w_obj): + iter, state = self.create_iter() + dtype = self.get_dtype() arr = convert_to_array(space, w_obj) - loop.flatiter_setitem(space, self, arr, 0, 1, self.get_size()) + loop.flatiter_setitem(space, dtype, arr, iter, state, 1, iter.size) def descr_get_flatiter(self, space): + from .flatiter import W_FlatIterator return space.wrap(W_FlatIterator(self)) def descr_item(self, space, __args__): @@ -715,29 +728,22 @@ loop.round(space, self, calc_dtype, self.get_shape(), decimals, out) return out - @unwrap_spec(side=str, w_sorter=WrappedDefault(None)) - def descr_searchsorted(self, space, w_v, side='left', w_sorter=None): + @unwrap_spec(w_side=WrappedDefault('left'), w_sorter=WrappedDefault(None)) + def descr_searchsorted(self, space, w_v, w_side=None, w_sorter=None): if not space.is_none(w_sorter): raise OperationError(space.w_NotImplementedError, space.wrap( 'sorter not supported in searchsort')) - if not side or len(side) < 1: - raise OperationError(space.w_ValueError, space.wrap( - "expected nonempty string for keyword 'side'")) - elif side[0] == 'l' or side[0] == 'L': - side = 'l' - elif side[0] == 'r' or side[0] == 'R': - side = 'r' - else: - raise oefmt(space.w_ValueError, - "'%s' is an invalid value for keyword 'side'", side) - if len(self.get_shape()) > 1: + side = searchside_converter(space, w_side) + if len(self.get_shape()) != 1: raise oefmt(space.w_ValueError, "a must be a 1-d array") v = convert_to_array(space, w_v) - if len(v.get_shape()) > 1: - raise oefmt(space.w_ValueError, "v must be a 1-d array-like") ret = W_NDimArray.from_shape( space, v.get_shape(), descriptor.get_dtype_cache(space).w_longdtype) - app_searchsort(space, self, v, space.wrap(side), ret) + if side == NPY.SEARCHLEFT: + binsearch = loop.binsearch_left + else: + binsearch = loop.binsearch_right + binsearch(space, self, v, ret) if ret.is_scalar(): return ret.get_scalar_value() return ret @@ -1295,31 +1301,6 @@ return res """, filename=__file__).interphook('ptp') -app_searchsort = applevel(r""" - def searchsort(arr, v, side, result): - import operator - def func(a, op, val): - imin = 0 - imax = a.size - while imin < imax: - imid = imin + ((imax - imin) >> 1) - if op(a[imid], val): - imin = imid +1 - else: - imax = imid - return imin - if side == 'l': - op = operator.lt - else: - op = operator.le - if v.size < 2: - result[...] = func(arr, op, v) - else: - for i in range(v.size): - result[i] = func(arr, op, v[i]) - return result -""", filename=__file__).interphook('searchsort') - W_NDimArray.typedef = TypeDef("numpy.ndarray", __new__ = interp2app(descr_new_array), @@ -1407,6 +1388,7 @@ flags = GetSetProperty(W_NDimArray.descr_get_flags), fill = interp2app(W_NDimArray.descr_fill), + tobytes = interp2app(W_NDimArray.descr_tostring), tostring = interp2app(W_NDimArray.descr_tostring), mean = interp2app(W_NDimArray.descr_mean), @@ -1485,23 +1467,3 @@ def _reconstruct(space, w_subtype, w_shape, w_dtype): return descr_new_array(space, w_subtype, w_shape, w_dtype) - - -W_FlatIterator.typedef = TypeDef("numpy.flatiter", - __iter__ = interp2app(W_FlatIterator.descr_iter), - __getitem__ = interp2app(W_FlatIterator.descr_getitem), - __setitem__ = interp2app(W_FlatIterator.descr_setitem), - __len__ = interp2app(W_FlatIterator.descr_len), - - __eq__ = interp2app(W_FlatIterator.descr_eq), - __ne__ = interp2app(W_FlatIterator.descr_ne), - __lt__ = interp2app(W_FlatIterator.descr_lt), - __le__ = interp2app(W_FlatIterator.descr_le), - __gt__ = interp2app(W_FlatIterator.descr_gt), - __ge__ = interp2app(W_FlatIterator.descr_ge), - - next = interp2app(W_FlatIterator.descr_next), - base = GetSetProperty(W_FlatIterator.descr_base), - index = GetSetProperty(W_FlatIterator.descr_index), - coords = GetSetProperty(W_FlatIterator.descr_coords), -) diff --git a/pypy/module/micronumpy/nditer.py b/pypy/module/micronumpy/nditer.py --- a/pypy/module/micronumpy/nditer.py +++ b/pypy/module/micronumpy/nditer.py @@ -313,6 +313,7 @@ # create an iterator for each operand for i in range(len(self.seq)): it = get_iter(space, self.order, self.seq[i], iter_shape, self.dtypes[i]) + it.contiguous = False self.iters.append((it, it.reset())) def set_op_axes(self, space, w_op_axes): diff --git a/pypy/module/micronumpy/sort.py b/pypy/module/micronumpy/selection.py rename from pypy/module/micronumpy/sort.py rename to pypy/module/micronumpy/selection.py 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,6 @@ from pypy.interpreter.error import OperationError, oefmt from rpython.rlib import jit +from rpython.rlib.rarithmetic import ovfcheck def issequence_w(space, w_obj): @@ -23,7 +24,7 @@ def product(s): i = 1 for x in s: - i *= x + i = ovfcheck(i * x) return i diff --git a/pypy/module/micronumpy/test/test_arrayops.py b/pypy/module/micronumpy/test/test_arrayops.py --- a/pypy/module/micronumpy/test/test_arrayops.py +++ b/pypy/module/micronumpy/test/test_arrayops.py @@ -199,3 +199,19 @@ a.put(23, -1, mode=1) # wrap assert (a == array([0, 1, -10, -1, -15])).all() raises(TypeError, "arange(5).put(22, -5, mode='zzzz')") # unrecognized mode + + def test_result_type(self): + import numpy as np + exc = raises(ValueError, np.result_type) + assert str(exc.value) == "at least one array or dtype is required" + exc = raises(TypeError, np.result_type, a=2) + assert str(exc.value) == "result_type() takes no keyword arguments" + assert np.result_type(True) is np.dtype('bool') + assert np.result_type(1) is np.dtype('int') + assert np.result_type(1.) is np.dtype('float64') + assert np.result_type(1+2j) is np.dtype('complex128') + assert np.result_type(1, 1.) is np.dtype('float64') + assert np.result_type(np.array([1, 2])) is np.dtype('int') + assert np.result_type(np.array([1, 2]), 1, 1+2j) is np.dtype('complex128') + assert np.result_type(np.array([1, 2]), 1, 'float64') is np.dtype('float64') + assert np.result_type(np.array([1, 2]), 1, None) is np.dtype('float64') diff --git a/pypy/module/micronumpy/test/test_compile.py b/pypy/module/micronumpy/test/test_compile.py --- a/pypy/module/micronumpy/test/test_compile.py +++ b/pypy/module/micronumpy/test/test_compile.py @@ -330,3 +330,12 @@ results = interp.results[0] assert isinstance(results, W_NDimArray) assert results.get_dtype().is_int() + + def test_searchsorted(self): + interp = self.run(''' + a = [1, 4, 5, 6, 9] + b = |30| -> ::-1 + c = searchsorted(a, b) + c -> -1 + ''') + assert interp.results[0].value == 0 diff --git a/pypy/module/micronumpy/test/test_iterators.py b/pypy/module/micronumpy/test/test_iterators.py --- a/pypy/module/micronumpy/test/test_iterators.py +++ b/pypy/module/micronumpy/test/test_iterators.py @@ -3,7 +3,15 @@ class MockArray(object): - start = 0 + flags = 0 + + class dtype: + elsize = 1 + + def __init__(self, shape, strides, start=0): + self.shape = shape + self.strides = strides + self.start = start class TestIterDirect(object): @@ -14,19 +22,24 @@ strides = [5, 1] backstrides = [x * (y - 1) for x,y in zip(strides, shape)] assert backstrides == [10, 4] - i = ArrayIter(MockArray, support.product(shape), shape, + i = ArrayIter(MockArray(shape, strides), support.product(shape), shape, strides, backstrides) + assert i.contiguous s = i.reset() s = i.next(s) s = i.next(s) s = i.next(s) assert s.offset == 3 assert not i.done(s) + assert s.indices == [0,0] + s = i.update(s) assert s.indices == [0,3] #cause a dimension overflow s = i.next(s) s = i.next(s) assert s.offset == 5 + assert s.indices == [0,3] + s = i.update(s) assert s.indices == [1,0] #Now what happens if the array is transposed? strides[-1] != 1 @@ -34,8 +47,9 @@ strides = [1, 3] backstrides = [x * (y - 1) for x,y in zip(strides, shape)] assert backstrides == [2, 12] - i = ArrayIter(MockArray, support.product(shape), shape, + i = ArrayIter(MockArray(shape, strides), support.product(shape), shape, strides, backstrides) + assert not i.contiguous s = i.reset() s = i.next(s) s = i.next(s) @@ -49,52 +63,20 @@ assert s.offset == 1 assert s.indices == [1,0] - def test_iterator_step(self): - #iteration in C order with #contiguous layout => strides[-1] is 1 - #skip less than the shape + def test_iterator_goto(self): shape = [3, 5] - strides = [5, 1] - backstrides = [x * (y - 1) for x,y in zip(strides, shape)] - assert backstrides == [10, 4] - i = ArrayIter(MockArray, support.product(shape), shape, - strides, backstrides) - s = i.reset() - s = i.next_skip_x(s, 2) - s = i.next_skip_x(s, 2) - s = i.next_skip_x(s, 2) - assert s.offset == 6 - assert not i.done(s) - assert s.indices == [1,1] - #And for some big skips - s = i.next_skip_x(s, 5) - assert s.offset == 11 - assert s.indices == [2,1] - s = i.next_skip_x(s, 5) - # Note: the offset does not overflow but recycles, - # this is good for broadcast - assert s.offset == 1 - assert s.indices == [0,1] - assert i.done(s) - - #Now what happens if the array is transposed? strides[-1] != 1 - # therefore layout is non-contiguous strides = [1, 3] backstrides = [x * (y - 1) for x,y in zip(strides, shape)] assert backstrides == [2, 12] - i = ArrayIter(MockArray, support.product(shape), shape, + a = MockArray(shape, strides, 42) + i = ArrayIter(a, support.product(shape), shape, strides, backstrides) + assert not i.contiguous s = i.reset() - s = i.next_skip_x(s, 2) - s = i.next_skip_x(s, 2) - s = i.next_skip_x(s, 2) - assert s.offset == 4 - assert s.indices == [1,1] - assert not i.done(s) - s = i.next_skip_x(s, 5) - assert s.offset == 5 - assert s.indices == [2,1] - assert not i.done(s) - s = i.next_skip_x(s, 5) - assert s.indices == [0,1] - assert s.offset == 3 - assert i.done(s) + assert s.index == 0 + assert s.indices == [0, 0] + assert s.offset == a.start + s = i.goto(11) + assert s.index == 11 + assert s.indices is None + assert s.offset == a.start + 5 diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -384,6 +384,19 @@ assert zeros((), dtype='S').shape == () assert zeros((), dtype='S').dtype == '|S1' + def test_check_shape(self): + import numpy as np + for func in [np.zeros, np.empty]: + exc = raises(ValueError, func, [0, -1, 1], 'int8') + assert str(exc.value) == "negative dimensions are not allowed" + exc = raises(ValueError, func, [2, -1, 3], 'int8') + assert str(exc.value) == "negative dimensions are not allowed" + + exc = raises(ValueError, func, [975]*7, 'int8') + assert str(exc.value) == "array is too big." + exc = raises(ValueError, func, [26244]*5, 'int8') + assert str(exc.value) == "array is too big." + def test_empty_like(self): import numpy as np a = np.empty_like(np.zeros(())) @@ -2020,6 +2033,14 @@ def test_swapaxes(self): from numpypy import array + x = array([]) + assert x.swapaxes(0, 2) is x + x = array([[1, 2]]) + assert x.swapaxes(0, 0) is x + exc = raises(ValueError, x.swapaxes, -3, 0) + assert exc.value.message == "bad axis1 argument to swapaxes" + exc = raises(ValueError, x.swapaxes, 0, 3) + assert exc.value.message == "bad axis2 argument to swapaxes" # testcases from numpy docstring x = array([[1, 2, 3]]) assert (x.swapaxes(0, 1) == array([[1], [2], [3]])).all() @@ -2706,15 +2727,35 @@ b.next() assert b.index == 3 assert b.coords == (0, 3) + b.next() assert b[3] == 3 - assert (b[::3] == [0, 3, 6, 9]).all() - assert (b[2::5] == [2, 7]).all() - assert b[-2] == 8 - raises(IndexError, "b[11]") - raises(IndexError, "b[-11]") - raises(IndexError, 'b[0, 1]') assert b.index == 0 assert b.coords == (0, 0) + b.next() + assert (b[::3] == [0, 3, 6, 9]).all() + assert b.index == 0 + assert b.coords == (0, 0) + b.next() + assert (b[2::5] == [2, 7]).all() + assert b.index == 0 + assert b.coords == (0, 0) + b.next() + assert b[-2] == 8 + assert b.index == 0 + assert b.coords == (0, 0) + b.next() + raises(IndexError, "b[11]") + assert b.index == 0 + assert b.coords == (0, 0) + b.next() + raises(IndexError, "b[-11]") + assert b.index == 0 + assert b.coords == (0, 0) + b.next() + exc = raises(IndexError, 'b[0, 1]') + assert str(exc.value) == "unsupported iterator index" + assert b.index == 1 + assert b.coords == (0, 1) def test_flatiter_setitem(self): from numpypy import arange, array @@ -2722,9 +2763,25 @@ b = a.T.flat b[6::2] = [-1, -2] assert (a == [[0, 1, -1, 3], [4, 5, 6, -1], [8, 9, -2, 11]]).all() + assert b[2] == 8 + assert b.index == 0 + b.next() + b[6::2] = [-21, -42] + assert (a == [[0, 1, -21, 3], [4, 5, 6, -21], [8, 9, -42, 11]]).all() b[0:2] = [[[100]]] assert(a[0,0] == 100) assert(a[1,0] == 100) + b.next() + assert b.index == 1 + exc = raises(ValueError, "b[0] = [1, 2]") + assert str(exc.value) == "Error setting single item of array." + assert b.index == 0 + b.next() + raises(IndexError, "b[100] = 42") + assert b.index == 1 + exc = raises(IndexError, "b[0, 1] = 42") + assert str(exc.value) == "unsupported iterator index" + assert b.index == 1 def test_flatiter_ops(self): from numpypy import arange, array @@ -3348,6 +3405,7 @@ '@\x01\x99\x99\x99\x99\x99\x9a\xbf\xf1\x99\x99\x99\x99\x99\x9a' assert array(2.2-1.1j, dtype='<c16').tostring() == \ '\x9a\x99\x99\x99\x99\x99\x01@\x9a\x99\x99\x99\x99\x99\xf1\xbf' + assert a.tostring == a.tobytes def test_str(self): from numpypy import array diff --git a/pypy/module/micronumpy/test/test_sorting.py b/pypy/module/micronumpy/test/test_selection.py rename from pypy/module/micronumpy/test/test_sorting.py rename to pypy/module/micronumpy/test/test_selection.py --- a/pypy/module/micronumpy/test/test_sorting.py +++ b/pypy/module/micronumpy/test/test_selection.py @@ -352,21 +352,49 @@ def test_searchsort(self): import numpy as np - import sys + + a = np.array(2) + raises(ValueError, a.searchsorted, 3) + a = np.arange(1, 6) + ret = a.searchsorted(3) assert ret == 2 assert isinstance(ret, np.generic) + ret = a.searchsorted(np.array(3)) assert ret == 2 assert isinstance(ret, np.generic) + + ret = a.searchsorted(np.array([])) + assert isinstance(ret, np.ndarray) + assert ret.shape == (0,) + ret = a.searchsorted(np.array([3])) assert ret == 2 assert isinstance(ret, np.ndarray) + + ret = a.searchsorted(np.array([[2, 3]])) + assert (ret == [1, 2]).all() + assert ret.shape == (1, 2) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit