# Copyright (c) 2014 Google, Inc. # Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) # Copyright (c) 2015-2020 Claudiu Popa # Copyright (c) 2015 Ionel Cristian Maries # Copyright (c) 2016 Derek Gustafson # Copyright (c) 2018 Alan Chan # Copyright (c) 2019-2020 Ashley Whetter # Copyright (c) 2019 Svet # Copyright (c) 2020-2021 Pierre Sassoulas # Copyright (c) 2020 hippo91 # Copyright (c) 2021 Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> # 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 """Unittest for the logging checker.""" import sys from typing import Optional, Tuple import astroid import pytest from pylint.checkers import logging from pylint.interfaces import UNDEFINED from pylint.testutils import CheckerTestCase, MessageTest, set_config class TestLoggingModuleDetection(CheckerTestCase): CHECKER_CLASS = logging.LoggingChecker def test_detects_standard_logging_module(self) -> None: stmts = astroid.extract_node( """ import logging #@ logging.warn('%s' % '%s') #@ """ ) self.checker.visit_module(None) self.checker.visit_import(stmts[0]) with self.assertAddsMessages( MessageTest("logging-not-lazy", node=stmts[1], args=("lazy %",)) ): self.checker.visit_call(stmts[1]) def test_dont_crash_on_invalid_format_string(self) -> None: node = astroid.parse( """ import logging logging.error('0} - {1}'.format(1, 2)) """ ) self.walk(node) def test_detects_renamed_standard_logging_module(self) -> None: stmts = astroid.extract_node( """ import logging as blogging #@ blogging.warn('%s' % '%s') #@ """ ) self.checker.visit_module(None) self.checker.visit_import(stmts[0]) with self.assertAddsMessages( MessageTest("logging-not-lazy", node=stmts[1], args=("lazy %",)) ): self.checker.visit_call(stmts[1]) @set_config(logging_modules=["logging", "my.logging"]) def test_nonstandard_logging_module(self) -> None: stmts = astroid.extract_node( """ from my import logging as blogging #@ blogging.warn('%s' % '%s') #@ """ ) self.checker.visit_module(None) self.checker.visit_import(stmts[0]) with self.assertAddsMessages( MessageTest("logging-not-lazy", node=stmts[1], args=("lazy %",)) ): self.checker.visit_call(stmts[1]) def _assert_logging_format_no_messages(self, stmt: str) -> None: stmts = astroid.extract_node( """ import logging #@ logging.error #@ """.replace( "", stmt ) ) self.checker.visit_module(None) self.checker.visit_import(stmts[0]) with self.assertNoMessages(): self.checker.visit_call(stmts[1]) def _assert_logging_format_message( self, msg: str, stmt: str, args: Optional[Tuple[str]] = None, with_too_many: bool = False, ) -> None: stmts = astroid.extract_node( """ import logging #@ logging.error #@ """.replace( "", stmt ) ) self.checker.visit_module(None) self.checker.visit_import(stmts[0]) messages = [MessageTest(msg, node=stmts[1], args=args, confidence=UNDEFINED)] if with_too_many: messages.append( MessageTest( "logging-too-many-args", node=stmts[1], confidence=UNDEFINED ) ) with self.assertAddsMessages(*messages): self.checker.visit_call(stmts[1]) def _assert_logging_format_too_few_args(self, stmt: str) -> None: self._assert_logging_format_message("logging-too-few-args", stmt) def _assert_logging_format_too_many_args(self, stmt: str) -> None: self._assert_logging_format_message("logging-too-many-args", stmt) @set_config(logging_format_style="new") def test_brace_format_style_matching_arguments(self) -> None: self._assert_logging_format_no_messages("('constant string')") self._assert_logging_format_no_messages("('{}')") self._assert_logging_format_no_messages("('{}', 1)") self._assert_logging_format_no_messages("('{0}', 1)") self._assert_logging_format_no_messages("('{named}', {'named': 1})") self._assert_logging_format_no_messages("('{} {named}', 1, {'named': 1})") self._assert_logging_format_no_messages("('{0} {named}', 1, {'named': 1})") @set_config(logging_format_style="new") def test_brace_format_style_too_few_args(self) -> None: self._assert_logging_format_too_few_args("('{}, {}', 1)") self._assert_logging_format_too_few_args("('{0}, {1}', 1)") self._assert_logging_format_too_few_args( "('{named1}, {named2}', {'named1': 1})" ) self._assert_logging_format_too_few_args("('{0}, {named}', 1)") self._assert_logging_format_too_few_args("('{}, {named}', {'named': 1})") self._assert_logging_format_too_few_args("('{0}, {named}', {'named': 1})") @set_config(logging_format_style="new") def test_brace_format_style_not_enough_arguments(self) -> None: self._assert_logging_format_too_many_args("('constant string', 1, 2)") self._assert_logging_format_too_many_args("('{}', 1, 2)") self._assert_logging_format_too_many_args("('{0}', 1, 2)") self._assert_logging_format_too_many_args("('{}, {named}', 1, 2, {'named': 1})") self._assert_logging_format_too_many_args( "('{0}, {named}', 1, 2, {'named': 1})" ) @pytest.mark.skipif(sys.version_info < (3, 6), reason="F-string require >=3.6") @set_config(logging_format_style="new") def test_fstr_not_new_format_style_matching_arguments(self) -> None: msg = "logging-fstring-interpolation" args = ("lazy %",) self._assert_logging_format_message(msg, "(f'{named}')", args)