diff options
Diffstat (limited to 'pylint/checkers/stdlib.py')
-rw-r--r-- | pylint/checkers/stdlib.py | 45 |
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)) |