[pypy-commit] pypy default: merge in object-dtype2, which provides an object dtype for numpy

mattip noreply at buildbot.pypy.org
Thu Apr 23 19:03:15 CEST 2015


Author: mattip <matti.picus at gmail.com>
Branch: 
Changeset: r76905:bc2c76a447dc
Date: 2015-04-23 20:01 +0300
http://bitbucket.org/pypy/pypy/changeset/bc2c76a447dc/

Log:	merge in object-dtype2, which provides an object dtype for numpy

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
@@ -64,3 +64,6 @@
 
 .. 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/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
@@ -333,6 +335,44 @@
         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)
+
+ at 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 +387,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 +415,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 +471,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 +529,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,7 +553,7 @@
         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)
 
@@ -587,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")
 
 
@@ -655,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,
@@ -665,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(),
@@ -678,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,
@@ -690,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,
@@ -817,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,
@@ -825,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,
@@ -833,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'],
@@ -871,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],
@@ -889,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]
@@ -908,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)
@@ -960,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
@@ -473,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):
@@ -1349,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):
@@ -3150,11 +3151,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
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:
+            value = len(_all_objs_for_tests)
+            _all_objs_for_tests.append(w_obj)
+        raw_storage_setitem_unaligned(storage, i + offset, value)
+
+    @jit.dont_look_inside
+    def _read(self, storage, i, offset):
+        res = raw_storage_getitem_unaligned(self.T, storage, i + offset)
+        if we_are_translated():
+            gcref = rffi.cast(llmemory.GCREF, res)
+            w_obj = cast_gcref_to_instance(W_Root, gcref)
+        else:
+            w_obj = _all_objs_for_tests[res]
+        return w_obj
+
+    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, gcstruct)
+
+    def unbox(self, box):
+        if isinstance(box, self.BoxType):
+            return box.w_obj
+        else:
+            raise oefmt(self.space.w_NotImplementedError,
+                "object dtype cannot unbox %s", str(box))
+            
+    @specialize.argtype(1)
+    def box(self, w_obj):
+        if isinstance(w_obj, W_Root):
+            pass
+        elif isinstance(w_obj, bool):
+            w_obj = self.space.newbool(w_obj)
+        elif isinstance(w_obj, int):
+            w_obj = self.space.newint(w_obj)
+        elif isinstance(w_obj, lltype.Number):
+            w_obj = self.space.newint(w_obj)
+        elif isinstance(w_obj, float):
+            w_obj = self.space.newfloat(w_obj)
+        elif w_obj is None:
+            w_obj = self.space.w_None
+        else:
+            raise oefmt(self.space.w_NotImplementedError,
+                "cannot create object array/scalar from lltype")
+        return self.BoxType(w_obj)
+
+    @specialize.argtype(1, 2)
+    def box_complex(self, real, imag):
+        if isinstance(real, rffi.r_singlefloat):
+            real = rffi.cast(rffi.DOUBLE, real)
+        if isinstance(imag, rffi.r_singlefloat):
+            imag = rffi.cast(rffi.DOUBLE, imag)
+        w_obj = self.space.newcomplex(real, imag)
+        return self.BoxType(w_obj)
+
+    def str_format(self, box):
+        return self.space.str_w(self.space.repr(self.unbox(box)))
+
+    def runpack_str(self, space, s):
+        raise oefmt(space.w_NotImplementedError,
+                    "fromstring not implemented for object type")
+
+    def to_builtin_type(self, space, box):
+        assert isinstance(box, self.BoxType)
+        return box.w_obj
+
+    @staticmethod
+    def for_computation(v):
+        return v
+
+    @raw_binary_op
+    def eq(self, v1, v2):
+        return self.space.eq_w(v1, v2)
+
+    @simple_binary_op
+    def max(self, v1, v2):
+        if self.space.is_true(self.space.ge(v1, v2)):
+            return v1
+        return v2
+
+    @simple_binary_op
+    def min(self, v1, v2):
+        if self.space.is_true(self.space.le(v1, v2)):
+            return v1
+        return v2
+
+    @raw_unary_op
+    def bool(self,v):
+        return self._obool(v)
+
+    def _obool(self, v):
+        if self.space.is_true(v):
+            return True
+        return False
+
+    @raw_binary_op
+    def logical_and(self, v1, v2):
+        if self._obool(v1):
+            return self.space.bool_w(v2)
+        return self.space.bool_w(v1)
+
+    @raw_binary_op
+    def logical_or(self, v1, v2):
+        if self._obool(v1):
+            return self.space.bool_w(v1)
+        return self.space.bool_w(v2)
+
+    @raw_unary_op
+    def logical_not(self, v):
+        return not self._obool(v)
+
+    @raw_binary_op
+    def logical_xor(self, v1, v2):
+        a = self._obool(v1)
+        b = self._obool(v2)
+        return (not b and a) or (not a and b)
+
+    @simple_binary_op
+    def bitwise_and(self, v1, v2):
+        return self.space.and_(v1, v2)
+
+    @simple_binary_op
+    def bitwise_or(self, v1, v2):
+        return self.space.or_(v1, v2)
+
+    @simple_binary_op
+    def bitwise_xor(self, v1, v2):
+        return self.space.xor(v1, v2)
+
+    @simple_binary_op
+    def pow(self, v1, v2):
+        return self.space.pow(v1, v2, self.space.wrap(1))
+
+    @simple_unary_op
+    def reciprocal(self, v1):
+        return self.space.div(self.space.wrap(1.0), v1)
+
+    @simple_unary_op
+    def sign(self, v):
+        zero = self.space.wrap(0)
+        one = self.space.wrap(1)
+        m_one = self.space.wrap(-1)
+        if self.space.is_true(self.space.gt(v, zero)):
+            return one
+        elif self.space.is_true(self.space.lt(v, zero)):
+            return m_one
+        else:
+            return zero
+
+    @simple_unary_op
+    def real(self, v):
+        return v
+
+    @simple_unary_op
+    def imag(self, v):
+        return 0
+
+    @simple_unary_op
+    def square(self, v):
+        return self.space.mul(v, v)
+
+    @raw_binary_op
+    def le(self, v1, v2):
+        return self.space.bool_w(self.space.le(v1, v2))
+
+    @raw_binary_op
+    def ge(self, v1, v2):
+        return self.space.bool_w(self.space.ge(v1, v2))
+
+    @raw_binary_op
+    def lt(self, v1, v2):
+        return self.space.bool_w(self.space.lt(v1, v2))
+
+    @raw_binary_op
+    def gt(self, v1, v2):
+        return self.space.bool_w(self.space.gt(v1, v2))
+
+    @raw_binary_op
+    def ne(self, v1, v2):
+        return self.space.bool_w(self.space.ne(v1, v2))
+
+def add_attributeerr_op(cls, op):
+    def func(self, *args):
+        raise oefmt(self.space.w_AttributeError,
+            "%s", op)
+    func.__name__ = 'object_' + op
+    setattr(cls, op, func)
+
+def add_unsupported_op(cls, op):
+    def func(self, *args):
+        raise oefmt(self.space.w_TypeError,
+            "ufunc '%s' not supported for input types", op)
+    func.__name__ = 'object_' + op
+    setattr(cls, op, func)
+
+def add_unary_op(cls, op, method):
+    @simple_unary_op
+    def func(self, w_v):
+        space = self.space
+        w_impl = space.lookup(w_v, method)
+        if w_impl is None:
+            raise oefmt(space.w_AttributeError, 'unknown op "%s" on object' % op)
+        return space.get_and_call_function(w_impl, w_v)
+    func.__name__ = 'object_' + op
+    setattr(cls, op, func)
+
+def add_space_unary_op(cls, op):
+    @simple_unary_op
+    def func(self, v):
+        return getattr(self.space, op)(v)
+    func.__name__ = 'object_' + op
+    setattr(cls, op, func)
+
+def add_space_binary_op(cls, op):
+    @simple_binary_op
+    def func(self, v1, v2):
+        return getattr(self.space, op)(v1, v2)
+    func.__name__ = 'object_' + op
+    setattr(cls, op, func)
+
+for op in ('copysign', 'isfinite', 'isinf', 'isnan', 'logaddexp', 'logaddexp2',
+           'signbit'):
+    add_unsupported_op(ObjectType, op)
+for op in ('arctan2', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan',
+           'arctanh', 'ceil', 'floor', 'cos', 'sin', 'tan', 'cosh', 'sinh',
+           'tanh', 'radians', 'degrees', 'exp','exp2', 'expm1', 'fabs',
+           'log', 'log10', 'log1p', 'log2', 'sqrt', 'trunc'):
+    add_attributeerr_op(ObjectType, op)
+for op in ('abs', 'neg', 'pos', 'invert'):
+    add_space_unary_op(ObjectType, op)
+for op, method in (('conj', 'descr_conjugate'), ('rint', 'descr_rint')):
+    add_unary_op(ObjectType, op, method)
+for op in ('add', 'floordiv', 'div', 'mod', 'mul', 'sub', 'lshift', 'rshift'):
+    add_space_binary_op(ObjectType, op)
+
+ObjectType.fmax = ObjectType.max
+ObjectType.fmin = ObjectType.min
+ObjectType.fmod = ObjectType.mod
+
 class FlexibleType(BaseType):
     def get_element_size(self):
         return rffi.sizeof(self.T)
