def numpy_core_numerictypes_transform(): # TODO: Uniformize the generic API with the ndarray one. # According to numpy doc the generic object should expose # the same API than ndarray. This has been done here partially # through the astype method. generic_src = """ class generic(object): def __init__(self, value): self.T = np.ndarray([0, 0]) self.base = None self.data = None self.dtype = None self.flags = None # Should be a numpy.flatiter instance but not available for now # Putting an array instead so that iteration and indexing are authorized self.flat = np.ndarray([0, 0]) self.imag = None self.itemsize = None self.nbytes = None self.ndim = None self.real = None self.size = None self.strides = None def all(self): return uninferable def any(self): return uninferable def argmax(self): return uninferable def argmin(self): return uninferable def argsort(self): return uninferable def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0]) def base(self): return uninferable def byteswap(self): return uninferable def choose(self): return uninferable def clip(self): return uninferable def compress(self): return uninferable def conj(self): return uninferable def conjugate(self): return uninferable def copy(self): return uninferable def cumprod(self): return uninferable def cumsum(self): return uninferable def data(self): return uninferable def diagonal(self): return uninferable def dtype(self): return uninferable def dump(self): return uninferable def dumps(self): return uninferable def fill(self): return uninferable def flags(self): return uninferable def flat(self): return uninferable def flatten(self): return uninferable def getfield(self): return uninferable def imag(self): return uninferable def item(self): return uninferable def itemset(self): return uninferable def itemsize(self): return uninferable def max(self): return uninferable def mean(self): return uninferable def min(self): return uninferable def nbytes(self): return uninferable def ndim(self): return uninferable def newbyteorder(self): return uninferable def nonzero(self): return uninferable def prod(self): return uninferable def ptp(self): return uninferable def put(self): return uninferable def ravel(self): return uninferable def real(self): return uninferable def repeat(self): return uninferable def reshape(self): return uninferable def resize(self): return uninferable def round(self): return uninferable def searchsorted(self): return uninferable def setfield(self): return uninferable def setflags(self): return uninferable def shape(self): return uninferable def size(self): return uninferable def sort(self): return uninferable def squeeze(self): return uninferable def std(self): return uninferable def strides(self): return uninferable def sum(self): return uninferable def swapaxes(self): return uninferable def take(self): return uninferable def tobytes(self): return uninferable def tofile(self): return uninferable def tolist(self): return uninferable def tostring(self): return uninferable def trace(self): return uninferable def transpose(self): return uninferable def var(self): return uninferable def view(self): return uninferable """ if numpy_supports_type_hints(): generic_src += """ @classmethod def __class_getitem__(cls, value): return cls """ return parse(generic_src + """ class dtype(object): def __init__(self, obj, align=False, copy=False): self.alignment = None self.base = None self.byteorder = None self.char = None self.descr = None self.fields = None self.flags = None self.hasobject = None self.isalignedstruct = None self.isbuiltin = None self.isnative = None self.itemsize = None self.kind = None self.metadata = None self.name = None self.names = None self.num = None self.shape = None self.str = None self.subdtype = None self.type = None def newbyteorder(self, new_order='S'): return uninferable def __neg__(self): return uninferable class busdaycalendar(object): def __init__(self, weekmask='1111100', holidays=None): self.holidays = None self.weekmask = None class flexible(generic): pass class bool_(generic): pass class number(generic): def __neg__(self): return uninferable class datetime64(generic): def __init__(self, nb, unit=None): pass class void(flexible): def __init__(self, *args, **kwargs): self.base = None self.dtype = None self.flags = None def getfield(self): return uninferable def setfield(self): return uninferable class character(flexible): pass class integer(number): def __init__(self, value): self.denominator = None self.numerator = None class inexact(number): pass class str_(str, character): def maketrans(self, x, y=None, z=None): return uninferable class bytes_(bytes, character): def fromhex(self, string): return uninferable def maketrans(self, frm, to): return uninferable class signedinteger(integer): pass class unsignedinteger(integer): pass class complexfloating(inexact): pass class floating(inexact): pass class float64(floating, float): def fromhex(self, string): return uninferable class uint64(unsignedinteger): pass class complex64(complexfloating): pass class int16(signedinteger): pass class float96(floating): pass class int8(signedinteger): pass class uint32(unsignedinteger): pass class uint8(unsignedinteger): pass class _typedict(dict): pass class complex192(complexfloating): pass class timedelta64(signedinteger): def __init__(self, nb, unit=None): pass class int32(signedinteger): pass class uint16(unsignedinteger): pass class float32(floating): pass class complex128(complexfloating, complex): pass class float16(floating): pass class int64(signedinteger): pass buffer_type = memoryview bool8 = bool_ byte = int8 bytes0 = bytes_ cdouble = complex128 cfloat = complex128 clongdouble = complex192 clongfloat = complex192 complex_ = complex128 csingle = complex64 double = float64 float_ = float64 half = float16 int0 = int32 int_ = int32 intc = int32 intp = int32 long = int32 longcomplex = complex192 longdouble = float96 longfloat = float96 longlong = int64 object0 = object_ object_ = object_ short = int16 single = float32 singlecomplex = complex64 str0 = str_ string_ = bytes_ ubyte = uint8 uint = uint32 uint0 = uint32 uintc = uint32 uintp = uint32 ulonglong = uint64 unicode = str_ unicode_ = str_ ushort = uint16 void0 = void """)
class NumpyBrainNdarrayTest(unittest.TestCase): """ Test that calls to numpy functions returning arrays are correctly inferred """ ndarray_returning_ndarray_methods = ( "__abs__", "__add__", "__and__", "__array__", "__array_wrap__", "__copy__", "__deepcopy__", "__eq__", "__floordiv__", "__ge__", "__gt__", "__iadd__", "__iand__", "__ifloordiv__", "__ilshift__", "__imod__", "__imul__", "__invert__", "__ior__", "__ipow__", "__irshift__", "__isub__", "__itruediv__", "__ixor__", "__le__", "__lshift__", "__lt__", "__matmul__", "__mod__", "__mul__", "__ne__", "__neg__", "__or__", "__pos__", "__pow__", "__rshift__", "__sub__", "__truediv__", "__xor__", "all", "any", "argmax", "argmin", "argpartition", "argsort", "astype", "byteswap", "choose", "clip", "compress", "conj", "conjugate", "copy", "cumprod", "cumsum", "diagonal", "dot", "flatten", "getfield", "max", "mean", "min", "newbyteorder", "prod", "ptp", "ravel", "repeat", "reshape", "round", "searchsorted", "squeeze", "std", "sum", "swapaxes", "take", "trace", "transpose", "var", "view", ) def _inferred_ndarray_method_call(self, func_name): node = builder.extract_node( f""" import numpy as np test_array = np.ndarray((2, 2)) test_array.{func_name:s}() """ ) return node.infer() def _inferred_ndarray_attribute(self, attr_name): node = builder.extract_node( f""" import numpy as np test_array = np.ndarray((2, 2)) test_array.{attr_name:s} """ ) return node.infer() def test_numpy_function_calls_inferred_as_ndarray(self): """ Test that some calls to numpy functions are inferred as numpy.ndarray """ licit_array_types = ".ndarray" for func_ in self.ndarray_returning_ndarray_methods: with self.subTest(typ=func_): inferred_values = list(self._inferred_ndarray_method_call(func_)) self.assertTrue( len(inferred_values) == 1, msg=f"Too much inferred value for {func_:s}", ) self.assertTrue( inferred_values[-1].pytype() in licit_array_types, msg=f"Illicit type for {func_:s} ({inferred_values[-1].pytype()})", ) def test_numpy_ndarray_attribute_inferred_as_ndarray(self): """ Test that some numpy ndarray attributes are inferred as numpy.ndarray """ licit_array_types = ".ndarray" for attr_ in ("real", "imag", "shape", "T"): with self.subTest(typ=attr_): inferred_values = list(self._inferred_ndarray_attribute(attr_)) self.assertTrue( len(inferred_values) == 1, msg=f"Too much inferred value for {attr_:s}", ) self.assertTrue( inferred_values[-1].pytype() in licit_array_types, msg=f"Illicit type for {attr_:s} ({inferred_values[-1].pytype()})", ) @unittest.skipUnless( HAS_NUMPY and numpy_supports_type_hints(), f"This test requires the numpy library with a version above {NUMPY_VERSION_TYPE_HINTS_SUPPORT}", ) def test_numpy_ndarray_class_support_type_indexing(self): """ Test that numpy ndarray class can be subscripted (type hints) """ src = """ import numpy as np np.ndarray[int] """ node = builder.extract_node(src) cls_node = node.inferred()[0] self.assertIsInstance(cls_node, nodes.ClassDef) self.assertEqual(cls_node.name, "ndarray")
def infer_numpy_ndarray(node, context=None): ndarray = """ class ndarray(object): def __init__(self, shape, dtype=float, buffer=None, offset=0, strides=None, order=None): self.T = numpy.ndarray([0, 0]) self.base = None self.ctypes = None self.data = None self.dtype = None self.flags = None # Should be a numpy.flatiter instance but not available for now # Putting an array instead so that iteration and indexing are authorized self.flat = np.ndarray([0, 0]) self.imag = np.ndarray([0, 0]) self.itemsize = None self.nbytes = None self.ndim = None self.real = np.ndarray([0, 0]) self.shape = numpy.ndarray([0, 0]) self.size = None self.strides = None def __abs__(self): return numpy.ndarray([0, 0]) def __add__(self, value): return numpy.ndarray([0, 0]) def __and__(self, value): return numpy.ndarray([0, 0]) def __array__(self, dtype=None): return numpy.ndarray([0, 0]) def __array_wrap__(self, obj): return numpy.ndarray([0, 0]) def __contains__(self, key): return True def __copy__(self): return numpy.ndarray([0, 0]) def __deepcopy__(self, memo): return numpy.ndarray([0, 0]) def __divmod__(self, value): return (numpy.ndarray([0, 0]), numpy.ndarray([0, 0])) def __eq__(self, value): return numpy.ndarray([0, 0]) def __float__(self): return 0. def __floordiv__(self): return numpy.ndarray([0, 0]) def __ge__(self, value): return numpy.ndarray([0, 0]) def __getitem__(self, key): return uninferable def __gt__(self, value): return numpy.ndarray([0, 0]) def __iadd__(self, value): return numpy.ndarray([0, 0]) def __iand__(self, value): return numpy.ndarray([0, 0]) def __ifloordiv__(self, value): return numpy.ndarray([0, 0]) def __ilshift__(self, value): return numpy.ndarray([0, 0]) def __imod__(self, value): return numpy.ndarray([0, 0]) def __imul__(self, value): return numpy.ndarray([0, 0]) def __int__(self): return 0 def __invert__(self): return numpy.ndarray([0, 0]) def __ior__(self, value): return numpy.ndarray([0, 0]) def __ipow__(self, value): return numpy.ndarray([0, 0]) def __irshift__(self, value): return numpy.ndarray([0, 0]) def __isub__(self, value): return numpy.ndarray([0, 0]) def __itruediv__(self, value): return numpy.ndarray([0, 0]) def __ixor__(self, value): return numpy.ndarray([0, 0]) def __le__(self, value): return numpy.ndarray([0, 0]) def __len__(self): return 1 def __lshift__(self, value): return numpy.ndarray([0, 0]) def __lt__(self, value): return numpy.ndarray([0, 0]) def __matmul__(self, value): return numpy.ndarray([0, 0]) def __mod__(self, value): return numpy.ndarray([0, 0]) def __mul__(self, value): return numpy.ndarray([0, 0]) def __ne__(self, value): return numpy.ndarray([0, 0]) def __neg__(self): return numpy.ndarray([0, 0]) def __or__(self, value): return numpy.ndarray([0, 0]) def __pos__(self): return numpy.ndarray([0, 0]) def __pow__(self): return numpy.ndarray([0, 0]) def __repr__(self): return str() def __rshift__(self): return numpy.ndarray([0, 0]) def __setitem__(self, key, value): return uninferable def __str__(self): return str() def __sub__(self, value): return numpy.ndarray([0, 0]) def __truediv__(self, value): return numpy.ndarray([0, 0]) def __xor__(self, value): return numpy.ndarray([0, 0]) def all(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) def any(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) def argmax(self, axis=None, out=None): return np.ndarray([0, 0]) def argmin(self, axis=None, out=None): return np.ndarray([0, 0]) def argpartition(self, kth, axis=-1, kind='introselect', order=None): return np.ndarray([0, 0]) def argsort(self, axis=-1, kind='quicksort', order=None): return np.ndarray([0, 0]) def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0]) def byteswap(self, inplace=False): return np.ndarray([0, 0]) def choose(self, choices, out=None, mode='raise'): return np.ndarray([0, 0]) def clip(self, min=None, max=None, out=None): return np.ndarray([0, 0]) def compress(self, condition, axis=None, out=None): return np.ndarray([0, 0]) def conj(self): return np.ndarray([0, 0]) def conjugate(self): return np.ndarray([0, 0]) def copy(self, order='C'): return np.ndarray([0, 0]) def cumprod(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0]) def cumsum(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0]) def diagonal(self, offset=0, axis1=0, axis2=1): return np.ndarray([0, 0]) def dot(self, b, out=None): return np.ndarray([0, 0]) def dump(self, file): return None def dumps(self): return str() def fill(self, value): return None def flatten(self, order='C'): return np.ndarray([0, 0]) def getfield(self, dtype, offset=0): return np.ndarray([0, 0]) def item(self, *args): return uninferable def itemset(self, *args): return None def max(self, axis=None, out=None): return np.ndarray([0, 0]) def mean(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) def min(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0]) def newbyteorder(self, new_order='S'): return np.ndarray([0, 0]) def nonzero(self): return (1,) def partition(self, kth, axis=-1, kind='introselect', order=None): return None def prod(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) def ptp(self, axis=None, out=None): return np.ndarray([0, 0]) def put(self, indices, values, mode='raise'): return None def ravel(self, order='C'): return np.ndarray([0, 0]) def repeat(self, repeats, axis=None): return np.ndarray([0, 0]) def reshape(self, shape, order='C'): return np.ndarray([0, 0]) def resize(self, new_shape, refcheck=True): return None def round(self, decimals=0, out=None): return np.ndarray([0, 0]) def searchsorted(self, v, side='left', sorter=None): return np.ndarray([0, 0]) def setfield(self, val, dtype, offset=0): return None def setflags(self, write=None, align=None, uic=None): return None def sort(self, axis=-1, kind='quicksort', order=None): return None def squeeze(self, axis=None): return np.ndarray([0, 0]) def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0]) def sum(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0]) def swapaxes(self, axis1, axis2): return np.ndarray([0, 0]) def take(self, indices, axis=None, out=None, mode='raise'): return np.ndarray([0, 0]) def tobytes(self, order='C'): return b'' def tofile(self, fid, sep="", format="%s"): return None def tolist(self, ): return [] def tostring(self, order='C'): return b'' def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): return np.ndarray([0, 0]) def transpose(self, *axes): return np.ndarray([0, 0]) def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0]) def view(self, dtype=None, type=None): return np.ndarray([0, 0]) """ if numpy_supports_type_hints(): ndarray += """ @classmethod def __class_getitem__(cls, value): return cls """ node = extract_node(ndarray) return node.infer(context=context)
class NumpyBrainCoreNumericTypesTest(unittest.TestCase): """ Test of all the missing types defined in numerictypes module. """ all_types = [ "uint16", "uint32", "uint64", "float16", "float32", "float64", "float96", "complex64", "complex128", "complex192", "timedelta64", "datetime64", "unicode_", "str_", "bool_", "bool8", "byte", "int8", "bytes0", "bytes_", "cdouble", "cfloat", "character", "clongdouble", "clongfloat", "complexfloating", "csingle", "double", "flexible", "floating", "half", "inexact", "int0", "longcomplex", "longdouble", "longfloat", "short", "signedinteger", "single", "singlecomplex", "str0", "ubyte", "uint", "uint0", "uintc", "uintp", "ulonglong", "unsignedinteger", "ushort", "void0", ] def _inferred_numpy_attribute(self, attrib): node = builder.extract_node(f""" import numpy.core.numerictypes as tested_module missing_type = tested_module.{attrib:s}""") return next(node.value.infer()) def test_numpy_core_types(self): """ Test that all defined types have ClassDef type. """ for typ in self.all_types: with self.subTest(typ=typ): inferred = self._inferred_numpy_attribute(typ) self.assertIsInstance(inferred, nodes.ClassDef) def test_generic_types_have_methods(self): """ Test that all generic derived types have specified methods """ generic_methods = [ "all", "any", "argmax", "argmin", "argsort", "astype", "base", "byteswap", "choose", "clip", "compress", "conj", "conjugate", "copy", "cumprod", "cumsum", "data", "diagonal", "dtype", "dump", "dumps", "fill", "flags", "flat", "flatten", "getfield", "imag", "item", "itemset", "itemsize", "max", "mean", "min", "nbytes", "ndim", "newbyteorder", "nonzero", "prod", "ptp", "put", "ravel", "real", "repeat", "reshape", "resize", "round", "searchsorted", "setfield", "setflags", "shape", "size", "sort", "squeeze", "std", "strides", "sum", "swapaxes", "take", "tobytes", "tofile", "tolist", "tostring", "trace", "transpose", "var", "view", ] for type_ in ( "bool_", "bytes_", "character", "complex128", "complex192", "complex64", "complexfloating", "datetime64", "flexible", "float16", "float32", "float64", "float96", "floating", "generic", "inexact", "int16", "int32", "int32", "int64", "int8", "integer", "number", "signedinteger", "str_", "timedelta64", "uint16", "uint32", "uint32", "uint64", "uint8", "unsignedinteger", "void", ): with self.subTest(typ=type_): inferred = self._inferred_numpy_attribute(type_) for meth in generic_methods: with self.subTest(meth=meth): self.assertTrue( meth in {m.name for m in inferred.methods()}) def test_generic_types_have_attributes(self): """ Test that all generic derived types have specified attributes """ generic_attr = [ "base", "data", "dtype", "flags", "flat", "imag", "itemsize", "nbytes", "ndim", "real", "size", "strides", ] for type_ in ( "bool_", "bytes_", "character", "complex128", "complex192", "complex64", "complexfloating", "datetime64", "flexible", "float16", "float32", "float64", "float96", "floating", "generic", "inexact", "int16", "int32", "int32", "int64", "int8", "integer", "number", "signedinteger", "str_", "timedelta64", "uint16", "uint32", "uint32", "uint64", "uint8", "unsignedinteger", "void", ): with self.subTest(typ=type_): inferred = self._inferred_numpy_attribute(type_) for attr in generic_attr: with self.subTest(attr=attr): self.assertNotEqual(len(inferred.getattr(attr)), 0) def test_number_types_have_unary_operators(self): """ Test that number types have unary operators """ unary_ops = ("__neg__", ) for type_ in ( "float64", "float96", "floating", "int16", "int32", "int32", "int64", "int8", "integer", "number", "signedinteger", "uint16", "uint32", "uint32", "uint64", "uint8", "unsignedinteger", ): with self.subTest(typ=type_): inferred = self._inferred_numpy_attribute(type_) for attr in unary_ops: with self.subTest(attr=attr): self.assertNotEqual(len(inferred.getattr(attr)), 0) def test_array_types_have_unary_operators(self): """ Test that array types have unary operators """ unary_ops = ("__neg__", "__invert__") for type_ in ("ndarray", ): with self.subTest(typ=type_): inferred = self._inferred_numpy_attribute(type_) for attr in unary_ops: with self.subTest(attr=attr): self.assertNotEqual(len(inferred.getattr(attr)), 0) def test_datetime_astype_return(self): """ Test that the return of astype method of the datetime object is inferred as a ndarray. PyCQA/pylint#3332 """ node = builder.extract_node(""" import numpy as np import datetime test_array = np.datetime64(1, 'us') test_array.astype(datetime.datetime) """) licit_array_types = ".ndarray" inferred_values = list(node.infer()) self.assertTrue( len(inferred_values) == 1, msg="Too much inferred value for datetime64.astype", ) self.assertTrue( inferred_values[-1].pytype() in licit_array_types, msg="Illicit type for {:s} ({})".format( "datetime64.astype", inferred_values[-1].pytype()), ) @unittest.skipUnless( HAS_NUMPY and numpy_supports_type_hints(), f"This test requires the numpy library with a version above {NUMPY_VERSION_TYPE_HINTS_SUPPORT}", ) def test_generic_types_are_subscriptables(self): """ Test that all types deriving from generic are subscriptables """ for type_ in ( "bool_", "bytes_", "character", "complex128", "complex192", "complex64", "complexfloating", "datetime64", "flexible", "float16", "float32", "float64", "float96", "floating", "generic", "inexact", "int16", "int32", "int32", "int64", "int8", "integer", "number", "signedinteger", "str_", "timedelta64", "uint16", "uint32", "uint32", "uint64", "uint8", "unsignedinteger", "void", ): with self.subTest(type_=type_): src = f""" import numpy as np np.{type_}[int] """ node = builder.extract_node(src) cls_node = node.inferred()[0] self.assertIsInstance(cls_node, nodes.ClassDef) self.assertEqual(cls_node.name, type_)