Author: Amaury Forgeot d'Arc <amaur...@gmail.com> Branch: more-rposix Changeset: r77004:bbd40bb55e9d Date: 2015-04-23 23:28 +0200 http://bitbucket.org/pypy/pypy/changeset/bbd40bb55e9d/
Log: hg merge default diff too long, truncating to 2000 out of 6881 lines diff --git a/.tddium.requirements.txt b/.tddium.requirements.txt deleted file mode 100644 --- a/.tddium.requirements.txt +++ /dev/null @@ -1,1 +0,0 @@ -pytest diff --git a/lib-python/2.7/test/test_urllib2net.py b/lib-python/2.7/test/test_urllib2net.py --- a/lib-python/2.7/test/test_urllib2net.py +++ b/lib-python/2.7/test/test_urllib2net.py @@ -102,11 +102,8 @@ def test_ftp(self): urls = [ - 'ftp://ftp.kernel.org/pub/linux/kernel/README', - 'ftp://ftp.kernel.org/pub/linux/kernel/non-existent-file', - #'ftp://ftp.kernel.org/pub/leenox/kernel/test', - 'ftp://gatekeeper.research.compaq.com/pub/DEC/SRC' - '/research-reports/00README-Legal-Rules-Regs', + 'ftp://ftp.debian.org/debian/README', + 'ftp://ftp.debian.org/debian/non-existent-file', ] self._test_urls(urls, self._extra_handlers()) @@ -255,6 +252,7 @@ with test_support.transient_internet(url, timeout=None): u = _urlopen_with_retry(url) self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) + u.close() def test_http_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) @@ -266,6 +264,7 @@ finally: socket.setdefaulttimeout(None) self.assertEqual(u.fp._sock.fp._sock.gettimeout(), 60) + u.close() def test_http_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) @@ -277,20 +276,23 @@ finally: socket.setdefaulttimeout(None) self.assertIsNone(u.fp._sock.fp._sock.gettimeout()) + u.close() def test_http_timeout(self): url = "http://www.example.com" with test_support.transient_internet(url): u = _urlopen_with_retry(url, timeout=120) self.assertEqual(u.fp._sock.fp._sock.gettimeout(), 120) + u.close() - FTP_HOST = "ftp://ftp.mirror.nl/pub/gnu/" + FTP_HOST = 'ftp://ftp.debian.org/debian/' def test_ftp_basic(self): self.assertIsNone(socket.getdefaulttimeout()) with test_support.transient_internet(self.FTP_HOST, timeout=None): u = _urlopen_with_retry(self.FTP_HOST) self.assertIsNone(u.fp.fp._sock.gettimeout()) + u.close() def test_ftp_default_timeout(self): self.assertIsNone(socket.getdefaulttimeout()) @@ -301,6 +303,7 @@ finally: socket.setdefaulttimeout(None) self.assertEqual(u.fp.fp._sock.gettimeout(), 60) + u.close() def test_ftp_no_timeout(self): self.assertIsNone(socket.getdefaulttimeout(),) @@ -311,11 +314,16 @@ finally: socket.setdefaulttimeout(None) self.assertIsNone(u.fp.fp._sock.gettimeout()) + u.close() def test_ftp_timeout(self): with test_support.transient_internet(self.FTP_HOST): - u = _urlopen_with_retry(self.FTP_HOST, timeout=60) + try: + u = _urlopen_with_retry(self.FTP_HOST, timeout=60) + except: + raise self.assertEqual(u.fp.fp._sock.gettimeout(), 60) + u.close() def test_main(): diff --git a/lib_pypy/_tkinter/tklib.py b/lib_pypy/_tkinter/tklib.py --- a/lib_pypy/_tkinter/tklib.py +++ b/lib_pypy/_tkinter/tklib.py @@ -1,7 +1,7 @@ # C bindings with libtcl and libtk. from cffi import FFI -import sys +import sys, os tkffi = FFI() @@ -135,9 +135,12 @@ linklibs = ['tcl', 'tk'] libdirs = [] else: - incdirs=['/usr/include/tcl'] - linklibs=['tcl', 'tk'] - libdirs = [] + for _ver in ['', '8.6', '8.5', '']: + incdirs = ['/usr/include/tcl' + _ver] + linklibs = ['tcl' + _ver, 'tk' + _ver] + libdirs = [] + if os.path.isdir(incdirs[0]): + break tklib = tkffi.verify(""" #include <tcl.h> diff --git a/lib_pypy/pyrepl/simple_interact.py b/lib_pypy/pyrepl/simple_interact.py --- a/lib_pypy/pyrepl/simple_interact.py +++ b/lib_pypy/pyrepl/simple_interact.py @@ -33,6 +33,16 @@ return False return True +def _strip_final_indent(text): + # kill spaces and tabs at the end, but only if they follow '\n'. + # meant to remove the auto-indentation only (although it would of + # course also remove explicitly-added indentation). + short = text.rstrip(' \t') + n = len(short) + if n > 0 and text[n-1] == '\n': + return short + return text + def run_multiline_interactive_console(mainmodule=None): import code if mainmodule is None: @@ -41,7 +51,7 @@ def more_lines(unicodetext): # ooh, look at the hack: - src = "#coding:utf-8\n"+unicodetext.encode('utf-8') + src = "#coding:utf-8\n"+_strip_final_indent(unicodetext).encode('utf-8') try: code = console.compile(src, '<stdin>', 'single') except (OverflowError, SyntaxError, ValueError): @@ -58,7 +68,7 @@ returns_unicode=True) except EOFError: break - more = console.push(statement) + more = console.push(_strip_final_indent(statement)) assert not more except KeyboardInterrupt: console.write("\nKeyboardInterrupt\n") diff --git a/pypy/doc/getting-started-dev.rst b/pypy/doc/getting-started-dev.rst --- a/pypy/doc/getting-started-dev.rst +++ b/pypy/doc/getting-started-dev.rst @@ -207,12 +207,17 @@ large amount of options that can be used to customize pyinteractive.py). As an example of using PyPy from the command line, you could type:: - python pyinteractive.py -c "from test import pystone; pystone.main(10)" + python pyinteractive.py --withmod-time -c "from test import pystone; pystone.main(10)" Alternatively, as with regular Python, you can simply give a script name on the command line:: - python pyinteractive.py ../../lib-python/2.7/test/pystone.py 10 + python pyinteractive.py --withmod-time ../../lib-python/2.7/test/pystone.py 10 + +The ``--withmod-xxx`` option enables the built-in module ``xxx``. By +default almost none of them are, because initializing them takes time. +If you want anyway to enable all built-in modules, you can use +``--allworkingmodules``. See our :doc:`configuration sections <config/index>` for details about what all the commandline options do. 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 @@ -5,9 +5,65 @@ .. this is a revision shortly after release-2.5.1 .. startrev: cb01edcb59414d9d93056e54ed060673d24e67c1 +issue2005: +ignore errors on closing random file handles while importing a module (cpython compatibility) + +issue2013: +added constants to _ssl for TLS 1.1 and 1.2 + +issue2014: +Add PyLong_FromUnicode to cpyext. + +issue2017: +On non-Linux-x86 platforms, reduced the memory impact of +creating a lot of greenlets/tasklets. Particularly useful on Win32 and +on ARM, where you used to get a MemoryError after only 2500-5000 +greenlets (the 32-bit address space is exhausted). + +Update gdb_pypy for python3 (gdb comatability) + +Merged rstrategies into rpython which provides a library for Storage Strategies + +Support unicode strings in numpy.dtype creation i.e. np.dtype(u'int64') + +Various rpython cleanups for vmprof support + +issue2019: +Fix isspace as called by rpython unicode.strip() + +issue2023: +In the cpyext 'Concrete Object Layer' API, +don't call methods on the object (which can be overriden), +but directly on the concrete base type. + +issue2029: +Hide the default_factory attribute in a dict + +issue2027: +Better document pyinteractive and add --withmod-time + .. branch: gc-incminimark-pinning-improve + +branch gc-incminimark-pinning-improve: Object Pinning is now used in `bz2` and `rzlib` (therefore also affects Python's `zlib`). In case the data to compress/decompress is inside the nursery (incminimark) it no longer needs to create a non-moving copy of it. This saves one `malloc` and copying the data. Additionally a new GC environment variable is introduced (`PYPY_GC_MAX_PINNED`) primarily for debugging purposes. + +.. branch: refactor-pycall + +branch refactor-pycall: +Make `*`-unpacking in RPython function calls completely equivalent to passing +the tuple's elements as arguments. In other words, `f(*(a, b))` now behaves +exactly like `f(a, b)`. + +.. branch: issue2018 +branch issue2018: +Allow prebuilt rpython dict with function values + +.. branch: vmprof +.. Merged but then backed out, hopefully it will return as vmprof2 + +.. branch: object-dtype2 +Extend numpy dtypes to allow using objects with associated garbage collection hook diff --git a/pypy/goal/targetnumpystandalone.py b/pypy/goal/targetnumpystandalone.py deleted file mode 100644 --- a/pypy/goal/targetnumpystandalone.py +++ /dev/null @@ -1,43 +0,0 @@ - -""" Usage: - -./targetnumpystandalone-c <bytecode> array_size - -Will execute a give numpy bytecode. Arrays will be ranges (in float) modulo 10, -constants would be consecutive starting from one. - -Bytecode should contain letters 'a' 'l' and 'f' so far and be correct -""" - -import time -from pypy.module.micronumpy.compile import numpy_compile -from rpython.jit.codewriter.policy import JitPolicy -from rpython.rtyper.annlowlevel import hlstr - -def entry_point(argv): - if len(argv) != 3: - print __doc__ - return 1 - try: - size = int(argv[2]) - except ValueError: - print "INVALID LITERAL FOR INT:", argv[2] - print __doc__ - return 3 - t0 = time.time() - main(argv[0], size) - print "bytecode:", argv[0], "size:", size - print "took:", time.time() - t0 - return 0 - -def main(bc, size): - if not isinstance(bc, str): - bc = hlstr(bc) # for tests - a = numpy_compile(bc, size) - a = a.compute() - -def target(*args): - return entry_point, None - -def jitpolicy(driver): - return JitPolicy() diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py --- a/pypy/interpreter/astcompiler/assemble.py +++ b/pypy/interpreter/astcompiler/assemble.py @@ -1,5 +1,6 @@ """Python control flow graph generation and bytecode assembly.""" +import os from rpython.rlib import rfloat from rpython.rlib.objectmodel import we_are_translated @@ -9,6 +10,10 @@ from pypy.tool import stdlib_opcode as ops +class StackDepthComputationError(Exception): + pass + + class Instruction(object): """Represents a single opcode.""" @@ -55,11 +60,13 @@ reaches the end of the block, it continues to next_block. """ + marked = False + have_return = False + auto_inserted_return = False + def __init__(self): self.instructions = [] self.next_block = None - self.marked = False - self.have_return = False def _post_order_see(self, stack, nextblock): if nextblock.marked == 0: @@ -384,7 +391,11 @@ # look into a block when all the previous blocks have been done. self._max_depth = 0 for block in blocks: - self._do_stack_depth_walk(block) + depth = self._do_stack_depth_walk(block) + if block.auto_inserted_return and depth != 0: + os.write(2, "StackDepthComputationError in %s at %s:%s\n" % ( + self.compile_info.filename, self.name, self.first_lineno)) + raise StackDepthComputationError # fatal error return self._max_depth def _next_stack_depth_walk(self, nextblock, depth): @@ -393,20 +404,21 @@ def _do_stack_depth_walk(self, block): depth = block.initial_depth - done = False for instr in block.instructions: depth += _opcode_stack_effect(instr.opcode, instr.arg) if depth >= self._max_depth: self._max_depth = depth + jump_op = instr.opcode if instr.has_jump: target_depth = depth - jump_op = instr.opcode if jump_op == ops.FOR_ITER: target_depth -= 2 elif (jump_op == ops.SETUP_FINALLY or jump_op == ops.SETUP_EXCEPT or jump_op == ops.SETUP_WITH): - target_depth += 3 + if jump_op == ops.SETUP_WITH: + target_depth -= 1 # ignore the w_result just pushed + target_depth += 3 # add [exc_type, exc, unroller] if target_depth > self._max_depth: self._max_depth = target_depth elif (jump_op == ops.JUMP_IF_TRUE_OR_POP or @@ -415,10 +427,14 @@ self._next_stack_depth_walk(instr.jump[0], target_depth) if jump_op == ops.JUMP_ABSOLUTE or jump_op == ops.JUMP_FORWARD: # Nothing more can occur. - done = True break - if block.next_block and not done: - self._next_stack_depth_walk(block.next_block, depth) + elif jump_op == ops.RETURN_VALUE or jump_op == ops.RAISE_VARARGS: + # Nothing more can occur. + break + else: + if block.next_block: + self._next_stack_depth_walk(block.next_block, depth) + return depth def _build_lnotab(self, blocks): """Build the line number table for tracebacks and tracing.""" @@ -471,6 +487,7 @@ if self.add_none_to_final_return: self.load_const(self.space.w_None) self.emit_op(ops.RETURN_VALUE) + self.current_block.auto_inserted_return = True # Set the first lineno if it is not already explicitly set. if self.first_lineno == -1: if self.first_block.instructions: @@ -563,10 +580,10 @@ ops.INPLACE_OR: -1, ops.INPLACE_XOR: -1, - ops.SLICE+0: 1, - ops.SLICE+1: 0, - ops.SLICE+2: 0, - ops.SLICE+3: -1, + ops.SLICE+0: 0, + ops.SLICE+1: -1, + ops.SLICE+2: -1, + ops.SLICE+3: -2, ops.STORE_SLICE+0: -2, ops.STORE_SLICE+1: -3, ops.STORE_SLICE+2: -3, @@ -576,7 +593,7 @@ ops.DELETE_SLICE+2: -2, ops.DELETE_SLICE+3: -3, - ops.STORE_SUBSCR: -2, + ops.STORE_SUBSCR: -3, ops.DELETE_SUBSCR: -2, ops.GET_ITER: 0, @@ -593,7 +610,9 @@ ops.WITH_CLEANUP: -1, ops.POP_BLOCK: 0, - ops.END_FINALLY: -1, + ops.END_FINALLY: -3, # assume always 3: we pretend that SETUP_FINALLY + # pushes 3. In truth, it would only push 1 and + # the corresponding END_FINALLY only pops 1. ops.SETUP_WITH: 1, ops.SETUP_FINALLY: 0, ops.SETUP_EXCEPT: 0, @@ -604,7 +623,6 @@ ops.YIELD_VALUE: 0, ops.BUILD_CLASS: -2, ops.BUILD_MAP: 1, - ops.BUILD_SET: 1, ops.COMPARE_OP: -1, ops.LOOKUP_METHOD: 1, @@ -659,6 +677,9 @@ def _compute_BUILD_LIST(arg): return 1 - arg +def _compute_BUILD_SET(arg): + return 1 - arg + def _compute_MAKE_CLOSURE(arg): return -arg - 1 diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -772,6 +772,60 @@ code = compile_with_astcompiler(source, 'exec', self.space) assert code.co_stacksize == 2 + def test_stackeffect_bug3(self): + source = """if 1: + try: pass + finally: pass + try: pass + finally: pass + try: pass + finally: pass + try: pass + finally: pass + try: pass + finally: pass + try: pass + finally: pass + """ + code = compile_with_astcompiler(source, 'exec', self.space) + assert code.co_stacksize == 3 + + def test_stackeffect_bug4(self): + source = """if 1: + with a: pass + with a: pass + with a: pass + with a: pass + with a: pass + with a: pass + """ + code = compile_with_astcompiler(source, 'exec', self.space) + assert code.co_stacksize == 4 + + def test_stackeffect_bug5(self): + source = """if 1: + a[:]; a[:]; a[:]; a[:]; a[:]; a[:] + a[1:]; a[1:]; a[1:]; a[1:]; a[1:]; a[1:] + a[:2]; a[:2]; a[:2]; a[:2]; a[:2]; a[:2] + a[1:2]; a[1:2]; a[1:2]; a[1:2]; a[1:2]; a[1:2] + """ + code = compile_with_astcompiler(source, 'exec', self.space) + assert code.co_stacksize == 3 + + def test_stackeffect_bug6(self): + source = """if 1: + {1}; {1}; {1}; {1}; {1}; {1}; {1} + """ + code = compile_with_astcompiler(source, 'exec', self.space) + assert code.co_stacksize == 1 + + def test_stackeffect_bug7(self): + source = '''def f(): + for i in a: + return + ''' + code = compile_with_astcompiler(source, 'exec', self.space) + def test_lambda(self): yield self.st, "y = lambda x: x", "y(4)", 4 diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -374,14 +374,11 @@ return space.wrap(self.name) def fset_func_name(self, space, w_name): - try: + if space.isinstance_w(w_name, space.w_str): self.name = space.str_w(w_name) - except OperationError, e: - if e.match(space, space.w_TypeError): - raise OperationError(space.w_TypeError, - space.wrap("func_name must be set " - "to a string object")) - raise + else: + raise OperationError(space.w_TypeError, + space.wrap("__name__ must be set to a string object")) def fdel_func_doc(self, space): self.w_doc = space.w_None diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py --- a/pypy/interpreter/test/test_function.py +++ b/pypy/interpreter/test/test_function.py @@ -107,6 +107,12 @@ __name__ = "bar" assert f.__module__ == "foo"''' in {} + def test_set_name(self): + def f(): pass + f.__name__ = 'g' + assert f.func_name == 'g' + raises(TypeError, "f.__name__ = u'g'") + class AppTestFunction: def test_simple_call(self): diff --git a/pypy/module/_collections/app_defaultdict.py b/pypy/module/_collections/app_defaultdict.py --- a/pypy/module/_collections/app_defaultdict.py +++ b/pypy/module/_collections/app_defaultdict.py @@ -11,6 +11,7 @@ class defaultdict(dict): + __slots__ = ['default_factory'] def __init__(self, *args, **kwds): if len(args) > 0: @@ -20,7 +21,7 @@ raise TypeError("first argument must be callable") else: default_factory = None - self.default_factory = default_factory + defaultdict.default_factory.__set__(self, default_factory) super(defaultdict, self).__init__(*args, **kwds) def __missing__(self, key): @@ -33,15 +34,15 @@ return "defaultdict(...)" try: recurse.add(id(self)) - return "defaultdict(%s, %s)" % (repr(self.default_factory), super(defaultdict, self).__repr__()) + return "defaultdict(%s, %s)" % (self.default_factory, + super(defaultdict, self).__repr__()) finally: recurse.remove(id(self)) def copy(self): return type(self)(self.default_factory, self) - def __copy__(self): - return self.copy() + __copy__ = copy def __reduce__(self): """ @@ -55,4 +56,5 @@ This API is used by pickle.py and copy.py. """ - return (type(self), (self.default_factory,), None, None, self.iteritems()) + return (type(self), (self.default_factory,), None, None, + defaultdict.iteritems(self)) diff --git a/pypy/module/_collections/test/test_defaultdict.py b/pypy/module/_collections/test/test_defaultdict.py --- a/pypy/module/_collections/test/test_defaultdict.py +++ b/pypy/module/_collections/test/test_defaultdict.py @@ -54,3 +54,25 @@ assert len(d2) == 1 assert d2[2] == 3 assert d2[3] == 42 + + def test_no_dict(self): + import _collections + assert not hasattr(_collections.defaultdict(), '__dict__') + + def test_no_setattr(self): + import _collections + class D(_collections.defaultdict): + def __setattr__(self, attr, name): + raise AssertionError + d = D(int) + assert d['5'] == 0 + d['6'] += 3 + assert d['6'] == 3 + + def test_default_factory(self): + import _collections + f = lambda: 42 + d = _collections.defaultdict(f) + assert d.default_factory is f + d.default_factory = lambda: 43 + assert d['5'] == 43 diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -91,39 +91,39 @@ @cpython_api([PyObject], lltype.Void) def PyDict_Clear(space, w_obj): """Empty an existing dictionary of all key-value pairs.""" - space.call_method(w_obj, "clear") + space.call_method(space.w_dict, "clear", w_obj) @cpython_api([PyObject], PyObject) def PyDict_Copy(space, w_obj): """Return a new dictionary that contains the same key-value pairs as p. """ - return space.call_method(w_obj, "copy") + return space.call_method(space.w_dict, "copy", w_obj) @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_Update(space, w_obj, w_other): """This is the same as PyDict_Merge(a, b, 1) in C, or a.update(b) in Python. Return 0 on success or -1 if an exception was raised. """ - space.call_method(w_obj, "update", w_other) + space.call_method(space.w_dict, "update", w_obj, w_other) return 0 @cpython_api([PyObject], PyObject) def PyDict_Keys(space, w_obj): """Return a PyListObject containing all the keys from the dictionary, as in the dictionary method dict.keys().""" - return space.call_method(w_obj, "keys") + return space.call_method(space.w_dict, "keys", w_obj) @cpython_api([PyObject], PyObject) def PyDict_Values(space, w_obj): """Return a PyListObject containing all the values from the dictionary p, as in the dictionary method dict.values().""" - return space.call_method(w_obj, "values") + return space.call_method(space.w_dict, "values", w_obj) @cpython_api([PyObject], PyObject) def PyDict_Items(space, w_obj): """Return a PyListObject containing all the items from the dictionary, as in the dictionary method dict.items().""" - return space.call_method(w_obj, "items") + return space.call_method(space.w_dict, "items", w_obj) @cpython_api([PyObject, Py_ssize_tP, PyObjectP, PyObjectP], rffi.INT_real, error=CANNOT_FAIL) def PyDict_Next(space, w_dict, ppos, pkey, pvalue): @@ -175,7 +175,7 @@ # not complete. try: - w_iter = space.call_method(w_dict, "iteritems") + w_iter = space.call_method(space.w_dict, "iteritems", w_dict) pos = ppos[0] while pos: space.call_method(w_iter, "next") diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -65,7 +65,7 @@ """Insert the item item into list list in front of index index. Return 0 if successful; return -1 and set an exception if unsuccessful. Analogous to list.insert(index, item).""" - space.call_method(w_list, "insert", space.wrap(index), w_item) + space.call_method(space.w_list, "insert", w_list, space.wrap(index), w_item) return 0 @cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL) @@ -98,7 +98,7 @@ failure. This is equivalent to list.sort().""" if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) - space.call_method(w_list, "sort") + space.call_method(space.w_list, "sort", w_list) return 0 @cpython_api([PyObject], rffi.INT_real, error=-1) @@ -107,7 +107,7 @@ failure. This is the equivalent of list.reverse().""" if not isinstance(w_list, W_ListObject): PyErr_BadInternalCall(space) - space.call_method(w_list, "reverse") + space.call_method(space.w_list, "reverse", w_list) return 0 @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject) diff --git a/pypy/module/cpyext/setobject.py b/pypy/module/cpyext/setobject.py --- a/pypy/module/cpyext/setobject.py +++ b/pypy/module/cpyext/setobject.py @@ -36,7 +36,7 @@ values of brand new frozensets before they are exposed to other code.""" if not PySet_Check(space, w_s): PyErr_BadInternalCall(space) - space.call_method(w_s, 'add', w_obj) + space.call_method(space.w_set, 'add', w_s, w_obj) return 0 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) @@ -49,7 +49,7 @@ instance of set or its subtype.""" if not PySet_Check(space, w_s): PyErr_BadInternalCall(space) - space.call_method(w_s, 'discard', w_obj) + space.call_method(space.w_set, 'discard', w_s, w_obj) return 0 @@ -59,12 +59,12 @@ object from the set. Return NULL on failure. Raise KeyError if the set is empty. Raise a SystemError if set is an not an instance of set or its subtype.""" - return space.call_method(w_set, "pop") + return space.call_method(space.w_set, "pop", w_set) @cpython_api([PyObject], rffi.INT_real, error=-1) def PySet_Clear(space, w_set): """Empty an existing set of all elements.""" - space.call_method(w_set, 'clear') + space.call_method(space.w_set, 'clear', w_set) return 0 @cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL) 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 @@ -30,6 +30,9 @@ for c in ['MAXDIMS', 'CLIP', 'WRAP', 'RAISE']: interpleveldefs[c] = 'space.wrap(constants.%s)' % c + def startup(self, space): + from pypy.module.micronumpy.concrete import _setup + _setup() class UMathModule(MixedModule): appleveldefs = {} diff --git a/pypy/module/micronumpy/base.py b/pypy/module/micronumpy/base.py --- a/pypy/module/micronumpy/base.py +++ b/pypy/module/micronumpy/base.py @@ -34,11 +34,13 @@ @staticmethod def from_shape(space, shape, dtype, order='C', w_instance=None, zero=True): - from pypy.module.micronumpy import concrete + from pypy.module.micronumpy import concrete, descriptor, boxes from pypy.module.micronumpy.strides import calc_strides strides, backstrides = calc_strides(shape, dtype.base, order) impl = concrete.ConcreteArray(shape, dtype.base, order, strides, backstrides, zero=zero) + if dtype == descriptor.get_dtype_cache(space).w_objectdtype: + impl.fill(space, boxes.W_ObjectBox(space.w_None)) if w_instance: return wrap_impl(space, space.type(w_instance), w_instance, impl) return W_NDimArray(impl) @@ -123,7 +125,7 @@ def get_shape(self): return self.implementation.get_shape() - def get_dtype(self): + def get_dtype(self, space=None): return self.implementation.dtype def get_order(self): diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py --- a/pypy/module/micronumpy/boxes.py +++ b/pypy/module/micronumpy/boxes.py @@ -607,6 +607,19 @@ # arr.storage[i] = arg[i] return W_UnicodeBox(arr, 0, arr.dtype) +class W_ObjectBox(W_GenericBox): + descr__new__, _get_dtype, descr_reduce = new_dtype_getter(NPY.OBJECT) + + def __init__(self, w_obj): + self.w_obj = w_obj + + def convert_to(self, space, dtype): + if dtype.is_bool(): + return W_BoolBox(space.bool_w(self.w_obj)) + return self # XXX + + def descr__getattr__(self, space, w_key): + return space.getattr(self.w_obj, w_key) W_GenericBox.typedef = TypeDef("numpy.generic", __new__ = interp2app(W_GenericBox.descr__new__.im_func), @@ -856,3 +869,9 @@ __new__ = interp2app(W_UnicodeBox.descr__new__unicode_box.im_func), __len__ = interp2app(W_UnicodeBox.descr_len), ) + +W_ObjectBox.typedef = TypeDef("numpy.object_", W_ObjectBox.typedef, + __new__ = interp2app(W_ObjectBox.descr__new__.im_func), + __getattr__ = interp2app(W_ObjectBox.descr__getattr__), +) + 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 @@ -3,7 +3,7 @@ """ import re from pypy.interpreter import special -from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root +from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root, ObjSpace from pypy.interpreter.error import OperationError from rpython.rlib.objectmodel import specialize, instantiate from rpython.rlib.nonconst import NonConstant @@ -47,7 +47,7 @@ def lookup(self, name): return self.getdictvalue(self, name) -class FakeSpace(object): +class FakeSpace(ObjSpace): w_ValueError = W_TypeObject("ValueError") w_TypeError = W_TypeObject("TypeError") w_IndexError = W_TypeObject("IndexError") @@ -67,6 +67,7 @@ w_unicode = W_TypeObject("unicode") w_complex = W_TypeObject("complex") w_dict = W_TypeObject("dict") + w_object = W_TypeObject("object") def __init__(self): """NOT_RPYTHON""" @@ -88,7 +89,8 @@ return self.wrap(len(w_obj.items)) def getattr(self, w_obj, w_attr): - return StringObject(NonConstant('foo')) + assert isinstance(w_attr, StringObject) + return w_obj.getdictvalue(self, w_attr.v) def isinstance_w(self, w_obj, w_tp): try: 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 @@ -1,11 +1,11 @@ from pypy.interpreter.error import OperationError, oefmt -from rpython.rlib import jit +from rpython.rlib import jit, rgc from rpython.rlib.buffer import Buffer -from rpython.rlib.debug import make_sure_not_resized +from rpython.rlib.debug import make_sure_not_resized, debug_print from rpython.rlib.rawstorage import alloc_raw_storage, free_raw_storage, \ raw_storage_getitem, raw_storage_setitem, RAW_STORAGE -from rpython.rtyper.lltypesystem import rffi, lltype -from pypy.module.micronumpy import support, loop +from rpython.rtyper.lltypesystem import rffi, lltype, llmemory +from pypy.module.micronumpy import support, loop, constants as NPY from pypy.module.micronumpy.base import convert_to_array, W_NDimArray, \ ArrayArgumentException from pypy.module.micronumpy.iterators import ArrayIter @@ -13,11 +13,13 @@ RecordChunk, calc_strides, calc_new_strides, shape_agreement, calculate_broadcast_strides, calc_backstrides) from rpython.rlib.objectmodel import keepalive_until_here +from rpython.rtyper.annlowlevel import cast_gcref_to_instance +from pypy.interpreter.baseobjspace import W_Root class BaseConcreteArray(object): _immutable_fields_ = ['dtype?', 'storage', 'start', 'size', 'shape[*]', - 'strides[*]', 'backstrides[*]', 'order'] + 'strides[*]', 'backstrides[*]', 'order', 'gcstruct'] start = 0 parent = None flags = 0 @@ -326,13 +328,54 @@ return ArrayBuffer(self, readonly) def astype(self, space, dtype): - strides, backstrides = calc_strides(self.get_shape(), dtype, - self.order) + # we want to create a new array, but must respect the strides + # in self. So find a factor of the itemtype.elsize, and use this + factor = float(dtype.elsize) / self.dtype.elsize + strides = [int(factor*s) for s in self.get_strides()] + backstrides = [int(factor*s) for s in self.get_backstrides()] impl = ConcreteArray(self.get_shape(), dtype, self.order, strides, backstrides) loop.setslice(space, impl.get_shape(), impl, self) return impl +OBJECTSTORE = lltype.GcStruct('ObjectStore', + ('length', lltype.Signed), + ('step', lltype.Signed), + ('storage', llmemory.Address), + rtti=True) +offset_of_storage = llmemory.offsetof(OBJECTSTORE, 'storage') +offset_of_length = llmemory.offsetof(OBJECTSTORE, 'length') +offset_of_step = llmemory.offsetof(OBJECTSTORE, 'step') + +V_OBJECTSTORE = lltype.nullptr(OBJECTSTORE) + +def customtrace(gc, obj, callback, arg): + #debug_print('in customtrace w/obj', obj) + length = (obj + offset_of_length).signed[0] + step = (obj + offset_of_step).signed[0] + storage = (obj + offset_of_storage).address[0] + #debug_print('tracing', length, 'objects in ndarray.storage') + i = 0 + while i < length: + gc._trace_callback(callback, arg, storage) + storage += step + i += 1 + +lambda_customtrace = lambda: customtrace + +def _setup(): + rgc.register_custom_trace_hook(OBJECTSTORE, lambda_customtrace) + +@jit.dont_look_inside +def _create_objectstore(storage, length, elsize): + gcstruct = lltype.malloc(OBJECTSTORE) + # JIT does not support cast_ptr_to_adr + gcstruct.storage = llmemory.cast_ptr_to_adr(storage) + #print 'create gcstruct',gcstruct,'with storage',storage,'as',gcstruct.storage + gcstruct.length = length + gcstruct.step = elsize + return gcstruct + class ConcreteArrayNotOwning(BaseConcreteArray): def __init__(self, shape, dtype, order, strides, backstrides, storage, start=0): @@ -347,10 +390,11 @@ self.backstrides = backstrides self.storage = storage self.start = start + self.gcstruct = V_OBJECTSTORE def fill(self, space, box): self.dtype.itemtype.fill(self.storage, self.dtype.elsize, - box, 0, self.size, 0) + box, 0, self.size, 0, self.gcstruct) def set_shape(self, space, orig_array, new_shape): strides, backstrides = calc_strides(new_shape, self.dtype, @@ -374,17 +418,24 @@ def base(self): return None - class ConcreteArray(ConcreteArrayNotOwning): def __init__(self, shape, dtype, order, strides, backstrides, storage=lltype.nullptr(RAW_STORAGE), zero=True): + gcstruct = V_OBJECTSTORE if storage == lltype.nullptr(RAW_STORAGE): - storage = dtype.itemtype.malloc(support.product(shape) * - dtype.elsize, zero=zero) + length = support.product(shape) + if dtype.num == NPY.OBJECT: + storage = dtype.itemtype.malloc(length * dtype.elsize, zero=True) + gcstruct = _create_objectstore(storage, length, dtype.elsize) + else: + storage = dtype.itemtype.malloc(length * dtype.elsize, zero=zero) ConcreteArrayNotOwning.__init__(self, shape, dtype, order, strides, backstrides, storage) + self.gcstruct = gcstruct def __del__(self): + if self.gcstruct: + self.gcstruct.length = 0 free_raw_storage(self.storage, track_allocation=False) @@ -423,6 +474,7 @@ parent = parent.parent # one level only self.parent = parent self.storage = parent.storage + self.gcstruct = parent.gcstruct self.order = parent.order self.dtype = dtype self.size = support.product(shape) * self.dtype.elsize @@ -480,6 +532,7 @@ class VoidBoxStorage(BaseConcreteArray): def __init__(self, size, dtype): self.storage = alloc_raw_storage(size) + self.gcstruct = V_OBJECTSTORE self.dtype = dtype self.size = size 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 @@ -38,6 +38,34 @@ raise oefmt(space.w_ValueError, "object __array__ method not producing an array") +def try_interface_method(space, w_object): + try: + w_interface = space.getattr(w_object, space.wrap("__array_interface__")) + except OperationError, e: + if e.match(space, space.w_AttributeError): + return None + raise + if w_interface is None: + # happens from compile.py + return None + version = space.int_w(space.finditem(w_interface, space.wrap("version"))) + if version < 3: + raise oefmt(space.w_NotImplementedError, + "__array_interface__ version %d not supported", version) + # make a view into the data + w_shape = space.finditem(w_interface, space.wrap('shape')) + w_dtype = space.finditem(w_interface, space.wrap('typestr')) + w_descr = space.finditem(w_interface, space.wrap('descr')) + data_w = space.listview(space.finditem(w_interface, space.wrap('data'))) + w_strides = space.finditem(w_interface, space.wrap('strides')) + shape = [space.int_w(i) for i in space.listview(w_shape)] + dtype = descriptor.decode_w_dtype(space, w_dtype) + rw = space.is_true(data_w[1]) + #print 'create view from shape',shape,'dtype',dtype,'descr',w_descr,'data',data_w[0],'rw',rw + raise oefmt(space.w_NotImplementedError, + "creating array from __array_interface__ not supported yet") + return + @unwrap_spec(ndmin=int, copy=bool, subok=bool) def array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False, @@ -63,7 +91,11 @@ # continue with w_array, but do further operations in place w_object = w_array copy = False - + if not isinstance(w_object, W_NDimArray): + w_array = try_interface_method(space, w_object) + if w_array is not None: + w_object = w_array + copy = False dtype = descriptor.decode_w_dtype(space, w_dtype) if space.is_none(w_order): diff --git a/pypy/module/micronumpy/descriptor.py b/pypy/module/micronumpy/descriptor.py --- a/pypy/module/micronumpy/descriptor.py +++ b/pypy/module/micronumpy/descriptor.py @@ -6,7 +6,7 @@ from pypy.interpreter.typedef import (TypeDef, GetSetProperty, interp_attrproperty, interp_attrproperty_w) from rpython.rlib import jit -from rpython.rlib.objectmodel import specialize, compute_hash +from rpython.rlib.objectmodel import specialize, compute_hash, we_are_translated from rpython.rlib.rarithmetic import r_longlong, r_ulonglong from pypy.module.micronumpy import types, boxes, base, support, constants as NPY from pypy.module.micronumpy.appbridge import get_appbridge_cache @@ -56,7 +56,7 @@ self.char = char self.w_box_type = w_box_type if byteorder is None: - if itemtype.get_element_size() == 1: + if itemtype.get_element_size() == 1 or isinstance(itemtype, types.ObjectType): byteorder = NPY.IGNORE else: byteorder = NPY.NATIVE @@ -112,6 +112,9 @@ def is_str(self): return self.num == NPY.STRING + def is_object(self): + return self.num == NPY.OBJECT + def is_str_or_unicode(self): return self.num == NPY.STRING or self.num == NPY.UNICODE @@ -428,7 +431,7 @@ self.names.append(name) self.fields[name] = offset, dtype - self.itemtype = types.RecordType() + self.itemtype = types.RecordType(space) if self.is_flexible(): self.elsize = size @@ -443,7 +446,7 @@ endian = NPY.OPPBYTE if self.is_native() else NPY.NATBYTE elif newendian != NPY.IGNORE: endian = newendian - itemtype = self.itemtype.__class__(endian in (NPY.NATIVE, NPY.NATBYTE)) + itemtype = self.itemtype.__class__(space, endian in (NPY.NATIVE, NPY.NATBYTE)) fields = self.fields if fields is None: fields = {} @@ -482,7 +485,7 @@ fields[fldname] = (offset, subdtype) offset += subdtype.elsize names.append(fldname) - return W_Dtype(types.RecordType(), NPY.VOID, NPY.VOIDLTR, NPY.VOIDLTR, + return W_Dtype(types.RecordType(space), NPY.VOID, NPY.VOIDLTR, NPY.VOIDLTR, space.gettypefor(boxes.W_VoidBox), names=names, fields=fields, elsize=offset) @@ -493,8 +496,17 @@ def dtype_from_spec(space, w_spec): - w_lst = get_appbridge_cache(space).call_method(space, - 'numpy.core._internal', '_commastring', Arguments(space, [w_spec])) + + if we_are_translated(): + w_lst = get_appbridge_cache(space).call_method(space, + 'numpy.core._internal', '_commastring', Arguments(space, [w_spec])) + else: + # testing, handle manually + if space.eq_w(w_spec, space.wrap('u4,u4,u4')): + w_lst = space.newlist([space.wrap('u4')]*3) + else: + raise oefmt(space.w_RuntimeError, + "cannot parse w_spec") if not space.isinstance_w(w_lst, space.w_list) or space.len_w(w_lst) < 1: raise oefmt(space.w_RuntimeError, "_commastring is not returning a list with len >= 1") @@ -541,15 +553,17 @@ if size == 1: return subdtype size *= subdtype.elsize - return W_Dtype(types.VoidType(), NPY.VOID, NPY.VOIDLTR, NPY.VOIDLTR, + return W_Dtype(types.VoidType(space), NPY.VOID, NPY.VOIDLTR, NPY.VOIDLTR, space.gettypefor(boxes.W_VoidBox), shape=shape, subdtype=subdtype, elsize=size) if space.is_none(w_dtype): return cache.w_float64dtype - elif space.isinstance_w(w_dtype, w_subtype): + if space.isinstance_w(w_dtype, w_subtype): return w_dtype - elif space.isinstance_w(w_dtype, space.w_str): + if space.isinstance_w(w_dtype, space.w_unicode): + w_dtype = space.wrap(space.str_w(w_dtype)) # may raise if invalid + if space.isinstance_w(w_dtype, space.w_str): name = space.str_w(w_dtype) if _check_for_commastring(name): return dtype_from_spec(space, w_dtype) @@ -585,8 +599,7 @@ if w_dtype is dtype.w_box_type: return dtype if space.isinstance_w(w_dtype, space.w_type): - raise oefmt(space.w_NotImplementedError, - "cannot create dtype with type '%N'", w_dtype) + return cache.w_objectdtype raise oefmt(space.w_TypeError, "data type not understood") @@ -653,7 +666,7 @@ def new_string_dtype(space, size, char=NPY.STRINGLTR): return W_Dtype( - types.StringType(), + types.StringType(space), elsize=size, num=NPY.STRING, kind=NPY.STRINGLTR, @@ -663,7 +676,7 @@ def new_unicode_dtype(space, size): - itemtype = types.UnicodeType() + itemtype = types.UnicodeType(space) return W_Dtype( itemtype, elsize=size * itemtype.get_element_size(), @@ -676,7 +689,7 @@ def new_void_dtype(space, size): return W_Dtype( - types.VoidType(), + types.VoidType(space), elsize=size, num=NPY.VOID, kind=NPY.VOIDLTR, @@ -688,126 +701,126 @@ class DtypeCache(object): def __init__(self, space): self.w_booldtype = W_Dtype( - types.Bool(), + types.Bool(space), num=NPY.BOOL, kind=NPY.GENBOOLLTR, char=NPY.BOOLLTR, w_box_type=space.gettypefor(boxes.W_BoolBox), ) self.w_int8dtype = W_Dtype( - types.Int8(), + types.Int8(space), num=NPY.BYTE, kind=NPY.SIGNEDLTR, char=NPY.BYTELTR, w_box_type=space.gettypefor(boxes.W_Int8Box), ) self.w_uint8dtype = W_Dtype( - types.UInt8(), + types.UInt8(space), num=NPY.UBYTE, kind=NPY.UNSIGNEDLTR, char=NPY.UBYTELTR, w_box_type=space.gettypefor(boxes.W_UInt8Box), ) self.w_int16dtype = W_Dtype( - types.Int16(), + types.Int16(space), num=NPY.SHORT, kind=NPY.SIGNEDLTR, char=NPY.SHORTLTR, w_box_type=space.gettypefor(boxes.W_Int16Box), ) self.w_uint16dtype = W_Dtype( - types.UInt16(), + types.UInt16(space), num=NPY.USHORT, kind=NPY.UNSIGNEDLTR, char=NPY.USHORTLTR, w_box_type=space.gettypefor(boxes.W_UInt16Box), ) self.w_int32dtype = W_Dtype( - types.Int32(), + types.Int32(space), num=NPY.INT, kind=NPY.SIGNEDLTR, char=NPY.INTLTR, w_box_type=space.gettypefor(boxes.W_Int32Box), ) self.w_uint32dtype = W_Dtype( - types.UInt32(), + types.UInt32(space), num=NPY.UINT, kind=NPY.UNSIGNEDLTR, char=NPY.UINTLTR, w_box_type=space.gettypefor(boxes.W_UInt32Box), ) self.w_longdtype = W_Dtype( - types.Long(), + types.Long(space), num=NPY.LONG, kind=NPY.SIGNEDLTR, char=NPY.LONGLTR, w_box_type=space.gettypefor(boxes.W_LongBox), ) self.w_ulongdtype = W_Dtype( - types.ULong(), + types.ULong(space), num=NPY.ULONG, kind=NPY.UNSIGNEDLTR, char=NPY.ULONGLTR, w_box_type=space.gettypefor(boxes.W_ULongBox), ) self.w_int64dtype = W_Dtype( - types.Int64(), + types.Int64(space), num=NPY.LONGLONG, kind=NPY.SIGNEDLTR, char=NPY.LONGLONGLTR, w_box_type=space.gettypefor(boxes.W_Int64Box), ) self.w_uint64dtype = W_Dtype( - types.UInt64(), + types.UInt64(space), num=NPY.ULONGLONG, kind=NPY.UNSIGNEDLTR, char=NPY.ULONGLONGLTR, w_box_type=space.gettypefor(boxes.W_UInt64Box), ) self.w_float32dtype = W_Dtype( - types.Float32(), + types.Float32(space), num=NPY.FLOAT, kind=NPY.FLOATINGLTR, char=NPY.FLOATLTR, w_box_type=space.gettypefor(boxes.W_Float32Box), ) self.w_float64dtype = W_Dtype( - types.Float64(), + types.Float64(space), num=NPY.DOUBLE, kind=NPY.FLOATINGLTR, char=NPY.DOUBLELTR, w_box_type=space.gettypefor(boxes.W_Float64Box), ) self.w_floatlongdtype = W_Dtype( - types.FloatLong(), + types.FloatLong(space), num=NPY.LONGDOUBLE, kind=NPY.FLOATINGLTR, char=NPY.LONGDOUBLELTR, w_box_type=space.gettypefor(boxes.W_FloatLongBox), ) self.w_complex64dtype = W_Dtype( - types.Complex64(), + types.Complex64(space), num=NPY.CFLOAT, kind=NPY.COMPLEXLTR, char=NPY.CFLOATLTR, w_box_type=space.gettypefor(boxes.W_Complex64Box), ) self.w_complex128dtype = W_Dtype( - types.Complex128(), + types.Complex128(space), num=NPY.CDOUBLE, kind=NPY.COMPLEXLTR, char=NPY.CDOUBLELTR, w_box_type=space.gettypefor(boxes.W_Complex128Box), ) self.w_complexlongdtype = W_Dtype( - types.ComplexLong(), + types.ComplexLong(space), num=NPY.CLONGDOUBLE, kind=NPY.COMPLEXLTR, char=NPY.CLONGDOUBLELTR, w_box_type=space.gettypefor(boxes.W_ComplexLongBox), ) self.w_stringdtype = W_Dtype( - types.StringType(), + types.StringType(space), elsize=0, num=NPY.STRING, kind=NPY.STRINGLTR, @@ -815,7 +828,7 @@ w_box_type=space.gettypefor(boxes.W_StringBox), ) self.w_unicodedtype = W_Dtype( - types.UnicodeType(), + types.UnicodeType(space), elsize=0, num=NPY.UNICODE, kind=NPY.UNICODELTR, @@ -823,7 +836,7 @@ w_box_type=space.gettypefor(boxes.W_UnicodeBox), ) self.w_voiddtype = W_Dtype( - types.VoidType(), + types.VoidType(space), elsize=0, num=NPY.VOID, kind=NPY.VOIDLTR, @@ -831,26 +844,33 @@ w_box_type=space.gettypefor(boxes.W_VoidBox), ) self.w_float16dtype = W_Dtype( - types.Float16(), + types.Float16(space), num=NPY.HALF, kind=NPY.FLOATINGLTR, char=NPY.HALFLTR, w_box_type=space.gettypefor(boxes.W_Float16Box), ) self.w_intpdtype = W_Dtype( - types.Long(), + types.Long(space), num=NPY.LONG, kind=NPY.SIGNEDLTR, char=NPY.INTPLTR, w_box_type=space.gettypefor(boxes.W_LongBox), ) self.w_uintpdtype = W_Dtype( - types.ULong(), + types.ULong(space), num=NPY.ULONG, kind=NPY.UNSIGNEDLTR, char=NPY.UINTPLTR, w_box_type=space.gettypefor(boxes.W_ULongBox), ) + self.w_objectdtype = W_Dtype( + types.ObjectType(space), + num=NPY.OBJECT, + kind=NPY.OBJECTLTR, + char=NPY.OBJECTLTR, + w_box_type=space.gettypefor(boxes.W_ObjectBox), + ) aliases = { NPY.BOOL: ['bool_', 'bool8'], NPY.BYTE: ['byte'], @@ -869,6 +889,7 @@ NPY.CLONGDOUBLE: ['clongdouble', 'clongfloat'], NPY.STRING: ['string_', 'str'], NPY.UNICODE: ['unicode_'], + NPY.OBJECT: ['object_'], } self.alternate_constructors = { NPY.BOOL: [space.w_bool], @@ -887,6 +908,8 @@ NPY.UNICODE: [space.w_unicode], NPY.VOID: [space.gettypefor(boxes.W_GenericBox)], #space.w_buffer, # XXX no buffer in space + NPY.OBJECT: [space.gettypefor(boxes.W_ObjectBox), + space.w_object], } float_dtypes = [self.w_float16dtype, self.w_float32dtype, self.w_float64dtype, self.w_floatlongdtype] @@ -906,7 +929,7 @@ self.w_int64dtype, self.w_uint64dtype, ] + float_dtypes + complex_dtypes + [ self.w_stringdtype, self.w_unicodedtype, self.w_voiddtype, - self.w_intpdtype, self.w_uintpdtype, + self.w_intpdtype, self.w_uintpdtype, self.w_objectdtype, ] self.float_dtypes_by_num_bytes = sorted( (dtype.elsize, dtype) @@ -958,6 +981,7 @@ 'USHORT': self.w_uint16dtype, 'FLOAT': self.w_float32dtype, 'BOOL': self.w_booldtype, + 'OBJECT': self.w_objectdtype, } typeinfo_partial = { 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 @@ -202,11 +202,16 @@ return self elif isinstance(w_idx, W_NDimArray) and w_idx.get_dtype().is_bool() \ and w_idx.ndims() > 0: - return self.getitem_filter(space, w_idx) - try: - return self.implementation.descr_getitem(space, self, w_idx) - except ArrayArgumentException: - return self.getitem_array_int(space, w_idx) + w_ret = self.getitem_filter(space, w_idx) + else: + try: + w_ret = self.implementation.descr_getitem(space, self, w_idx) + except ArrayArgumentException: + w_ret = self.getitem_array_int(space, w_idx) + if isinstance(w_ret, boxes.W_ObjectBox): + #return the W_Root object, not a scalar + w_ret = w_ret.w_obj + return w_ret def getitem(self, space, index_list): return self.implementation.getitem_index(space, index_list) @@ -550,6 +555,7 @@ else: strides = self.descr_get_strides(space) space.setitem_str(w_d, 'strides', strides) + space.setitem_str(w_d, 'version', space.wrap(3)) return w_d w_pypy_data = None @@ -845,7 +851,7 @@ "new type not compatible with array.")) # Strides, shape does not change v = impl.astype(space, dtype) - return wrap_impl(space, w_type, self, v) + return wrap_impl(space, w_type, self, v) strides = impl.get_strides() if dims == 1 or strides[0] <strides[-1]: # Column-major, resize first dimension @@ -1205,7 +1211,7 @@ "improper dtype '%R'", dtype) self.implementation = W_NDimArray.from_shape_and_storage( space, [space.int_w(i) for i in space.listview(shape)], - rffi.str2charp(space.str_w(storage), track_allocation=False), + rffi.str2charp(space.str_w(storage), track_allocation=False), dtype, storage_bytes=space.len_w(storage), owning=True).implementation def descr___array_finalize__(self, space, w_obj): diff --git a/pypy/module/micronumpy/test/dummy_module.py b/pypy/module/micronumpy/test/dummy_module.py --- a/pypy/module/micronumpy/test/dummy_module.py +++ b/pypy/module/micronumpy/test/dummy_module.py @@ -20,7 +20,7 @@ for t in types: globals()[t] = dtype(t).type -types = ['bool', 'int', 'float', 'complex', 'str', 'string', 'unicode'] +types = ['bool', 'int', 'float', 'complex', 'str', 'string', 'unicode', 'object'] for t in types: globals()[t + '_'] = dtype(t).type del types diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py --- a/pypy/module/micronumpy/test/test_dtypes.py +++ b/pypy/module/micronumpy/test/test_dtypes.py @@ -58,6 +58,7 @@ assert exc.value[0] == "there are no fields defined" assert dtype('int8').num == 1 + assert dtype(u'int8').num == 1 assert dtype('int8').name == 'int8' assert dtype('void').name == 'void' assert dtype(int).fields is None @@ -472,11 +473,13 @@ class O(object): pass for o in [object, O]: - if '__pypy__' not in sys.builtin_module_names: + print np.dtype(o).byteorder + if self.ptr_size == 4: + assert np.dtype(o).str == '|O4' + elif self.ptr_size == 8: assert np.dtype(o).str == '|O8' else: - exc = raises(NotImplementedError, "np.dtype(o)") - assert exc.value[0] == "cannot create dtype with type '%s'" % o.__name__ + assert False,'self._ptr_size unknown' class AppTestTypes(BaseAppTestDtypes): def test_abstract_types(self): @@ -1348,15 +1351,4 @@ assert a[0] == 1 assert (a + a)[1] == 4 -class AppTestObjectDtypes(BaseNumpyAppTest): - def test_scalar_from_object(self): - from numpy import array - import sys - class Polynomial(object): - pass - if '__pypy__' in sys.builtin_module_names: - exc = raises(NotImplementedError, array, Polynomial()) - assert exc.value.message.find('unable to create dtype from objects') >= 0 - else: - a = array(Polynomial()) - assert a.shape == () + 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 @@ -17,6 +17,7 @@ def __init__(self): self.base = self self.elsize = 1 + self.num = 0 def create_slice(space, a, chunks): @@ -2182,7 +2183,8 @@ assert b.dtype == 'bool' a = arange(6, dtype='f4').reshape(2,3) - b = a.astype('i4') + b = a.T.astype('i4') + assert (a.T.strides == b.strides) a = array('x').astype('S3').dtype assert a.itemsize == 3 @@ -3150,11 +3152,7 @@ assert b[35] == 200 b[[slice(25, 30)]] = range(5) assert all(a[:5] == range(5)) - import sys - if '__pypy__' not in sys.builtin_module_names: - raises(TypeError, 'b[[[slice(25, 125)]]]') - else: - raises(NotImplementedError, 'b[[[slice(25, 125)]]]') + raises(IndexError, 'b[[[slice(25, 125)]]]') def test_cumsum(self): from numpy import arange diff --git a/pypy/module/micronumpy/test/test_object_arrays.py b/pypy/module/micronumpy/test/test_object_arrays.py new file mode 100644 --- /dev/null +++ b/pypy/module/micronumpy/test/test_object_arrays.py @@ -0,0 +1,162 @@ +from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest + + +class AppTestObjectDtypes(BaseNumpyAppTest): + def test_scalar_from_object(self): + from numpy import array + import sys + class Polynomial(object): + def whatami(self): + return 'an object' + a = array(Polynomial()) + assert a.shape == () + assert a.sum().whatami() == 'an object' + + def test_uninitialized_object_array_is_filled_by_None(self): + import numpy as np + + a = np.ndarray([5], dtype="O") + + assert a[0] == None + + def test_object_arrays_add(self): + import numpy as np + + a = np.array(["foo"], dtype=object) + b = np.array(["bar"], dtype=object) + raises(TypeError, np.add, a, 1) + res = a + b + assert res[0] == "foobar" + + def test_bool_func(self): + import numpy as np + a = np.array(["foo"], dtype=object) + b = a and complex(1, -1) + assert b == complex(1, -1) + b = np.array(complex(1, -1)) and a + assert (b == a).all() + c = np.array([1, 2, 3]) + assert (a[0] != c[0]) + assert (c[0] != a[0]) + assert (a[0] > c[0]) + assert (not a[0] < c[0]) + assert (c[0] < a[0]) + assert (not c[0] > a[0]) + + def test_logical_ufunc(self): + import numpy as np + import sys + + if '__pypy__' in sys.builtin_module_names: + skip('need to refactor use of raw_xxx_op in types to make this work') + a = np.array(["foo"], dtype=object) + b = np.array([1], dtype=object) + d = np.array([complex(1, 10)], dtype=object) + c = np.logical_and(a, 1) + assert c.dtype == np.dtype('object') + assert c == 1 + c = np.logical_and(b, complex(1, -1)) + assert c.dtype == np.dtype('object') + assert c == complex(1, -1) + c = np.logical_and(d, b) + assert c == 1 + c = b & 1 + assert c.dtype == np.dtype('object') + assert (c == 1).all() + c = np.array(1) & b + assert (c == b).all() + + def test_reduce(self): + import numpy as np + class O(object): + def whatami(self): + return 'an object' + fiveOs = [O()] * 5 + a = np.array(fiveOs, dtype=object) + print np.maximum + b = np.maximum.reduce(a) + assert b is not None + + def test_complex_op(self): + import numpy as np + import sys + a = np.array(['abc', 'def'], dtype=object) + b = np.array([1, 2, 3], dtype=object) + c = np.array([complex(1, 1), complex(1, -1)], dtype=object) + for arg in (a,b,c): + assert (arg == np.real(arg)).all() + assert (0 == np.imag(arg)).all() + if '__pypy__' in sys.builtin_module_names: + skip('not implemented yet') + raises(AttributeError, np.conj, a) + res = np.conj(b) + assert (res == b).all() + res = np.conj(c) + assert res[0] == c[1] and res[1] == c[0] + + def test_keep_object_alive(self): + # only translated does it really test the gc + import numpy as np + import gc + class O(object): + def whatami(self): + return 'an object' + fiveOs = [O()] * 5 + a = np.array(fiveOs, dtype=object) + del fiveOs + gc.collect() + assert a[2].whatami() == 'an object' + + def test_array_interface(self): + import numpy as np + import sys + class DummyArray(object): + def __init__(self, interface, base=None): + self.__array_interface__ = interface + self.base = base + a = np.array([(1, 2, 3)], dtype='u4,u4,u4') + b = np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype='u4,u4,u4') + interface = dict(a.__array_interface__) + interface['shape'] = tuple([3]) + interface['strides'] = tuple([0]) + if '__pypy__' in sys.builtin_module_names: + skip('not implemented yet') + c = np.array(DummyArray(interface, base=a)) + c.dtype = a.dtype + #print c + assert (c == np.array([(1, 2, 3), (1, 2, 3), (1, 2, 3)], dtype='u4,u4,u4') ).all() + + def test_for_object_scalar_creation(self): + import numpy as np + import sys + a = np.object_() + b = np.object_(3) + b2 = np.object_(3.0) + c = np.object_([4, 5]) + d = np.array([None])[0] + assert a is None + assert type(b) is int + assert type(b2) is float + assert type(c) is np.ndarray + assert c.dtype == object + assert type(d) is type(None) + if '__pypy__' in sys.builtin_module_names: + skip('not implemented yet') + e = np.object_([None, {}, []]) + assert e.dtype == object + + def test_mem_array_creation_invalid_specification(self): + # while not specifically testing object dtype, this + # test segfaulted during ObjectType.store due to + # missing gc hooks + import numpy as np + import sys + ytype = np.object_ + if '__pypy__' in sys.builtin_module_names: + ytype = str + dt = np.dtype([('x', int), ('y', ytype)]) + # Correct way + a = np.array([(1, 'object')], dt) + # Wrong way - should complain about writing buffer to object dtype + raises(ValueError, np.array, [1, 'object'], dt) + diff --git a/pypy/module/micronumpy/test/test_selection.py b/pypy/module/micronumpy/test/test_selection.py --- a/pypy/module/micronumpy/test/test_selection.py +++ b/pypy/module/micronumpy/test/test_selection.py @@ -12,14 +12,11 @@ exp = sorted(range(len(exp)), key=exp.__getitem__) c = a.copy() res = a.argsort() - assert (res == exp).all(), '%r\n%r\n%r' % (a,res,exp) + assert (res == exp).all(), 'Failed sortng %r\na=%r\nres=%r\nexp=%r' % (dtype,a,res,exp) assert (a == c).all() # not modified a = arange(100, dtype=dtype) assert (a.argsort() == a).all() - import sys - if '__pypy__' in sys.builtin_module_names: - raises(NotImplementedError, 'arange(10,dtype="float16").argsort()') def test_argsort_ndim(self): from numpy import array @@ -63,14 +60,13 @@ 'i2', complex]: a = array([6, 4, -1, 3, 8, 3, 256+20, 100, 101], dtype=dtype) exp = sorted(list(a)) - res = a.copy() - res.sort() - assert (res == exp).all(), '%r\n%r\n%r' % (a,res,exp) + a.sort() + assert (a == exp).all(), 'Failed sorting %r\n%r\n%r' % (dtype, a, exp) a = arange(100, dtype=dtype) c = a.copy() a.sort() - assert (a == c).all() + assert (a == c).all(), 'Failed sortng %r\na=%r\nc=%r' % (dtype,a,c) def test_sort_nonnative(self): from numpy import array @@ -222,6 +218,7 @@ def test_sort_objects(self): # test object array sorts. + skip('object type not supported yet') from numpy import empty try: a = empty((101,), dtype=object) 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 @@ -397,11 +397,11 @@ for i in range(3): assert min_c_b[i] == min(b[i], c) - def test_scalar(self): + def test_all_available(self): # tests that by calling all available ufuncs on scalars, none will # raise uncaught interp-level exceptions, (and crash the test) # and those that are uncallable can be accounted for. - # test on the four base-class dtypes: int, bool, float, complex + # test on the base-class dtypes: int, bool, float, complex, object # We need this test since they have no common base class. import numpy as np def find_uncallable_ufuncs(dtype): @@ -412,6 +412,11 @@ if isinstance(u, np.ufunc): try: u(* [array] * u.nin) + except AttributeError: + pass + except NotImplementedError: + print s + uncallable.add(s) except TypeError: assert s not in uncallable uncallable.add(s) @@ -427,6 +432,9 @@ 'fabs', 'fmod', 'invert', 'mod', 'logaddexp', 'logaddexp2', 'left_shift', 'right_shift', 'copysign', 'signbit', 'ceil', 'floor', 'trunc']) + assert find_uncallable_ufuncs('object') == set( + ['isnan', 'logaddexp2', 'copysign', 'isfinite', 'signbit', + 'isinf', 'logaddexp']) def test_int_only(self): from numpy import bitwise_and, array diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py --- a/pypy/module/micronumpy/test/test_zjit.py +++ b/pypy/module/micronumpy/test/test_zjit.py @@ -9,6 +9,7 @@ from pypy.module.micronumpy.compile import FakeSpace, Parser, InterpreterState from pypy.module.micronumpy.base import W_NDimArray +py.test.skip('move these to pypyjit/test_pypy_c/test_micronumpy') class TestNumpyJit(LLJitMixin): graph = None 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 @@ -3,8 +3,9 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.objspace.std.floatobject import float2string from pypy.objspace.std.complexobject import str_format +from pypy.interpreter.baseobjspace import W_Root, ObjSpace from rpython.rlib import clibffi, jit, rfloat, rcomplex -from rpython.rlib.objectmodel import specialize +from rpython.rlib.objectmodel import specialize, we_are_translated from rpython.rlib.rarithmetic import widen, byteswap, r_ulonglong, \ most_neg_value_of, LONG_BIT from rpython.rlib.rawstorage import (alloc_raw_storage, @@ -14,10 +15,12 @@ pack_float80, unpack_float80) from rpython.rlib.rstruct.nativefmttable import native_is_bigendian from rpython.rlib.rstruct.runpack import runpack -from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rtyper.annlowlevel import cast_instance_to_gcref,\ + cast_gcref_to_instance +from rpython.rtyper.lltypesystem import lltype, rffi, llmemory from rpython.tool.sourcetools import func_with_new_name from pypy.module.micronumpy import boxes -from pypy.module.micronumpy.concrete import SliceArray, VoidBoxStorage +from pypy.module.micronumpy.concrete import SliceArray, VoidBoxStorage, V_OBJECTSTORE from pypy.module.micronumpy.strides import calc_strides degToRad = math.pi / 180.0 @@ -109,10 +112,12 @@ return dispatcher class BaseType(object): - _immutable_fields_ = ['native'] + _immutable_fields_ = ['native', 'space'] - def __init__(self, native=True): + def __init__(self, space, native=True): + assert isinstance(space, ObjSpace) self.native = native + self.space = space def __repr__(self): return self.__class__.__name__ @@ -191,7 +196,7 @@ with arr as storage: self._write(storage, i, offset, self.unbox(box)) - def fill(self, storage, width, box, start, stop, offset): + def fill(self, storage, width, box, start, stop, offset, gcstruct): value = self.unbox(box) for i in xrange(start, stop, width): self._write(storage, i, offset, value) @@ -306,7 +311,7 @@ @raw_unary_op def rint(self, v): - float64 = Float64() + float64 = Float64(self.space) return float64.rint(float64.box(v)) class Bool(BaseType, Primitive): @@ -399,7 +404,7 @@ def round(self, v, decimals=0): if decimals != 0: return v - return Float64().box(self.unbox(v)) + return Float64(self.space).box(self.unbox(v)) class Integer(Primitive): _mixin_ = True @@ -444,7 +449,7 @@ self.T is rffi.LONG or self.T is rffi.LONGLONG): if v2 == -1 and v1 == self.for_computation(most_neg_value_of(self.T)): return self.box(0) - return self.box(v1 // v2) + return self.box(v1 / v2) @simple_binary_op def mod(self, v1, v2): @@ -1152,7 +1157,7 @@ with arr as storage: self._write(storage, i, offset, self.unbox(box)) - def fill(self, storage, width, box, start, stop, offset): + def fill(self, storage, width, box, start, stop, offset, gcstruct): value = self.unbox(box) for i in xrange(start, stop, width): self._write(storage, i, offset, value) @@ -1253,25 +1258,25 @@ def ge(self, v1, v2): return self._lt(v2, v1) or self._eq(v2, v1) - def _bool(self, v): + def _cbool(self, v): return bool(v[0]) or bool(v[1]) @raw_binary_op def logical_and(self, v1, v2): - return self._bool(v1) and self._bool(v2) + return self._cbool(v1) and self._cbool(v2) @raw_binary_op def logical_or(self, v1, v2): - return self._bool(v1) or self._bool(v2) + return self._cbool(v1) or self._cbool(v2) @raw_unary_op def logical_not(self, v): - return not self._bool(v) + return not self._cbool(v) @raw_binary_op def logical_xor(self, v1, v2): - a = self._bool(v1) - b = self._bool(v2) + a = self._cbool(v1) + b = self._cbool(v2) return (not b and a) or (not a and b) def min(self, v1, v2): @@ -1629,6 +1634,283 @@ BoxType = boxes.W_ComplexLongBox ComponentBoxType = boxes.W_FloatLongBox +_all_objs_for_tests = [] # for tests + +class ObjectType(Primitive, BaseType): + T = lltype.Signed + BoxType = boxes.W_ObjectBox + + def get_element_size(self): + return rffi.sizeof(lltype.Signed) + + def coerce(self, space, dtype, w_item): + if isinstance(w_item, boxes.W_ObjectBox): + return w_item + return boxes.W_ObjectBox(w_item) + + def coerce_subtype(self, space, w_subtype, w_item): + # return the item itself + return self.unbox(self.box(w_item)) + + def store(self, arr, i, offset, box): + if arr.gcstruct is V_OBJECTSTORE: + raise oefmt(self.space.w_NotImplementedError, + "cannot store object in array with no gc hook") + self._write(arr.storage, i, offset, self.unbox(box), + arr.gcstruct) + + def read(self, arr, i, offset, dtype=None): + return self.box(self._read(arr.storage, i, offset)) + + def byteswap(self, w_v): + return w_v + + @jit.dont_look_inside + def _write(self, storage, i, offset, w_obj, gcstruct): + # no GC anywhere in this function! + if we_are_translated(): + from rpython.rlib import rgc + rgc.ll_writebarrier(gcstruct) + value = rffi.cast(lltype.Signed, cast_instance_to_gcref(w_obj)) + else: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit