Author: Carl Friedrich Bolz <cfb...@gmx.de>
Branch: cells-local-stack
Changeset: r77299:93e76a7a9923
Date: 2015-05-12 17:07 +0200
http://bitbucket.org/pypy/pypy/changeset/93e76a7a9923/

Log:    merge default

diff --git a/lib-python/2.7/socket.py b/lib-python/2.7/socket.py
--- a/lib-python/2.7/socket.py
+++ b/lib-python/2.7/socket.py
@@ -145,6 +145,34 @@
             name = hostname
     return name
 
+class RefCountingWarning(UserWarning):
+    pass
+
+def _do_reuse_or_drop(socket, methname):
+    try:
+        method = getattr(socket, methname)
+    except (AttributeError, TypeError):
+        warnings.warn("""'%s' object has no _reuse/_drop methods
+{{
+    You make use (or a library you are using makes use) of the internal
+    classes '_socketobject' and '_fileobject' in socket.py, initializing
+    them with custom objects.  On PyPy, these custom objects need two
+    extra methods, _reuse() and _drop(), that maintain an explicit
+    reference counter.  When _drop() has been called as many times as
+    _reuse(), then the object should be freed.
+
+    Without these methods, you get the warning here.  This is to
+    prevent the following situation: if your (or the library's) code
+    relies on reference counting for prompt closing, then on PyPy, the
+    __del__ method will be called later than on CPython.  You can
+    easily end up in a situation where you open and close a lot of
+    (high-level) '_socketobject' or '_fileobject', but the (low-level)
+    custom objects will accumulate before their __del__ are called.
+    You quickly risk running out of file descriptors, for example.
+}}""" % (socket.__class__.__name__,), RefCountingWarning, stacklevel=3)
+    else:
+        method()
+
 
 _socketmethods = (
     'bind', 'connect', 'connect_ex', 'fileno', 'listen',
@@ -182,19 +210,7 @@
         if _sock is None:
             _sock = _realsocket(family, type, proto)
         else:
-            # PyPy note about refcounting: implemented with _reuse()/_drop()
-            # on the class '_socket.socket'.  Python 3 did it differently
-            # with a reference counter on this class 'socket._socketobject'
-            # instead, but it is a less compatible change.
-            
-            # Note that a few libraries (like eventlet) poke at the
-            # private implementation of socket.py, passing custom
-            # objects to _socketobject().  These libraries need the
-            # following fix for use on PyPy: the custom objects need
-            # methods _reuse() and _drop() that maintains an explicit
-            # reference counter, starting at 0.  When it drops back to
-            # zero, close() must be called.
-            _sock._reuse()
+            _do_reuse_or_drop(_sock, '_reuse')
 
         self._sock = _sock
 
@@ -228,13 +244,13 @@
     def close(self):
         s = self._sock
         self._sock = _closedsocket()
-        s._drop()
+        _do_reuse_or_drop(s, '_drop')
     close.__doc__ = _realsocket.close.__doc__
 
     def accept(self):
         sock, addr = self._sock.accept()
         sockobj = _socketobject(_sock=sock)
-        sock._drop()    # already a copy in the _socketobject()
+        _do_reuse_or_drop(sock, '_drop') # already a copy in the 
_socketobject()
         return sockobj, addr
     accept.__doc__ = _realsocket.accept.__doc__
 
@@ -290,14 +306,7 @@
                  "_close"]
 
     def __init__(self, sock, mode='rb', bufsize=-1, close=False):
-        # Note that a few libraries (like eventlet) poke at the
-        # private implementation of socket.py, passing custom
-        # objects to _fileobject().  These libraries need the
-        # following fix for use on PyPy: the custom objects need
-        # methods _reuse() and _drop() that maintains an explicit
-        # reference counter, starting at 0.  When it drops back to
-        # zero, close() must be called.
-        sock._reuse()
+        _do_reuse_or_drop(sock, '_reuse')
         self._sock = sock
         self.mode = mode # Not actually used in this version
         if bufsize < 0:
@@ -338,7 +347,7 @@
                 if self._close:
                     s.close()
                 else:
-                    s._drop()
+                    _do_reuse_or_drop(s, '_drop')
 
     def __del__(self):
         try:
