BUG: Fix PyType_FastSubclass() check for numpy.int_ and Py3k inheritance by zyv · Pull Request #3526 · numpy/numpy

The tp_flags change sounds correct, but the py3 issue is a doc bug --
np.int_ is not intended to inherit from int on py3, because on py3 int
is not longer a fixed-width integer type.

On Tue, Jul 16, 2013 at 2:40 PM, Yury V. Zaytsev
notifications@github.com wrote:

Quote from the documentation at
http://docs.scipy.org/doc/numpy/reference/arrays.scalars.html :

Five of the scalar types are essentially equivalent to fundamental Python
types and therefore inherit from them as well as from the generic array
scalar type <...>

However, presently, the code deviates from this document in two ways:

  1. For numpy.int_ type, the correct tp_flags are not set, which means that
    if one uses specialized fast-path Python C-API macro PyInt_Check on its
    instances to check whether the object is a subclass of a Python builtin, the
    result will be different (False) as compared to PyObject_TypeCheck (True).

This results in a different behavior of seemingly the same logic run through
the Python interpreter using its MRO facilities and hand-written or
generated modules, using optimized C-API calls directly.

  1. If one is using Py3k, then the code is expanded only to contain
    SINGLE_INHERIT macros, which means that numpy.int_ is not a subclass of int
    under Py3k at all.

My patch fixes these two issues by (a) introducing new macro
DUAL_INHERIT_INT, which expands to DUAL_INHERIT(XXX, Long, XXX) under Py3k
and DUAL_INHERIT(XXX, Int, XXX) otherwise and (b) setting tp_flags correctly
from within this macro.

In order to test my changes, you will need the following minimal Cython
program that generates code using optimized C-API calls:

def inspect(x):
print("My MRO: {0}".format(type.mro(type(x))))
if isinstance(x, int):
print("I'm an normal integer number")
elif isinstance(x, long):
print("I'm an < Py3k long integer number")
elif isinstance(x, float):
print("I'm a floating point number")
else:
print("I don't know who I am")

Test output on the x86_64 machine:

Python 2.7, without patch

In [1]: import bt

In [2]: import numpy as np

In [3]: type.mro(type(np.int_(0)))
Out[3]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, int, object]

In [4]: type.mro(type(np.int32(0)))
Out[4]: [numpy.int32, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, object]

In [5]: type.mro(type(np.int64(0)))
Out[5]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, int, object]

In [6]: bt.inspect(np.int_(0))
My MRO: [<type 'numpy.int64'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'int'>, <type 'object'>]
I don't know who I am <--- WRONG!

In [7]: bt.inspect(np.int32(0))
My MRO: [<type 'numpy.int32'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'object'>]
I don't know who I am

In [8]: bt.inspect(np.int64(0))
My MRO: [<type 'numpy.int64'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'int'>, <type 'object'>]
I don't know who I am <--- WRONG!

Python 3.2, without patch

In [1]: import bt

In [2]: import numpy as np

In [3]: type.mro(type(np.int_(0)))
Out[3]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.object]
^^^^ WRONG!
In [4]: type.mro(type(np.int32(0)))
Out[4]: [numpy.int32, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.object]

In [5]: type.mro(type(np.int64(0)))
Out[5]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.object]
^^^^ WRONG!
In [6]: bt.inspect(np.int_(0))
My MRO: [<class 'numpy.int64'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'object'>]
I don't know who I am
^^^^ WRONG!
In [7]: bt.inspect(np.int32(0))
My MRO: [<class 'numpy.int32'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'object'>]
I don't know who I am

In [8]: bt.inspect(np.int64(0))
My MRO: [<class 'numpy.int64'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'object'>]
I don't know who I am
^^^^ WRONG!

Python 2.7, with patch

In [1]: import bt

In [2]: import numpy as np

In [3]: type.mro(type(np.int_(0)))
Out[3]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, int, object]

In [4]: type.mro(type(np.int32(0)))
Out[4]: [numpy.int32, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, object]

In [5]: type.mro(type(np.int64(0)))
Out[5]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, int, object]

In [6]: bt.inspect(np.int_(0))
My MRO: [<type 'numpy.int64'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'int'>, <type 'object'>]
I'm an normal integer number
^^^^ FIXED!
In [7]: bt.inspect(np.int32(0))
My MRO: [<type 'numpy.int32'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'object'>]
I don't know who I am

In [8]: bt.inspect(np.int64(0))
My MRO: [<type 'numpy.int64'>, <type 'numpy.signedinteger'>, <type
'numpy.integer'>, <type 'numpy.number'>, <type 'numpy.generic'>, <type
'int'>, <type 'object'>]
I'm an normal integer number
^^^^ FIXED!

Python 3.2, with patch

In [1]: import bt

In [2]: import numpy as np

In [3]: type.mro(type(np.int_(0)))
Out[3]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.int, builtins.object]
^^^^ FIXED!
In [4]: type.mro(type(np.int32(0)))
Out[4]: [numpy.int32, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.object]

In [5]: type.mro(type(np.int64(0)))
Out[5]: [numpy.int64, numpy.signedinteger, numpy.integer, numpy.number,
numpy.generic, builtins.int, builtins.object]
^^^^ FIXED!
In [6]: bt.inspect(np.int_(0))
My MRO: [<class 'numpy.int64'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'int'>, <class 'object'>]
I'm an normal integer number
^^^^ FIXED!
In [7]: bt.inspect(np.int32(0))
My MRO: [<class 'numpy.int32'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'object'>]
I don't know who I am

In [8]: bt.inspect(np.int64(0))
My MRO: [<class 'numpy.int64'>, <class 'numpy.signedinteger'>, <class
'numpy.integer'>, <class 'numpy.number'>, <class 'numpy.generic'>, <class
'int'>, <class 'object'>]
I'm an normal integer number
^^^^ FIXED!

Thank you for considering my bugfix for inclusion in NumPy,

--Yury.


You can merge this Pull Request by running

git pull https://github.com/zyv/numpy fix_int_inheritance

Or view, comment on, or merge it at:

#3526

Commit Summary

BUG: Fix PyType_FastSubclass() check for numpy.int_ and Py3k inheritance

File Changes

M numpy/core/src/multiarray/multiarraymodule.c (34)

Patch Links:

https://github.com/numpy/numpy/pull/3526.patch
https://github.com/numpy/numpy/pull/3526.diff