From bc3d11bf95ccb14afbf98363bd66ab8c92bbccda Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 23 Nov 2021 10:37:43 +0100 Subject: [PATCH] Activate and fix existing use-set-for-membership checks --- pylint/checkers/base.py | 16 ++++++++-------- pylint/checkers/classes.py | 18 +++++++----------- pylint/checkers/format.py | 8 ++++---- pylint/checkers/imports.py | 2 +- .../refactoring/implicit_booleaness_checker.py | 2 +- .../refactoring/recommendation_checker.py | 2 +- .../refactoring/refactoring_checker.py | 12 ++++++------ pylint/checkers/similar.py | 12 ++++++------ pylint/checkers/stdlib.py | 2 +- pylint/checkers/strings.py | 10 +++++----- pylint/checkers/typecheck.py | 10 +++++----- pylint/checkers/utils.py | 6 +++--- pylint/checkers/variables.py | 2 +- pylint/config/option.py | 6 +++--- pylint/config/options_provider_mixin.py | 2 +- pylint/extensions/typing.py | 6 +----- pylint/graph.py | 4 ++-- pylint/lint/pylinter.py | 6 +++--- pylint/utils/pragma_parser.py | 2 +- pylintrc | 1 + 20 files changed, 61 insertions(+), 68 deletions(-) diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 36aaa46639..0206b12025 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -2251,7 +2251,7 @@ def _check_docstring( func.bound, astroid.Instance ): # Strings. - if func.bound.name in ("str", "unicode", "bytes"): + if func.bound.name in {"str", "unicode", "bytes"}: return if node_type == "module": message = "missing-module-docstring" @@ -2386,7 +2386,7 @@ def _is_singleton_const(node) -> bool: # True/False singletons have a special-cased message in case the user is # mistakenly using == or != to check for truthiness - if singleton in (True, False): + if singleton in {True, False}: suggestion_template = ( "{} if checking for the singleton value {}, or {} if testing for {}" ) @@ -2440,7 +2440,7 @@ def _is_float_nan(node): def _is_numpy_nan(node): if isinstance(node, nodes.Attribute) and node.attrname == "NaN": if isinstance(node.expr, nodes.Name): - return node.expr.name in ("numpy", "nmp", "np") + return node.expr.name in {"numpy", "nmp", "np"} return False def _is_nan(node) -> bool: @@ -2540,16 +2540,16 @@ def visit_compare(self, node: nodes.Compare) -> None: left = node.left operator, right = node.ops[0] - if operator in ("==", "!="): + if operator in {"==", "!="}: self._check_singleton_comparison( left, right, node, checking_for_absence=operator == "!=" ) - if operator in ("==", "!=", "is", "is not"): + if operator in {"==", "!=", "is", "is not"}: self._check_nan_comparison( - left, right, node, checking_for_absence=operator in ("!=", "is not") + left, right, node, checking_for_absence=operator in {"!=", "is not"} ) - if operator in ("is", "is not"): + if operator in {"is", "is not"}: self._check_literal_comparison(right, node) def _check_unidiomatic_typecheck(self, node): @@ -2567,7 +2567,7 @@ def _check_type_x_is_y(self, node, left, operator, right): ): return - if operator in ("is", "is not") and _is_one_arg_pos_call(right): + if operator in {"is", "is not"} and _is_one_arg_pos_call(right): right_func = utils.safe_infer(right.func) if ( isinstance(right_func, nodes.ClassDef) diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index d80111db1f..b56aa4616f 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -178,7 +178,7 @@ def _definition_equivalent_to_call(definition, call): def _positional_parameters(method): positional = method.args.args - if method.type in ("classmethod", "method"): + if method.type in {"classmethod", "method"}: positional = positional[1:] return positional @@ -1000,14 +1000,10 @@ def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None: if attribute.attrname != assign_attr.attrname: continue - if ( - assign_attr.expr.name - in [ - "cls", - node.name, - ] - and attribute.expr.name in ["cls", "self", node.name] - ): + if assign_attr.expr.name in { + "cls", + node.name, + } and attribute.expr.name in {"cls", "self", node.name}: # If assigned to cls or class name, can be accessed by cls/self/class name break @@ -1130,11 +1126,11 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None: if node.decorators: for decorator in node.decorators.nodes: - if isinstance(decorator, nodes.Attribute) and decorator.attrname in ( + if isinstance(decorator, nodes.Attribute) and decorator.attrname in { "getter", "setter", "deleter", - ): + }: # attribute affectation will call this method, not hiding it return if isinstance(decorator, nodes.Name): diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 3756d7d0e7..cdbacd4cfd 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -411,9 +411,9 @@ def _check_keyword_parentheses( contains_double_parens -= 1 continue # ')' can't happen after if (foo), since it would be a syntax error. - if tokens[i + 1].string in (":", ")", "]", "}", "in") or tokens[ + if tokens[i + 1].string in {":", ")", "]", "}", "in"} or tokens[ i + 1 - ].type in (tokenize.NEWLINE, tokenize.ENDMARKER, tokenize.COMMENT): + ].type in {tokenize.NEWLINE, tokenize.ENDMARKER, tokenize.COMMENT}: if contains_walrus_operator and walrus_operator_depth - 1 == depth: return # The empty tuple () is always accepted. @@ -424,7 +424,7 @@ def _check_keyword_parentheses( self.add_message( "superfluous-parens", line=line_num, args=keyword_token ) - elif keyword_token in ("return", "yield"): + elif keyword_token in {"return", "yield"}: self.add_message( "superfluous-parens", line=line_num, args=keyword_token ) @@ -439,7 +439,7 @@ def _check_keyword_parentheses( return # 'and' and 'or' are the only boolean operators with lower precedence # than 'not', so parens are only required when they are found. - if token[1] in ("and", "or"): + if token[1] in {"and", "or"}: found_and_or = True # A yield inside an expression must always be in parentheses, # quit early without error. diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index 174c8c8e39..9d05ebbb82 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -734,7 +734,7 @@ def _check_imports_order(self, _module_node): ) import_category = isort_driver.place_module(package) node_and_package_import = (node, package) - if import_category in ("FUTURE", "STDLIB"): + if import_category in {"FUTURE", "STDLIB"}: std_imports.append(node_and_package_import) wrong_import = ( third_party_not_ignored diff --git a/pylint/checkers/refactoring/implicit_booleaness_checker.py b/pylint/checkers/refactoring/implicit_booleaness_checker.py index fbcb295363..02e5d82571 100644 --- a/pylint/checkers/refactoring/implicit_booleaness_checker.py +++ b/pylint/checkers/refactoring/implicit_booleaness_checker.py @@ -177,7 +177,7 @@ def _check_use_implicit_booleaness_not_comparison( continue # No need to check for operator when visiting compare node - if operator in ("==", "!=", ">=", ">", "<=", "<"): + if operator in {"==", "!=", ">=", ">", "<=", "<"}: collection_literal = "{}" if isinstance(literal_node, nodes.List): collection_literal = "[]" diff --git a/pylint/checkers/refactoring/recommendation_checker.py b/pylint/checkers/refactoring/recommendation_checker.py index 3bccd5fcbe..210513ddc5 100644 --- a/pylint/checkers/refactoring/recommendation_checker.py +++ b/pylint/checkers/refactoring/recommendation_checker.py @@ -106,7 +106,7 @@ def _check_use_maxsplit_arg(self, node: nodes.Call) -> None: # Check if call is split() or rsplit() if not ( isinstance(node.func, nodes.Attribute) - and node.func.attrname in ("split", "rsplit") + and node.func.attrname in {"split", "rsplit"} and isinstance(utils.safe_infer(node.func), astroid.BoundMethod) ): return diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 7929d77e00..7568f3eeaa 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -833,14 +833,14 @@ def _check_consider_using_min_max_builtin(self, node: nodes.If): if right_statement_value != body_value: return - if operator in ("<", "<="): + if operator in {"<", "<="}: reduced_to = "{target} = max({target}, {item})".format( target=target_assignation, item=body_value ) self.add_message( "consider-using-max-builtin", node=node, args=(reduced_to,) ) - elif operator in (">", ">="): + elif operator in {">", ">="}: reduced_to = "{target} = min({target}, {item})".format( target=target_assignation, item=body_value ) @@ -963,7 +963,7 @@ def _check_consider_using_generator(self, node): # remove square brackets '[]' inside_comp = node.args[0].as_string()[1:-1] call_name = node.func.name - if call_name in ["any", "all"]: + if call_name in {"any", "all"}: self.add_message( "use-a-generator", node=node, @@ -1227,12 +1227,12 @@ def _find_lower_upper_bounds(comparison_node, uses): if value is None: continue - if operator in ("<", "<="): + if operator in {"<", "<="}: if operand is left_operand: uses[value]["lower_bound"].add(comparison_node) elif operand is right_operand: uses[value]["upper_bound"].add(comparison_node) - elif operator in (">", ">="): + elif operator in {">", ">="}: if operand is left_operand: uses[value]["upper_bound"].add(comparison_node) elif operand is right_operand: @@ -1482,7 +1482,7 @@ def _check_consider_using_with(self, node: nodes.Call): def _check_use_list_or_dict_literal(self, node: nodes.Call) -> None: """Check if empty list or dict is created by using the literal [] or {}""" - if node.as_string() in ("list()", "dict()"): + if node.as_string() in {"list()", "dict()"}: inferred = utils.safe_infer(node.func) if isinstance(inferred, nodes.ClassDef) and not node.args: if inferred.qname() == "builtins.list": diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index b609cd539c..f9cb9edbc7 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -916,17 +916,17 @@ def Run(argv=None): ignore_signatures = False opts, args = getopt(argv, s_opts, l_opts) for opt, val in opts: - if opt in ("-d", "--duplicates"): + if opt in {"-d", "--duplicates"}: min_lines = int(val) - elif opt in ("-h", "--help"): + elif opt in {"-h", "--help"}: usage() - elif opt in ("-i", "--ignore-comments"): + elif opt in {"-i", "--ignore-comments"}: ignore_comments = True - elif opt in ("--ignore-docstrings",): + elif opt in {"--ignore-docstrings"}: ignore_docstrings = True - elif opt in ("--ignore-imports",): + elif opt in {"--ignore-imports"}: ignore_imports = True - elif opt in ("--ignore-signatures",): + elif opt in {"--ignore-signatures"}: ignore_signatures = True if not args: usage(1) diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index d1fc49eb76..f77f7a2b3a 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -574,7 +574,7 @@ def _check_redundant_assert(self, node, infer): isinstance(infer, astroid.BoundMethod) and node.args and isinstance(node.args[0], nodes.Const) - and infer.name in ["assertTrue", "assertFalse"] + and infer.name in {"assertTrue", "assertFalse"} ): self.add_message( "redundant-unittest-assert", diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index 3bdc8b1964..eb43411de8 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -429,9 +429,9 @@ def visit_call(self, node: nodes.Call) -> None: if ( isinstance(func, astroid.BoundMethod) and isinstance(func.bound, astroid.Instance) - and func.bound.name in ("str", "unicode", "bytes") + and func.bound.name in {"str", "unicode", "bytes"} ): - if func.name in ("strip", "lstrip", "rstrip") and node.args: + if func.name in {"strip", "lstrip", "rstrip"} and node.args: arg = utils.safe_infer(node.args[0]) if not isinstance(arg, nodes.Const) or not isinstance(arg.value, str): return @@ -942,11 +942,11 @@ def str_eval(token): We have to support all string literal notations: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals """ - if token[0:2].lower() in ("fr", "rf"): + if token[0:2].lower() in {"fr", "rf"}: token = token[2:] - elif token[0].lower() in ("r", "u", "f"): + elif token[0].lower() in {"r", "u", "f"}: token = token[1:] - if token[0:3] in ('"""', "'''"): + if token[0:3] in {'"""', "'''"}: return token[3:-3] return token[1:-1] diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 57c1478368..c32b19b6d0 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -486,7 +486,7 @@ def _emit_no_member( return False if metaclass: # Renamed in Python 3.10 to `EnumType` - return metaclass.qname() in ("enum.EnumMeta", "enum.EnumType") + return metaclass.qname() in {"enum.EnumMeta", "enum.EnumType"} return False if not has_known_bases(owner): return False @@ -525,7 +525,7 @@ def _emit_no_member( and isinstance(owner.parent, nodes.ClassDef) and owner.parent.name == "EnumMeta" and owner_name == "__members__" - and node.attrname in ["items", "values", "keys"] + and node.attrname in {"items", "values", "keys"} ): # Avoid false positive on Enum.__members__.{items(), values, keys} # See https://github.com/PyCQA/pylint/issues/4123 @@ -1560,7 +1560,7 @@ def _check_invalid_sequence_index(self, subscript: nodes.Subscript): return None # Instance values must be int, slice, or have an __index__ method elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in ("builtins.int", "builtins.slice"): + if index_type.pytype() in {"builtins.int", "builtins.slice"}: return None try: index_type.getattr("__index__") @@ -1603,7 +1603,7 @@ def _check_invalid_slice_index(self, node: nodes.Slice) -> None: # Instance values must be of type int, None or an object # with __index__ elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in ("builtins.int", "builtins.NoneType"): + if index_type.pytype() in {"builtins.int", "builtins.NoneType"}: continue try: @@ -1805,7 +1805,7 @@ def visit_compare(self, node: nodes.Compare) -> None: return op, right = node.ops[0] - if op in ["in", "not in"]: + if op in {"in", "not in"}: self._check_membership_test(right) @check_messages( diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 9d8e6f9bf2..c7f2cec928 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -730,7 +730,7 @@ def inherit_from_std_ex(node: nodes.NodeNG) -> bool: """ ancestors = node.ancestors() if hasattr(node, "ancestors") else [] return any( - ancestor.name in ("Exception", "BaseException") + ancestor.name in {"Exception", "BaseException"} and ancestor.root().name == EXCEPTIONS_MODULE for ancestor in itertools.chain([node], ancestors) ) @@ -802,7 +802,7 @@ def is_property_setter_or_deleter(node: nodes.FunctionDef) -> bool: def _is_property_decorator(decorator: nodes.Name) -> bool: for inferred in decorator.infer(): if isinstance(inferred, nodes.ClassDef): - if inferred.qname() in ("builtins.property", "functools.cached_property"): + if inferred.qname() in {"builtins.property", "functools.cached_property"}: return True for ancestor in inferred.ancestors(): if ancestor.name == "property" and ancestor.root().name == "builtins": @@ -1688,5 +1688,5 @@ def returns_bool(node: nodes.NodeNG) -> bool: return ( isinstance(node, nodes.Return) and isinstance(node.value, nodes.Const) - and node.value.value in (True, False) + and node.value.value in {True, False} ) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index a87f94c65a..cfedb56ddb 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2131,7 +2131,7 @@ def _check_all(self, node: nodes.Module, not_consumed): assigned = next(node.igetattr("__all__")) if assigned is astroid.Uninferable: return - if not assigned.pytype() in ["builtins.list", "builtins.tuple"]: + if not assigned.pytype() in {"builtins.list", "builtins.tuple"}: line, col = assigned.tolineno, assigned.col_offset self.add_message("invalid-all-format", line=line, col_offset=col, node=node) return diff --git a/pylint/config/option.py b/pylint/config/option.py index 684c7e8940..58547656c1 100644 --- a/pylint/config/option.py +++ b/pylint/config/option.py @@ -52,9 +52,9 @@ def _yn_validator(opt, _, value): return bool(value) if isinstance(value, str): value = value.lower() - if value in ("y", "yes", "true"): + if value in {"y", "yes", "true"}: return True - if value in ("n", "no", "false"): + if value in {"n", "no", "false"}: return False msg = "option %s: invalid yn value %r, should be in (y, yes, true, n, no, false)" raise optparse.OptionValueError(msg % (opt, value)) @@ -164,7 +164,7 @@ def __init__(self, *opts, **attrs): self.help = optparse.SUPPRESS_HELP def _check_choice(self): - if self.type in ("choice", "multiple_choice"): + if self.type in {"choice", "multiple_choice"}: if self.choices is None: raise optparse.OptionError( "must supply a list of choices for type 'choice'", self diff --git a/pylint/config/options_provider_mixin.py b/pylint/config/options_provider_mixin.py index a8526095c4..d3872135cc 100644 --- a/pylint/config/options_provider_mixin.py +++ b/pylint/config/options_provider_mixin.py @@ -56,7 +56,7 @@ def set_option(self, optname, value, action=None, optdict=None): action = optdict.get("action", "store") if action == "store": setattr(self.config, self.option_attrname(optname, optdict), value) - elif action in ("store_true", "count"): + elif action in {"store_true", "count"}: setattr(self.config, self.option_attrname(optname, optdict), 0) elif action == "store_false": setattr(self.config, self.option_attrname(optname, optdict), 1) diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index 92282b9837..01bfe1b285 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -194,11 +194,7 @@ def _check_for_alternative_union_syntax( inferred = safe_infer(node) if not ( isinstance(inferred, nodes.FunctionDef) - and inferred.qname() - in ( - "typing.Optional", - "typing.Union", - ) + and inferred.qname() in {"typing.Optional", "typing.Union"} or isinstance(inferred, astroid.bases.Instance) and inferred.qname() == "typing._SpecialForm" ): diff --git a/pylint/graph.py b/pylint/graph.py index 038db7e4ee..70a6186784 100644 --- a/pylint/graph.py +++ b/pylint/graph.py @@ -64,11 +64,11 @@ def __init__( if size: self.emit(f'size="{size}"') if charset: - assert charset.lower() in ( + assert charset.lower() in { "utf-8", "iso-8859-1", "latin1", - ), f"unsupported charset {charset}" + }, f"unsupported charset {charset}" self.emit(f'charset="{charset}"') for param in additional_param.items(): self.emit("=".join(param)) diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index b9bea1bb25..c95aa680ec 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -766,7 +766,7 @@ def any_fail_on_issues(self): def disable_noerror_messages(self): for msgcat, msgids in self.msgs_store._msgs_by_category.items(): # enable only messages with 'error' severity and above ('fatal') - if msgcat in ["E", "F"]: + if msgcat in {"E", "F"}: for msgid in msgids: self.enable(msgid) else: @@ -834,7 +834,7 @@ def process_tokens(self, tokens): continue try: for pragma_repr in parse_pragma(match.group(2)): - if pragma_repr.action in ("disable-all", "skip-file"): + if pragma_repr.action in {"disable-all", "skip-file"}: if pragma_repr.action == "disable-all": self.add_message( "deprecated-pragma", @@ -1597,7 +1597,7 @@ def _set_msg_status( ignore_unknown: bool = False, ) -> None: """Do some tests and then iterate over message defintions to set state""" - assert scope in ("package", "module") + assert scope in {"package", "module"} if msgid == "all": for _msgid in MSG_TYPES: self._set_msg_status(_msgid, enable, scope, line, ignore_unknown) diff --git a/pylint/utils/pragma_parser.py b/pylint/utils/pragma_parser.py index 549519cf7b..02ad254930 100644 --- a/pylint/utils/pragma_parser.py +++ b/pylint/utils/pragma_parser.py @@ -122,7 +122,7 @@ def parse_pragma(pylint_pragma: str) -> Generator[PragmaRepresenter, None, None] action = value messages = [] assignment_required = action in MESSAGE_KEYWORDS - elif kind in ("MESSAGE_STRING", "MESSAGE_NUMBER"): + elif kind in {"MESSAGE_STRING", "MESSAGE_NUMBER"}: messages.append(value) assignment_required = False else: diff --git a/pylintrc b/pylintrc index 2998670865..3005cc6766 100644 --- a/pylintrc +++ b/pylintrc @@ -20,6 +20,7 @@ load-plugins= pylint.extensions.check_elif, pylint.extensions.bad_builtin, pylint.extensions.for_any_all, + pylint.extensions.set_membership, pylint.extensions.code_style, pylint.extensions.overlapping_exceptions, pylint.extensions.typing,