diff --git a/lib_pypy/_functools.py b/lib_pypy/_functools.py
--- a/lib_pypy/_functools.py
+++ b/lib_pypy/_functools.py
@@ -8,16 +8,16 @@
     partial(func, *args, **keywords) - new function with partial application
     of the given arguments and keywords.
     """
-
-    def __init__(self, *args, **keywords):
-        if not args:
-            raise TypeError('__init__() takes at least 2 arguments (1 given)')
-        func, args = args[0], args[1:]
+    def __init__(*args, **keywords):
+        if len(args) < 2:
+            raise TypeError('__init__() takes at least 2 arguments (%d given)'
+                            % len(args))
+        self, func, args = args[0], args[1], args[2:]
         if not callable(func):
             raise TypeError("the first argument must be callable")
         self._func = func
         self._args = args
-        self._keywords = keywords or None
+        self._keywords = keywords
 
     def __delattr__(self, key):
         if key == '__dict__':
@@ -37,19 +37,22 @@
         return self._keywords
 
     def __call__(self, *fargs, **fkeywords):
-        if self.keywords is not None:
-            fkeywords = dict(self.keywords, **fkeywords)
-        return self.func(*(self.args + fargs), **fkeywords)
+        if self._keywords:
+            fkeywords = dict(self._keywords, **fkeywords)
+        return self._func(*(self._args + fargs), **fkeywords)
 
     def __reduce__(self):
         d = dict((k, v) for k, v in self.__dict__.iteritems() if k not in
                 ('_func', '_args', '_keywords'))
         if len(d) == 0:
             d = None
-        return (type(self), (self.func,),
-                (self.func, self.args, self.keywords, d))
+        return (type(self), (self._func,),
+                (self._func, self._args, self._keywords, d))
 
     def __setstate__(self, state):
-        self._func, self._args, self._keywords, d = state
+        func, args, keywords, d = state
         if d is not None:
             self.__dict__.update(d)
+        self._func = func
+        self._args = args
+        self._keywords = keywords
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
@@ -78,3 +78,6 @@
 .. branch: remove-frame-debug-attrs
 Remove the debug attributes from frames only used for tracing and replace
 them with a debug object that is created on-demand
+
+.. branch: can_cast
+Implement np.can_cast, np.min_scalar_type and missing dtype comparison 
operations.
diff --git a/pypy/module/micronumpy/__init__.py 
b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -20,8 +20,10 @@
         'concatenate': 'arrayops.concatenate',
         'count_nonzero': 'arrayops.count_nonzero',
         'dot': 'arrayops.dot',
-        'result_type': 'arrayops.result_type',
         'where': 'arrayops.where',
+        'result_type': 'casting.result_type',
+        'can_cast': 'casting.can_cast',
+        'min_scalar_type': 'casting.min_scalar_type',
 
         'set_string_function': 'appbridge.set_string_function',
         'typeinfo': 'descriptor.get_dtype_cache(space).w_typeinfo',
diff --git a/pypy/module/micronumpy/arrayops.py 
b/pypy/module/micronumpy/arrayops.py
--- a/pypy/module/micronumpy/arrayops.py
+++ b/pypy/module/micronumpy/arrayops.py
@@ -1,13 +1,11 @@
-from rpython.rlib import jit
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import unwrap_spec
 from pypy.module.micronumpy import loop, descriptor, ufuncs, support, \
     constants as NPY
 from pypy.module.micronumpy.base import convert_to_array, W_NDimArray
 from pypy.module.micronumpy.converters import clipmode_converter
-from pypy.module.micronumpy.strides import Chunk, Chunks, shape_agreement, \
-    shape_agreement_multiple
-from .boxes import W_GenericBox
+from pypy.module.micronumpy.strides import (
+    Chunk, Chunks, shape_agreement, shape_agreement_multiple)
 
 
 def where(space, w_arr, w_x=None, w_y=None):
@@ -285,28 +283,3 @@
     else:
         loop.diagonal_array(space, arr, out, offset, axis1, axis2, shape)
     return out
-
-
-@jit.unroll_safe
-def result_type(space, __args__):
-    args_w, kw_w = __args__.unpack()
-    if kw_w:
-        raise oefmt(space.w_TypeError, "result_type() takes no keyword 
arguments")
-    if not args_w:
-        raise oefmt(space.w_ValueError, "at least one array or dtype is 
required")
-    result = None
-    for w_arg in args_w:
-        if isinstance(w_arg, W_NDimArray):
-            dtype = w_arg.get_dtype()
-        elif isinstance(w_arg, W_GenericBox) or (
-                space.isinstance_w(w_arg, space.w_int) or
-                space.isinstance_w(w_arg, space.w_float) or
-                space.isinstance_w(w_arg, space.w_complex) or
-                space.isinstance_w(w_arg, space.w_long) or
-                space.isinstance_w(w_arg, space.w_bool)):
-            dtype = ufuncs.find_dtype_for_scalar(space, w_arg)
-        else:
-            dtype = space.interp_w(descriptor.W_Dtype,
-                space.call_function(space.gettypefor(descriptor.W_Dtype), 
w_arg))
-        result = ufuncs.find_binop_result_dtype(space, result, dtype)
-    return result
diff --git a/pypy/module/micronumpy/boxes.py b/pypy/module/micronumpy/boxes.py
--- a/pypy/module/micronumpy/boxes.py
+++ b/pypy/module/micronumpy/boxes.py
@@ -874,4 +874,3 @@
     __new__ = interp2app(W_ObjectBox.descr__new__.im_func),
     __getattr__ = interp2app(W_ObjectBox.descr__getattr__),
 )
-
diff --git a/pypy/module/micronumpy/casting.py 
b/pypy/module/micronumpy/casting.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/casting.py
@@ -0,0 +1,108 @@
+"""Functions and helpers for converting between dtypes"""
+
+from rpython.rlib import jit
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.error import oefmt
+
+from pypy.module.micronumpy.base import W_NDimArray, convert_to_array
+from pypy.module.micronumpy import constants as NPY
+from pypy.module.micronumpy.ufuncs import (
+    find_binop_result_dtype, find_dtype_for_scalar)
+from .types import (
+    Bool, ULong, Long, Float64, Complex64, UnicodeType, VoidType, ObjectType)
+from .descriptor import get_dtype_cache, as_dtype, is_scalar_w
+
+@jit.unroll_safe
+def result_type(space, __args__):
+    args_w, kw_w = __args__.unpack()
+    if kw_w:
+        raise oefmt(space.w_TypeError,
+            "result_type() takes no keyword arguments")
+    if not args_w:
+        raise oefmt(space.w_ValueError,
+            "at least one array or dtype is required")
+    result = None
+    for w_arg in args_w:
+        dtype = as_dtype(space, w_arg)
+        result = find_binop_result_dtype(space, result, dtype)
+    return result
+
+@unwrap_spec(casting=str)
+def can_cast(space, w_from, w_totype, casting='safe'):
+    try:
+        target = as_dtype(space, w_totype, allow_None=False)
+    except TypeError:
+        raise oefmt(space.w_TypeError,
+            "did not understand one of the types; 'None' not accepted")
+    if isinstance(w_from, W_NDimArray):
+        return space.wrap(can_cast_array(space, w_from, target, casting))
+    elif is_scalar_w(space, w_from):
+        w_scalar = as_scalar(space, w_from)
+        w_arr = W_NDimArray.from_scalar(space, w_scalar)
+        return space.wrap(can_cast_array(space, w_arr, target, casting))
+
+    try:
+        origin = as_dtype(space, w_from, allow_None=False)
+    except TypeError:
+        raise oefmt(space.w_TypeError,
+            "did not understand one of the types; 'None' not accepted")
+    return space.wrap(can_cast_type(space, origin, target, casting))
+
+kind_ordering = {
+    Bool.kind: 0, ULong.kind: 1, Long.kind: 2,
+    Float64.kind: 4, Complex64.kind: 5,
+    NPY.STRINGLTR: 6, NPY.STRINGLTR2: 6,
+    UnicodeType.kind: 7, VoidType.kind: 8, ObjectType.kind: 9}
+
+def can_cast_type(space, origin, target, casting):
+    # equivalent to PyArray_CanCastTypeTo
+    if casting == 'no':
+        return origin.eq(space, target)
+    elif casting == 'equiv':
+        return origin.num == target.num and origin.elsize == target.elsize
+    elif casting == 'unsafe':
+        return True
+    elif casting == 'same_kind':
+        if origin.can_cast_to(target):
+            return True
+        if origin.kind in kind_ordering and target.kind in kind_ordering:
+            return kind_ordering[origin.kind] <= kind_ordering[target.kind]
+        return False
+    else:
+        return origin.can_cast_to(target)
+
+def can_cast_array(space, w_from, target, casting):
+    # equivalent to PyArray_CanCastArrayTo
+    origin = w_from.get_dtype()
+    if w_from.is_scalar():
+        return can_cast_scalar(
+            space, origin, w_from.get_scalar_value(), target, casting)
+    else:
+        return can_cast_type(space, origin, target, casting)
+
+def can_cast_scalar(space, from_type, value, target, casting):
+    # equivalent to CNumPy's can_cast_scalar_to
+    if from_type == target or casting == 'unsafe':
+        return True
+    if not from_type.is_number() or casting in ('no', 'equiv'):
+        return can_cast_type(space, from_type, target, casting)
+    if not from_type.is_native():
+        value = value.descr_byteswap(space)
+    dtypenum, altnum = value.min_dtype()
+    if target.is_unsigned():
+        dtypenum = altnum
+    dtype = get_dtype_cache(space).dtypes_by_num[dtypenum]
+    return can_cast_type(space, dtype, target, casting)
+
+def as_scalar(space, w_obj):
+    dtype = find_dtype_for_scalar(space, w_obj)
+    return dtype.coerce(space, w_obj)
+
+def min_scalar_type(space, w_a):
+    w_array = convert_to_array(space, w_a)
+    dtype = w_array.get_dtype()
+    if w_array.is_scalar() and dtype.is_number():
+        num, alt_num = w_array.get_scalar_value().min_dtype()
+        return get_dtype_cache(space).dtypes_by_num[num]
+    else:
+        return dtype
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
@@ -8,7 +8,9 @@
 from rpython.rlib import jit
 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 rpython.rlib.signature import finishsigs, signature, types as ann
+from pypy.module.micronumpy import types, boxes, support, constants as NPY
+from .base import W_NDimArray
 from pypy.module.micronumpy.appbridge import get_appbridge_cache
 from pypy.module.micronumpy.converters import byteorder_converter
 
@@ -36,24 +38,21 @@
         if not space.is_none(w_arr):
             dtype = find_binop_result_dtype(space, dtype, w_arr.get_dtype())
     assert dtype is not None
-    out = base.W_NDimArray.from_shape(space, shape, dtype)
+    out = W_NDimArray.from_shape(space, shape, dtype)
     return out
 
 
+_REQ_STRLEN = [0, 3, 5, 10, 10, 20, 20, 20, 20]  # data for can_cast_to()
+
+@finishsigs
 class W_Dtype(W_Root):
     _immutable_fields_ = [
-        "itemtype?", "num", "kind", "char", "w_box_type",
-        "byteorder?", "names?", "fields?", "elsize?", "alignment?",
-        "shape?", "subdtype?", "base?",
-    ]
+        "itemtype?", "w_box_type", "byteorder?", "names?", "fields?",
+        "elsize?", "alignment?", "shape?", "subdtype?", "base?"]
 
-    def __init__(self, itemtype, num, kind, char, w_box_type,
-                 byteorder=None, names=[], fields={},
-                 elsize=None, shape=[], subdtype=None):
+    def __init__(self, itemtype, w_box_type, byteorder=None, names=[],
+                 fields={}, elsize=None, shape=[], subdtype=None):
         self.itemtype = itemtype
-        self.num = num
-        self.kind = kind
-        self.char = char
         self.w_box_type = w_box_type
         if byteorder is None:
             if itemtype.get_element_size() == 1 or isinstance(itemtype, 
types.ObjectType):
@@ -74,6 +73,18 @@
         else:
             self.base = subdtype.base
 
+    @property
+    def num(self):
+        return self.itemtype.num
+
+    @property
+    def kind(self):
+        return self.itemtype.kind
+
+    @property
+    def char(self):
+        return self.itemtype.char
+
     def __repr__(self):
         if self.fields:
             return '<DType %r>' % self.fields
@@ -87,6 +98,41 @@
     def box_complex(self, real, imag):
         return self.itemtype.box_complex(real, imag)
 
+    @signature(ann.self(), ann.self(), returns=ann.bool())
+    def can_cast_to(self, other):
+        # equivalent to PyArray_CanCastTo
+        result = self.itemtype.can_cast_to(other.itemtype)
+        if result:
+            if self.num == NPY.STRING:
+                if other.num == NPY.STRING:
+                    return self.elsize <= other.elsize
+                elif other.num == NPY.UNICODE:
+                    return self.elsize * 4 <= other.elsize
+            elif self.num == NPY.UNICODE and other.num == NPY.UNICODE:
+                return self.elsize <= other.elsize
+            elif other.num in (NPY.STRING, NPY.UNICODE):
+                if other.num == NPY.STRING:
+                    char_size = 1
+                else:  # NPY.UNICODE
+                    char_size = 4
+                if other.elsize == 0:
+                    return True
+                if self.is_bool():
+                    return other.elsize >= 5 * char_size
+                elif self.is_unsigned():
+                    if self.elsize > 8 or self.elsize < 0:
+                        return False
+                    else:
+                        return (other.elsize >=
+                                _REQ_STRLEN[self.elsize] * char_size)
+                elif self.is_signed():
+                    if self.elsize > 8 or self.elsize < 0:
+                        return False
+                    else:
+                        return (other.elsize >=
+                                (_REQ_STRLEN[self.elsize] + 1) * char_size)
+        return result
+
     def coerce(self, space, w_item):
         return self.itemtype.coerce(space, self, w_item)
 
@@ -109,6 +155,9 @@
     def is_complex(self):
         return self.kind == NPY.COMPLEXLTR
 
+    def is_number(self):
+        return self.is_int() or self.is_float() or self.is_complex()
+
     def is_str(self):
         return self.num == NPY.STRING
 
@@ -259,6 +308,22 @@
     def descr_ne(self, space, w_other):
         return space.wrap(not self.eq(space, w_other))
 
+    def descr_le(self, space, w_other):
+        w_other = as_dtype(space, w_other)
+        return space.wrap(self.can_cast_to(w_other))
+
+    def descr_ge(self, space, w_other):
+        w_other = as_dtype(space, w_other)
+        return space.wrap(w_other.can_cast_to(self))
+
+    def descr_lt(self, space, w_other):
+        w_other = as_dtype(space, w_other)
+        return space.wrap(self.can_cast_to(w_other) and not self.eq(space, 
w_other))
+
+    def descr_gt(self, space, w_other):
+        w_other = as_dtype(space, w_other)
+        return space.wrap(w_other.can_cast_to(self) and not self.eq(space, 
w_other))
+
     def _compute_hash(self, space, x):
         from rpython.rlib.rarithmetic import intmask
         if not self.fields and self.subdtype is None:
@@ -450,7 +515,7 @@
         fields = self.fields
         if fields is None:
             fields = {}
-        return W_Dtype(itemtype, self.num, self.kind, self.char,
+        return W_Dtype(itemtype,
                        self.w_box_type, byteorder=endian, elsize=self.elsize,
                        names=self.names, fields=fields,
                        shape=self.shape, subdtype=self.subdtype)
@@ -485,8 +550,7 @@
         fields[fldname] = (offset, subdtype)
         offset += subdtype.elsize
         names.append(fldname)
-    return W_Dtype(types.RecordType(space), NPY.VOID, NPY.VOIDLTR, NPY.VOIDLTR,
-                   space.gettypefor(boxes.W_VoidBox),
+    return W_Dtype(types.RecordType(space), space.gettypefor(boxes.W_VoidBox),
                    names=names, fields=fields, elsize=offset)
 
 
@@ -553,7 +617,7 @@
         if size == 1:
             return subdtype
         size *= subdtype.elsize
-        return W_Dtype(types.VoidType(space), NPY.VOID, NPY.VOIDLTR, 
NPY.VOIDLTR,
+        return W_Dtype(types.VoidType(space),
                        space.gettypefor(boxes.W_VoidBox),
                        shape=shape, subdtype=subdtype, elsize=size)
 
@@ -630,6 +694,10 @@
 
     __eq__ = interp2app(W_Dtype.descr_eq),
     __ne__ = interp2app(W_Dtype.descr_ne),
+    __lt__ = interp2app(W_Dtype.descr_lt),
+    __le__ = interp2app(W_Dtype.descr_le),
+    __gt__ = interp2app(W_Dtype.descr_gt),
+    __ge__ = interp2app(W_Dtype.descr_ge),
     __hash__ = interp2app(W_Dtype.descr_hash),
     __str__= interp2app(W_Dtype.descr_str),
     __repr__ = interp2app(W_Dtype.descr_repr),
@@ -654,7 +722,10 @@
         except ValueError:
             raise oefmt(space.w_TypeError, "data type not understood")
     if char == NPY.CHARLTR:
-        return new_string_dtype(space, 1, NPY.CHARLTR)
+        return W_Dtype(
+            types.CharType(space),
+            elsize=1,
+            w_box_type=space.gettypefor(boxes.W_StringBox))
     elif char == NPY.STRINGLTR or char == NPY.STRINGLTR2:
         return new_string_dtype(space, size)
     elif char == NPY.UNICODELTR:
@@ -664,13 +735,10 @@
     assert False
 
 
-def new_string_dtype(space, size, char=NPY.STRINGLTR):
+def new_string_dtype(space, size):
     return W_Dtype(
         types.StringType(space),
         elsize=size,
-        num=NPY.STRING,
-        kind=NPY.STRINGLTR,
-        char=char,
         w_box_type=space.gettypefor(boxes.W_StringBox),
     )
 
@@ -680,9 +748,6 @@
     return W_Dtype(
         itemtype,
         elsize=size * itemtype.get_element_size(),
-        num=NPY.UNICODE,
-        kind=NPY.UNICODELTR,
-        char=NPY.UNICODELTR,
         w_box_type=space.gettypefor(boxes.W_UnicodeBox),
     )
 
@@ -691,9 +756,6 @@
     return W_Dtype(
         types.VoidType(space),
         elsize=size,
-        num=NPY.VOID,
-        kind=NPY.VOIDLTR,
-        char=NPY.VOIDLTR,
         w_box_type=space.gettypefor(boxes.W_VoidBox),
     )
 
@@ -702,173 +764,93 @@
     def __init__(self, space):
         self.w_booldtype = W_Dtype(
             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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(space),
             elsize=0,
-            num=NPY.STRING,
-            kind=NPY.STRINGLTR,
-            char=NPY.STRINGLTR,
             w_box_type=space.gettypefor(boxes.W_StringBox),
         )
         self.w_unicodedtype = W_Dtype(
             types.UnicodeType(space),
             elsize=0,
-            num=NPY.UNICODE,
-            kind=NPY.UNICODELTR,
-            char=NPY.UNICODELTR,
             w_box_type=space.gettypefor(boxes.W_UnicodeBox),
         )
         self.w_voiddtype = W_Dtype(
             types.VoidType(space),
             elsize=0,
-            num=NPY.VOID,
-            kind=NPY.VOIDLTR,
-            char=NPY.VOIDLTR,
             w_box_type=space.gettypefor(boxes.W_VoidBox),
         )
         self.w_float16dtype = W_Dtype(
             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(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(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 = {
@@ -929,7 +911,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_objectdtype,
+            self.w_objectdtype,
         ]
         self.float_dtypes_by_num_bytes = sorted(
             (dtype.elsize, dtype)
@@ -970,8 +952,7 @@
             'CLONGDOUBLE': self.w_complexlongdtype,
             #'DATETIME',
             'UINT': self.w_uint32dtype,
-            'INTP': self.w_intpdtype,
-            'UINTP': self.w_uintpdtype,
+            'INTP': self.w_longdtype,
             'HALF': self.w_float16dtype,
             'BYTE': self.w_int8dtype,
             #'TIMEDELTA',
@@ -1001,7 +982,11 @@
             space.setitem(w_typeinfo, space.wrap(k), space.gettypefor(v))
         for k, dtype in typeinfo_full.iteritems():
             itembits = dtype.elsize * 8
-            items_w = [space.wrap(dtype.char),
+            if k in ('INTP', 'UINTP'):
+                char = getattr(NPY, k + 'LTR')
+            else:
+                char = dtype.char
+            items_w = [space.wrap(char),
                        space.wrap(dtype.num),
                        space.wrap(itembits),
                        space.wrap(dtype.itemtype.get_element_size())]
@@ -1024,3 +1009,26 @@
 
 def get_dtype_cache(space):
     return space.fromcache(DtypeCache)
+
+def as_dtype(space, w_arg, allow_None=True):
+    from pypy.module.micronumpy.ufuncs import find_dtype_for_scalar
+    # roughly equivalent to CNumPy's PyArray_DescrConverter2
+    if not allow_None and space.is_none(w_arg):
+        raise TypeError("Cannot create dtype from None here")
+    if isinstance(w_arg, W_NDimArray):
+        return w_arg.get_dtype()
+    elif is_scalar_w(space, w_arg):
+        result = find_dtype_for_scalar(space, w_arg)
+        assert result is not None  # XXX: not guaranteed
+        return result
+    else:
+        return space.interp_w(W_Dtype,
+            space.call_function(space.gettypefor(W_Dtype), w_arg))
+
+def is_scalar_w(space, w_arg):
+    return (isinstance(w_arg, boxes.W_GenericBox) or
+            space.isinstance_w(w_arg, space.w_int) or
+            space.isinstance_w(w_arg, space.w_float) or
+            space.isinstance_w(w_arg, space.w_complex) or
+            space.isinstance_w(w_arg, space.w_long) or
+            space.isinstance_w(w_arg, space.w_bool))
diff --git a/pypy/module/micronumpy/test/test_arrayops.py 
b/pypy/module/micronumpy/test/test_arrayops.py
--- a/pypy/module/micronumpy/test/test_arrayops.py
+++ b/pypy/module/micronumpy/test/test_arrayops.py
@@ -199,19 +199,3 @@
         a.put(23, -1, mode=1)  # wrap
         assert (a == array([0, 1, -10, -1, -15])).all()
         raises(TypeError, "arange(5).put(22, -5, mode='zzzz')")  # 
unrecognized mode
-
-    def test_result_type(self):
-        import numpy as np
-        exc = raises(ValueError, np.result_type)
-        assert str(exc.value) == "at least one array or dtype is required"
-        exc = raises(TypeError, np.result_type, a=2)
-        assert str(exc.value) == "result_type() takes no keyword arguments"
-        assert np.result_type(True) is np.dtype('bool')
-        assert np.result_type(1) is np.dtype('int')
-        assert np.result_type(1.) is np.dtype('float64')
-        assert np.result_type(1+2j) is np.dtype('complex128')
-        assert np.result_type(1, 1.) is np.dtype('float64')
-        assert np.result_type(np.array([1, 2])) is np.dtype('int')
-        assert np.result_type(np.array([1, 2]), 1, 1+2j) is 
np.dtype('complex128')
-        assert np.result_type(np.array([1, 2]), 1, 'float64') is 
np.dtype('float64')
-        assert np.result_type(np.array([1, 2]), 1, None) is np.dtype('float64')
diff --git a/pypy/module/micronumpy/test/test_casting.py 
b/pypy/module/micronumpy/test/test_casting.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_casting.py
@@ -0,0 +1,121 @@
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+
+class AppTestNumSupport(BaseNumpyAppTest):
+    def test_result_type(self):
+        import numpy as np
+        exc = raises(ValueError, np.result_type)
+        assert str(exc.value) == "at least one array or dtype is required"
+        exc = raises(TypeError, np.result_type, a=2)
+        assert str(exc.value) == "result_type() takes no keyword arguments"
+        assert np.result_type(True) is np.dtype('bool')
+        assert np.result_type(1) is np.dtype('int')
+        assert np.result_type(1.) is np.dtype('float64')
+        assert np.result_type(1+2j) is np.dtype('complex128')
+        assert np.result_type(1, 1.) is np.dtype('float64')
+        assert np.result_type(np.array([1, 2])) is np.dtype('int')
+        assert np.result_type(np.array([1, 2]), 1, 1+2j) is 
np.dtype('complex128')
+        assert np.result_type(np.array([1, 2]), 1, 'float64') is 
np.dtype('float64')
+        assert np.result_type(np.array([1, 2]), 1, None) is np.dtype('float64')
+
+    def test_can_cast(self):
+        import numpy as np
+
+        assert np.can_cast(np.int32, np.int64)
+        assert np.can_cast(np.float64, complex)
+        assert not np.can_cast(np.complex64, float)
+
+        assert np.can_cast('i8', 'f8')
+        assert not np.can_cast('i8', 'f4')
+        assert np.can_cast('i4', 'S11')
+
+        assert np.can_cast('i8', 'i8', 'no')
+        assert not np.can_cast('<i8', '>i8', 'no')
+
+        assert np.can_cast('<i8', '>i8', 'equiv')
+        assert not np.can_cast('<i4', '>i8', 'equiv')
+
+        assert np.can_cast('<i4', '>i8', 'safe')
+        assert not np.can_cast('<i8', '>i4', 'safe')
+
+        assert np.can_cast('<i8', '>i4', 'same_kind')
+        assert not np.can_cast('<i8', '>u4', 'same_kind')
+
+        assert np.can_cast('<i8', '>u4', 'unsafe')
+
+        assert np.can_cast('bool', 'S5')
+        assert not np.can_cast('bool', 'S4')
+
+        assert np.can_cast('b', 'S4')
+        assert not np.can_cast('b', 'S3')
+
+        assert np.can_cast('u1', 'S3')
+        assert not np.can_cast('u1', 'S2')
+        assert np.can_cast('u2', 'S5')
+        assert not np.can_cast('u2', 'S4')
+        assert np.can_cast('u4', 'S10')
+        assert not np.can_cast('u4', 'S9')
+        assert np.can_cast('u8', 'S20')
+        assert not np.can_cast('u8', 'S19')
+
+        assert np.can_cast('i1', 'S4')
+        assert not np.can_cast('i1', 'S3')
+        assert np.can_cast('i2', 'S6')
+        assert not np.can_cast('i2', 'S5')
+        assert np.can_cast('i4', 'S11')
+        assert not np.can_cast('i4', 'S10')
+        assert np.can_cast('i8', 'S21')
+        assert not np.can_cast('i8', 'S20')
+
+        assert np.can_cast('bool', 'S5')
+        assert not np.can_cast('bool', 'S4')
+
+        assert np.can_cast('b', 'U4')
+        assert not np.can_cast('b', 'U3')
+
+        assert np.can_cast('u1', 'U3')
+        assert not np.can_cast('u1', 'U2')
+        assert np.can_cast('u2', 'U5')
+        assert not np.can_cast('u2', 'U4')
+        assert np.can_cast('u4', 'U10')
+        assert not np.can_cast('u4', 'U9')
+        assert np.can_cast('u8', 'U20')
+        assert not np.can_cast('u8', 'U19')
+
+        assert np.can_cast('i1', 'U4')
+        assert not np.can_cast('i1', 'U3')
+        assert np.can_cast('i2', 'U6')
+        assert not np.can_cast('i2', 'U5')
+        assert np.can_cast('i4', 'U11')
+        assert not np.can_cast('i4', 'U10')
+        assert np.can_cast('i8', 'U21')
+        assert not np.can_cast('i8', 'U20')
+
+        raises(TypeError, np.can_cast, 'i4', None)
+        raises(TypeError, np.can_cast, None, 'i4')
+
+    def test_can_cast_scalar(self):
+        import numpy as np
+        assert np.can_cast(True, np.bool_)
+        assert np.can_cast(True, np.int8)
+        assert not np.can_cast(0, np.bool_)
+        assert np.can_cast(127, np.int8)
+        assert not np.can_cast(128, np.int8)
+        assert np.can_cast(128, np.int16)
+
+        assert np.can_cast(np.float32('inf'), np.float32)
+        assert np.can_cast(float('inf'), np.float32)  # XXX: False in CNumPy?!
+        assert np.can_cast(3.3e38, np.float32)
+        assert not np.can_cast(3.4e38, np.float32)
+
+        assert np.can_cast(1 + 2j, np.complex64)
+        assert not np.can_cast(1 + 1e50j, np.complex64)
+        assert np.can_cast(1., np.complex64)
+        assert not np.can_cast(1e50, np.complex64)
+
+    def test_min_scalar_type(self):
+        import numpy as np
+        assert np.min_scalar_type(2**8 - 1) == np.dtype('uint8')
+        assert np.min_scalar_type(2**64 - 1) == np.dtype('uint64')
+        # XXX: np.asarray(2**64) fails with OverflowError
+        # assert np.min_scalar_type(2**64) == np.dtype('O')
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
@@ -112,6 +112,11 @@
         raises(TypeError, lambda: dtype("int8") == 3)
         assert dtype(bool) == bool
 
+    def test_dtype_cmp(self):
+        from numpy import dtype
+        assert dtype('int8') <= dtype('int8')
+        assert not (dtype('int8') < dtype('int8'))
+
     def test_dtype_aliases(self):
         from numpy import dtype
         assert dtype('bool8') is dtype('bool')
@@ -1287,7 +1292,7 @@
         from cPickle import loads, dumps
 
         d = dtype([("x", "int32"), ("y", "int32"), ("z", "int32"), ("value", 
float)])
-        assert d.__reduce__() == (dtype, ('V20', 0, 1), (3, '|', None, 
+        assert d.__reduce__() == (dtype, ('V20', 0, 1), (3, '|', None,
                      ('x', 'y', 'z', 'value'),
                      {'y': (dtype('int32'), 4), 'x': (dtype('int32'), 0),
                       'z': (dtype('int32'), 8), 'value': (dtype('float64'), 
12),
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
@@ -1818,7 +1818,7 @@
         s[...] = 2
         v = s.view(x.__class__)
         assert (v == 2).all()
-    
+
     def test_tolist_scalar(self):
         from numpy import dtype
         int32 = dtype('int32').type
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
@@ -1,5 +1,6 @@
 import functools
 import math
+from rpython.rlib.unroll import unrolling_iterable
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.objspace.std.floatobject import float2string
 from pypy.objspace.std.complexobject import str_format
@@ -22,6 +23,7 @@
 from pypy.module.micronumpy import boxes
 from pypy.module.micronumpy.concrete import SliceArray, VoidBoxStorage, 
V_OBJECTSTORE
 from pypy.module.micronumpy.strides import calc_strides
+from . import constants as NPY
 
 degToRad = math.pi / 180.0
 log2 = math.log(2)
@@ -128,6 +130,14 @@
         else:
             return alloc_raw_storage(size, track_allocation=False, zero=False)
 
+    @classmethod
+    def basesize(cls):
+        return rffi.sizeof(cls.T)
+
+    def can_cast_to(self, other):
+        # equivalent to PyArray_CanCastSafely
+        return casting_table[self.num][other.num]
+
 class Primitive(object):
     _mixin_ = True
 
@@ -316,6 +326,9 @@
 
 class Bool(BaseType, Primitive):
     T = lltype.Bool
+    num = NPY.BOOL
+    kind = NPY.GENBOOLLTR
+    char = NPY.BOOLLTR
     BoxType = boxes.W_BoolBox
     format_code = "?"
 
@@ -408,6 +421,7 @@
 
 class Integer(Primitive):
     _mixin_ = True
+    signed = True
 
     def _base_coerce(self, space, w_item):
         if w_item is None:
@@ -551,33 +565,54 @@
 
 class Int8(BaseType, Integer):
     T = rffi.SIGNEDCHAR
+    num = NPY.BYTE
+    kind = NPY.SIGNEDLTR
+    char = NPY.BYTELTR
     BoxType = boxes.W_Int8Box
     format_code = "b"
 
 class UInt8(BaseType, Integer):
     T = rffi.UCHAR
+    num = NPY.UBYTE
+    kind = NPY.UNSIGNEDLTR
+    char = NPY.UBYTELTR
     BoxType = boxes.W_UInt8Box
     format_code = "B"
+    signed = False
 
 class Int16(BaseType, Integer):
     T = rffi.SHORT
+    num = NPY.SHORT
+    kind = NPY.SIGNEDLTR
+    char = NPY.SHORTLTR
     BoxType = boxes.W_Int16Box
     format_code = "h"
 
 class UInt16(BaseType, Integer):
     T = rffi.USHORT
+    num = NPY.USHORT
+    kind = NPY.UNSIGNEDLTR
+    char = NPY.USHORTLTR
     BoxType = boxes.W_UInt16Box
     format_code = "H"
+    signed = False
 
 class Int32(BaseType, Integer):
     T = rffi.INT
+    num = NPY.INT
+    kind = NPY.SIGNEDLTR
+    char = NPY.INTLTR
     BoxType = boxes.W_Int32Box
     format_code = "i"
 
 class UInt32(BaseType, Integer):
     T = rffi.UINT
+    num = NPY.UINT
+    kind = NPY.UNSIGNEDLTR
+    char = NPY.UINTLTR
     BoxType = boxes.W_UInt32Box
     format_code = "I"
+    signed = False
 
 def _int64_coerce(self, space, w_item):
     try:
@@ -594,6 +629,9 @@
 
 class Int64(BaseType, Integer):
     T = rffi.LONGLONG
+    num = NPY.LONGLONG
+    kind = NPY.SIGNEDLTR
+    char = NPY.LONGLONGLTR
     BoxType = boxes.W_Int64Box
     format_code = "q"
 
@@ -615,13 +653,20 @@
 
 class UInt64(BaseType, Integer):
     T = rffi.ULONGLONG
+    num = NPY.ULONGLONG
+    kind = NPY.UNSIGNEDLTR
+    char = NPY.ULONGLONGLTR
     BoxType = boxes.W_UInt64Box
     format_code = "Q"
+    signed = False
 
     _coerce = func_with_new_name(_uint64_coerce, '_coerce')
 
 class Long(BaseType, Integer):
     T = rffi.LONG
+    num = NPY.LONG
+    kind = NPY.SIGNEDLTR
+    char = NPY.LONGLTR
     BoxType = boxes.W_LongBox
     format_code = "l"
 
@@ -640,8 +685,12 @@
 
 class ULong(BaseType, Integer):
     T = rffi.ULONG
+    num = NPY.ULONG
+    kind = NPY.UNSIGNEDLTR
+    char = NPY.ULONGLTR
     BoxType = boxes.W_ULongBox
     format_code = "L"
+    signed = False
 
     _coerce = func_with_new_name(_ulong_coerce, '_coerce')
 
@@ -974,7 +1023,11 @@
 class Float16(BaseType, Float):
     _STORAGE_T = rffi.USHORT
     T = rffi.SHORT
+    num = NPY.HALF
+    kind = NPY.FLOATINGLTR
+    char = NPY.HALFLTR
     BoxType = boxes.W_Float16Box
+    max_value = 65000.
 
     @specialize.argtype(1)
     def box(self, value):
@@ -1014,13 +1067,21 @@
 
 class Float32(BaseType, Float):
     T = rffi.FLOAT
+    num = NPY.FLOAT
+    kind = NPY.FLOATINGLTR
+    char = NPY.FLOATLTR
     BoxType = boxes.W_Float32Box
     format_code = "f"
+    max_value = 3.4e38
 
 class Float64(BaseType, Float):
     T = rffi.DOUBLE
+    num = NPY.DOUBLE
+    kind = NPY.FLOATINGLTR
+    char = NPY.DOUBLELTR
     BoxType = boxes.W_Float64Box
     format_code = "d"
+    max_value = 1.7e308
 
 class ComplexFloating(object):
     _mixin_ = True
@@ -1592,28 +1653,46 @@
 
 class Complex64(ComplexFloating, BaseType):
     T = rffi.FLOAT
+    num = NPY.CFLOAT
+    kind = NPY.COMPLEXLTR
+    char = NPY.CFLOATLTR
     BoxType = boxes.W_Complex64Box
     ComponentBoxType = boxes.W_Float32Box
+    ComponentType = Float32
 
 class Complex128(ComplexFloating, BaseType):
     T = rffi.DOUBLE
+    num = NPY.CDOUBLE
+    kind = NPY.COMPLEXLTR
+    char = NPY.CDOUBLELTR
     BoxType = boxes.W_Complex128Box
     ComponentBoxType = boxes.W_Float64Box
+    ComponentType = Float64
 
 if boxes.long_double_size == 8:
     class FloatLong(BaseType, Float):
         T = rffi.DOUBLE
+        num = NPY.LONGDOUBLE
+        kind = NPY.FLOATINGLTR
+        char = NPY.LONGDOUBLELTR
         BoxType = boxes.W_FloatLongBox
         format_code = "d"
 
     class ComplexLong(ComplexFloating, BaseType):
         T = rffi.DOUBLE
+        num = NPY.CLONGDOUBLE
+        kind = NPY.COMPLEXLTR
+        char = NPY.CLONGDOUBLELTR
         BoxType = boxes.W_ComplexLongBox
         ComponentBoxType = boxes.W_FloatLongBox
+        ComponentType = FloatLong
 
 elif boxes.long_double_size in (12, 16):
     class FloatLong(BaseType, Float):
         T = rffi.LONGDOUBLE
+        num = NPY.LONGDOUBLE
+        kind = NPY.FLOATINGLTR
+        char = NPY.LONGDOUBLELTR
         BoxType = boxes.W_FloatLongBox
 
         def runpack_str(self, space, s):
@@ -1631,13 +1710,20 @@
 
     class ComplexLong(ComplexFloating, BaseType):
         T = rffi.LONGDOUBLE
+        num = NPY.CLONGDOUBLE
+        kind = NPY.COMPLEXLTR
+        char = NPY.CLONGDOUBLELTR
         BoxType = boxes.W_ComplexLongBox
         ComponentBoxType = boxes.W_FloatLongBox
+        ComponentType = FloatLong
 
 _all_objs_for_tests = [] # for tests
 
 class ObjectType(Primitive, BaseType):
     T = lltype.Signed
+    num = NPY.OBJECT
+    kind = NPY.OBJECTLTR
+    char = NPY.OBJECTLTR
     BoxType = boxes.W_ObjectBox
 
     def get_element_size(self):
@@ -1698,7 +1784,7 @@
         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):
@@ -1949,6 +2035,9 @@
 
 class StringType(FlexibleType):
     T = lltype.Char
+    num = NPY.STRING
+    kind = NPY.STRINGLTR
+    char = NPY.STRINGLTR
 
     @jit.unroll_safe
     def coerce(self, space, dtype, w_item):
@@ -2046,6 +2135,9 @@
 
 class UnicodeType(FlexibleType):
     T = lltype.Char
+    num = NPY.UNICODE
+    kind = NPY.UNICODELTR
+    char = NPY.UNICODELTR
 
     def get_element_size(self):
         return 4  # always UTF-32
@@ -2110,6 +2202,9 @@
 
 class VoidType(FlexibleType):
     T = lltype.Char
+    num = NPY.VOID
+    kind = NPY.VOIDLTR
+    char = NPY.VOIDLTR
 
     def _coerce(self, space, arr, ofs, dtype, w_items, shape):
         # TODO: Make sure the shape and the array match
@@ -2194,8 +2289,14 @@
                     "item() for Void aray with no fields not implemented"))
         return space.newtuple(ret_unwrapped)
 
+class CharType(StringType):
+    char = NPY.CHARLTR
+
 class RecordType(FlexibleType):
     T = lltype.Char
+    num = NPY.VOID
+    kind = NPY.VOIDLTR
+    char = NPY.VOIDLTR
 
     def read(self, arr, i, offset, dtype=None):
         if dtype is None:
@@ -2313,8 +2414,11 @@
 del tp
 
 all_float_types = []
+float_types = []
 all_int_types = []
+int_types = []
 all_complex_types = []
+complex_types = []
 
 def _setup():
     # compute alignment
@@ -2323,9 +2427,168 @@
             tp.alignment = 
widen(clibffi.cast_type_to_ffitype(tp.T).c_alignment)
             if issubclass(tp, Float):
                 all_float_types.append((tp, 'float'))
+                float_types.append(tp)
             if issubclass(tp, Integer):
                 all_int_types.append((tp, 'int'))
+                int_types.append(tp)
             if issubclass(tp, ComplexFloating):
                 all_complex_types.append((tp, 'complex'))
+                complex_types.append(tp)
 _setup()
 del _setup
+
+casting_table = [[False] * NPY.NTYPES for _ in range(NPY.NTYPES)]
+number_types = int_types + float_types + complex_types
+all_types = number_types + [ObjectType, StringType, UnicodeType, VoidType]
+
+def enable_cast(type1, type2):
+    casting_table[type1.num][type2.num] = True
+
+for tp in all_types:
+    enable_cast(tp, tp)
+    if tp.num != NPY.DATETIME:
+        enable_cast(Bool, tp)
+    enable_cast(tp, ObjectType)
+    enable_cast(tp, VoidType)
+enable_cast(StringType, UnicodeType)
+#enable_cast(Bool, TimeDelta)
+
+for tp in number_types:
+    enable_cast(tp, StringType)
+    enable_cast(tp, UnicodeType)
+
+for tp1 in int_types:
+    for tp2 in int_types:
+        if tp1.signed:
+            if tp2.signed and tp1.basesize() <= tp2.basesize():
+                enable_cast(tp1, tp2)
+        else:
+            if tp2.signed and tp1.basesize() < tp2.basesize():
+                enable_cast(tp1, tp2)
+            elif not tp2.signed and tp1.basesize() <= tp2.basesize():
+                enable_cast(tp1, tp2)
+for tp1 in int_types:
+    for tp2 in float_types + complex_types:
+        size1 = tp1.basesize()
+        size2 = tp2.basesize()
+        if (size1 < 8 and size2 > size1) or (size1 >= 8 and size2 >= size1):
+            enable_cast(tp1, tp2)
+for tp1 in float_types:
+    for tp2 in float_types + complex_types:
+        if tp1.basesize() <= tp2.basesize():
+            enable_cast(tp1, tp2)
+for tp1 in complex_types:
+    for tp2 in complex_types:
+        if tp1.basesize() <= tp2.basesize():
+            enable_cast(tp1, tp2)
+
+_int_types = [(Int8, UInt8), (Int16, UInt16), (Int32, UInt32),
+        (Int64, UInt64), (Long, ULong)]
+for Int_t, UInt_t in _int_types:
+    Int_t.Unsigned = UInt_t
+    UInt_t.Signed = Int_t
+    size = rffi.sizeof(Int_t.T)
+    Int_t.min_value = rffi.cast(Int_t.T, -1) << (8*size - 1)
+    Int_t.max_value = ~Int_t.min_value
+    UInt_t.max_value = ~rffi.cast(UInt_t.T, 0)
+
+
+signed_types = [Int8, Int16, Int32, Int64, Long]
+
+def make_integer_min_dtype(Int_t, UInt_t):
+    smaller_types = [tp for tp in signed_types
+            if rffi.sizeof(tp.T) < rffi.sizeof(Int_t.T)]
+    smaller_types = unrolling_iterable(
+            [(tp, tp.Unsigned) for tp in smaller_types])
+    def min_dtype(self):
+        value = rffi.cast(UInt64.T, self.value)
+        for Small, USmall in smaller_types:
+            signed_max = rffi.cast(UInt64.T, Small.max_value)
+            unsigned_max = rffi.cast(UInt64.T, USmall.max_value)
+            if value <= unsigned_max:
+                if value <= signed_max:
+                    return Small.num, USmall.num
+                else:
+                    return USmall.num, USmall.num
+        if value <= rffi.cast(UInt64.T, Int_t.max_value):
+            return Int_t.num, UInt_t.num
+        else:
+            return UInt_t.num, UInt_t.num
+    UInt_t.BoxType.min_dtype = min_dtype
+
+    def min_dtype(self):
+        value = rffi.cast(Int64.T, self.value)
+        if value >= 0:
+            for Small, USmall in smaller_types:
+                signed_max = rffi.cast(Int64.T, Small.max_value)
+                unsigned_max = rffi.cast(Int64.T, USmall.max_value)
+                if value <= unsigned_max:
+                    if value <= signed_max:
+                        return Small.num, USmall.num
+                    else:
+                        return USmall.num, USmall.num
+            return Int_t.num, UInt_t.num
+        else:
+            for Small, USmall in smaller_types:
+                signed_min = rffi.cast(Int64.T, Small.min_value)
+                if value >= signed_min:
+                        return Small.num, Small.num
+            return Int_t.num, Int_t.num
+    Int_t.BoxType.min_dtype = min_dtype
+
+for Int_t in signed_types:
+    UInt_t = Int_t.Unsigned
+    make_integer_min_dtype(Int_t, UInt_t)
+
+
+smaller_float_types = {
+    Float16: [], Float32: [Float16], Float64: [Float16, Float32],
+    FloatLong: [Float16, Float32, Float64]}
+
+def make_float_min_dtype(Float_t):
+    smaller_types = unrolling_iterable(smaller_float_types[Float_t])
+    smallest_type = Float16
+
+    def min_dtype(self):
+        value = float(self.value)
+        if not rfloat.isfinite(value):
+            tp = smallest_type
+        else:
+            for SmallFloat in smaller_types:
+                if -SmallFloat.max_value < value < SmallFloat.max_value:
+                    tp = SmallFloat
+                    break
+            else:
+                tp = Float_t
+        return tp.num, tp.num
+    Float_t.BoxType.min_dtype = min_dtype
+
+for Float_t in float_types:
+    make_float_min_dtype(Float_t)
+
+smaller_complex_types = {
+    Complex64: [], Complex128: [Complex64],
+    ComplexLong: [Complex64, Complex128]}
+
+def make_complex_min_dtype(Complex_t):
+    smaller_types = unrolling_iterable(smaller_complex_types[Complex_t])
+
+    def min_dtype(self):
+        real, imag = float(self.real), float(self.imag)
+        for CSmall in smaller_types:
+            max_value = CSmall.ComponentType.max_value
+
+            if -max_value < real < max_value and -max_value < imag < max_value:
+                tp = CSmall
+                break
+        else:
+            tp = Complex_t
+        return tp.num, tp.num
+    Complex_t.BoxType.min_dtype = min_dtype
+
+for Complex_t in complex_types:
+    make_complex_min_dtype(Complex_t)
+
+def min_dtype(self):
+    return Bool.num, Bool.num
+Bool.BoxType.min_dtype = min_dtype
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py 
b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -435,7 +435,6 @@
             guard_value(i4, 1, descr=...)
             guard_isnull(p5, descr=...)
             guard_nonnull_class(p12, ConstClass(W_IntObject), descr=...)
-            guard_value(i8, 0, descr=...)
             guard_value(p2, ConstPtr(ptr21), descr=...)
             i22 = getfield_gc_pure(p12, descr=<FieldS 
pypy.objspace.std.intobject.W_IntObject.inst_intval .*>)
             i24 = int_lt(i22, 5000)
diff --git a/pypy/module/test_lib_pypy/test_functools.py 
b/pypy/module/test_lib_pypy/test_functools.py
--- a/pypy/module/test_lib_pypy/test_functools.py
+++ b/pypy/module/test_lib_pypy/test_functools.py
@@ -6,8 +6,10 @@
 def test_partial_reduce():
     partial = _functools.partial(test_partial_reduce)
     state = partial.__reduce__()
+    d = state[2][2]
     assert state == (type(partial), (test_partial_reduce,),
-                     (test_partial_reduce, (), None, None))
+                     (test_partial_reduce, (), d, None))
+    assert d is None or d == {}      # both are acceptable
 
 def test_partial_setstate():
     partial = _functools.partial(object)
@@ -30,3 +32,15 @@
     assert str(exc.value) == "a partial object's dictionary may not be deleted"
     with pytest.raises(AttributeError):
         del partial.zzz
+
+def test_self_keyword():
+    partial = _functools.partial(dict, self=42)
+    assert partial(other=43) == {'self': 42, 'other': 43}
+
+def test_no_keywords():
+    kw1 = _functools.partial(dict).keywords
+    kw2 = _functools.partial(dict, **{}).keywords
+    # CPython gives different results for these two cases, which is not
+    # possible to emulate in pure Python; see issue #2043
+    assert kw1 == {} or kw1 is None
+    assert kw2 == {}
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to