diff --git a/pylint/lint/expand_modules.py b/pylint/lint/expand_modules.py index ece3e0624d..0116c9bb7a 100644 --- a/pylint/lint/expand_modules.py +++ b/pylint/lint/expand_modules.py @@ -136,32 +136,28 @@ def expand_modules( is_namespace = modutils.is_namespace(spec) is_directory = modutils.is_directory(spec) if not is_namespace: - if filepath in result: - # Always set arg flag if module explicitly given. - result[filepath]["isarg"] = True - else: - result[filepath] = { - "path": filepath, - "name": modname, - "isarg": True, - "basepath": filepath, - "basename": modname, - "isignored": False, - } + default: ModuleDescriptionDict = { + "path": filepath, + "name": modname, + "isarg": True, + "basepath": filepath, + "basename": modname, + "isignored": False, + } + result.setdefault(filepath, default)["isarg"] = True has_init = ( - not (modname.endswith(".__init__") or modname == "__init__") - and os.path.basename(filepath) == "__init__.py" + modparts[-1] != "__init__" and os.path.basename(filepath) == "__init__.py" ) if has_init or is_namespace or is_directory: for subfilepath in modutils.get_module_files( - os.path.dirname(filepath) or ".", ignore_list, list_all=is_namespace + os.path.dirname(filepath) or ".", [], list_all=is_namespace ): subfilepath = os.path.normpath(subfilepath) if filepath == subfilepath: continue - if _is_in_ignore_list_re( - os.path.basename(subfilepath), ignore_list_re - ) or _is_in_ignore_list_re(subfilepath, ignore_list_paths_re): + if _is_ignored_file( + subfilepath, ignore_list, ignore_list_re, ignore_list_paths_re + ): result[subfilepath] = { "path": subfilepath, "name": "", diff --git a/pylint/testutils/lint_module_test.py b/pylint/testutils/lint_module_test.py index 37839c8908..73efcda375 100644 --- a/pylint/testutils/lint_module_test.py +++ b/pylint/testutils/lint_module_test.py @@ -267,10 +267,16 @@ def error_msg_for_unequal_messages( ) if missing: msg.append("\nExpected in testdata:") - msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(missing)) + msg.extend( # pragma: no cover + f" {msg[0]:3}: {msg[1]} (times {times})" + for msg, times in sorted(missing.items()) + ) if unexpected: msg.append("\nUnexpected in testdata:") - msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(unexpected)) + msg.extend( + f" {msg[0]:3}: {msg[1]} (times {times})" + for msg, times in sorted(unexpected.items()) + ) error_msg = "\n".join(msg) if self._config and self._config.getoption("verbose") > 0: error_msg += "\n\nActual pylint output for this file:\n" diff --git a/pyproject.toml b/pyproject.toml index 81dabca666..46b0c3dcb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ dependencies = [ # Also upgrade requirements_test_min.txt. # Pinned to dev of second minor update to allow editable installs and fix primer issues, # see https://github.com/pylint-dev/astroid/issues/1341 - "astroid>=3.3.8,<=4.0.0.dev0", + "astroid>=4.0.0a0,<=4.1.0.dev0", "colorama>=0.4.5; sys_platform=='win32'", "dill>=0.2; python_version<'3.11'", "dill>=0.3.6; python_version>='3.11'", diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 3fd42d5f69..1d6796125c 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -1,6 +1,6 @@ .[testutils,spelling] # astroid dependency is also defined in pyproject.toml -astroid==3.3.8 # Pinned to a specific version for tests +astroid==4.0.0-a0 # Pinned to a specific version for tests typing-extensions~=4.12 py~=1.11.0 pytest~=8.3 diff --git a/tests/checkers/unittest_variables.py b/tests/checkers/unittest_variables.py index f43a712b10..7b8817172e 100644 --- a/tests/checkers/unittest_variables.py +++ b/tests/checkers/unittest_variables.py @@ -22,7 +22,7 @@ class TestVariablesChecker(CheckerTestCase): def test_all_elements_without_parent(self) -> None: node = astroid.extract_node("__all__ = []") - node.value.elts.append(astroid.Const("test")) + node.value.elts.append(astroid.Const("test", parent=None)) root = node.root() with self.assertNoMessages(): self.checker.visit_module(root) diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.py b/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.py new file mode 100644 index 0000000000..67145ec4d3 --- /dev/null +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.py @@ -0,0 +1,5 @@ +# [line-too-long, missing-module-docstring, undefined-all-variable, undefined-all-variable, undefined-all-variable, undefined-all-variable, undefined-all-variable] +# ↓ equivalent to __all__ = ["C", "O", "N", "S", "T"] +__all__ = list("CONST") +# +CONST = 42 diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.txt b/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.txt new file mode 100644 index 0000000000..1b29481100 --- /dev/null +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_list_confusion.txt @@ -0,0 +1,7 @@ +line-too-long:1:0:None:None::Line too long (163/100):UNDEFINED +missing-module-docstring:1:0:None:None::Missing module docstring:HIGH +undefined-all-variable:1:0:None:None::Undefined variable name 'C' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'N' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'O' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'S' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'T' in __all__:UNDEFINED diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.py b/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.py new file mode 100644 index 0000000000..e51087848b --- /dev/null +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.py @@ -0,0 +1,5 @@ +# [line-too-long, missing-module-docstring, undefined-all-variable, undefined-all-variable, undefined-all-variable, undefined-all-variable, undefined-all-variable] +# ↓ equivalent to __all__ = ("C", "O", "N", "S", "T") +__all__ = tuple("CONST") + +CONST = 42 diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.txt b/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.txt new file mode 100644 index 0000000000..1b29481100 --- /dev/null +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_tuple_confusion.txt @@ -0,0 +1,7 @@ +line-too-long:1:0:None:None::Line too long (163/100):UNDEFINED +missing-module-docstring:1:0:None:None::Missing module docstring:HIGH +undefined-all-variable:1:0:None:None::Undefined variable name 'C' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'N' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'O' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'S' in __all__:UNDEFINED +undefined-all-variable:1:0:None:None::Undefined variable name 'T' in __all__:UNDEFINED diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_3.py b/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_3.py index 3725d055e4..7eb9b824af 100644 --- a/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_3.py +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_3.py @@ -1,4 +1,5 @@ """Test valid __all__ format.""" -__all__ = list("CONST") + +__all__ = list(["CONST"]) CONST = 42 diff --git a/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_4.py b/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_4.py index 65a8c384b5..eb6db49dcf 100644 --- a/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_4.py +++ b/tests/functional/i/invalid/invalid_all/invalid_all_format_valid_4.py @@ -1,4 +1,5 @@ """Test valid __all__ format.""" -__all__ = tuple("CONST") + +__all__ = tuple(["CONST"]) CONST = 42 diff --git a/tests/lint/unittest_expand_modules.py b/tests/lint/unittest_expand_modules.py index f56dc577be..054687d0ab 100644 --- a/tests/lint/unittest_expand_modules.py +++ b/tests/lint/unittest_expand_modules.py @@ -127,20 +127,28 @@ def test__is_in_ignore_list_re_match() -> None: # A directory that is not a python package. REPORTERS_PATH = Path(__file__).parent.parent / "reporters" test_reporters = { # pylint: disable=consider-using-namedtuple-or-dataclass - str(REPORTERS_PATH / "unittest_json_reporter.py"): { - "path": str(REPORTERS_PATH / "unittest_json_reporter.py"), - "name": "reporters.unittest_json_reporter", - "isarg": False, + str(REPORTERS_PATH / "__init__.py"): { + "basename": "reporters", "basepath": str(REPORTERS_PATH / "__init__.py"), + "isarg": True, + "name": "reporters", + "path": str(REPORTERS_PATH / "__init__.py"), + "isignored": False, + }, + str(REPORTERS_PATH / "unittest_json_reporter.py"): { "basename": "reporters", + "basepath": str(REPORTERS_PATH / "__init__.py"), + "isarg": False, + "name": "reporters.unittest_json_reporter", + "path": str(REPORTERS_PATH / "unittest_json_reporter.py"), "isignored": False, }, str(REPORTERS_PATH / "unittest_reporting.py"): { + "basename": "reporters", + "basepath": str(REPORTERS_PATH / "__init__.py"), + "isarg": False, "path": str(REPORTERS_PATH / "unittest_reporting.py"), "name": "reporters.unittest_reporting", - "isarg": False, - "basepath": str(REPORTERS_PATH / "__init__.py"), - "basename": "reporters", "isignored": False, }, } diff --git a/tests/reporters/__init__.py b/tests/reporters/__init__.py new file mode 100644 index 0000000000..e69de29bb2