# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt """Unit tests for the imports checker.""" import os import astroid from pytest import CaptureFixture from pylint.checkers import imports from pylint.interfaces import UNDEFINED from pylint.testutils import CheckerTestCase, MessageTest from pylint.testutils._run import _Run as Run REGR_DATA = os.path.join(os.path.dirname(__file__), "..", "regrtest_data", "") class TestImportsChecker(CheckerTestCase): CHECKER_CLASS = imports.ImportsChecker def test_relative_beyond_top_level(self) -> None: module = astroid.MANAGER.ast_from_module_name("beyond_top", REGR_DATA) import_from = module.body[0] msg = MessageTest( msg_id="relative-beyond-top-level", node=import_from, line=1, col_offset=0, end_line=1, end_col_offset=25, ) with self.assertAddsMessages(msg): self.checker.visit_importfrom(import_from) with self.assertNoMessages(): self.checker.visit_importfrom(module.body[1]) with self.assertNoMessages(): self.checker.visit_importfrom(module.body[2].body[0]) @staticmethod def test_relative_beyond_top_level_two(capsys: CaptureFixture[str]) -> None: Run( [ f"{os.path.join(REGR_DATA, 'beyond_top_two')}", "-d all", "-e relative-beyond-top-level", ], exit=False, ) output, errors = capsys.readouterr() top_level_function = os.path.join( REGR_DATA, "beyond_top_two/namespace_package/top_level_function.py" ) Run( [top_level_function, "-d all", "-e relative-beyond-top-level"], exit=False, ) output2, errors2 = capsys.readouterr() assert len(output.split("\n")) == 5 assert len(output2.split("\n")) == 5 assert errors == errors2 @staticmethod def test_relative_beyond_top_level_three(capsys: CaptureFixture[str]) -> None: Run( [ f"{os.path.join(REGR_DATA, 'beyond_top_three/a.py')}", "-d all", "-e relative-beyond-top-level", ], exit=False, ) output, errors = capsys.readouterr() assert len(output.split("\n")) == 5 assert errors == "" @staticmethod def test_relative_beyond_top_level_four(capsys: CaptureFixture[str]) -> None: Run( [ f"{os.path.join(REGR_DATA, 'beyond_top_four/module')}", "-d missing-docstring,unused-import", ], exit=False, ) output, errors = capsys.readouterr() assert len(output.split("\n")) == 5 assert errors == "" def test_wildcard_import_init(self) -> None: module = astroid.MANAGER.ast_from_module_name("init_wildcard", REGR_DATA) import_from = module.body[0] with self.assertNoMessages(): self.checker.visit_importfrom(import_from) def test_wildcard_import_non_init(self) -> None: module = astroid.MANAGER.ast_from_module_name("wildcard", REGR_DATA) import_from = module.body[0] msg = MessageTest( msg_id="wildcard-import", node=import_from, args="empty", confidence=UNDEFINED, line=1, col_offset=0, end_line=1, end_col_offset=19, ) with self.assertAddsMessages(msg): self.checker.visit_importfrom(import_from) @staticmethod def test_preferred_module(capsys: CaptureFixture[str]) -> None: """ Tests preferred-module configuration option """ # test preferred-modules case with base module import Run( [ f"{os.path.join(REGR_DATA, 'preferred_module/unpreferred_module.py')}", "-d all", "-e preferred-module", # prefer sys instead of os (for triggering test) "--preferred-modules=os:sys", ], exit=False, ) output, errors = capsys.readouterr() # assert that we saw preferred-modules triggered assert "Prefer importing 'sys' instead of 'os'" in output # assert there were no errors assert len(errors) == 0 # test preferred-modules trigger case with submodules Run( [ f"{os.path.join(REGR_DATA, 'preferred_module/unpreferred_submodule.py')}", "-d all", "-e preferred-module", # prefer os.path instead of pathlib (for triggering test) "--preferred-modules=os.path:pathlib", ], exit=False, ) output, errors = capsys.readouterr() # assert that we saw preferred-modules triggered assert "Prefer importing 'pathlib' instead of 'os.path'" in output # assert there were no errors assert len(errors) == 0 # test preferred-modules ignore case with submodules Run( [ f"{os.path.join(REGR_DATA, 'preferred_module/unpreferred_submodule.py')}", "-d all", "-e preferred-module", # prefer pathlib instead of os.stat (for untriggered test) "--preferred-modules=os.stat:pathlib", ], exit=False, ) output, errors = capsys.readouterr() # assert that we didn't see preferred-modules triggered assert "Your code has been rated at 10.00/10" in output # assert there were no errors assert len(errors) == 0 # test preferred-modules base module for implemented submodule (from ... import) Run( [ f"{os.path.join(REGR_DATA, 'preferred_module/unpreferred_submodule.py')}", "-d all", "-e preferred-module", # prefer pathlib instead of os (for triggering test) "--preferred-modules=os:pathlib", ], exit=False, ) output, errors = capsys.readouterr() # assert that we saw preferred-modules triggered with base module assert "Prefer importing 'pathlib' instead of 'os'" in output # assert there were no errors assert len(errors) == 0 # Test for challenges with preferred modules indefinite matches Run( [ f"{os.path.join(REGR_DATA, 'preferred_module/unpreferred_submodule.py')}", "-d all", "-e preferred-module", # prefer pathlib instead of random (testing to avoid regression) # pathlib shouldn't match with path, which is in the test file "--preferred-modules=random:pathlib", ], exit=False, ) _, errors = capsys.readouterr() # Assert there were no errors assert len(errors) == 0 @staticmethod def test_allow_reexport_package(capsys: CaptureFixture[str]) -> None: """Test --allow-reexport-from-package option.""" # Option disabled - useless-import-alias should always be emitted Run( [ f"{os.path.join(REGR_DATA, 'allow_reexport')}", "--allow-reexport-from-package=no", "-sn", ], exit=False, ) output, errors = capsys.readouterr() assert len(output.split("\n")) == 7, f"Expected 7 line breaks in:{output}" assert ( "__init__.py:1:0: C0414: Import alias does not rename original package (useless-import-alias)" in output ) assert ( "file.py:2:0: C0414: Import alias does not rename original package (useless-import-alias)" in output ) assert len(errors) == 0 # Option enabled - useless-import-alias should only be emitted for 'file.py' Run( [ f"{os.path.join(REGR_DATA, 'allow_reexport')}", "--allow-reexport-from-package=yes", "--disable=missing-module-docstring", "-sn", ], exit=False, ) output, errors = capsys.readouterr() assert len(output.split("\n")) == 3 assert "__init__.py" not in output assert ( "file.py:2:0: C0414: Import alias does not rename original package (useless-import-alias)" in output ) assert len(errors) == 0