summaryrefslogtreecommitdiff
path: root/pylint/checkers/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'pylint/checkers/base.py')
-rw-r--r--pylint/checkers/base.py59
1 files changed, 57 insertions, 2 deletions
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index aeaa85379..cc7e90237 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -1661,7 +1661,6 @@ KNOWN_NAME_TYPES = {
"inlinevar",
}
-
HUMAN_READABLE_TYPES = {
"module": "module",
"const": "constant",
@@ -1724,7 +1723,6 @@ def _create_naming_options():
class NameChecker(_BasicChecker):
-
msgs = {
"C0102": (
'Black listed name "%s"',
@@ -2342,6 +2340,12 @@ class ComparisonChecker(_BasicChecker):
"callable was made, which might suggest that some parenthesis were omitted, "
"resulting in potential unwanted behaviour.",
),
+ "W0177": (
+ "Comparison %s should be %s",
+ "nan-comparison",
+ "Used when an expression is compared to NaN"
+ "values like numpy.NaN and float('nan')",
+ ),
}
def _check_singleton_comparison(
@@ -2402,6 +2406,52 @@ class ComparisonChecker(_BasicChecker):
args=(f"'{root_node.as_string()}'", suggestion),
)
+ def _check_nan_comparison(
+ self, left_value, right_value, root_node, checking_for_absence: bool = False
+ ):
+ def _is_float_nan(node):
+ try:
+ if isinstance(node, astroid.Call) and len(node.args) == 1:
+ if (
+ node.args[0].value.lower() == "nan"
+ and node.inferred()[0].pytype() == "builtins.float"
+ ):
+ return True
+ return False
+ except AttributeError:
+ return False
+
+ def _is_numpy_nan(node):
+ if isinstance(node, astroid.Attribute) and node.attrname == "NaN":
+ if isinstance(node.expr, astroid.Name):
+ return node.expr.name in ("numpy", "nmp", "np")
+ return False
+
+ def _is_nan(node) -> bool:
+ return _is_float_nan(node) or _is_numpy_nan(node)
+
+ nan_left = _is_nan(left_value)
+ if not nan_left and not _is_nan(right_value):
+ return
+
+ absence_text = ""
+ if checking_for_absence:
+ absence_text = "not "
+ if nan_left:
+ suggestion = "'{}math.isnan({})'".format(
+ absence_text, right_value.as_string()
+ )
+ else:
+ suggestion = "'{}math.isnan({})'".format(
+ absence_text, left_value.as_string()
+ )
+
+ self.add_message(
+ "nan-comparison",
+ node=root_node,
+ args=("'{}'".format(root_node.as_string()), suggestion),
+ )
+
def _check_literal_comparison(self, literal, node):
"""Check if we compare to a literal, which is usually what we do not want to do."""
nodes = (astroid.List, astroid.Tuple, astroid.Dict, astroid.Set)
@@ -2495,6 +2545,11 @@ class ComparisonChecker(_BasicChecker):
self._check_singleton_comparison(
left, right, node, checking_for_absence=operator == "!="
)
+
+ if operator in ("==", "!=", "is", "is not"):
+ self._check_nan_comparison(
+ left, right, node, checking_for_absence=operator in ("!=", "is not")
+ )
if operator in ("is", "is not"):
self._check_literal_comparison(right, node)