# Copyright (c) 2009-2011, 2014 LOGILAB S.A. (Paris, FRANCE) # Copyright (c) 2012 FELD Boris # Copyright (c) 2013-2014 Google, Inc. # Copyright (c) 2014-2020 Claudiu Popa # Copyright (c) 2014 buck # Copyright (c) 2014 Arun Persaud # Copyright (c) 2015 Harut # Copyright (c) 2015 Ionel Cristian Maries # Copyright (c) 2016 Petr Pulc # Copyright (c) 2016 Derek Gustafson # Copyright (c) 2017 Krzysztof Czapla # Copyright (c) 2017 Ɓukasz Rogalski # Copyright (c) 2017 James M. Allen # Copyright (c) 2017 vinnyrose # Copyright (c) 2018, 2020 Bryce Guinta # Copyright (c) 2018 Bryce Guinta # Copyright (c) 2018, 2020 Anthony Sottile # Copyright (c) 2019-2021 Pierre Sassoulas # Copyright (c) 2019 Hugo van Kemenade # Copyright (c) 2019 Ashley Whetter # Copyright (c) 2020 hippo91 # Copyright (c) 2021 Andreas Finkler # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE """Check format checker helper functions""" import os import tempfile import tokenize import astroid from pylint import lint, reporters from pylint.checkers.format import FormatChecker from pylint.testutils import CheckerTestCase, Message, _tokenize_str class TestMultiStatementLine(CheckerTestCase): CHECKER_CLASS = FormatChecker def testSingleLineIfStmts(self): stmt = astroid.extract_node( """ if True: pass #@ """ ) self.checker.config.single_line_if_stmt = False with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_if_stmt = True with self.assertNoMessages(): self.visitFirst(stmt) stmt = astroid.extract_node( """ if True: pass #@ else: pass """ ) with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) def testSingleLineClassStmts(self): stmt = astroid.extract_node( """ class MyError(Exception): pass #@ """ ) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertNoMessages(): self.visitFirst(stmt) stmt = astroid.extract_node( """ class MyError(Exception): a='a' #@ """ ) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertNoMessages(): self.visitFirst(stmt) stmt = astroid.extract_node( """ class MyError(Exception): a='a'; b='b' #@ """ ) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) def testTryExceptFinallyNoMultipleStatement(self): tree = astroid.extract_node( """ try: #@ pass except: pass finally: pass""" ) with self.assertNoMessages(): self.visitFirst(tree) def visitFirst(self, tree): self.checker.process_tokens([]) self.checker.visit_default(tree.body[0]) def test_ellipsis_is_ignored(self): code = """ from typing import overload @overload def concat2(arg1: str) -> str: ... """ tree = astroid.extract_node(code) with self.assertNoMessages(): self.visitFirst(tree) code = """ def concat2(arg1: str) -> str: ... """ stmt = astroid.extract_node(code) with self.assertAddsMessages(Message("multiple-statements", node=stmt.body[0])): self.visitFirst(stmt) class TestSuperfluousParentheses(CheckerTestCase): CHECKER_CLASS = FormatChecker def testCheckKeywordParensHandlesValidCases(self): cases = [ "if foo:", "if foo():", "if (x and y) or z:", "assert foo()", "assert ()", "if (1, 2) in (3, 4):", "if (a or b) in c:", "return (x for x in x)", "if (x for x in x):", "for x in (x for x in x):", "not (foo or bar)", "not (foo or bar) and baz", ] with self.assertNoMessages(): for code in cases: self.checker._check_keyword_parentheses(_tokenize_str(code), 0) def testCheckKeywordParensHandlesUnnecessaryParens(self): cases = [ (Message("superfluous-parens", line=1, args="if"), "if (foo):", 0), (Message("superfluous-parens", line=1, args="if"), "if ((foo, bar)):", 0), (Message("superfluous-parens", line=1, args="if"), "if (foo(bar)):", 0), ( Message("superfluous-parens", line=1, args="return"), "return ((x for x in x))", 0, ), (Message("superfluous-parens", line=1, args="not"), "not (foo)", 0), (Message("superfluous-parens", line=1, args="not"), "if not (foo):", 1), (Message("superfluous-parens", line=1, args="if"), "if (not (foo)):", 0), (Message("superfluous-parens", line=1, args="not"), "if (not (foo)):", 2), ( Message("superfluous-parens", line=1, args="for"), "for (x) in (1, 2, 3):", 0, ), ( Message("superfluous-parens", line=1, args="if"), "if (1) in (1, 2, 3):", 0, ), ] for msg, code, offset in cases: with self.assertAddsMessages(msg): self.checker._check_keyword_parentheses(_tokenize_str(code), offset) def testNoSuperfluousParensWalrusOperatorIf(self): """Parenthesis change the meaning of assignment in the walrus operator and so are not superfluous:""" code = "if (odd := is_odd(i))" offset = 0 with self.assertNoMessages(): self.checker._check_keyword_parentheses(_tokenize_str(code), offset) def testPositiveSuperfluousParensWalrusOperatorIf(self): """Test positive superfluous parens with the walrus operator""" code = "if ((odd := is_odd(i))):" msg = Message("superfluous-parens", line=1, args="if") with self.assertAddsMessages(msg): self.checker._check_keyword_parentheses(_tokenize_str(code), 0) def testNoSuperfluousParensWalrusOperatorNot(self): """Test superfluous-parens with the not operator""" code = "not (foo := 5)" with self.assertNoMessages(): self.checker._check_keyword_parentheses(_tokenize_str(code), 0) def testCheckIfArgsAreNotUnicode(self): cases = [("if (foo):", 0), ("assert (1 == 1)", 0)] for code, offset in cases: self.checker._check_keyword_parentheses(_tokenize_str(code), offset) got = self.linter.release_messages() assert isinstance(got[-1].args, str) def testFuturePrintStatementWithoutParensWarning(self): code = """from __future__ import print_function print('Hello world!') """ tree = astroid.parse(code) with self.assertNoMessages(): self.checker.process_module(tree) self.checker.process_tokens(_tokenize_str(code)) def testKeywordParensFalsePositive(self): code = "if 'bar' in (DICT or {}):" with self.assertNoMessages(): self.checker._check_keyword_parentheses(_tokenize_str(code), start=2) class TestCheckSpace(CheckerTestCase): CHECKER_CLASS = FormatChecker def test_encoding_token(self): """Make sure the encoding token doesn't change the checker's behavior _tokenize_str doesn't produce an encoding token, but reading a file does """ with self.assertNoMessages(): encoding_token = tokenize.TokenInfo( tokenize.ENCODING, "utf-8", (0, 0), (0, 0), "" ) tokens = [encoding_token] + _tokenize_str( "if (\n None):\n pass\n" ) self.checker.process_tokens(tokens) def test_disable_global_option_end_of_line(): """ Test for issue with disabling tokenizer messages that extend beyond the scope of the ast tokens """ file_ = tempfile.NamedTemporaryFile( # pylint: disable=consider-using-with "w", delete=False ) with file_: file_.write( """ mylist = [ None ] """ ) try: linter = lint.PyLinter() checker = FormatChecker(linter) linter.register_checker(checker) args = linter.load_command_line_configuration( [file_.name, "-d", "bad-continuation"] ) myreporter = reporters.CollectingReporter() linter.set_reporter(myreporter) linter.check(args) assert not myreporter.messages finally: os.remove(file_.name)