summaryrefslogtreecommitdiff
path: root/pylint/checkers/stdlib.py
diff options
context:
space:
mode:
Diffstat (limited to 'pylint/checkers/stdlib.py')
-rw-r--r--pylint/checkers/stdlib.py45
1 files changed, 44 insertions, 1 deletions
diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py
index ac8b876..5331704 100644
--- a/pylint/checkers/stdlib.py
+++ b/pylint/checkers/stdlib.py
@@ -27,10 +27,15 @@ from pylint.checkers import BaseChecker
from pylint.checkers import utils
+TYPECHECK_COMPARISON_OPERATORS = frozenset(('is', 'is not', '==', '!=', 'in', 'not in'))
+LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
+
if sys.version_info >= (3, 0):
OPEN_MODULE = '_io'
+ TYPE_QNAME = 'builtins.type'
else:
OPEN_MODULE = '__builtin__'
+ TYPE_QNAME = '__builtin__.type'
def _check_mode_str(mode):
@@ -76,6 +81,15 @@ def _check_mode_str(mode):
return True
+def _is_one_arg_pos_call(call):
+ """Is this a call with exactly 1 argument,
+ where that argument is positional?
+ """
+ return (isinstance(call, astroid.CallFunc)
+ and len(call.args) == 1
+ and not isinstance(call.args[0], astroid.Keyword))
+
+
class StdlibChecker(BaseChecker):
__implements__ = (IAstroidChecker,)
name = 'stdlib'
@@ -99,7 +113,13 @@ class StdlibChecker(BaseChecker):
'The first argument of assertTrue and assertFalse is '
'a condition. If a constant is passed as parameter, that '
'condition will be always true. In this case a warning '
- 'should be emitted.')
+ 'should be emitted.'),
+ 'W1504': ('Using type() instead of isinstance() for a typecheck.',
+ 'unidiomatic-typecheck',
+ 'The idiomatic way to perform an explicit typecheck in '
+ 'Python is to use isinstance(x, Y) rather than '
+ 'type(x) == Y, type(x) is Y. Though there are unusual '
+ 'situations where these give different results.')
}
@utils.check_messages('bad-open-mode', 'redundant-unittest-assert')
@@ -132,6 +152,14 @@ class StdlibChecker(BaseChecker):
for value in node.values:
self._check_datetime(value)
+ @utils.check_messages('unidiomatic-typecheck')
+ def visit_compare(self, node):
+ operator, right = node.ops[0]
+ if operator in TYPECHECK_COMPARISON_OPERATORS:
+ left = node.left
+ if _is_one_arg_pos_call(left):
+ self._check_type_x_is_y(node, left, operator, right)
+
def _check_redundant_assert(self, node, infer):
if (isinstance(infer, astroid.BoundMethod) and
node.args and isinstance(node.args[0], astroid.Const) and
@@ -168,6 +196,21 @@ class StdlibChecker(BaseChecker):
args=mode_arg.value)
+ def _check_type_x_is_y(self, node, left, operator, right):
+ """Check for expressions like type(x) == Y."""
+ left_func = utils.safe_infer(left.func)
+ if isinstance(left_func, astroid.Class) and left_func.qname() == TYPE_QNAME:
+ if operator in ('is', 'is not') and _is_one_arg_pos_call(right):
+ right_func = utils.safe_infer(right.func)
+ if isinstance(right_func, astroid.Class) and right_func.qname() == TYPE_QNAME:
+ # type(x) == type(a)
+ right_arg = utils.safe_infer(right.args[0])
+ if not isinstance(right_arg, LITERAL_NODE_TYPES):
+ # not e.g. type(x) == type([])
+ return
+ self.add_message('unidiomatic-typecheck', node=node)
+
+
def register(linter):
"""required method to auto register this checker """
linter.register_checker(StdlibChecker(linter))