Skip to content

Commit

Permalink
Merge pull request #643 from hippo91/bug_pylint_2436
Browse files Browse the repository at this point in the history
Bug pylint 2436
  • Loading branch information
hippo91 authored Feb 24, 2019
2 parents 9a3fb9d + bb10f80 commit 67f7daa
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
6 changes: 6 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ What's New in astroid 2.2.0?
============================
Release Date: TBA

<<<<<<< HEAD
* Fix a bug concerning inference of calls to numpy function that should not return Tuple or List
instances.

Close PyCQA/pylint#2436

* ``typed_ast`` gets installed for Python 3.7, meaning type comments can now work on 3.7.

* Fix a bug concerning inference of unary operators on numpy types.
Expand Down
64 changes: 64 additions & 0 deletions 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 @@ -481,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
38 changes: 38 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 @@ -618,5 +619,42 @@ def test_array_types_have_unary_operators(self):
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()

0 comments on commit 67f7daa

Please sign in to comment.