Skip to content

Bug pylint 2436 #643

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Feb 24, 2019
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b3fa94a
Just breakpoint to debug and mark the origin of the problem
hippo91 Dec 22, 2018
b85b6da
Revert "Just breakpoint to debug and mark the origin of the problem"
hippo91 Dec 22, 2018
b1937b9
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Jan 3, 2019
fd3cdad
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Jan 20, 2019
99eabe2
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Jan 21, 2019
6ce0649
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Jan 26, 2019
098ebd9
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Jan 27, 2019
fa113aa
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Feb 2, 2019
bdbd072
Adding support for unary operators for numpy ndarray and numbers. Add…
hippo91 Feb 3, 2019
b14c130
Refactoring to code DRY
hippo91 Feb 3, 2019
2aaad9c
Add a unittest checking that the call to numpy.array function is not …
hippo91 Feb 9, 2019
6b5991d
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Feb 9, 2019
e6ec4fd
Merge branch 'master' into bug_pylint_2436
hippo91 Feb 9, 2019
ec8797c
Refactors and generalizes unittests dealing with tuple or list infere…
hippo91 Feb 9, 2019
8846de6
Add of ChangeLog entry
hippo91 Feb 9, 2019
d35ca77
Format according to Black
hippo91 Feb 9, 2019
2f1aec5
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Feb 15, 2019
1d9b387
Merge branch 'master' into bug_pylint_2436
hippo91 Feb 15, 2019
a00f191
Merge branch 'master' of https://github.com/PyCQA/astroid
hippo91 Feb 24, 2019
bb10f80
Merges master
hippo91 Feb 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ What's New in astroid 2.2.0?
============================
Release Date: TBA

* Fix a bug concerning inference of unary operators on numpy types and
inference of calls to numpy function that should not return Tuple or List
instances.

Close PyCQA/pylint#2436

* Fix a crash with ``typing.NamedTuple`` and empty fields. Close PyCQA/pylint#2745

Expand Down
71 changes: 70 additions & 1 deletion astroid/brain/brain_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

"""Astroid hooks for numpy."""

import functools
import astroid


Expand Down Expand Up @@ -270,6 +271,7 @@ def __init__(self, obj, align=False, copy=False):
self.type = None

def newbyteorder(self, new_order='S'): return any
def __neg__(self): return any


class ndarray(object):
Expand All @@ -291,6 +293,9 @@ def __init__(self, shape, dtype=float, buffer=None, offset=0,
self.size = None
self.strides = None

def __neg__(self): return any
def __inv__(self): return any
def __invert__(self): return any
def all(self): return any
def any(self): return any
def argmax(self): return any
Expand Down Expand Up @@ -356,7 +361,8 @@ def __init__(self, weekmask='1111100', holidays=None):

class flexible(generic): pass
class bool_(generic): pass
class number(generic): pass
class number(generic):
def __neg__(self): return any
class datetime64(generic): pass


Expand Down Expand Up @@ -476,6 +482,69 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=None):
)


def _looks_like_numpy_function(func_name, numpy_module_name, node):
"""
Return True if the current node correspond to the function inside
the numpy module in parameters

:param node: the current node
:type node: FunctionDef
:param func_name: name of the function
:type func_name: str
:param numpy_module_name: name of the numpy module
:type numpy_module_name: str
:return: True if the current node correspond to the function looked for
:rtype: bool
"""
return node.name == func_name and node.parent.name == numpy_module_name


def numpy_function_infer_call_result(node):
"""
A wrapper around infer_call_result method bounded to the node.

:param node: the node which infer_call_result should be filtered
:type node: FunctionDef
:return: a function that filter the results of the call to node.infer_call_result
:rtype: function
"""
#  Put the origin infer_call_result method into the closure
origin_infer_call_result = node.infer_call_result

def infer_call_result_wrapper(caller=None, context=None):
"""
Call the origin infer_call_result method bounded to the node instance and
filter the results to remove List and Tuple instances
"""
unfiltered_infer_call_result = origin_infer_call_result(caller, context)
return (
x
for x in unfiltered_infer_call_result
if not isinstance(x, (astroid.List, astroid.Tuple))
)

return infer_call_result_wrapper


def _replace_numpy_function_infer_call_result(node, context=None):
node.infer_call_result = numpy_function_infer_call_result(node)
return


astroid.MANAGER.register_transform(
astroid.FunctionDef,
_replace_numpy_function_infer_call_result,
functools.partial(
_looks_like_numpy_function, "linspace", "numpy.core.function_base"
),
)

astroid.MANAGER.register_transform(
astroid.FunctionDef,
_replace_numpy_function_infer_call_result,
functools.partial(_looks_like_numpy_function, "array", "numpy.core.records"),
)

astroid.register_module_extender(
astroid.MANAGER, "numpy.core.umath", numpy_core_umath_transform
)
Expand Down
82 changes: 82 additions & 0 deletions astroid/tests/unittest_brain_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from astroid import builder
from astroid import nodes
from astroid import node_classes


class SubTestWrapper(unittest.TestCase):
Expand Down Expand Up @@ -573,6 +574,87 @@ def test_generic_types_have_attributes(self):
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__", "__inv__", "__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)


@unittest.skipUnless(HAS_NUMPY, "This test requires the numpy library.")
class NumpyBrainFunctionReturningArrayTest(SubTestWrapper):
"""
Test that calls to numpy functions returning arrays are correctly inferred
"""

def _inferred_numpy_func_call(self, func_name, *func_args):
node = builder.extract_node(
"""
import numpy as np
func = np.{:s}
func({:s})
""".format(
func_name, ",".join(func_args)
)
)
return node.infer()

def test_numpy_function_calls_not_inferred_as_list(self):
"""
Test that some calls to numpy functions are not inferred as list nor tuple
"""
for func_ in (("array", "[1, 2]"),):
with self.subTest(typ=func_):
for inferred in self._inferred_numpy_func_call(*func_):
self.assertFalse(isinstance(inferred, node_classes.List))

def test_numpy_function_calls_not_inferred_as_tuple(self):
"""
Test that some calls to numpy functions are not inferred as list nor tuple
"""
for func_ in (("array", "(1, 2)"), ("linspace", "1, 100")):
with self.subTest(typ=func_):
for inferred in self._inferred_numpy_func_call(*func_):
self.assertFalse(isinstance(inferred, node_classes.Tuple))


if __name__ == "__main__":
unittest.main()