@@ -1758,7 +2040,7 @@
     def bool(self, v):
         return bool(self.to_str(v))
 
-    def fill(self, storage, width, box, start, stop, offset):
+    def fill(self, storage, width, box, start, stop, offset, gcstruct):
         for i in xrange(start, stop, width):
             self._store(storage, i, offset, box, width)
 
@@ -1775,6 +2057,57 @@
         raise OperationError(space.w_NotImplementedError, space.wrap(
             "coerce (probably from set_item) not implemented for unicode type"))
 
+    def store(self, arr, i, offset, box):
+        assert isinstance(box, boxes.W_UnicodeBox)
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def read(self, arr, i, offset, dtype=None):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def str_format(self, item):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def to_builtin_type(self, space, box):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def eq(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def ne(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def lt(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def le(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def gt(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def ge(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def logical_and(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def logical_or(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def logical_not(self, v):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    @str_binary_op
+    def logical_xor(self, v1, v2):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def bool(self, v):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+    def fill(self, storage, width, box, start, stop, offset, gcstruct):
+        raise oefmt(self.space.w_NotImplementedError, "unicode type not completed")
+
+
 class VoidType(FlexibleType):
     T = lltype.Char
 
@@ -1882,6 +2215,9 @@
                 items_w = space.fixedview(w_item)
             elif isinstance(w_item, W_NDimArray) and w_item.is_scalar():
                 items_w = space.fixedview(w_item.get_scalar_value())
+            elif space.isinstance_w(w_item, space.w_list):
+                raise oefmt(space.w_TypeError,
+                            "expected a readable buffer object")
             else:
                 # XXX support initializing from readable buffers
                 items_w = [w_item] * len(dtype.fields)
@@ -1913,7 +2249,7 @@
             for k in range(size):
                 storage[k + i + ofs] = box_storage[k + box.ofs]
 
-    def fill(self, storage, width, box, start, stop, offset):
+    def fill(self, storage, width, box, start, stop, offset, gcstruct):
         assert isinstance(box, boxes.W_VoidBox)
         assert width == box.dtype.elsize
         for i in xrange(start, stop, width):
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -349,7 +349,7 @@
         if dtype.is_flexible():
             raise OperationError(space.w_TypeError,
                       space.wrap('Not implemented for this type'))
-        if (self.int_only and not dtype.is_int() or
+        if (self.int_only and not (dtype.is_int() or dtype.is_object()) or
                 not self.allow_bool and dtype.is_bool() or
                 not self.allow_complex and dtype.is_complex()):
             raise oefmt(space.w_TypeError,
@@ -378,6 +378,8 @@
             w_val = self.func(calc_dtype,
                               w_obj.get_scalar_value().convert_to(space, calc_dtype))
             if out is None:
+                if res_dtype.is_object():
+                    w_val = w_obj.get_scalar_value()
                 return w_val
             w_val = res_dtype.coerce(space, w_val)
             if out.is_scalar():
@@ -434,11 +436,20 @@
         w_rhs = numpify(space, w_rhs)
         w_ldtype = _get_dtype(space, w_lhs)
         w_rdtype = _get_dtype(space, w_rhs)
-        if w_ldtype.is_str() and w_rdtype.is_str() and \
+        if w_ldtype.is_object() or w_rdtype.is_object():
+            pass
+        elif w_ldtype.is_str() and w_rdtype.is_str() and \
                 self.comparison_func:
             pass
-        elif (w_ldtype.is_str() or w_rdtype.is_str()) and \
+        elif (w_ldtype.is_str()) and \
                 self.comparison_func and w_out is None:
+            if self.name in ('equal', 'less_equal', 'less'):
+               return space.wrap(False)
+            return space.wrap(True) 
+        elif (w_rdtype.is_str()) and \
+                self.comparison_func and w_out is None:
+            if self.name in ('not_equal','less', 'less_equal'):
+               return space.wrap(True)
             return space.wrap(False)
         elif w_ldtype.is_flexible() or w_rdtype.is_flexible():
             if self.comparison_func:
@@ -463,9 +474,9 @@
             w_ldtype, w_rdtype,
             promote_to_float=self.promote_to_float,
             promote_bools=self.promote_bools)
-        if (self.int_only and (not w_ldtype.is_int() or
-                               not w_rdtype.is_int() or
-                               not calc_dtype.is_int()) or
+        if (self.int_only and (not (w_ldtype.is_int() or w_ldtype.is_object()) or
+                               not (w_rdtype.is_int() or w_rdtype.is_object()) or
+                               not (calc_dtype.is_int() or calc_dtype.is_object())) or
                 not self.allow_bool and (w_ldtype.is_bool() or
                                          w_rdtype.is_bool()) or
                 not self.allow_complex and (w_ldtype.is_complex() or
@@ -643,7 +654,7 @@
             # from frompyfunc
             pass
         # mimic NpyIter_AdvancedNew with a nditer
-        w_itershape = space.newlist([space.wrap(i) for i in iter_shape]) 
+        w_itershape = space.newlist([space.wrap(i) for i in iter_shape])
         nd_it = W_NDIter(space, space.newlist(inargs + outargs), w_flags,
                       w_op_flags, w_op_dtypes, w_casting, w_op_axes,
                       w_itershape)
@@ -749,7 +760,7 @@
                 else:
                     raise oefmt(space.w_TypeError, "a type-string for %s " \
                         "requires 1 typecode or %d typecode(s) before and %d" \
-                        " after the -> sign, not '%s'", self.name, self.nin, 
+                        " after the -> sign, not '%s'", self.name, self.nin,
                         self.nout, type_tup)
             except KeyError:
                 raise oefmt(space.w_ValueError, "unknown typecode in" \
@@ -773,11 +784,11 @@
             for j in range(self.nargs):
                 if dtypes[j] is not None and dtypes[j] != _dtypes[i+j]:
                     allok = False
-            if allok:    
+            if allok:
                 break
         else:
             if len(self.funcs) > 1:
-                
+
                 dtypesstr = ''
                 for d in dtypes:
                     if d is None:
@@ -787,7 +798,7 @@
                 _dtypesstr = ','.join(['%s%s%s' % (d.byteorder, d.kind, d.elsize) \
                                 for d in _dtypes])
                 raise oefmt(space.w_TypeError,
-                     "input dtype [%s] did not match any known dtypes [%s] ", 
+                     "input dtype [%s] did not match any known dtypes [%s] ",
                      dtypesstr,_dtypesstr)
             i = 0
         # Fill in empty dtypes
@@ -807,7 +818,7 @@
             assert isinstance(curarg, W_NDimArray)
             if len(arg_shapes[i]) != curarg.ndims():
                 # reshape
-                
+
                 sz = product(curarg.get_shape()) * curarg.get_dtype().elsize
                 with curarg.implementation as storage:
                     inargs[i] = W_NDimArray.from_shape_and_storage(
@@ -865,7 +876,7 @@
                             "%s of gufunc was not specified",
                              self.name, name, _i, core_dim_index, self.signature)
                     target_dims.append(v)
-                arg_shapes.append(iter_shape + target_dims) 
+                arg_shapes.append(iter_shape + target_dims)
                 continue
             n = len(curarg.get_shape()) - num_dims
             if n < 0:
@@ -907,7 +918,7 @@
                     raise oefmt(space.w_ValueError, "%s: %s operand %d has a "
                         "mismatch in its core dimension %d, with gufunc "
                         "signature %s (expected %d, got %d)",
-                         self.name, name, _i, j, 
+                         self.name, name, _i, j,
                          self.signature, matched_dims[core_dim_index],
                          dims_to_match[core_dim_index])
             #print 'adding',iter_shape,'+',dims_to_match,'to arg_shapes'
@@ -950,6 +961,10 @@
         return dt1
     if dt1 is None:
         return dt2
+
+    if dt1.num == NPY.OBJECT or dt2.num == NPY.OBJECT:
+        return get_dtype_cache(space).w_objectdtype
+
     # dt1.num should be <= dt2.num
     if dt1.num > dt2.num:
         dt1, dt2 = dt2, dt1
@@ -1032,6 +1047,8 @@
 @jit.unroll_safe
 def find_unaryop_result_dtype(space, dt, promote_to_float=False,
         promote_bools=False, promote_to_largest=False):
+    if dt.is_object():
+        return dt
     if promote_to_largest:
         if dt.kind == NPY.GENBOOLLTR or dt.kind == NPY.SIGNEDLTR:
             if dt.elsize * 8 < LONG_BIT:
@@ -1064,6 +1081,7 @@
     uint64_dtype = get_dtype_cache(space).w_uint64dtype
     complex_dtype = get_dtype_cache(space).w_complex128dtype
     float_dtype = get_dtype_cache(space).w_float64dtype
+    object_dtype = get_dtype_cache(space).w_objectdtype
     if isinstance(w_obj, boxes.W_GenericBox):
         dtype = w_obj.get_dtype(space)
         return find_binop_result_dtype(space, dtype, current_guess)
@@ -1097,9 +1115,10 @@
                 return variable_dtype(space,
                                                    'S%d' % space.len_w(w_obj))
         return current_guess
-    raise oefmt(space.w_NotImplementedError,
-                'unable to create dtype from objects, "%T" instance not '
-                'supported', w_obj)
+    return object_dtype
+    #raise oefmt(space.w_NotImplementedError,
+    #            'unable to create dtype from objects, "%T" instance not '
+    #            'supported', w_obj)
 
 
 def ufunc_dtype_caller(space, ufunc_name, op_name, nin, comparison_func,
@@ -1263,7 +1282,7 @@
      w_identity=None, name='', doc='', stack_inputs=False):
     ''' frompyfunc(func, nin, nout) #cpython numpy compatible
         frompyfunc(func, nin, nout, dtypes=None, signature='',
-                   identity=None, name='', doc='', 
+                   identity=None, name='', doc='',
                    stack_inputs=False)
 
     Takes an arbitrary Python function and returns a ufunc.
@@ -1282,7 +1301,7 @@
     dtypes: None or [dtype, ...] of the input, output args for each function,
          or 'match' to force output to exactly match input dtype
          Note that 'match' is a pypy-only extension to allow non-object
-         return dtypes      
+         return dtypes
     signature*: str, default=''
          The mapping of input args to output args, defining the
          inner-loop indexing. If it is empty, the func operates on scalars
@@ -1293,7 +1312,7 @@
     stack_inputs*: boolean, whether the function is of the form
             out = func(*in)  False
             or
-            func(*[in + out])    True 
+            func(*[in + out])    True
 
     only one of out_dtype or signature may be specified
 
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -686,6 +686,15 @@
         lambda_customtrace = lambda: customtrace
     """
 
+ at specialize.ll()
+def ll_writebarrier(gc_obj):
+    """Use together with custom tracers.  When you update some object pointer
+    stored in raw memory, you must call this function on 'gc_obj', which must
+    be the object of type TP with the custom tracer (*not* the value stored!).
+    This makes sure that the custom hook will be called again."""
+    from rpython.rtyper.lltypesystem.lloperation import llop
+    llop.gc_writebarrier(lltype.Void, gc_obj)
+
 class RegisterGcTraceEntry(ExtRegistryEntry):
     _about_ = register_custom_trace_hook
 


More information about the pypy-commit mailing list