summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
Diffstat (limited to 'pylint')
-rw-r--r--pylint/__init__.py26
-rw-r--r--pylint/__main__.py4
-rw-r--r--pylint/__pkginfo__.py10
-rw-r--r--pylint/checkers/__init__.py14
-rw-r--r--pylint/checkers/async.py4
-rw-r--r--pylint/checkers/bad_chained_comparison.py4
-rw-r--r--pylint/checkers/base/__init__.py4
-rw-r--r--pylint/checkers/base/basic_checker.py20
-rw-r--r--pylint/checkers/base/basic_error_checker.py4
-rw-r--r--pylint/checkers/base/comparison_checker.py4
-rw-r--r--pylint/checkers/base/docstring_checker.py11
-rw-r--r--pylint/checkers/base/name_checker/__init__.py4
-rw-r--r--pylint/checkers/base/name_checker/checker.py11
-rw-r--r--pylint/checkers/base/name_checker/naming_style.py4
-rw-r--r--pylint/checkers/base/pass_checker.py4
-rw-r--r--pylint/checkers/base_checker.py57
-rw-r--r--pylint/checkers/classes/__init__.py4
-rw-r--r--pylint/checkers/classes/class_checker.py11
-rw-r--r--pylint/checkers/classes/special_methods_checker.py4
-rw-r--r--pylint/checkers/deprecated.py4
-rw-r--r--pylint/checkers/design_analysis.py4
-rw-r--r--pylint/checkers/dunder_methods.py6
-rw-r--r--pylint/checkers/ellipsis_checker.py4
-rw-r--r--pylint/checkers/exceptions.py33
-rw-r--r--pylint/checkers/format.py17
-rw-r--r--pylint/checkers/imports.py32
-rw-r--r--pylint/checkers/lambda_expressions.py4
-rw-r--r--pylint/checkers/logging.py12
-rw-r--r--pylint/checkers/mapreduce_checker.py32
-rw-r--r--pylint/checkers/method_args.py4
-rw-r--r--pylint/checkers/misc.py5
-rw-r--r--pylint/checkers/modified_iterating_checker.py4
-rw-r--r--pylint/checkers/nested_min_max.py20
-rw-r--r--pylint/checkers/newstyle.py4
-rw-r--r--pylint/checkers/non_ascii_names.py4
-rw-r--r--pylint/checkers/raw_metrics.py12
-rw-r--r--pylint/checkers/refactoring/__init__.py4
-rw-r--r--pylint/checkers/refactoring/implicit_booleaness_checker.py134
-rw-r--r--pylint/checkers/refactoring/not_checker.py4
-rw-r--r--pylint/checkers/refactoring/recommendation_checker.py8
-rw-r--r--pylint/checkers/refactoring/refactoring_checker.py26
-rw-r--r--pylint/checkers/similar.py8
-rw-r--r--pylint/checkers/spelling.py12
-rw-r--r--pylint/checkers/stdlib.py6
-rw-r--r--pylint/checkers/strings.py14
-rw-r--r--pylint/checkers/threading_checker.py4
-rw-r--r--pylint/checkers/typecheck.py44
-rw-r--r--pylint/checkers/unicode.py4
-rw-r--r--pylint/checkers/unsupported_version.py6
-rw-r--r--pylint/checkers/utils.py223
-rw-r--r--pylint/checkers/variables.py380
-rw-r--r--pylint/config/__init__.py66
-rw-r--r--pylint/config/_pylint_config/__init__.py4
-rw-r--r--pylint/config/_pylint_config/generate_command.py11
-rw-r--r--pylint/config/_pylint_config/help_message.py4
-rw-r--r--pylint/config/_pylint_config/main.py4
-rw-r--r--pylint/config/_pylint_config/setup.py4
-rw-r--r--pylint/config/_pylint_config/utils.py11
-rw-r--r--pylint/config/argument.py13
-rw-r--r--pylint/config/arguments_manager.py439
-rw-r--r--pylint/config/arguments_provider.py177
-rw-r--r--pylint/config/callback_actions.py11
-rw-r--r--pylint/config/config_file_parser.py27
-rw-r--r--pylint/config/config_initialization.py4
-rw-r--r--pylint/config/configuration_mixin.py41
-rw-r--r--pylint/config/deprecation_actions.py5
-rw-r--r--pylint/config/environment_variable.py11
-rw-r--r--pylint/config/exceptions.py4
-rw-r--r--pylint/config/find_default_config_files.py53
-rw-r--r--pylint/config/help_formatter.py9
-rw-r--r--pylint/config/option.py239
-rw-r--r--pylint/config/option_manager_mixin.py372
-rw-r--r--pylint/config/option_parser.py56
-rw-r--r--pylint/config/options_provider_mixin.py123
-rw-r--r--pylint/config/utils.py27
-rw-r--r--pylint/constants.py54
-rwxr-xr-xpylint/epylint.py224
-rw-r--r--pylint/exceptions.py4
-rw-r--r--pylint/extensions/__init__.py4
-rw-r--r--pylint/extensions/_check_docs_utils.py4
-rw-r--r--pylint/extensions/bad_builtin.py4
-rw-r--r--pylint/extensions/broad_try_clause.py4
-rw-r--r--pylint/extensions/check_elif.py4
-rw-r--r--pylint/extensions/code_style.py4
-rw-r--r--pylint/extensions/comparetozero.py95
-rw-r--r--pylint/extensions/comparison_placement.py4
-rw-r--r--pylint/extensions/confusing_elif.py4
-rw-r--r--pylint/extensions/consider_refactoring_into_while_condition.py4
-rw-r--r--pylint/extensions/consider_ternary_expression.py4
-rw-r--r--pylint/extensions/dict_init_mutate.py4
-rw-r--r--pylint/extensions/docparams.py4
-rw-r--r--pylint/extensions/docstyle.py4
-rw-r--r--pylint/extensions/dunder.py4
-rw-r--r--pylint/extensions/empty_comment.py4
-rw-r--r--pylint/extensions/emptystring.py78
-rw-r--r--pylint/extensions/eq_without_hash.py6
-rw-r--r--pylint/extensions/for_any_all.py4
-rw-r--r--pylint/extensions/magic_value.py4
-rw-r--r--pylint/extensions/mccabe.py4
-rw-r--r--pylint/extensions/no_self_use.py4
-rw-r--r--pylint/extensions/overlapping_exceptions.py4
-rw-r--r--pylint/extensions/private_import.py4
-rw-r--r--pylint/extensions/redefined_loop_name.py4
-rw-r--r--pylint/extensions/redefined_variable_type.py4
-rw-r--r--pylint/extensions/set_membership.py4
-rw-r--r--pylint/extensions/typing.py4
-rw-r--r--pylint/extensions/while_used.py4
-rw-r--r--pylint/graph.py4
-rw-r--r--pylint/interfaces.py106
-rw-r--r--pylint/lint/__init__.py13
-rw-r--r--pylint/lint/base_options.py4
-rw-r--r--pylint/lint/caching.py5
-rw-r--r--pylint/lint/expand_modules.py19
-rw-r--r--pylint/lint/message_state_handler.py16
-rw-r--r--pylint/lint/parallel.py22
-rw-r--r--pylint/lint/pylinter.py167
-rw-r--r--pylint/lint/report_functions.py4
-rw-r--r--pylint/lint/run.py22
-rw-r--r--pylint/lint/utils.py102
-rw-r--r--pylint/message/__init__.py4
-rw-r--r--pylint/message/_deleted_message_ids.py16
-rw-r--r--pylint/message/message.py24
-rw-r--r--pylint/message/message_definition.py22
-rw-r--r--pylint/message/message_definition_store.py6
-rw-r--r--pylint/message/message_id_store.py4
-rw-r--r--pylint/py.typed0
-rw-r--r--pylint/pyreverse/__init__.py4
-rw-r--r--pylint/pyreverse/diadefslib.py17
-rw-r--r--pylint/pyreverse/diagrams.py27
-rw-r--r--pylint/pyreverse/dot_printer.py6
-rw-r--r--pylint/pyreverse/inspector.py53
-rw-r--r--pylint/pyreverse/main.py49
-rw-r--r--pylint/pyreverse/mermaidjs_printer.py9
-rw-r--r--pylint/pyreverse/plantuml_printer.py11
-rw-r--r--pylint/pyreverse/printer.py6
-rw-r--r--pylint/pyreverse/printer_factory.py6
-rw-r--r--pylint/pyreverse/utils.py9
-rw-r--r--pylint/pyreverse/vcg_printer.py303
-rw-r--r--pylint/pyreverse/writer.py31
-rw-r--r--pylint/reporters/__init__.py4
-rw-r--r--pylint/reporters/base_reporter.py23
-rw-r--r--pylint/reporters/collecting_reporter.py4
-rw-r--r--pylint/reporters/json_reporter.py14
-rw-r--r--pylint/reporters/multi_reporter.py4
-rw-r--r--pylint/reporters/reports_handler_mix_in.py4
-rw-r--r--pylint/reporters/text.py143
-rw-r--r--pylint/reporters/ureports/__init__.py4
-rw-r--r--pylint/reporters/ureports/base_writer.py4
-rw-r--r--pylint/reporters/ureports/nodes.py6
-rw-r--r--pylint/reporters/ureports/text_writer.py4
-rw-r--r--pylint/testutils/__init__.py4
-rw-r--r--pylint/testutils/_primer/__init__.py4
-rw-r--r--pylint/testutils/_primer/package_to_lint.py11
-rw-r--r--pylint/testutils/_primer/primer.py4
-rw-r--r--pylint/testutils/_primer/primer_command.py12
-rw-r--r--pylint/testutils/_primer/primer_compare_command.py4
-rw-r--r--pylint/testutils/_primer/primer_prepare_command.py11
-rw-r--r--pylint/testutils/_primer/primer_run_command.py10
-rw-r--r--pylint/testutils/_run.py9
-rw-r--r--pylint/testutils/checker_test_case.py24
-rw-r--r--pylint/testutils/configuration_test.py4
-rw-r--r--pylint/testutils/constants.py4
-rw-r--r--pylint/testutils/decorator.py4
-rw-r--r--pylint/testutils/functional/__init__.py4
-rw-r--r--pylint/testutils/functional/find_functional_tests.py104
-rw-r--r--pylint/testutils/functional/lint_module_output_update.py4
-rw-r--r--pylint/testutils/functional/test_file.py14
-rw-r--r--pylint/testutils/functional_test_file.py24
-rw-r--r--pylint/testutils/get_test_info.py4
-rw-r--r--pylint/testutils/global_test_linter.py4
-rw-r--r--pylint/testutils/lint_module_test.py8
-rw-r--r--pylint/testutils/output_line.py70
-rw-r--r--pylint/testutils/pyreverse.py16
-rw-r--r--pylint/testutils/reporter_for_tests.py6
-rw-r--r--pylint/testutils/testing_pylintrc4
-rw-r--r--pylint/testutils/tokenize_str.py4
-rw-r--r--pylint/testutils/unittest_linter.py12
-rw-r--r--pylint/testutils/utils.py4
-rw-r--r--pylint/typing.py13
-rw-r--r--pylint/utils/__init__.py8
-rw-r--r--pylint/utils/ast_walker.py4
-rw-r--r--pylint/utils/docs.py41
-rw-r--r--pylint/utils/file_state.py54
-rw-r--r--pylint/utils/linterstats.py12
-rw-r--r--pylint/utils/pragma_parser.py4
-rw-r--r--pylint/utils/utils.py85
186 files changed, 1256 insertions, 4219 deletions
diff --git a/pylint/__init__.py b/pylint/__init__.py
index 2cc7edadb..cc0f609aa 100644
--- a/pylint/__init__.py
+++ b/pylint/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -9,14 +9,12 @@ __all__ = [
"version",
"modify_sys_path",
"run_pylint",
- "run_epylint",
"run_symilar",
"run_pyreverse",
]
import os
import sys
-import warnings
from collections.abc import Sequence
from typing import NoReturn
@@ -48,22 +46,6 @@ def _run_pylint_config(argv: Sequence[str] | None = None) -> None:
_PylintConfigRun(argv or sys.argv[1:])
-def run_epylint(argv: Sequence[str] | None = None) -> NoReturn:
- """Run epylint.
-
- argv can be a list of strings normally supplied as arguments on the command line
- """
- from pylint.epylint import Run as EpylintRun
-
- warnings.warn(
- "'run_epylint' will be removed in pylint 3.0, use "
- "https://github.com/emacsorphanage/pylint instead.",
- DeprecationWarning,
- stacklevel=1,
- )
- EpylintRun(argv)
-
-
def run_pyreverse(argv: Sequence[str] | None = None) -> NoReturn:
"""Run pyreverse.
@@ -96,12 +78,12 @@ def modify_sys_path() -> None:
- Remove the first entry. This will always be either "" or the working directory
- Remove the working directory from the second and third entries
if PYTHONPATH includes a ":" at the beginning or the end.
- https://github.com/PyCQA/pylint/issues/3636
+ https://github.com/pylint-dev/pylint/issues/3636
Don't remove it if PYTHONPATH contains the cwd or '.' as the entry will
only be added once.
- Don't remove the working directory from the rest. It will be included
if pylint is installed in an editable configuration (as the last item).
- https://github.com/PyCQA/pylint/issues/4161
+ https://github.com/pylint-dev/pylint/issues/4161
"""
cwd = os.getcwd()
if sys.path[0] in ("", ".", cwd):
diff --git a/pylint/__main__.py b/pylint/__main__.py
index 7df5805f9..448ac55b6 100644
--- a/pylint/__main__.py
+++ b/pylint/__main__.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
import pylint
diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py
index 3566c348f..5a86f05b0 100644
--- a/pylint/__pkginfo__.py
+++ b/pylint/__pkginfo__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""This module exists for compatibility reasons.
@@ -9,14 +9,14 @@ It's updated via tbump, do not modify.
from __future__ import annotations
-__version__ = "2.17.4"
+__version__ = "3.0.0b1"
def get_numversion_from_version(v: str) -> tuple[int, int, int]:
"""Kept for compatibility reason.
- See https://github.com/PyCQA/pylint/issues/4399
- https://github.com/PyCQA/pylint/issues/4420,
+ See https://github.com/pylint-dev/pylint/issues/4399
+ https://github.com/pylint-dev/pylint/issues/4420,
"""
version = v.replace("pylint-", "")
result_version = []
diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py
index ed641d8e5..0f9985918 100644
--- a/pylint/checkers/__init__.py
+++ b/pylint/checkers/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utilities methods and classes for checkers.
@@ -42,8 +42,7 @@ messages nor reports. XXX not true, emit a 07 report !
from __future__ import annotations
-import sys
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
from pylint.checkers.base_checker import (
BaseChecker,
@@ -51,14 +50,8 @@ from pylint.checkers.base_checker import (
BaseTokenChecker,
)
from pylint.checkers.deprecated import DeprecatedMixin
-from pylint.checkers.mapreduce_checker import MapReduceMixin
from pylint.utils import LinterStats, diff_string, register_plugins
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
@@ -141,7 +134,6 @@ __all__ = [
"BaseTokenChecker",
"BaseRawFileChecker",
"initialize",
- "MapReduceMixin",
"DeprecatedMixin",
"register_plugins",
]
diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py
index b06ec60c8..a8ee77302 100644
--- a/pylint/checkers/async.py
+++ b/pylint/checkers/async.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for anything related to the async protocol (PEP 492)."""
diff --git a/pylint/checkers/bad_chained_comparison.py b/pylint/checkers/bad_chained_comparison.py
index 8c3aeb9cc..2e1912160 100644
--- a/pylint/checkers/bad_chained_comparison.py
+++ b/pylint/checkers/bad_chained_comparison.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/base/__init__.py b/pylint/checkers/base/__init__.py
index f427cbf21..c9067b405 100644
--- a/pylint/checkers/base/__init__.py
+++ b/pylint/checkers/base/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
# pylint: disable=duplicate-code # This is similar to the __init__ of .name_checker
diff --git a/pylint/checkers/base/basic_checker.py b/pylint/checkers/base/basic_checker.py
index 062e67a97..18de86142 100644
--- a/pylint/checkers/base/basic_checker.py
+++ b/pylint/checkers/base/basic_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Basic checker for Python code."""
@@ -8,9 +8,8 @@ from __future__ import annotations
import collections
import itertools
-import sys
from collections.abc import Iterator
-from typing import TYPE_CHECKING, cast
+from typing import TYPE_CHECKING, Literal, cast
import astroid
from astroid import nodes, objects, util
@@ -24,11 +23,6 @@ from pylint.utils import LinterStats
if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
class _BasicChecker(BaseChecker):
"""Permits separating multiple checks with the same checker name into
@@ -329,7 +323,7 @@ class BasicChecker(_BasicChecker):
nodes.Subscript,
)
inferred = None
- emit = isinstance(test, (nodes.Const,) + structs + const_nodes)
+ emit = isinstance(test, (nodes.Const, *structs, *const_nodes))
maybe_generator_call = None
if not isinstance(test, except_nodes):
inferred = utils.safe_infer(test)
@@ -367,9 +361,9 @@ class BasicChecker(_BasicChecker):
try:
# Just forcing the generator to infer all elements.
# astroid.exceptions.InferenceError are false positives
- # see https://github.com/PyCQA/pylint/pull/8185
+ # see https://github.com/pylint-dev/pylint/pull/8185
if isinstance(inferred, nodes.FunctionDef):
- call_inferred = list(inferred.infer_call_result())
+ call_inferred = list(inferred.infer_call_result(node))
elif isinstance(inferred, nodes.Lambda):
call_inferred = list(inferred.infer_call_result(node))
except astroid.InferenceError:
@@ -462,7 +456,7 @@ class BasicChecker(_BasicChecker):
# Heuristic: only run inference for names that begin with an uppercase char
# This reduces W0133's coverage, but retains acceptable runtime performance
- # For more details, see: https://github.com/PyCQA/pylint/issues/8073
+ # For more details, see: https://github.com/pylint-dev/pylint/issues/8073
inferred = utils.safe_infer(expr) if name[:1].isupper() else None
if isinstance(inferred, objects.ExceptionInstance):
self.add_message(
diff --git a/pylint/checkers/base/basic_error_checker.py b/pylint/checkers/base/basic_error_checker.py
index 25038247c..ccf9773a7 100644
--- a/pylint/checkers/base/basic_error_checker.py
+++ b/pylint/checkers/base/basic_error_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Basic Error checker from the basic checker."""
diff --git a/pylint/checkers/base/comparison_checker.py b/pylint/checkers/base/comparison_checker.py
index ffbd27374..14d40c7d6 100644
--- a/pylint/checkers/base/comparison_checker.py
+++ b/pylint/checkers/base/comparison_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Comparison checker from the basic checker."""
diff --git a/pylint/checkers/base/docstring_checker.py b/pylint/checkers/base/docstring_checker.py
index 791b085b5..91b3e7d4a 100644
--- a/pylint/checkers/base/docstring_checker.py
+++ b/pylint/checkers/base/docstring_checker.py
@@ -1,13 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Docstring checker from the basic checker."""
from __future__ import annotations
import re
-import sys
+from typing import Literal
import astroid
from astroid import nodes
@@ -21,11 +21,6 @@ from pylint.checkers.utils import (
is_property_setter,
)
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
# do not require a doc string on private/system methods
NO_REQUIRED_DOC_RGX = re.compile("^_")
diff --git a/pylint/checkers/base/name_checker/__init__.py b/pylint/checkers/base/name_checker/__init__.py
index 3d6818b7a..dec4335f5 100644
--- a/pylint/checkers/base/name_checker/__init__.py
+++ b/pylint/checkers/base/name_checker/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
__all__ = [
"NameChecker",
diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py
index 58f7198ef..56ae925fb 100644
--- a/pylint/checkers/base/name_checker/checker.py
+++ b/pylint/checkers/base/name_checker/checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Basic checker for Python code."""
@@ -476,7 +476,12 @@ class NameChecker(_BasicChecker):
# global introduced variable aren't in the function locals
if node.name in frame and node.name not in frame.argnames():
if not _redefines_import(node):
- self._check_name("variable", node.name, node)
+ if isinstance(
+ assign_type, nodes.AnnAssign
+ ) and self._assigns_typealias(assign_type.annotation):
+ self._check_name("typealias", node.name, node)
+ else:
+ self._check_name("variable", node.name, node)
# Check names defined in class scopes
elif isinstance(frame, nodes.ClassDef):
diff --git a/pylint/checkers/base/name_checker/naming_style.py b/pylint/checkers/base/name_checker/naming_style.py
index 8ccec4289..6410f9181 100644
--- a/pylint/checkers/base/name_checker/naming_style.py
+++ b/pylint/checkers/base/name_checker/naming_style.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/base/pass_checker.py b/pylint/checkers/base/pass_checker.py
index 120d4c5a0..a9351ba75 100644
--- a/pylint/checkers/base/pass_checker.py
+++ b/pylint/checkers/base/pass_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from astroid import nodes
diff --git a/pylint/checkers/base_checker.py b/pylint/checkers/base_checker.py
index dd7a0222f..9785f9c4a 100644
--- a/pylint/checkers/base_checker.py
+++ b/pylint/checkers/base_checker.py
@@ -1,12 +1,11 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import abc
import functools
-import warnings
from collections.abc import Iterable, Sequence
from inspect import cleandoc
from tokenize import TokenInfo
@@ -17,7 +16,7 @@ from astroid import nodes
from pylint.config.arguments_provider import _ArgumentsProvider
from pylint.constants import _MSG_ORDER, MAIN_CHECKER_NAME, WarningScope
from pylint.exceptions import InvalidMessageError
-from pylint.interfaces import Confidence, IRawChecker, ITokenChecker, implements
+from pylint.interfaces import Confidence
from pylint.message.message_definition import MessageDefinition
from pylint.typing import (
ExtraMessageOptions,
@@ -47,18 +46,9 @@ class BaseChecker(_ArgumentsProvider):
def __init__(self, linter: PyLinter) -> None:
"""Checker instances should have the linter as argument."""
- if getattr(self, "__implements__", None):
- warnings.warn(
- "Using the __implements__ inheritance pattern for BaseChecker is no "
- "longer supported. Child classes should only inherit BaseChecker or any "
- "of the other checker types from pylint.checkers.",
- DeprecationWarning,
- stacklevel=2,
- )
if self.name is not None:
self.name = self.name.lower()
self.linter = linter
-
_ArgumentsProvider.__init__(self, linter)
def __gt__(self, other: Any) -> bool:
@@ -96,11 +86,9 @@ class BaseChecker(_ArgumentsProvider):
See: MessageHandlerMixIn.get_full_documentation()
"""
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- return self.get_full_documentation(
- msgs=self.msgs, options=self.options_and_values(), reports=self.reports
- )
+ return self.get_full_documentation(
+ msgs=self.msgs, options=self._options_and_values(), reports=self.reports
+ )
def get_full_documentation(
self,
@@ -193,21 +181,10 @@ class BaseChecker(_ArgumentsProvider):
def create_message_definition_from_tuple(
self, msgid: str, msg_tuple: MessageDefinitionTuple
) -> MessageDefinition:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- if isinstance(self, (BaseTokenChecker, BaseRawFileChecker)):
- default_scope = WarningScope.LINE
- # TODO: 3.0: Remove deprecated if-statement
- elif implements(self, (IRawChecker, ITokenChecker)):
- warnings.warn( # pragma: no cover
- "Checkers should subclass BaseTokenChecker or BaseRawFileChecker "
- "instead of using the __implements__ mechanism. Use of __implements__ "
- "will no longer be supported in pylint 3.0",
- DeprecationWarning,
- )
- default_scope = WarningScope.LINE # pragma: no cover
- else:
- default_scope = WarningScope.NODE
+ if isinstance(self, (BaseTokenChecker, BaseRawFileChecker)):
+ default_scope = WarningScope.LINE
+ else:
+ default_scope = WarningScope.NODE
options: ExtraMessageOptions = {}
if len(msg_tuple) == 4:
(msg, symbol, descr, options) = msg_tuple # type: ignore[misc]
@@ -234,20 +211,6 @@ class BaseChecker(_ArgumentsProvider):
for msgid, msg_tuple in sorted(self.msgs.items())
]
- def get_message_definition(self, msgid: str) -> MessageDefinition:
- # TODO: 3.0: Remove deprecated method
- warnings.warn(
- "'get_message_definition' is deprecated and will be removed in 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- for message_definition in self.messages:
- if message_definition.msgid == msgid:
- return message_definition
- error_msg = f"MessageDefinition for '{msgid}' does not exists. "
- error_msg += f"Choose from {[m.msgid for m in self.messages]}."
- raise InvalidMessageError(error_msg)
-
def open(self) -> None:
"""Called before visiting project (i.e. set of modules)."""
diff --git a/pylint/checkers/classes/__init__.py b/pylint/checkers/classes/__init__.py
index e51194439..422fae2ee 100644
--- a/pylint/checkers/classes/__init__.py
+++ b/pylint/checkers/classes/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py
index e09df1706..77a795bcb 100644
--- a/pylint/checkers/classes/class_checker.py
+++ b/pylint/checkers/classes/class_checker.py
@@ -1,15 +1,15 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Classes checker for Python code."""
from __future__ import annotations
import collections
-import sys
from collections import defaultdict
from collections.abc import Callable, Sequence
+from functools import cached_property
from itertools import chain, zip_longest
from re import Pattern
from typing import TYPE_CHECKING, Any, Union
@@ -47,11 +47,6 @@ if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter
-if sys.version_info >= (3, 8):
- from functools import cached_property
-else:
- from astroid.decorators import cachedproperty as cached_property
-
_AccessNodes = Union[nodes.Attribute, nodes.AssignAttr]
INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
diff --git a/pylint/checkers/classes/special_methods_checker.py b/pylint/checkers/classes/special_methods_checker.py
index 9de5e619f..025f28562 100644
--- a/pylint/checkers/classes/special_methods_checker.py
+++ b/pylint/checkers/classes/special_methods_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Special methods checker and helper function's module."""
diff --git a/pylint/checkers/deprecated.py b/pylint/checkers/deprecated.py
index f6a82b1da..821a9836c 100644
--- a/pylint/checkers/deprecated.py
+++ b/pylint/checkers/deprecated.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker mixin for deprecated functionality."""
diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py
index 11ff7a5a1..701615d89 100644
--- a/pylint/checkers/design_analysis.py
+++ b/pylint/checkers/design_analysis.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for signs of poor design."""
diff --git a/pylint/checkers/dunder_methods.py b/pylint/checkers/dunder_methods.py
index b66844901..535dbb168 100644
--- a/pylint/checkers/dunder_methods.py
+++ b/pylint/checkers/dunder_methods.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -23,7 +23,7 @@ class DunderCallChecker(BaseChecker):
Docs: https://docs.python.org/3/reference/datamodel.html#basic-customization
We exclude names in list pylint.constants.EXTRA_DUNDER_METHODS such as
- __index__ (see https://github.com/PyCQA/pylint/issues/6795)
+ __index__ (see https://github.com/pylint-dev/pylint/issues/6795)
since these either have no alternative method of being called or
have a genuine use case for being called manually.
diff --git a/pylint/checkers/ellipsis_checker.py b/pylint/checkers/ellipsis_checker.py
index 52b15c702..4e7e3bd35 100644
--- a/pylint/checkers/ellipsis_checker.py
+++ b/pylint/checkers/ellipsis_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Ellipsis checker for Python code."""
diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py
index c2bd246d7..5757b8d55 100644
--- a/pylint/checkers/exceptions.py
+++ b/pylint/checkers/exceptions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checks for various exception related errors."""
@@ -210,16 +210,17 @@ class ExceptionRaiseRefVisitor(BaseVisitor):
"notimplemented-raised", node=self._node, confidence=HIGH
)
return
-
try:
- exceptions = list(_annotated_unpack_infer(node))
+ exceptions = [
+ c
+ for _, c in _annotated_unpack_infer(node)
+ if isinstance(c, nodes.ClassDef)
+ ]
except astroid.InferenceError:
return
- for _, exception in exceptions:
- if isinstance(
- exception, nodes.ClassDef
- ) and self._checker._is_overgeneral_exception(exception):
+ for exception in exceptions:
+ if self._checker._is_overgeneral_exception(exception):
self._checker.add_message(
"broad-exception-raised",
args=exception.name,
@@ -306,13 +307,13 @@ class ExceptionsChecker(checkers.BaseChecker):
def open(self) -> None:
self._builtin_exceptions = _builtin_exceptions()
+ # TODO 3.1: Remove this check and put it elsewhere
for exc_name in self.linter.config.overgeneral_exceptions:
if "." not in exc_name:
warnings.warn_explicit(
- "Specifying exception names in the overgeneral-exceptions option"
- " without module name is deprecated and support for it"
- " will be removed in pylint 3.0."
- f" Use fully qualified name (maybe 'builtins.{exc_name}' ?) instead.",
+ f"'{exc_name}' is not a proper value for the 'overgeneral-exceptions' option. "
+ f"Use fully qualified name (maybe 'builtins.{exc_name}' ?) instead. "
+ "This will cease to be checked at runtime in 3.1.0.",
category=UserWarning,
filename="pylint: Command line or configuration file",
lineno=1,
@@ -646,13 +647,7 @@ class ExceptionsChecker(checkers.BaseChecker):
exceptions_classes += [exc for _, exc in exceptions]
def _is_overgeneral_exception(self, exception: nodes.ClassDef) -> bool:
- return (
- exception.qname() in self.linter.config.overgeneral_exceptions
- # TODO: 3.0: not a qualified name, deprecated
- or "." not in exception.name
- and exception.name in self.linter.config.overgeneral_exceptions
- and exception.root().name == utils.EXCEPTIONS_MODULE
- )
+ return exception.qname() in self.linter.config.overgeneral_exceptions
def register(linter: PyLinter) -> None:
diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py
index 562c48f5d..3e3cd3227 100644
--- a/pylint/checkers/format.py
+++ b/pylint/checkers/format.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Python code format's checker.
@@ -13,11 +13,10 @@ Some parts of the process_token method is based from The Tab Nanny std module.
from __future__ import annotations
-import sys
import tokenize
from functools import reduce
from re import Match
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
from astroid import nodes
@@ -31,10 +30,6 @@ from pylint.utils.pragma_parser import OPTION_PO, PragmaParserError, parse_pragm
if TYPE_CHECKING:
from pylint.lint import PyLinter
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
_KEYWORD_TOKENS = {
"assert",
@@ -346,7 +341,7 @@ class FormatChecker(BaseTokenChecker, BaseRawFileChecker):
if found_and_or:
return
if keyword_token == "in":
- # This special case was added in https://github.com/PyCQA/pylint/pull/4948
+ # This special case was added in https://github.com/pylint-dev/pylint/pull/4948
# but it could be removed in the future. Avoid churn for now.
return
self.add_message(
@@ -667,8 +662,8 @@ class FormatChecker(BaseTokenChecker, BaseRawFileChecker):
self.add_message("missing-final-newline", line=lineno + offset)
continue
# We don't test for trailing whitespaces in strings
- # See https://github.com/PyCQA/pylint/issues/6936
- # and https://github.com/PyCQA/pylint/issues/3822
+ # See https://github.com/pylint-dev/pylint/issues/6936
+ # and https://github.com/pylint-dev/pylint/issues/3822
if tokens.type(line_start) != tokenize.STRING:
self.check_trailing_whitespace_ending(line, lineno + offset)
diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py
index ef7fe50cc..42649f3d9 100644
--- a/pylint/checkers/imports.py
+++ b/pylint/checkers/imports.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Imports checkers for Python code."""
@@ -12,6 +12,7 @@ import os
import sys
from collections import defaultdict
from collections.abc import ItemsView, Sequence
+from functools import cached_property
from typing import TYPE_CHECKING, Any, Dict, List, Union
import astroid
@@ -23,6 +24,7 @@ from pylint.checkers.utils import (
get_import_name,
in_type_checking_block,
is_from_fallback_block,
+ is_module_ignored,
is_sys_guard,
node_ignores_exception,
)
@@ -37,11 +39,6 @@ from pylint.utils.linterstats import LinterStats
if TYPE_CHECKING:
from pylint.lint import PyLinter
-if sys.version_info >= (3, 8):
- from functools import cached_property
-else:
- from astroid.decorators import cachedproperty as cached_property
-
# The dictionary with Any should actually be a _ImportTree again
# but mypy doesn't support recursive types yet
@@ -84,18 +81,6 @@ DEPRECATED_MODULES = {
}
-def _qualified_names(modname: str | None) -> list[str]:
- """Split the names of the given module into subparts.
-
- For example,
- _qualified_names('pylint.checkers.ImportsChecker')
- returns
- ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker']
- """
- names = modname.split(".") if modname is not None else ""
- return [".".join(names[0 : i + 1]) for i in range(len(names))]
-
-
def _get_first_import(
node: ImportNode,
context: nodes.LocalsDictNodeNG,
@@ -153,12 +138,11 @@ def _get_first_import(
def _ignore_import_failure(
node: ImportNode,
- modname: str | None,
+ modname: str,
ignored_modules: Sequence[str],
) -> bool:
- for submodule in _qualified_names(modname):
- if submodule in ignored_modules:
- return True
+ if is_module_ignored(modname, ignored_modules):
+ return True
# Ignore import failure if part of guarded import block
# I.e. `sys.version_info` or `typing.TYPE_CHECKING`
@@ -852,7 +836,7 @@ class ImportsChecker(DeprecatedMixin, BaseChecker):
return std_imports, external_imports, local_imports
def _get_imported_module(
- self, importnode: ImportNode, modname: str | None
+ self, importnode: ImportNode, modname: str
) -> nodes.Module | None:
try:
return importnode.do_import_module(modname)
diff --git a/pylint/checkers/lambda_expressions.py b/pylint/checkers/lambda_expressions.py
index 69c5b738e..18c03060d 100644
--- a/pylint/checkers/lambda_expressions.py
+++ b/pylint/checkers/lambda_expressions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py
index 3064dc926..461047bdf 100644
--- a/pylint/checkers/logging.py
+++ b/pylint/checkers/logging.py
@@ -1,14 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for use of Python logging."""
from __future__ import annotations
import string
-import sys
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
import astroid
from astroid import bases, nodes
@@ -19,11 +18,6 @@ from pylint.checkers import utils
from pylint.checkers.utils import infer_all
from pylint.typing import MessageDefinitionTuple
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
diff --git a/pylint/checkers/mapreduce_checker.py b/pylint/checkers/mapreduce_checker.py
deleted file mode 100644
index 96e86d7c0..000000000
--- a/pylint/checkers/mapreduce_checker.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-from __future__ import annotations
-
-import abc
-import warnings
-from typing import TYPE_CHECKING, Any
-
-if TYPE_CHECKING:
- from pylint.lint import PyLinter
-
-
-class MapReduceMixin(metaclass=abc.ABCMeta):
- """A mixin design to allow multi-process/threaded runs of a Checker."""
-
- def __init__(self) -> None:
- warnings.warn(
- "MapReduceMixin has been deprecated and will be removed in pylint 3.0. "
- "To make a checker reduce map data simply implement get_map_data and reduce_map_data.",
- DeprecationWarning,
- stacklevel=2,
- )
-
- @abc.abstractmethod
- def get_map_data(self) -> Any:
- """Returns merge-able/reducible data that will be examined."""
-
- @abc.abstractmethod
- def reduce_map_data(self, linter: PyLinter, data: list[Any]) -> None:
- """For a given Checker, receives data for all mapped runs."""
diff --git a/pylint/checkers/method_args.py b/pylint/checkers/method_args.py
index 8862328e3..59083fa25 100644
--- a/pylint/checkers/method_args.py
+++ b/pylint/checkers/method_args.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Variables checkers for Python code."""
diff --git a/pylint/checkers/misc.py b/pylint/checkers/misc.py
index 8f6495735..f81dc43e7 100644
--- a/pylint/checkers/misc.py
+++ b/pylint/checkers/misc.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check source code is ascii only or has an encoding declaration (PEP 263)."""
@@ -29,6 +29,7 @@ class ByIdManagedMessagesChecker(BaseRawFileChecker):
"%s",
"use-symbolic-message-instead",
"Used when a message is enabled or disabled by id.",
+ {"default_enabled": False},
)
}
options = ()
diff --git a/pylint/checkers/modified_iterating_checker.py b/pylint/checkers/modified_iterating_checker.py
index bdc8fff7f..9d89f7255 100644
--- a/pylint/checkers/modified_iterating_checker.py
+++ b/pylint/checkers/modified_iterating_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/nested_min_max.py b/pylint/checkers/nested_min_max.py
index e9aa409f0..219382ff5 100644
--- a/pylint/checkers/nested_min_max.py
+++ b/pylint/checkers/nested_min_max.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for use of nested min/max functions."""
@@ -10,6 +10,7 @@ import copy
from typing import TYPE_CHECKING
from astroid import nodes, objects
+from astroid.const import Context
from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages, safe_infer
@@ -96,7 +97,20 @@ class NestedMinMaxChecker(BaseChecker):
if isinstance(
inferred, (nodes.List, nodes.Tuple, nodes.Set, *DICT_TYPES)
):
- splat_node = nodes.Starred(lineno=inferred.lineno)
+ splat_node = nodes.Starred(
+ ctx=Context.Load,
+ lineno=inferred.lineno,
+ col_offset=0,
+ parent=nodes.NodeNG(
+ lineno=None,
+ col_offset=None,
+ end_lineno=None,
+ end_col_offset=None,
+ parent=None,
+ ),
+ end_lineno=0,
+ end_col_offset=0,
+ )
splat_node.value = arg
fixed_node.args = (
fixed_node.args[:idx]
diff --git a/pylint/checkers/newstyle.py b/pylint/checkers/newstyle.py
index 0aed97183..392faeaea 100644
--- a/pylint/checkers/newstyle.py
+++ b/pylint/checkers/newstyle.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for new / old style related problems."""
diff --git a/pylint/checkers/non_ascii_names.py b/pylint/checkers/non_ascii_names.py
index 455e575ad..825db1b11 100644
--- a/pylint/checkers/non_ascii_names.py
+++ b/pylint/checkers/non_ascii_names.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""All alphanumeric unicode character are allowed in Python but due
to similarities in how they look they can be confused.
diff --git a/pylint/checkers/raw_metrics.py b/pylint/checkers/raw_metrics.py
index 3469dc15e..ef4535345 100644
--- a/pylint/checkers/raw_metrics.py
+++ b/pylint/checkers/raw_metrics.py
@@ -1,22 +1,16 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-import sys
import tokenize
-from typing import TYPE_CHECKING, Any, cast
+from typing import TYPE_CHECKING, Any, Literal, cast
from pylint.checkers import BaseTokenChecker
from pylint.reporters.ureports.nodes import Paragraph, Section, Table, Text
from pylint.utils import LinterStats, diff_string
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
diff --git a/pylint/checkers/refactoring/__init__.py b/pylint/checkers/refactoring/__init__.py
index d215dcee2..785ce3f96 100644
--- a/pylint/checkers/refactoring/__init__.py
+++ b/pylint/checkers/refactoring/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Looks for code which can be refactored."""
diff --git a/pylint/checkers/refactoring/implicit_booleaness_checker.py b/pylint/checkers/refactoring/implicit_booleaness_checker.py
index 73503f8e6..5b91990b0 100644
--- a/pylint/checkers/refactoring/implicit_booleaness_checker.py
+++ b/pylint/checkers/refactoring/implicit_booleaness_checker.py
@@ -1,9 +1,11 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
+import itertools
+
import astroid
from astroid import bases, nodes, util
@@ -12,6 +14,14 @@ from pylint.checkers import utils
from pylint.interfaces import HIGH, INFERENCE
+def _is_constant_zero(node: str | nodes.NodeNG) -> bool:
+ # We have to check that node.value is not False because node.value == 0 is True
+ # when node.value is False
+ return (
+ isinstance(node, astroid.Const) and node.value == 0 and node.value is not False
+ )
+
+
class ImplicitBooleanessChecker(checkers.BaseChecker):
"""Checks for incorrect usage of comparisons or len() inside conditions.
@@ -56,23 +66,47 @@ class ImplicitBooleanessChecker(checkers.BaseChecker):
"C1802": (
"Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty",
"use-implicit-booleaness-not-len",
- "Used when Pylint detects that len(sequence) is being used "
- "without explicit comparison inside a condition to determine if a sequence is empty. "
- "Instead of coercing the length to a boolean, either "
- "rely on the fact that empty sequences are false or "
- "compare the length against a scalar.",
+ "Empty sequences are considered false in a boolean context. You can either"
+ " remove the call to 'len' (``if not x``) or compare the length against a"
+ "scalar (``if len(x) > 1``).",
{"old_names": [("C1801", "len-as-condition")]},
),
"C1803": (
- "'%s' can be simplified to '%s' as an empty %s is falsey",
+ '"%s" can be simplified to "%s", if it is strictly a sequence, as an empty %s is falsey',
"use-implicit-booleaness-not-comparison",
- "Used when Pylint detects that collection literal comparison is being "
- "used to check for emptiness; Use implicit booleaness instead "
- "of a collection classes; empty collections are considered as false",
+ "Empty sequences are considered false in a boolean context. Following this"
+ " check blindly in weakly typed code base can create hard to debug issues."
+ " If the value can be something else that is falsey but not a sequence (for"
+ " example ``None``, an empty string, or ``0``) the code will not be "
+ "equivalent.",
+ ),
+ "C1804": (
+ '"%s" can be simplified to "%s", if it is striclty a string, as an empty string is falsey',
+ "use-implicit-booleaness-not-comparison-to-string",
+ "Empty string are considered false in a boolean context. Following this"
+ " check blindly in weakly typed code base can create hard to debug issues."
+ " If the value can be something else that is falsey but not a string (for"
+ " example ``None``, an empty sequence, or ``0``) the code will not be "
+ "equivalent.",
+ {
+ "default_enabled": False,
+ "old_names": [("C1901", "compare-to-empty-string")],
+ },
+ ),
+ "C1805": (
+ '"%s" can be simplified to "%s", if it is strictly an int, as 0 is falsey',
+ "use-implicit-booleaness-not-comparison-to-zero",
+ "0 is considered false in a boolean context. Following this"
+ " check blindly in weakly typed code base can create hard to debug issues."
+ " If the value can be something else that is falsey but not an int (for"
+ " example ``None``, an empty string, or an empty sequence) the code will not be "
+ "equivalent.",
+ {"default_enabled": False, "old_names": [("C2001", "compare-to-zero")]},
),
}
options = ()
+ _operators = {"!=", "==", "is not", "is"}
@utils.only_required_for_messages("use-implicit-booleaness-not-len")
def visit_call(self, node: nodes.Call) -> None:
@@ -146,9 +180,83 @@ class ImplicitBooleanessChecker(checkers.BaseChecker):
"use-implicit-booleaness-not-len", node=node, confidence=HIGH
)
- @utils.only_required_for_messages("use-implicit-booleaness-not-comparison")
+ @utils.only_required_for_messages(
+ "use-implicit-booleaness-not-comparison",
+ "use-implicit-booleaness-not-comparison-to-string",
+ "use-implicit-booleaness-not-comparison-to-zero",
+ )
def visit_compare(self, node: nodes.Compare) -> None:
- self._check_use_implicit_booleaness_not_comparison(node)
+ if self.linter.is_message_enabled("use-implicit-booleaness-not-comparison"):
+ self._check_use_implicit_booleaness_not_comparison(node)
+ if self.linter.is_message_enabled(
+ "use-implicit-booleaness-not-comparison-to-zero"
+ ) or self.linter.is_message_enabled(
+ "use-implicit-booleaness-not-comparison-to-str"
+ ):
+ self._check_compare_to_str_or_zero(node)
+
+ def _check_compare_to_str_or_zero(self, node: nodes.Compare) -> None:
+ # note: astroid.Compare has the left most operand in node.left
+ # while the rest are a list of tuples in node.ops
+ # the format of the tuple is ('compare operator sign', node)
+ # here we squash everything into `ops` to make it easier for processing later
+ ops: list[tuple[str, nodes.NodeNG]] = [("", node.left), *node.ops]
+ iter_ops = iter(ops)
+ all_ops = list(itertools.chain(*iter_ops))
+ for ops_idx in range(len(all_ops) - 2):
+ op_2 = all_ops[ops_idx + 1]
+ if op_2 not in self._operators:
+ continue
+ op_1 = all_ops[ops_idx]
+ op_3 = all_ops[ops_idx + 2]
+ error_detected = False
+ if self.linter.is_message_enabled(
+ "use-implicit-booleaness-not-comparison-to-zero"
+ ):
+ # 0 ?? X
+ if _is_constant_zero(op_1):
+ error_detected = True
+ op = op_3
+ # X ?? 0
+ elif _is_constant_zero(op_3):
+ error_detected = True
+ op = op_1
+ if error_detected:
+ original = f"{op_1.as_string()} {op_2} {op_3.as_string()}"
+ suggestion = (
+ op.as_string()
+ if op_2 in {"!=", "is not"}
+ else f"not {op.as_string()}"
+ )
+ self.add_message(
+ "use-implicit-booleaness-not-comparison-to-zero",
+ args=(original, suggestion),
+ node=node,
+ confidence=HIGH,
+ )
+ error_detected = False
+ if self.linter.is_message_enabled(
+ "use-implicit-booleaness-not-comparison-to-str"
+ ):
+ node_name = ""
+ # x ?? ""
+ if utils.is_empty_str_literal(op_1):
+ error_detected = True
+ node_name = op_3.as_string()
+ # '' ?? X
+ elif utils.is_empty_str_literal(op_3):
+ error_detected = True
+ node_name = op_1.as_string()
+ if error_detected:
+ suggestion = (
+ f"not {node_name}" if op_2 in {"==", "is"} else node_name
+ )
+ self.add_message(
+ "use-implicit-booleaness-not-comparison-to-string",
+ args=(node.as_string(), suggestion),
+ node=node,
+ confidence=HIGH,
+ )
def _check_use_implicit_booleaness_not_comparison(
self, node: nodes.Compare
diff --git a/pylint/checkers/refactoring/not_checker.py b/pylint/checkers/refactoring/not_checker.py
index e356a70e1..555e21099 100644
--- a/pylint/checkers/refactoring/not_checker.py
+++ b/pylint/checkers/refactoring/not_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
import astroid
from astroid import nodes
diff --git a/pylint/checkers/refactoring/recommendation_checker.py b/pylint/checkers/refactoring/recommendation_checker.py
index 3a6d1033c..db2e2f604 100644
--- a/pylint/checkers/refactoring/recommendation_checker.py
+++ b/pylint/checkers/refactoring/recommendation_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -54,10 +54,10 @@ class RecommendationChecker(checkers.BaseChecker):
"are more efficient than ``sets``.",
),
"C0209": (
- "Formatting a regular string which could be a f-string",
+ "Formatting a regular string which could be an f-string",
"consider-using-f-string",
"Used when we detect a string that is being formatted with format() or % "
- "which could potentially be a f-string. The use of f-strings is preferred. "
+ "which could potentially be an f-string. The use of f-strings is preferred. "
"Requires Python 3.6 and ``py-version >= 3.6``.",
),
}
diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py
index ec4d3d71e..5d0a2dd7a 100644
--- a/pylint/checkers/refactoring/refactoring_checker.py
+++ b/pylint/checkers/refactoring/refactoring_checker.py
@@ -1,16 +1,15 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import collections
import copy
import itertools
-import sys
import tokenize
from collections.abc import Iterator
-from functools import reduce
+from functools import cached_property, reduce
from re import Pattern
from typing import TYPE_CHECKING, Any, NamedTuple, Union, cast
@@ -27,10 +26,6 @@ from pylint.interfaces import HIGH, INFERENCE, Confidence
if TYPE_CHECKING:
from pylint.lint import PyLinter
-if sys.version_info >= (3, 8):
- from functools import cached_property
-else:
- from astroid.decorators import cachedproperty as cached_property
NodesWithNestedBlocks = Union[
nodes.TryExcept, nodes.TryFinally, nodes.While, nodes.For, nodes.If
@@ -98,7 +93,7 @@ def _is_trailing_comma(tokens: list[tokenize.TokenInfo], index: int) -> bool:
if token.exact_type != tokenize.COMMA:
return False
# Must have remaining tokens on the same line such as NEWLINE
- left_tokens = list(itertools.islice(tokens, index + 1, None))
+ left_tokens = itertools.islice(tokens, index + 1, None)
more_tokens_on_line = False
for remaining_token in left_tokens:
@@ -644,9 +639,10 @@ class RefactoringChecker(checkers.BaseTokenChecker):
# token[2] is the actual position and also is
# reported by IronPython.
self._elifs.extend([token[2], tokens[index + 1][2]])
- elif _is_trailing_comma(tokens, index):
- if self.linter.is_message_enabled("trailing-comma-tuple"):
- self.add_message("trailing-comma-tuple", line=token.start[0])
+ elif self.linter.is_message_enabled(
+ "trailing-comma-tuple"
+ ) and _is_trailing_comma(tokens, index):
+ self.add_message("trailing-comma-tuple", line=token.start[0])
@utils.only_required_for_messages("consider-using-with")
def leave_module(self, _: nodes.Module) -> None:
@@ -1060,8 +1056,8 @@ class RefactoringChecker(checkers.BaseTokenChecker):
def _check_consider_using_generator(self, node: nodes.Call) -> None:
# 'any', 'all', definitely should use generator, while 'list', 'tuple',
# 'sum', 'max', and 'min' need to be considered first
- # See https://github.com/PyCQA/pylint/pull/3309#discussion_r576683109
- # https://github.com/PyCQA/pylint/pull/6595#issuecomment-1125704244
+ # See https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109
+ # https://github.com/pylint-dev/pylint/pull/6595#issuecomment-1125704244
# and https://peps.python.org/pep-0289/
checked_call = ["any", "all", "sum", "max", "min", "list", "tuple"]
if (
@@ -1175,7 +1171,7 @@ class RefactoringChecker(checkers.BaseTokenChecker):
if len(node.args) == 0:
# handle case when builtin.next is called without args.
- # see https://github.com/PyCQA/pylint/issues/7828
+ # see https://github.com/pylint-dev/pylint/issues/7828
return
inferred = utils.safe_infer(node.func)
diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py
index 2cfba16bf..2b08204f2 100644
--- a/pylint/checkers/similar.py
+++ b/pylint/checkers/similar.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""A similarities / code duplication command line tool and pylint checker.
@@ -848,15 +848,17 @@ class SimilarChecker(BaseRawFileChecker, Similar):
stream must implement the readlines method
"""
if self.linter.current_name is None:
+ # TODO: 3.0 Fix current_name
warnings.warn(
(
"In pylint 3.0 the current_name attribute of the linter object should be a string. "
"If unknown it should be initialized as an empty string."
),
DeprecationWarning,
+ stacklevel=2,
)
with node.stream() as stream:
- self.append_stream(self.linter.current_name, stream, node.file_encoding) # type: ignore[arg-type]
+ self.append_stream(self.linter.current_name, stream, node.file_encoding)
def close(self) -> None:
"""Compute and display similarities on closing (i.e. end of parsing)."""
diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py
index 3fbd017f8..91161c60d 100644
--- a/pylint/checkers/spelling.py
+++ b/pylint/checkers/spelling.py
@@ -1,27 +1,21 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for spelling errors in comments and docstrings."""
from __future__ import annotations
import re
-import sys
import tokenize
from re import Pattern
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, Literal
from astroid import nodes
from pylint.checkers import BaseTokenChecker
from pylint.checkers.utils import only_required_for_messages
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py
index 96b273c74..edc79867f 100644
--- a/pylint/checkers/stdlib.py
+++ b/pylint/checkers/stdlib.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checkers for various standard library functions."""
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
DeprecationDict = Dict[Tuple[int, int, int], Set[str]]
OPEN_FILES_MODE = ("open", "file")
-OPEN_FILES_FUNCS = OPEN_FILES_MODE + ("read_text", "write_text")
+OPEN_FILES_FUNCS = (*OPEN_FILES_MODE, "read_text", "write_text")
UNITTEST_CASE = "unittest.case"
THREADING_THREAD = "threading.Thread"
COPY_COPY = "copy.copy"
diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py
index 380d87fb0..2cc780da5 100644
--- a/pylint/checkers/strings.py
+++ b/pylint/checkers/strings.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for string formatting operations."""
@@ -8,11 +8,10 @@ from __future__ import annotations
import collections
import re
-import sys
import tokenize
from collections import Counter
from collections.abc import Iterable, Sequence
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
import astroid
from astroid import bases, nodes, util
@@ -26,11 +25,6 @@ from pylint.typing import MessageDefinitionTuple
if TYPE_CHECKING:
from pylint.lint import PyLinter
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
_AST_NODE_STR_TYPES = ("__builtin__.unicode", "__builtin__.str", "builtins.str")
# Prefixes for both strings and bytes literals per
@@ -816,7 +810,7 @@ class StringConstantChecker(BaseTokenChecker, BaseRawFileChecker):
token_index = (elt.lineno, elt.col_offset)
if token_index not in self.string_tokens:
# This may happen with Latin1 encoding
- # cf. https://github.com/PyCQA/pylint/issues/2610
+ # cf. https://github.com/pylint-dev/pylint/issues/2610
continue
matching_token, next_token = self.string_tokens[token_index]
# We detect string concatenation: the AST Const is the
diff --git a/pylint/checkers/threading_checker.py b/pylint/checkers/threading_checker.py
index df0dfe7cf..b289d6707 100644
--- a/pylint/checkers/threading_checker.py
+++ b/pylint/checkers/threading_checker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index 1a628f231..97a7460a8 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Try to find more bugs in the code using astroid inference capabilities."""
@@ -14,9 +14,9 @@ import shlex
import sys
import types
from collections.abc import Callable, Iterable, Iterator, Sequence
-from functools import singledispatch
+from functools import cached_property, singledispatch
from re import Pattern
-from typing import TYPE_CHECKING, Any, TypeVar, Union
+from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union
import astroid
import astroid.exceptions
@@ -53,13 +53,6 @@ from pylint.constants import PY310_PLUS
from pylint.interfaces import HIGH, INFERENCE
from pylint.typing import MessageDefinitionTuple
-if sys.version_info >= (3, 8):
- from functools import cached_property
- from typing import Literal
-else:
- from astroid.decorators import cachedproperty as cached_property
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
@@ -125,7 +118,7 @@ def _is_owner_ignored(
matches any name from the *ignored_classes* or if its qualified
name can be found in *ignored_classes*.
"""
- if is_module_ignored(owner.root(), ignored_modules):
+ if is_module_ignored(owner.root().qname(), ignored_modules):
return True
# Match against ignored classes.
@@ -158,7 +151,7 @@ def _(node: nodes.ClassDef | bases.Instance) -> Iterable[str]:
def _string_distance(seq1: str, seq2: str) -> int:
seq2_length = len(seq2)
- row = list(range(1, seq2_length + 1)) + [0]
+ row = [*list(range(1, seq2_length + 1)), 0]
for seq1_index, seq1_char in enumerate(seq1):
last_row = row
row = [0] * seq2_length + [seq1_index + 1]
@@ -733,7 +726,9 @@ def _no_context_variadic(
else:
inferred_statement = inferred.statement(future=True)
- if not length and isinstance(inferred_statement, nodes.Lambda):
+ if not length and isinstance(
+ inferred_statement, (nodes.Lambda, nodes.FunctionDef)
+ ):
is_in_starred_context = _has_parent_of_type(node, variadic_type, statement)
used_as_starred_argument = any(
variadic.value == name or variadic.value.parent_of(name)
@@ -1569,6 +1564,11 @@ accessed. Python regular expressions are accepted.",
node=node,
args=(keyword, callable_name),
)
+ elif (
+ keyword in [arg.name for arg in called.args.posonlyargs]
+ and called.args.kwarg
+ ):
+ pass
else:
parameters[i] = (parameters[i][0], True)
elif keyword in kwparams:
@@ -1654,7 +1654,7 @@ accessed. Python regular expressions are accepted.",
if not isinstance(inferred, nodes.FunctionDef):
return False
- for return_value in inferred.infer_call_result():
+ for return_value in inferred.infer_call_result(caller=None):
# infer_call_result() returns nodes.Const.None for None return values
# so this also catches non-returning decorators
if not isinstance(return_value, nodes.FunctionDef):
@@ -1685,9 +1685,9 @@ accessed. Python regular expressions are accepted.",
# Determine what method on the parent this index will use
# The parent of this node will be a Subscript, and the parent of that
# node determines if the Subscript is a get, set, or delete operation.
- if subscript.ctx is astroid.Store:
+ if subscript.ctx is astroid.Context.Store:
methodname = "__setitem__"
- elif subscript.ctx is astroid.Del:
+ elif subscript.ctx is astroid.Context.Del:
methodname = "__delitem__"
else:
methodname = "__getitem__"
@@ -2015,7 +2015,7 @@ accessed. Python regular expressions are accepted.",
# TODO: This check was disabled (by adding the leading underscore)
# due to false positives several years ago - can we re-enable it?
- # https://github.com/PyCQA/pylint/issues/6359
+ # https://github.com/pylint-dev/pylint/issues/6359
@only_required_for_messages("unsupported-binary-operation")
def _visit_binop(self, node: nodes.BinOp) -> None:
"""Detect TypeErrors for binary arithmetic operands."""
@@ -2023,7 +2023,7 @@ accessed. Python regular expressions are accepted.",
# TODO: This check was disabled (by adding the leading underscore)
# due to false positives several years ago - can we re-enable it?
- # https://github.com/PyCQA/pylint/issues/6359
+ # https://github.com/pylint-dev/pylint/issues/6359
@only_required_for_messages("unsupported-binary-operation")
def _visit_augassign(self, node: nodes.AugAssign) -> None:
"""Detect TypeErrors for augmented binary arithmetic operands."""
@@ -2109,13 +2109,13 @@ accessed. Python regular expressions are accepted.",
confidence=INFERENCE,
)
- if node.ctx == astroid.Load:
+ if node.ctx == astroid.Context.Load:
supported_protocol = supports_getitem
msg = "unsubscriptable-object"
- elif node.ctx == astroid.Store:
+ elif node.ctx == astroid.Context.Store:
supported_protocol = supports_setitem
msg = "unsupported-assignment-operation"
- elif node.ctx == astroid.Del:
+ elif node.ctx == astroid.Context.Del:
supported_protocol = supports_delitem
msg = "unsupported-delete-operation"
diff --git a/pylint/checkers/unicode.py b/pylint/checkers/unicode.py
index c7e011b6f..ab8410c3e 100644
--- a/pylint/checkers/unicode.py
+++ b/pylint/checkers/unicode.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Unicode and some other ASCII characters can be used to create programs that run
much different compared to what a human reader would expect from them.
diff --git a/pylint/checkers/unsupported_version.py b/pylint/checkers/unsupported_version.py
index 0c4a7c5bb..64f2630d8 100644
--- a/pylint/checkers/unsupported_version.py
+++ b/pylint/checkers/unsupported_version.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for features used that are not supported by all python versions
indicated by the py-version setting.
@@ -34,7 +34,7 @@ class UnsupportedVersionChecker(BaseChecker):
"F-strings are not supported by all versions included in the py-version setting",
"using-f-string-in-unsupported-version",
"Used when the py-version set by the user is lower than 3.6 and pylint encounters "
- "a f-string.",
+ "an f-string.",
),
"W2602": (
"typing.final is not supported by all versions included in the py-version setting",
diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py
index 771b21fff..6b66ad620 100644
--- a/pylint/checkers/utils.py
+++ b/pylint/checkers/utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Some functions that may be useful for various checkers."""
@@ -12,8 +12,6 @@ import itertools
import numbers
import re
import string
-import warnings
-from collections import deque
from collections.abc import Iterable, Iterator
from functools import lru_cache, partial
from re import Match
@@ -249,17 +247,6 @@ class InferredTypeError(Exception):
pass
-def is_inside_lambda(node: nodes.NodeNG) -> bool:
- """Return whether the given node is inside a lambda."""
- warnings.warn(
- "utils.is_inside_lambda will be removed in favour of calling "
- "utils.get_node_first_ancestor_of_type(x, nodes.Lambda) in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- return any(isinstance(parent, nodes.Lambda) for parent in node.node_ancestors())
-
-
def get_all_elements(
node: nodes.NodeNG,
) -> Iterable[nodes.NodeNG]:
@@ -369,9 +356,10 @@ def is_defined_before(var_node: nodes.Name) -> bool:
if defnode is None:
continue
defnode_scope = defnode.scope()
- if isinstance(defnode_scope, COMP_NODE_TYPES + (nodes.Lambda,)):
+ if isinstance(
+ defnode_scope, (*COMP_NODE_TYPES, nodes.Lambda, nodes.FunctionDef)
+ ):
# Avoid the case where var_node_scope is a nested function
- # FunctionDef is a Lambda until https://github.com/PyCQA/astroid/issues/291
if isinstance(defnode_scope, nodes.FunctionDef):
var_node_scope = var_node.scope()
if var_node_scope is not defnode_scope and isinstance(
@@ -505,25 +493,6 @@ def only_required_for_messages(
return store_messages
-def check_messages(
- *messages: str,
-) -> Callable[
- [AstCallbackMethod[_CheckerT, _NodeT]], AstCallbackMethod[_CheckerT, _NodeT]
-]:
- """Kept for backwards compatibility, deprecated.
-
- Use only_required_for_messages instead, which conveys the intent of the decorator much clearer.
- """
- warnings.warn(
- "utils.check_messages will be removed in favour of calling "
- "utils.only_required_for_messages in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
-
- return only_required_for_messages(*messages)
-
-
class IncompleteFormatString(Exception):
"""A format string ended in the middle of a format specifier."""
@@ -1162,6 +1131,7 @@ def node_ignores_exception(
return any(get_contextlib_suppressors(node, exception))
+@lru_cache(maxsize=1024)
def class_is_abstract(node: nodes.ClassDef) -> bool:
"""Return true if the given class node should be considered as an abstract
class.
@@ -1572,27 +1542,6 @@ def is_postponed_evaluation_enabled(node: nodes.NodeNG) -> bool:
return "annotations" in module.future_imports
-def is_class_subscriptable_pep585_with_postponed_evaluation_enabled(
- value: nodes.ClassDef, node: nodes.NodeNG
-) -> bool:
- """Check if class is subscriptable with PEP 585 and
- postponed evaluation enabled.
- """
- warnings.warn(
- "'is_class_subscriptable_pep585_with_postponed_evaluation_enabled' has been "
- "deprecated and will be removed in pylint 3.0. "
- "Use 'is_postponed_evaluation_enabled(node) and "
- "is_node_in_type_annotation_context(node)' instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return (
- is_postponed_evaluation_enabled(node)
- and value.qname() in SUBSCRIPTABLE_CLASSES_PEP585
- and is_node_in_type_annotation_context(node)
- )
-
-
def is_node_in_type_annotation_context(node: nodes.NodeNG) -> bool:
"""Check if node is in type annotation context.
@@ -1843,48 +1792,6 @@ def is_sys_guard(node: nodes.If) -> bool:
return False
-def is_typing_guard(node: nodes.If) -> bool:
- """Return True if IF stmt is a typing guard.
-
- >>> from typing import TYPE_CHECKING
- >>> if TYPE_CHECKING:
- >>> from xyz import a
- """
- warnings.warn(
- "This method will be removed in pylint 3.0; use in_type_checking_block() instead.",
- DeprecationWarning,
- stacklevel=2,
- ) # pragma: no cover
- return isinstance(
- node.test, (nodes.Name, nodes.Attribute)
- ) and node.test.as_string().endswith("TYPE_CHECKING")
-
-
-def is_node_in_typing_guarded_import_block(node: nodes.NodeNG) -> bool:
- """Return True if node is part for guarded `typing.TYPE_CHECKING` if block."""
- warnings.warn(
- "This method will be removed in pylint 3.0; use in_type_checking_block() instead.",
- DeprecationWarning,
- stacklevel=2,
- ) # pragma: no cover
- return isinstance(node.parent, nodes.If) and is_typing_guard(node.parent)
-
-
-def is_node_in_guarded_import_block(node: nodes.NodeNG) -> bool:
- """Return True if node is part for guarded if block.
-
- I.e. `sys.version_info` or `typing.TYPE_CHECKING`
- """
- warnings.warn(
- "This method will be removed in pylint 3.0; use in_type_checking_block() instead.",
- DeprecationWarning,
- stacklevel=2,
- ) # pragma: no cover
- return isinstance(node.parent, nodes.If) and (
- is_sys_guard(node.parent) or is_typing_guard(node.parent)
- )
-
-
def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool:
"""Check if the given variable name is reassigned in the same scope after the
current node.
@@ -2035,7 +1942,7 @@ def is_typing_member(node: nodes.NodeNG, names_to_check: tuple[str, ...]) -> boo
return False
-@lru_cache()
+@lru_cache
def in_for_else_branch(parent: nodes.NodeNG, stmt: nodes.Statement) -> bool:
"""Returns True if stmt is inside the else branch for a parent For stmt."""
return isinstance(parent, nodes.For) and any(
@@ -2144,37 +2051,30 @@ def is_augmented_assign(node: nodes.Assign) -> tuple[bool, str]:
return False, ""
+def _qualified_name_parts(qualified_module_name: str) -> list[str]:
+ """Split the names of the given module into subparts.
+
+ For example,
+ _qualified_name_parts('pylint.checkers.ImportsChecker')
+ returns
+ ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker']
+ """
+ names = qualified_module_name.split(".")
+ return [".".join(names[0 : i + 1]) for i in range(len(names))]
+
+
def is_module_ignored(
- module: nodes.Module,
- ignored_modules: Iterable[str],
+ qualified_module_name: str, ignored_modules: Iterable[str]
) -> bool:
ignored_modules = set(ignored_modules)
- module_name = module.name
- module_qname = module.qname()
-
- for ignore in ignored_modules:
- # Try to match the module name / fully qualified name directly
- if module_qname in ignored_modules or module_name in ignored_modules:
- return True
-
- # Try to see if the ignores pattern match against the module name.
- if fnmatch.fnmatch(module_qname, ignore):
+ for current_module in _qualified_name_parts(qualified_module_name):
+ # Try to match the module name directly
+ if current_module in ignored_modules:
return True
-
- # Otherwise, we might have a root module name being ignored,
- # and the qualified owner has more levels of depth.
- parts = deque(module_name.split("."))
- current_module = ""
-
- while parts:
- part = parts.popleft()
- if not current_module:
- current_module = part
- else:
- current_module += f".{part}"
- if current_module in ignored_modules:
+ for ignore in ignored_modules:
+ # Try to see if the ignores pattern match against the module name.
+ if fnmatch.fnmatch(current_module, ignore):
return True
-
return False
@@ -2214,53 +2114,6 @@ def is_class_attr(name: str, klass: nodes.ClassDef) -> bool:
return False
-def is_defined(name: str, node: nodes.NodeNG) -> bool:
- """Searches for a tree node that defines the given variable name."""
- is_defined_so_far = False
-
- if isinstance(node, nodes.NamedExpr):
- is_defined_so_far = node.target.name == name
-
- if isinstance(node, (nodes.Import, nodes.ImportFrom)):
- is_defined_so_far = any(node_name[0] == name for node_name in node.names)
-
- if isinstance(node, nodes.With):
- is_defined_so_far = any(
- isinstance(item[1], nodes.AssignName) and item[1].name == name
- for item in node.items
- )
-
- if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)):
- is_defined_so_far = node.name == name
-
- if isinstance(node, nodes.AnnAssign):
- is_defined_so_far = (
- node.value
- and isinstance(node.target, nodes.AssignName)
- and node.target.name == name
- )
-
- if isinstance(node, nodes.Assign):
- is_defined_so_far = any(
- any(
- (
- (
- isinstance(elt, nodes.Starred)
- and isinstance(elt.value, nodes.AssignName)
- and elt.value.name == name
- )
- or (isinstance(elt, nodes.AssignName) and elt.name == name)
- )
- for elt in get_all_elements(target)
- )
- for target in node.targets
- )
-
- return is_defined_so_far or any(
- is_defined(name, child) for child in node.get_children()
- )
-
-
def get_inverse_comparator(op: str) -> str:
"""Returns the inverse comparator given a comparator.
@@ -2317,12 +2170,30 @@ def not_condition_as_string(
return msg
+@lru_cache(maxsize=1000)
+def overridden_method(
+ klass: nodes.LocalsDictNodeNG, name: str | None
+) -> nodes.FunctionDef | None:
+ """Get overridden method if any."""
+ try:
+ parent = next(klass.local_attr_ancestors(name))
+ except (StopIteration, KeyError):
+ return None
+ try:
+ meth_node = parent[name]
+ except KeyError: # pragma: no cover
+ # We have found an ancestor defining <name> but it's not in the local
+ # dictionary. This may happen with astroid built from living objects.
+ return None
+ if isinstance(meth_node, nodes.FunctionDef):
+ return meth_node
+ return None # pragma: no cover
+
+
def clear_lru_caches() -> None:
"""Clear caches holding references to AST nodes."""
- # pylint: disable-next=import-outside-toplevel
- from pylint.checkers.variables import overridden_method
-
caches_holding_node_references: list[_lru_cache_wrapper[Any]] = [
+ class_is_abstract,
in_for_else_branch,
infer_all,
is_overload_stub,
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 8cd1ed83c..2504c0c84 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Variables checkers for Python code."""
@@ -11,11 +11,10 @@ import copy
import itertools
import os
import re
-import sys
from collections import defaultdict
from collections.abc import Generator, Iterable, Iterator
from enum import Enum
-from functools import lru_cache
+from functools import cached_property
from typing import TYPE_CHECKING, Any, NamedTuple
import astroid
@@ -26,18 +25,15 @@ from astroid.typing import InferenceResult
from pylint.checkers import BaseChecker, utils
from pylint.checkers.utils import (
in_type_checking_block,
+ is_module_ignored,
is_postponed_evaluation_enabled,
is_sys_guard,
+ overridden_method,
)
from pylint.constants import PY39_PLUS, TYPING_NEVER, TYPING_NORETURN
from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE
from pylint.typing import MessageDefinitionTuple
-if sys.version_info >= (3, 8):
- from functools import cached_property
-else:
- from astroid.decorators import cachedproperty as cached_property
-
if TYPE_CHECKING:
from pylint.lint import PyLinter
@@ -151,26 +147,6 @@ def _is_from_future_import(stmt: nodes.ImportFrom, name: str) -> bool | None:
return None
-@lru_cache(maxsize=1000)
-def overridden_method(
- klass: nodes.LocalsDictNodeNG, name: str | None
-) -> nodes.FunctionDef | None:
- """Get overridden method if any."""
- try:
- parent = next(klass.local_attr_ancestors(name))
- except (StopIteration, KeyError):
- return None
- try:
- meth_node = parent[name]
- except KeyError:
- # We have found an ancestor defining <name> but it's not in the local
- # dictionary. This may happen with astroid built from living objects.
- return None
- if isinstance(meth_node, nodes.FunctionDef):
- return meth_node
- return None
-
-
def _get_unpacking_extra_info(node: nodes.Assign, inferred: InferenceResult) -> str:
"""Return extra information to add to the message for unpacking-non-sequence
and unbalanced-tuple/dict-unpacking errors.
@@ -242,17 +218,11 @@ def _detect_global_scope(
return node.lineno < defframe.lineno # type: ignore[no-any-return]
if not isinstance(node.parent, (nodes.FunctionDef, nodes.Arguments)):
return False
- elif any(
- not isinstance(f, (nodes.ClassDef, nodes.Module)) for f in (frame, defframe)
- ):
- # Not interested in other frames, since they are already
- # not in a global scope.
- return False
break_scopes = []
- for current_scope in (scope, def_scope):
+ for current_scope in (scope or frame, def_scope):
# Look for parent scopes. If there is anything different
- # than a module or a class scope, then they frames don't
+ # than a module or a class scope, then the frames don't
# share a global scope.
parent_scope = current_scope
while parent_scope:
@@ -263,7 +233,7 @@ def _detect_global_scope(
parent_scope = parent_scope.parent.scope()
else:
break
- if break_scopes and len(set(break_scopes)) != 1:
+ if len(set(break_scopes)) > 1:
# Store different scopes than expected.
# If the stored scopes are, in fact, the very same, then it means
# that the two frames (frame and defframe) share the same scope,
@@ -564,7 +534,6 @@ class NamesConsumer:
copy.copy(node.locals), {}, collections.defaultdict(list), scope_type
)
self.node = node
- self._if_nodes_deemed_uncertain: set[nodes.If] = set()
def __repr__(self) -> str:
_to_consumes = [f"{k}->{v}" for k, v in self._atomic.to_consume.items()]
@@ -715,26 +684,28 @@ scope_type : {self._atomic.scope_type}
return found_nodes
@staticmethod
- def _exhaustively_define_name_raise_or_return(
- name: str, node: nodes.NodeNG
- ) -> bool:
- """Return True if there is a collectively exhaustive set of paths under
- this `if_node` that define `name`, raise, or return.
+ def _inferred_to_define_name_raise_or_return(name: str, node: nodes.NodeNG) -> bool:
+ """Return True if there is a path under this `if_node`
+ that is inferred to define `name`, raise, or return.
"""
# Handle try and with
if isinstance(node, (nodes.TryExcept, nodes.TryFinally)):
# Allow either a path through try/else/finally OR a path through ALL except handlers
- return (
- NamesConsumer._defines_name_raises_or_returns_recursive(name, node)
- or isinstance(node, nodes.TryExcept)
- and all(
- NamesConsumer._defines_name_raises_or_returns_recursive(
- name, handler
- )
- for handler in node.handlers
+ try_except_node = node
+ if isinstance(node, nodes.TryFinally):
+ try_except_node = next(
+ (child for child in node.nodes_of_class(nodes.TryExcept)),
+ None,
)
+ handlers = try_except_node.handlers if try_except_node else []
+ return NamesConsumer._defines_name_raises_or_returns_recursive(
+ name, node
+ ) or all(
+ NamesConsumer._defines_name_raises_or_returns_recursive(name, handler)
+ for handler in handlers
)
- if isinstance(node, nodes.With):
+
+ if isinstance(node, (nodes.With, nodes.For, nodes.While)):
return NamesConsumer._defines_name_raises_or_returns_recursive(name, node)
if not isinstance(node, nodes.If):
@@ -748,13 +719,29 @@ scope_type : {self._atomic.scope_type}
if NamesConsumer._defines_name_raises_or_returns(name, node):
return True
- # If there is no else, then there is no collectively exhaustive set of paths
- if not node.orelse:
- return False
+ test = node.test.value if isinstance(node.test, nodes.NamedExpr) else node.test
+ all_inferred = utils.infer_all(test)
+ only_search_if = False
+ only_search_else = True
+ for inferred in all_inferred:
+ if not isinstance(inferred, nodes.Const):
+ only_search_else = False
+ continue
+ val = inferred.value
+ only_search_if = only_search_if or (val != NotImplemented and val)
+ only_search_else = only_search_else and not val
+
+ # Only search else branch when test condition is inferred to be false
+ if all_inferred and only_search_else:
+ return NamesConsumer._branch_handles_name(name, node.orelse)
+ # Only search if branch when test condition is inferred to be true
+ if all_inferred and only_search_if:
+ return NamesConsumer._branch_handles_name(name, node.body)
+ # Search both if and else branches
return NamesConsumer._branch_handles_name(
name, node.body
- ) and NamesConsumer._branch_handles_name(name, node.orelse)
+ ) or NamesConsumer._branch_handles_name(name, node.orelse)
@staticmethod
def _branch_handles_name(name: str, body: Iterable[nodes.NodeNG]) -> bool:
@@ -762,9 +749,16 @@ scope_type : {self._atomic.scope_type}
NamesConsumer._defines_name_raises_or_returns(name, if_body_stmt)
or isinstance(
if_body_stmt,
- (nodes.If, nodes.TryExcept, nodes.TryFinally, nodes.With),
+ (
+ nodes.If,
+ nodes.TryExcept,
+ nodes.TryFinally,
+ nodes.With,
+ nodes.For,
+ nodes.While,
+ ),
)
- and NamesConsumer._exhaustively_define_name_raise_or_return(
+ and NamesConsumer._inferred_to_define_name_raise_or_return(
name, if_body_stmt
)
for if_body_stmt in body
@@ -776,57 +770,80 @@ scope_type : {self._atomic.scope_type}
"""Identify nodes of uncertain execution because they are defined under
tests that evaluate false.
- Don't identify a node if there is a collectively exhaustive set of paths
- that define the name, raise, or return (e.g. every if/else branch).
+ Don't identify a node if there is a path that is inferred to
+ define the name, raise, or return (e.g. any executed if/elif/else branch).
"""
uncertain_nodes = []
for other_node in found_nodes:
- if in_type_checking_block(other_node):
- continue
-
- if not isinstance(other_node, nodes.AssignName):
+ if isinstance(other_node, nodes.AssignName):
+ name = other_node.name
+ elif isinstance(other_node, (nodes.Import, nodes.ImportFrom)):
+ name = node.name
+ else:
continue
- closest_if = utils.get_node_first_ancestor_of_type(other_node, nodes.If)
- if closest_if is None:
- continue
- if node.frame() is not closest_if.frame():
- continue
- if closest_if is not None and closest_if.parent_of(node):
+ all_if = [
+ n
+ for n in other_node.node_ancestors()
+ if isinstance(n, nodes.If) and not n.parent_of(node)
+ ]
+ if not all_if:
continue
- # Name defined in every if/else branch
- if NamesConsumer._exhaustively_define_name_raise_or_return(
- other_node.name, closest_if
+ closest_if = all_if[0]
+ if (
+ isinstance(node, nodes.AssignName)
+ and node.frame() is not closest_if.frame()
):
continue
+ if closest_if.parent_of(node):
+ continue
- # Higher-level if already determined to be always false
- if any(
- if_node.parent_of(closest_if)
- for if_node in self._if_nodes_deemed_uncertain
- ):
- uncertain_nodes.append(other_node)
+ outer_if = all_if[-1]
+ if NamesConsumer._node_guarded_by_same_test(node, outer_if):
continue
- # All inferred values must test false
- if isinstance(closest_if.test, nodes.NamedExpr):
- test = closest_if.test.value
- else:
- test = closest_if.test
- all_inferred = utils.infer_all(test)
- if not all_inferred or not all(
- isinstance(inferred, nodes.Const) and not inferred.value
- for inferred in all_inferred
- ):
+ # Name defined in the if/else control flow
+ if NamesConsumer._inferred_to_define_name_raise_or_return(name, outer_if):
continue
uncertain_nodes.append(other_node)
- self._if_nodes_deemed_uncertain.add(closest_if)
return uncertain_nodes
@staticmethod
+ def _node_guarded_by_same_test(node: nodes.NodeNG, other_if: nodes.If) -> bool:
+ """Identify if `node` is guarded by an equivalent test as `other_if`.
+
+ Two tests are equivalent if their string representations are identical
+ or if their inferred values consist only of constants and those constants
+ are identical, and the if test guarding `node` is not a Name.
+ """
+ other_if_test_as_string = other_if.test.as_string()
+ other_if_test_all_inferred = utils.infer_all(other_if.test)
+ for ancestor in node.node_ancestors():
+ if not isinstance(ancestor, nodes.If):
+ continue
+ if ancestor.test.as_string() == other_if_test_as_string:
+ return True
+ if isinstance(ancestor.test, nodes.Name):
+ continue
+ all_inferred = utils.infer_all(ancestor.test)
+ if len(all_inferred) == len(other_if_test_all_inferred):
+ if any(
+ not isinstance(test, nodes.Const)
+ for test in (*all_inferred, *other_if_test_all_inferred)
+ ):
+ continue
+ if {test.value for test in all_inferred} != {
+ test.value for test in other_if_test_all_inferred
+ }:
+ continue
+ return True
+
+ return False
+
+ @staticmethod
def _uncertain_nodes_in_except_blocks(
found_nodes: list[nodes.NodeNG],
node: nodes.NodeNG,
@@ -921,6 +938,18 @@ scope_type : {self._atomic.scope_type}
for child_named_expr in node.nodes_of_class(nodes.NamedExpr)
):
return True
+ if isinstance(node, (nodes.Import, nodes.ImportFrom)) and any(
+ (node_name[1] and node_name[1] == name) or (node_name[0] == name)
+ for node_name in node.names
+ ):
+ return True
+ if isinstance(node, nodes.With) and any(
+ isinstance(item[1], nodes.AssignName) and item[1].name == name
+ for item in node.items
+ ):
+ return True
+ if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)) and node.name == name:
+ return True
return False
@staticmethod
@@ -939,18 +968,23 @@ scope_type : {self._atomic.scope_type}
for nested_stmt in stmt.get_children()
):
return True
+ if isinstance(
+ stmt, nodes.TryExcept
+ ) and NamesConsumer._defines_name_raises_or_returns_recursive(name, stmt):
+ return True
return False
@staticmethod
def _check_loop_finishes_via_except(
node: nodes.NodeNG, other_node_try_except: nodes.TryExcept
) -> bool:
- """Check for a case described in https://github.com/PyCQA/pylint/issues/5683.
+ """Check for a specific control flow scenario.
- It consists of a specific control flow scenario where the only
- non-break exit from a loop consists of the very except handler we are
- examining, such that code in the `else` branch of the loop can depend on it
- being assigned.
+ Described in https://github.com/pylint-dev/pylint/issues/5683.
+
+ A scenario where the only non-break exit from a loop consists of the very
+ except handler we are examining, such that code in the `else` branch of
+ the loop can depend on it being assigned.
Example:
@@ -1247,6 +1281,9 @@ class VariablesChecker(BaseChecker):
tuple[nodes.ExceptHandler, nodes.AssignName]
] = []
"""This is a queue, last in first out."""
+ self._evaluated_type_checking_scopes: dict[
+ str, list[nodes.LocalsDictNodeNG]
+ ] = {}
self._postponed_evaluation_enabled = False
@utils.only_required_for_messages(
@@ -1707,21 +1744,16 @@ class VariablesChecker(BaseChecker):
if found_nodes is None:
return (VariableVisitConsumerAction.CONTINUE, None)
if not found_nodes:
- if node.name in current_consumer.consumed_uncertain:
- confidence = CONTROL_FLOW
- else:
- confidence = HIGH
- self.add_message(
- "used-before-assignment",
- args=node.name,
- node=node,
- confidence=confidence,
- )
+ self._report_unfound_name_definition(node, current_consumer)
# Mark for consumption any nodes added to consumed_uncertain by
# get_next_to_consume() because they might not have executed.
+ nodes_to_consume = current_consumer.consumed_uncertain[node.name]
+ nodes_to_consume = self._filter_type_checking_import_from_consumption(
+ node, nodes_to_consume
+ )
return (
VariableVisitConsumerAction.RETURN,
- current_consumer.consumed_uncertain[node.name],
+ nodes_to_consume,
)
self._check_late_binding_closure(node)
@@ -1858,7 +1890,9 @@ class VariablesChecker(BaseChecker):
confidence=HIGH,
)
- elif self._is_only_type_assignment(node, defstmt):
+ elif not self._is_builtin(node.name) and self._is_only_type_assignment(
+ node, defstmt
+ ):
if node.scope().locals.get(node.name):
self.add_message(
"used-before-assignment", args=node.name, node=node, confidence=HIGH
@@ -1885,6 +1919,61 @@ class VariablesChecker(BaseChecker):
return (VariableVisitConsumerAction.RETURN, found_nodes)
+ def _report_unfound_name_definition(
+ self, node: nodes.NodeNG, current_consumer: NamesConsumer
+ ) -> None:
+ """Reports used-before-assignment when all name definition nodes
+ get filtered out by NamesConsumer.
+ """
+ if (
+ self._postponed_evaluation_enabled
+ and utils.is_node_in_type_annotation_context(node)
+ ):
+ return
+ if self._is_builtin(node.name):
+ return
+ if self._is_variable_annotation_in_function(node):
+ return
+ if (
+ node.name in self._evaluated_type_checking_scopes
+ and node.scope() in self._evaluated_type_checking_scopes[node.name]
+ ):
+ return
+
+ confidence = (
+ CONTROL_FLOW if node.name in current_consumer.consumed_uncertain else HIGH
+ )
+ self.add_message(
+ "used-before-assignment",
+ args=node.name,
+ node=node,
+ confidence=confidence,
+ )
+
+ def _filter_type_checking_import_from_consumption(
+ self, node: nodes.NodeNG, nodes_to_consume: list[nodes.NodeNG]
+ ) -> list[nodes.NodeNG]:
+ """Do not consume type-checking import node as used-before-assignment
+ may invoke in different scopes.
+ """
+ type_checking_import = next(
+ (
+ n
+ for n in nodes_to_consume
+ if isinstance(n, (nodes.Import, nodes.ImportFrom))
+ and in_type_checking_block(n)
+ ),
+ None,
+ )
+ # If used-before-assignment reported for usage of type checking import
+ # keep track of its scope
+ if type_checking_import and not self._is_variable_annotation_in_function(node):
+ self._evaluated_type_checking_scopes.setdefault(node.name, []).append(
+ node.scope()
+ )
+ nodes_to_consume = [n for n in nodes_to_consume if n != type_checking_import]
+ return nodes_to_consume
+
@utils.only_required_for_messages("no-name-in-module")
def visit_import(self, node: nodes.Import) -> None:
"""Check modules attribute accesses."""
@@ -2049,7 +2138,6 @@ class VariablesChecker(BaseChecker):
parent = parent.parent
return False
- # pylint: disable = too-many-branches
@staticmethod
def _is_variable_violation(
node: nodes.Name,
@@ -2203,42 +2291,6 @@ class VariablesChecker(BaseChecker):
anc is defnode.value for anc in node.node_ancestors()
)
- # Look for type checking definitions inside a type checking guard.
- # Relevant for function annotations only, not variable annotations (AnnAssign)
- if (
- isinstance(defstmt, (nodes.Import, nodes.ImportFrom))
- and isinstance(defstmt.parent, nodes.If)
- and in_type_checking_block(defstmt)
- and not in_type_checking_block(node)
- ):
- defstmt_parent = defstmt.parent
-
- maybe_annotation = utils.get_node_first_ancestor_of_type(
- node, nodes.AnnAssign
- )
- if not (
- maybe_annotation
- and utils.get_node_first_ancestor_of_type(
- maybe_annotation, nodes.FunctionDef
- )
- ):
- # Exempt those definitions that are used inside the type checking
- # guard or that are defined in any elif/else type checking guard branches.
- used_in_branch = defstmt_parent.parent_of(node)
- if not used_in_branch:
- if defstmt_parent.has_elif_block():
- defined_in_or_else = utils.is_defined(
- node.name, defstmt_parent.orelse[0]
- )
- else:
- defined_in_or_else = any(
- utils.is_defined(node.name, content)
- for content in defstmt_parent.orelse
- )
-
- if not defined_in_or_else:
- maybe_before_assign = True
-
return maybe_before_assign, annotation_return, use_outer_definition
@staticmethod
@@ -2254,7 +2306,7 @@ class VariablesChecker(BaseChecker):
return any(
VariablesChecker._maybe_used_and_assigned_at_once(elt)
for elt in defstmt.value.elts
- if isinstance(elt, NODES_WITH_VALUE_ATTR + (nodes.IfExp, nodes.Match))
+ if isinstance(elt, (*NODES_WITH_VALUE_ATTR, nodes.IfExp, nodes.Match))
)
value = defstmt.value
if isinstance(value, nodes.IfExp):
@@ -2278,18 +2330,15 @@ class VariablesChecker(BaseChecker):
for call in value.nodes_of_class(klass=nodes.Call)
)
- def _is_only_type_assignment(
- self, node: nodes.Name, defstmt: nodes.Statement
- ) -> bool:
+ def _is_builtin(self, name: str) -> bool:
+ return name in self.linter.config.additional_builtins or utils.is_builtin(name)
+
+ @staticmethod
+ def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool:
"""Check if variable only gets assigned a type and never a value."""
if not isinstance(defstmt, nodes.AnnAssign) or defstmt.value:
return False
- if node.name in self.linter.config.additional_builtins or utils.is_builtin(
- node.name
- ):
- return False
-
defstmt_frame = defstmt.frame(future=True)
node_frame = node.frame(future=True)
@@ -2378,6 +2427,16 @@ class VariablesChecker(BaseChecker):
return True
return False
+ @staticmethod
+ def _is_variable_annotation_in_function(node: nodes.NodeNG) -> bool:
+ is_annotation = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
+ return (
+ is_annotation
+ and utils.get_node_first_ancestor_of_type( # type: ignore[return-value]
+ is_annotation, nodes.FunctionDef
+ )
+ )
+
def _ignore_class_scope(self, node: nodes.NodeNG) -> bool:
"""Return True if the node is in a local class scope, as an assignment.
@@ -2427,10 +2486,7 @@ class VariablesChecker(BaseChecker):
# the usage is safe because the function will not be defined either if
# the variable is not defined.
scope = node.scope()
- # FunctionDef subclasses Lambda due to a curious ontology. Check both.
- # See https://github.com/PyCQA/astroid/issues/291
- # TODO: Revisit when astroid 3.0 includes the change
- if isinstance(scope, nodes.Lambda) and any(
+ if isinstance(scope, (nodes.Lambda, nodes.FunctionDef)) and any(
asmt.scope().parent_of(scope) for asmt in astmts
):
return
@@ -2481,7 +2537,7 @@ class VariablesChecker(BaseChecker):
else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue)
):
return
- # TODO: 2.16: Consider using RefactoringChecker._is_function_def_never_returning
+ # TODO: 3.0: Consider using RefactoringChecker._is_function_def_never_returning
if isinstance(else_stmt, nodes.Expr) and isinstance(
else_stmt.value, nodes.Call
):
@@ -2906,7 +2962,7 @@ class VariablesChecker(BaseChecker):
@staticmethod
def _nodes_to_unpack(node: nodes.NodeNG) -> list[nodes.NodeNG] | None:
"""Return the list of values of the `Assign` node."""
- if isinstance(node, (nodes.Tuple, nodes.List) + DICT_TYPES):
+ if isinstance(node, (nodes.Tuple, nodes.List, *DICT_TYPES)):
return node.itered() # type: ignore[no-any-return]
if isinstance(node, astroid.Instance) and any(
ancestor.qname() == "typing.NamedTuple" for ancestor in node.ancestors()
@@ -2961,7 +3017,9 @@ class VariablesChecker(BaseChecker):
if not isinstance(module, nodes.Module):
return None
except astroid.NotFoundError:
- if module.name in self._ignored_modules:
+ # Unable to import `name` from `module`. Since `name` may itself be a
+ # module, we first check if it matches the ignored modules.
+ if is_module_ignored(f"{module.qname()}.{name}", self._ignored_modules):
return None
self.add_message(
"no-name-in-module", args=(name, module.name), node=node
diff --git a/pylint/config/__init__.py b/pylint/config/__init__.py
index 5f90bbae0..5dbda321c 100644
--- a/pylint/config/__init__.py
+++ b/pylint/config/__init__.py
@@ -1,67 +1,9 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-__all__ = [
- "ConfigurationMixIn", # Deprecated
- "find_default_config_files",
- "find_pylintrc", # Deprecated
- "Option", # Deprecated
- "OptionsManagerMixIn", # Deprecated
- "OptionParser", # Deprecated
- "OptionsProviderMixIn", # Deprecated
- "UnsupportedAction", # Deprecated
- "PYLINTRC",
- "USER_HOME", # Compatibility with the old API
- "PYLINT_HOME", # Compatibility with the old API
- "save_results", # Compatibility with the old API # Deprecated
- "load_results", # Compatibility with the old API # Deprecated
-]
+__all__ = ["find_default_config_files"]
-import warnings
-
-from pylint.config.arguments_provider import UnsupportedAction
-from pylint.config.configuration_mixin import ConfigurationMixIn
-from pylint.config.environment_variable import PYLINTRC
-from pylint.config.find_default_config_files import (
- find_default_config_files,
- find_pylintrc,
-)
-from pylint.config.option import Option
-from pylint.config.option_manager_mixin import OptionsManagerMixIn
-from pylint.config.option_parser import OptionParser # type: ignore[attr-defined]
-from pylint.config.options_provider_mixin import ( # type: ignore[attr-defined]
- OptionsProviderMixIn,
-)
-from pylint.constants import PYLINT_HOME, USER_HOME
-from pylint.utils import LinterStats
-
-
-def load_results(base: str) -> LinterStats | None:
- # TODO: 3.0: Remove deprecated function
- # pylint: disable=import-outside-toplevel
- from pylint.lint.caching import load_results as _real_load_results
-
- warnings.warn(
- "'pylint.config.load_results' is deprecated, please use "
- "'pylint.lint.load_results' instead. This will be removed in 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _real_load_results(base, PYLINT_HOME)
-
-
-def save_results(results: LinterStats, base: str) -> None:
- # TODO: 3.0: Remove deprecated function
- # pylint: disable=import-outside-toplevel
- from pylint.lint.caching import save_results as _real_save_results
-
- warnings.warn(
- "'pylint.config.save_results' is deprecated, please use "
- "'pylint.lint.save_results' instead. This will be removed in 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _real_save_results(results, base, PYLINT_HOME)
+from pylint.config.find_default_config_files import find_default_config_files
diff --git a/pylint/config/_pylint_config/__init__.py b/pylint/config/_pylint_config/__init__.py
index 622d0dfe3..8d36bd88b 100644
--- a/pylint/config/_pylint_config/__init__.py
+++ b/pylint/config/_pylint_config/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Everything related to the 'pylint-config' command.
diff --git a/pylint/config/_pylint_config/generate_command.py b/pylint/config/_pylint_config/generate_command.py
index 110069b90..d1b73c99b 100644
--- a/pylint/config/_pylint_config/generate_command.py
+++ b/pylint/config/_pylint_config/generate_command.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Everything related to the 'pylint-config generate' command."""
from __future__ import annotations
-import warnings
from io import StringIO
from typing import TYPE_CHECKING
@@ -29,10 +28,8 @@ def generate_interactive_config(linter: PyLinter) -> None:
config_string = linter._generate_config_file(minimal=minimal)
else:
output_stream = StringIO()
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- linter.generate_config(stream=output_stream, skipsections=("Commands",))
- config_string = output_stream.getvalue()
+ linter._generate_config(stream=output_stream, skipsections=("Commands",))
+ config_string = output_stream.getvalue()
if to_file:
with open(output_file_name, "w", encoding="utf-8") as f:
diff --git a/pylint/config/_pylint_config/help_message.py b/pylint/config/_pylint_config/help_message.py
index 15c9803e7..7ba947429 100644
--- a/pylint/config/_pylint_config/help_message.py
+++ b/pylint/config/_pylint_config/help_message.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Everything related to the 'pylint-config -h' command and subcommands."""
diff --git a/pylint/config/_pylint_config/main.py b/pylint/config/_pylint_config/main.py
index 716702df5..e562da2ef 100644
--- a/pylint/config/_pylint_config/main.py
+++ b/pylint/config/_pylint_config/main.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Everything related to the 'pylint-config' command."""
diff --git a/pylint/config/_pylint_config/setup.py b/pylint/config/_pylint_config/setup.py
index ae52b99ba..211f9bc6d 100644
--- a/pylint/config/_pylint_config/setup.py
+++ b/pylint/config/_pylint_config/setup.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Everything related to the setup of the 'pylint-config' command."""
diff --git a/pylint/config/_pylint_config/utils.py b/pylint/config/_pylint_config/utils.py
index cd5f8affe..f9185e8b1 100644
--- a/pylint/config/_pylint_config/utils.py
+++ b/pylint/config/_pylint_config/utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utils for the 'pylint-config' command."""
@@ -9,12 +9,7 @@ from __future__ import annotations
import sys
from collections.abc import Callable
from pathlib import Path
-from typing import TypeVar
-
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
+from typing import Literal, TypeVar
if sys.version_info >= (3, 10):
from typing import ParamSpec
diff --git a/pylint/config/argument.py b/pylint/config/argument.py
index d648ae7e4..d826cbd3e 100644
--- a/pylint/config/argument.py
+++ b/pylint/config/argument.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Definition of an Argument class and transformers for various argument types.
@@ -13,10 +13,9 @@ import argparse
import os
import pathlib
import re
-import sys
from collections.abc import Callable
from glob import glob
-from typing import Any, Pattern, Sequence, Tuple, Union
+from typing import Any, Literal, Pattern, Sequence, Tuple, Union
from pylint import interfaces
from pylint import utils as pylint_utils
@@ -24,12 +23,6 @@ from pylint.config.callback_actions import _CallbackAction, _ExtendAction
from pylint.config.deprecation_actions import _NewNamesAction, _OldNamesAction
from pylint.constants import PY38_PLUS
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
-
_ArgumentTypes = Union[
str,
int,
diff --git a/pylint/config/arguments_manager.py b/pylint/config/arguments_manager.py
index ef1d0cc72..b99c9476f 100644
--- a/pylint/config/arguments_manager.py
+++ b/pylint/config/arguments_manager.py
@@ -1,24 +1,18 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Arguments manager class used to handle command-line arguments and options."""
from __future__ import annotations
import argparse
-import configparser
-import copy
-import optparse # pylint: disable=deprecated-module
-import os
import re
import sys
import textwrap
import warnings
-from collections import OrderedDict
from collections.abc import Sequence
-from pathlib import Path
-from typing import TYPE_CHECKING, Any, TextIO, Union
+from typing import TYPE_CHECKING, Any, TextIO
import tomlkit
@@ -37,11 +31,6 @@ from pylint.config.exceptions import (
_UnrecognizedOptionError,
)
from pylint.config.help_formatter import _HelpFormatter
-from pylint.config.option import Option
-from pylint.config.option_parser import OptionParser # type: ignore[attr-defined]
-from pylint.config.options_provider_mixin import ( # type: ignore[attr-defined]
- OptionsProviderMixIn,
-)
from pylint.config.utils import _convert_option_to_argument, _parse_rich_type_value
from pylint.constants import MAIN_CHECKER_NAME
from pylint.typing import DirectoryNamespaceDict, OptionDict
@@ -55,10 +44,7 @@ else:
if TYPE_CHECKING:
from pylint.config.arguments_provider import _ArgumentsProvider
-ConfigProvider = Union["_ArgumentsProvider", OptionsProviderMixIn]
-
-# pylint: disable-next=too-many-instance-attributes
class _ArgumentsManager:
"""Arguments manager class used to handle command-line arguments and options."""
@@ -95,21 +81,6 @@ class _ArgumentsManager:
self._directory_namespaces: DirectoryNamespaceDict = {}
"""Mapping of directories and their respective namespace objects."""
- # TODO: 3.0: Remove deprecated attributes introduced to keep API
- # parity with optparse. Until '_maxlevel'
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.reset_parsers(usage or "")
- # list of registered options providers
- self._options_providers: list[ConfigProvider] = []
- # dictionary associating option name to checker
- self._all_options: OrderedDict[str, ConfigProvider] = OrderedDict()
- self._short_options: dict[str, str] = {}
- self._nocallback_options: dict[ConfigProvider, str] = {}
- self._mygroups: dict[str, optparse.OptionGroup] = {}
- # verbosity
- self._maxlevel: int = 0
-
@property
def config(self) -> argparse.Namespace:
"""Namespace for all options."""
@@ -119,25 +90,6 @@ class _ArgumentsManager:
def config(self, value: argparse.Namespace) -> None:
self._config = value
- @property
- def options_providers(self) -> list[ConfigProvider]:
- # TODO: 3.0: Remove deprecated attribute.
- warnings.warn(
- "options_providers has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- return self._options_providers
-
- @options_providers.setter
- def options_providers(self, value: list[ConfigProvider]) -> None:
- warnings.warn(
- "Setting options_providers has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- self._options_providers = value
-
def _register_options_provider(self, provider: _ArgumentsProvider) -> None:
"""Register an options provider and load its defaults."""
for opt, optdict in provider.options:
@@ -281,173 +233,12 @@ class _ArgumentsManager:
return parsed_args
- def reset_parsers(self, usage: str = "") -> None: # pragma: no cover
- """DEPRECATED."""
- warnings.warn(
- "reset_parsers has been deprecated. Parsers should be instantiated "
- "once during initialization and do not need to be reset.",
- DeprecationWarning,
- stacklevel=2,
- )
- # configuration file parser
- self.cfgfile_parser = configparser.ConfigParser(
- inline_comment_prefixes=("#", ";")
- )
- # command line parser
- self.cmdline_parser = OptionParser(Option, usage=usage)
- self.cmdline_parser.options_manager = self
- self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS)
-
- def register_options_provider(
- self, provider: ConfigProvider, own_group: bool = True
- ) -> None: # pragma: no cover
- """DEPRECATED: Register an options provider."""
- warnings.warn(
- "register_options_provider has been deprecated. Options providers and "
- "arguments providers should be registered by initializing ArgumentsProvider. "
- "This automatically registers the provider on the ArgumentsManager.",
- DeprecationWarning,
- stacklevel=2,
- )
- self.options_providers.append(provider)
- non_group_spec_options = [
- option for option in provider.options if "group" not in option[1]
- ]
- groups = getattr(provider, "option_groups", ())
- if own_group and non_group_spec_options:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.add_option_group(
- provider.name.upper(),
- provider.__doc__,
- non_group_spec_options,
- provider,
- )
- else:
- for opt, optdict in non_group_spec_options:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.add_optik_option(provider, self.cmdline_parser, opt, optdict)
- for gname, gdoc in groups:
- gname = gname.upper()
- goptions = [
- option
- for option in provider.options
- if option[1].get("group", "").upper() == gname # type: ignore[union-attr]
- ]
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.add_option_group(gname, gdoc, goptions, provider)
-
- def add_option_group(
- self,
- group_name: str,
- _: str | None,
- options: list[tuple[str, OptionDict]],
- provider: ConfigProvider,
- ) -> None: # pragma: no cover
- """DEPRECATED."""
- warnings.warn(
- "add_option_group has been deprecated. Option groups should be "
- "registered by initializing ArgumentsProvider. "
- "This automatically registers the group on the ArgumentsManager.",
- DeprecationWarning,
- stacklevel=2,
- )
- # add option group to the command line parser
- if group_name in self._mygroups:
- group = self._mygroups[group_name]
- else:
- group = optparse.OptionGroup(
- self.cmdline_parser, title=group_name.capitalize()
- )
- self.cmdline_parser.add_option_group(group)
- self._mygroups[group_name] = group
- # add section to the config file
- if (
- group_name != "DEFAULT"
- and group_name not in self.cfgfile_parser._sections # type: ignore[attr-defined]
- ):
- self.cfgfile_parser.add_section(group_name)
- # add provider's specific options
- for opt, optdict in options:
- if not isinstance(optdict.get("action", "store"), str):
- optdict["action"] = "callback"
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.add_optik_option(provider, group, opt, optdict)
-
- def add_optik_option(
- self,
- provider: ConfigProvider,
- optikcontainer: optparse.OptionParser | optparse.OptionGroup,
- opt: str,
- optdict: OptionDict,
- ) -> None: # pragma: no cover
- """DEPRECATED."""
- warnings.warn(
- "add_optik_option has been deprecated. Options should be automatically "
- "added by initializing an ArgumentsProvider.",
- DeprecationWarning,
- stacklevel=2,
- )
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- args, optdict = self.optik_option(provider, opt, optdict)
- option = optikcontainer.add_option(*args, **optdict)
- self._all_options[opt] = provider
- self._maxlevel = max(self._maxlevel, option.level or 0)
-
- def optik_option(
- self, provider: ConfigProvider, opt: str, optdict: OptionDict
- ) -> tuple[list[str], OptionDict]: # pragma: no cover
- """DEPRECATED: Get our personal option definition and return a suitable form for
- use with optik/optparse.
- """
- warnings.warn(
- "optik_option has been deprecated. Parsing of option dictionaries should be done "
- "automatically by initializing an ArgumentsProvider.",
- DeprecationWarning,
- stacklevel=2,
- )
- optdict = copy.copy(optdict)
- if "action" in optdict:
- self._nocallback_options[provider] = opt
- else:
- optdict["action"] = "callback"
- optdict["callback"] = self.cb_set_provider_option
- # default is handled here and *must not* be given to optik if you
- # want the whole machinery to work
- if "default" in optdict:
- if (
- "help" in optdict
- and optdict.get("default") is not None
- and optdict["action"] not in ("store_true", "store_false")
- ):
- optdict["help"] += " [current: %default]" # type: ignore[operator]
- del optdict["default"]
- args = ["--" + str(opt)]
- if "short" in optdict:
- self._short_options[optdict["short"]] = opt # type: ignore[index]
- args.append("-" + optdict["short"]) # type: ignore[operator]
- del optdict["short"]
- # cleanup option definition dict before giving it to optik
- for key in list(optdict.keys()):
- if key not in self._optik_option_attrs:
- optdict.pop(key)
- return args, optdict
-
- def generate_config(
+ def _generate_config(
self, stream: TextIO | None = None, skipsections: tuple[str, ...] = ()
- ) -> None: # pragma: no cover
- """DEPRECATED: Write a configuration file according to the current configuration
+ ) -> None:
+ """Write a configuration file according to the current configuration
into the given stream or stdout.
"""
- warnings.warn(
- "generate_config has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
options_by_section = {}
sections = []
for group in sorted(
@@ -503,202 +294,10 @@ class _ArgumentsManager:
)
printed = True
- def load_provider_defaults(self) -> None: # pragma: no cover
- """DEPRECATED: Initialize configuration using default values."""
- warnings.warn(
- "load_provider_defaults has been deprecated. Parsing of option defaults should be done "
- "automatically by initializing an ArgumentsProvider.",
- DeprecationWarning,
- stacklevel=2,
- )
- for provider in self.options_providers:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- provider.load_defaults()
-
- def read_config_file(
- self, config_file: Path | None = None, verbose: bool = False
- ) -> None: # pragma: no cover
- """DEPRECATED: Read the configuration file but do not load it (i.e. dispatching
- values to each option's provider).
-
- :raises OSError: When the specified config file doesn't exist
- """
- warnings.warn(
- "read_config_file has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- if not config_file:
- if verbose:
- print(
- "No config file found, using default configuration", file=sys.stderr
- )
- return
- config_file = Path(os.path.expandvars(config_file)).expanduser()
- if not config_file.exists():
- raise OSError(f"The config file {str(config_file)} doesn't exist!")
- parser = self.cfgfile_parser
- if config_file.suffix == ".toml":
- try:
- self._parse_toml(config_file, parser)
- except tomllib.TOMLDecodeError:
- pass
- else:
- # Use this encoding in order to strip the BOM marker, if any.
- with open(config_file, encoding="utf_8_sig") as fp:
- parser.read_file(fp)
- # normalize each section's title
- for sect, values in list(parser._sections.items()): # type: ignore[attr-defined]
- if sect.startswith("pylint."):
- sect = sect[len("pylint.") :]
- if not sect.isupper() and values:
- parser._sections[sect.upper()] = values # type: ignore[attr-defined]
-
- if verbose:
- print(f"Using config file '{config_file}'", file=sys.stderr)
-
- @staticmethod
- def _parse_toml(
- config_file: Path, parser: configparser.ConfigParser
- ) -> None: # pragma: no cover
- """DEPRECATED: Parse and handle errors of a toml configuration file.
-
- TODO: 3.0: Remove deprecated method.
- """
- with open(config_file, mode="rb") as fp:
- content = tomllib.load(fp)
- try:
- sections_values = content["tool"]["pylint"]
- except KeyError:
- return
- for section, values in sections_values.items():
- section_name = section.upper()
- # TOML has rich types, convert values to
- # strings as ConfigParser expects.
- if not isinstance(values, dict):
- continue
- for option, value in values.items():
- if isinstance(value, bool):
- values[option] = "yes" if value else "no"
- elif isinstance(value, list):
- values[option] = ",".join(value)
- else:
- values[option] = str(value)
- for option, value in values.items():
- try:
- parser.set(section_name, option, value=value)
- except configparser.NoSectionError:
- parser.add_section(section_name)
- parser.set(section_name, option, value=value)
-
- def load_config_file(self) -> None: # pragma: no cover
- """DEPRECATED: Dispatch values previously read from a configuration file to each
- option's provider.
- """
- warnings.warn(
- "load_config_file has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- parser = self.cfgfile_parser
- for section in parser.sections():
- for option, value in parser.items(section):
- try:
- self.global_set_option(option, value)
- except (KeyError, optparse.OptionError):
- continue
-
- def load_configuration(self, **kwargs: Any) -> None: # pragma: no cover
- """DEPRECATED: Override configuration according to given parameters."""
- warnings.warn(
- "load_configuration has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- return self.load_configuration_from_config(kwargs)
-
- def load_configuration_from_config(
- self, config: dict[str, Any]
- ) -> None: # pragma: no cover
- warnings.warn(
- "DEPRECATED: load_configuration_from_config has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- for opt, opt_value in config.items():
- opt = opt.replace("_", "-")
- provider = self._all_options[opt]
- provider.set_option(opt, opt_value)
-
- def load_command_line_configuration(
- self, args: list[str] | None = None
- ) -> list[str]: # pragma: no cover
- """DEPRECATED: Override configuration according to command line parameters.
-
- return additional arguments
- """
- warnings.warn(
- "load_command_line_configuration has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- args = sys.argv[1:] if args is None else list(args)
- (options, args) = self.cmdline_parser.parse_args(args=args)
- for provider in self._nocallback_options:
- config = provider.config
- for attr in config.__dict__.keys():
- value = getattr(options, attr, None)
- if value is None:
- continue
- setattr(config, attr, value)
- return args # type: ignore[return-value]
-
- def help(self, level: int | None = None) -> str:
+ def help(self) -> str:
"""Return the usage string based on the available options."""
- if level is not None:
- warnings.warn(
- "Supplying a 'level' argument to help() has been deprecated."
- "You can call help() without any arguments.",
- DeprecationWarning,
- stacklevel=2,
- )
return self._arg_parser.format_help()
- def cb_set_provider_option( # pragma: no cover
- self, option: Any, opt: Any, value: Any, parser: Any
- ) -> None:
- """DEPRECATED: Optik callback for option setting."""
- # TODO: 3.0: Remove deprecated method.
- warnings.warn(
- "cb_set_provider_option has been deprecated. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- if opt.startswith("--"):
- # remove -- on long option
- opt = opt[2:]
- else:
- # short option, get its long equivalent
- opt = self._short_options[opt[1:]]
- # trick since we can't set action='store_true' on options
- if value is None:
- value = 1
- self.set_option(opt, value)
-
- def global_set_option(self, opt: str, value: Any) -> None: # pragma: no cover
- """DEPRECATED: Set option on the correct option provider."""
- # TODO: 3.0: Remove deprecated method.
- warnings.warn(
- "global_set_option has been deprecated. You can use _arguments_manager.set_option "
- "or linter.set_option to set options on the global configuration object.",
- DeprecationWarning,
- stacklevel=2,
- )
- self.set_option(opt, value)
-
def _generate_config_file(self, *, minimal: bool = False) -> str:
"""Write a configuration file according to the current configuration into
stdout.
@@ -795,30 +394,8 @@ class _ArgumentsManager:
return str(toml_string)
- def set_option(
- self,
- optname: str,
- value: Any,
- action: str | None = "default_value",
- optdict: None | str | OptionDict = "default_value",
- ) -> None:
+ def set_option(self, optname: str, value: Any) -> None:
"""Set an option on the namespace object."""
- # TODO: 3.0: Remove deprecated arguments.
- if action != "default_value":
- warnings.warn(
- "The 'action' argument has been deprecated. You can use set_option "
- "without the 'action' or 'optdict' arguments.",
- DeprecationWarning,
- stacklevel=2,
- )
- if optdict != "default_value":
- warnings.warn(
- "The 'optdict' argument has been deprecated. You can use set_option "
- "without the 'action' or 'optdict' arguments.",
- DeprecationWarning,
- stacklevel=2,
- )
-
self.config = self._arg_parser.parse_known_args(
[f"--{optname.replace('_', '-')}", _parse_rich_type_value(value)],
self.config,
diff --git a/pylint/config/arguments_provider.py b/pylint/config/arguments_provider.py
index f5aac2f1d..7f75718ca 100644
--- a/pylint/config/arguments_provider.py
+++ b/pylint/config/arguments_provider.py
@@ -1,14 +1,11 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Arguments provider class used to expose options."""
from __future__ import annotations
-import argparse
-import optparse # pylint: disable=deprecated-module
-import warnings
from collections.abc import Iterator
from typing import Any
@@ -16,19 +13,6 @@ from pylint.config.arguments_manager import _ArgumentsManager
from pylint.typing import OptionDict, Options
-class UnsupportedAction(Exception):
- """Raised by set_option when it doesn't know what to do for an action."""
-
- def __init__(self, *args: object) -> None:
- # TODO: 3.0: Remove deprecated exception
- warnings.warn(
- "UnsupportedAction has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__(*args)
-
-
class _ArgumentsProvider:
"""Base class for classes that provide arguments."""
@@ -47,170 +31,35 @@ class _ArgumentsProvider:
self._arguments_manager._register_options_provider(self)
- self._level = 0
-
- @property
- def level(self) -> int:
- # TODO: 3.0: Remove deprecated attribute
- warnings.warn(
- "The level attribute has been deprecated. It was used to display the checker in the help or not,"
- " and everything is displayed in the help now. It will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- return self._level
-
- @level.setter
- def level(self, value: int) -> None:
- # TODO: 3.0: Remove deprecated attribute
- warnings.warn(
- "Setting the level attribute has been deprecated. It was used to display the checker "
- "in the help or not, and everything is displayed in the help now. It will be removed "
- "in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- self._level = value
-
- @property
- def config(self) -> argparse.Namespace:
- # TODO: 3.0: Remove deprecated attribute
- warnings.warn(
- "The checker-specific config attribute has been deprecated. Please use "
- "'linter.config' to access the global configuration object.",
- DeprecationWarning,
- stacklevel=2,
- )
- return self._arguments_manager.config
-
- def load_defaults(self) -> None: # pragma: no cover
- """DEPRECATED: Initialize the provider using default values."""
- warnings.warn(
- "load_defaults has been deprecated. Option groups should be "
- "registered by initializing an ArgumentsProvider. "
- "This automatically registers the group on the ArgumentsManager.",
- DeprecationWarning,
- stacklevel=2,
- )
- for opt, optdict in self.options:
- action = optdict.get("action")
- if action != "callback":
- # callback action have no default
- if optdict is None:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- optdict = self.get_option_def(opt)
- default = optdict.get("default")
- self.set_option(opt, default, action, optdict)
-
- def option_attrname(
- self, opt: str, optdict: OptionDict | None = None
- ) -> str: # pragma: no cover
- """DEPRECATED: Get the config attribute corresponding to opt."""
- warnings.warn(
- "option_attrname has been deprecated. It will be removed "
- "in a future release.",
- DeprecationWarning,
- stacklevel=2,
- )
- if optdict is None:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- optdict = self.get_option_def(opt)
- return optdict.get("dest", opt.replace("-", "_")) # type: ignore[return-value]
-
- def option_value(self, opt: str) -> Any: # pragma: no cover
- """DEPRECATED: Get the current value for the given option."""
- warnings.warn(
- "option_value has been deprecated. It will be removed "
- "in a future release.",
- DeprecationWarning,
- stacklevel=2,
- )
+ def _option_value(self, opt: str) -> Any:
+ """Get the current value for the given option."""
return getattr(self._arguments_manager.config, opt.replace("-", "_"), None)
- def set_option( # pragma: no cover
- self,
- optname: Any,
- value: Any,
- action: Any = None, # pylint: disable=unused-argument
- optdict: Any = None, # pylint: disable=unused-argument
- ) -> None:
- """DEPRECATED: Method called to set an option (registered in the options
- list).
- """
- # TODO: 3.0: Remove deprecated method.
- warnings.warn(
- "set_option has been deprecated. You can use _arguments_manager.set_option "
- "or linter.set_option to set options on the global configuration object.",
- DeprecationWarning,
- stacklevel=2,
- )
- self._arguments_manager.set_option(optname, value)
-
- def get_option_def(self, opt: str) -> OptionDict: # pragma: no cover
- """DEPRECATED: Return the dictionary defining an option given its name.
-
- :raises OptionError: If the option isn't found.
- """
- warnings.warn(
- "get_option_def has been deprecated. It will be removed "
- "in a future release.",
- DeprecationWarning,
- stacklevel=2,
- )
- assert self.options
- for option in self.options:
- if option[0] == opt:
- return option[1]
- raise optparse.OptionError(
- f"no such option {opt} in section {self.name!r}", opt # type: ignore[arg-type]
- )
-
- def options_by_section(
+ def _options_by_section(
self,
) -> Iterator[
tuple[str, list[tuple[str, OptionDict, Any]]]
| tuple[None, dict[str, list[tuple[str, OptionDict, Any]]]]
- ]: # pragma: no cover
- """DEPRECATED: Return an iterator on options grouped by section.
+ ]:
+ """Return an iterator on options grouped by section.
(section, [list of (optname, optdict, optvalue)])
"""
- # TODO 3.0: Make this function private see
- # https://github.com/PyCQA/pylint/pull/6665#discussion_r880143229
- # It's only used in '_get_global_options_documentation'
- warnings.warn(
- "options_by_section has been deprecated. It will be removed "
- "in a future release.",
- DeprecationWarning,
- stacklevel=2,
- )
sections: dict[str, list[tuple[str, OptionDict, Any]]] = {}
for optname, optdict in self.options:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- sections.setdefault(optdict.get("group"), []).append( # type: ignore[arg-type]
- (optname, optdict, self.option_value(optname))
- )
+ sections.setdefault(optdict.get("group"), []).append( # type: ignore[arg-type]
+ (optname, optdict, self._option_value(optname))
+ )
if None in sections:
yield None, sections.pop(None) # type: ignore[call-overload]
for section, options in sorted(sections.items()):
yield section.upper(), options
- def options_and_values(
+ def _options_and_values(
self, options: Options | None = None
- ) -> Iterator[tuple[str, OptionDict, Any]]: # pragma: no cover
+ ) -> Iterator[tuple[str, OptionDict, Any]]:
"""DEPRECATED."""
- warnings.warn(
- "options_and_values has been deprecated. It will be removed "
- "in a future release.",
- DeprecationWarning,
- stacklevel=2,
- )
if options is None:
options = self.options
for optname, optdict in options:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- yield optname, optdict, self.option_value(optname)
+ yield optname, optdict, self._option_value(optname)
diff --git a/pylint/config/callback_actions.py b/pylint/config/callback_actions.py
index a4c633464..7c93f2553 100644
--- a/pylint/config/callback_actions.py
+++ b/pylint/config/callback_actions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
# pylint: disable=too-many-arguments, redefined-builtin, duplicate-code
@@ -11,7 +11,6 @@ from __future__ import annotations
import abc
import argparse
import sys
-import warnings
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Any
@@ -265,11 +264,9 @@ class _GenerateRCFileAction(_AccessRunObjectAction):
values: str | Sequence[Any] | None,
option_string: str | None = "--generate-rcfile",
) -> None:
- # TODO: 2.x: Deprecate this after the auto-upgrade functionality of
+ # TODO: 3.x: Deprecate this after the auto-upgrade functionality of
# pylint-config is sufficient.
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- self.run.linter.generate_config(skipsections=("Commands",))
+ self.run.linter._generate_config(skipsections=("Commands",))
sys.exit(0)
diff --git a/pylint/config/config_file_parser.py b/pylint/config/config_file_parser.py
index 019f9b738..047df5889 100644
--- a/pylint/config/config_file_parser.py
+++ b/pylint/config/config_file_parser.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Configuration file parser class."""
@@ -9,7 +9,6 @@ from __future__ import annotations
import configparser
import os
import sys
-import warnings
from pathlib import Path
from typing import TYPE_CHECKING
@@ -41,23 +40,13 @@ class _ConfigurationFileParser:
config_content: dict[str, str] = {}
options: list[str] = []
+ ini_file_with_sections = self._ini_file_with_sections(file_path)
for section in parser.sections():
- if self._ini_file_with_sections(file_path) and not section.startswith(
- "pylint"
- ):
- if section.lower() == "master":
- # TODO: 3.0: Remove deprecated handling of master, only allow 'pylint.' sections
- warnings.warn(
- "The use of 'MASTER' or 'master' as configuration section for pylint "
- "has been deprecated, as it's bad practice to not start sections titles "
- "with the tool name. Please use 'pylint.main' instead.",
- UserWarning,
- )
- else:
- continue
- for opt, value in parser[section].items():
- config_content[opt] = value
- options += [f"--{opt}", value]
+ if ini_file_with_sections and not section.startswith("pylint"):
+ continue
+ for option, value in parser[section].items():
+ config_content[option] = value
+ options += [f"--{option}", value]
return config_content, options
@staticmethod
diff --git a/pylint/config/config_initialization.py b/pylint/config/config_initialization.py
index 3c9c4e22a..4f7b614ee 100644
--- a/pylint/config/config_initialization.py
+++ b/pylint/config/config_initialization.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/config/configuration_mixin.py b/pylint/config/configuration_mixin.py
deleted file mode 100644
index 55857224a..000000000
--- a/pylint/config/configuration_mixin.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-from __future__ import annotations
-
-import warnings
-from typing import Any
-
-from pylint.config.option_manager_mixin import OptionsManagerMixIn
-from pylint.config.options_provider_mixin import ( # type: ignore[attr-defined]
- OptionsProviderMixIn,
-)
-
-
-class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn): # type: ignore[misc]
- """Basic mixin for simple configurations which don't need the
- manager / providers model.
- """
-
- def __init__(self, *args: Any, **kwargs: Any) -> None:
- # TODO: 3.0: Remove deprecated class
- warnings.warn(
- "ConfigurationMixIn has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- if not args:
- kwargs.setdefault("usage", "")
- OptionsManagerMixIn.__init__(self, *args, **kwargs)
- OptionsProviderMixIn.__init__(self)
- if not getattr(self, "option_groups", None):
- self.option_groups: list[tuple[str, str]] = []
- for _, optdict in self.options:
- try:
- gdef = (optdict["group"].upper(), "")
- except KeyError:
- continue
- if gdef not in self.option_groups:
- self.option_groups.append(gdef)
- self.register_options_provider(self, own_group=False)
diff --git a/pylint/config/deprecation_actions.py b/pylint/config/deprecation_actions.py
index ceef200a7..85a77cc78 100644
--- a/pylint/config/deprecation_actions.py
+++ b/pylint/config/deprecation_actions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
# pylint: disable=too-many-arguments, redefined-builtin
@@ -104,4 +104,5 @@ class _NewNamesAction(argparse._StoreAction):
f"{self.option_strings[0]} has been deprecated. Please look into "
f"using any of the following options: {', '.join(self.new_names)}.",
DeprecationWarning,
+ stacklevel=2,
)
diff --git a/pylint/config/environment_variable.py b/pylint/config/environment_variable.py
deleted file mode 100644
index 291b1651c..000000000
--- a/pylint/config/environment_variable.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-import warnings
-
-from pylint.config.find_default_config_files import find_pylintrc
-
-with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- PYLINTRC = find_pylintrc()
diff --git a/pylint/config/exceptions.py b/pylint/config/exceptions.py
index cb4fc1f8f..982e3f494 100644
--- a/pylint/config/exceptions.py
+++ b/pylint/config/exceptions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py
index 43e682a58..3b03f6357 100644
--- a/pylint/config/find_default_config_files.py
+++ b/pylint/config/find_default_config_files.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import configparser
import os
import sys
-import warnings
from collections.abc import Iterator
from pathlib import Path
@@ -17,7 +16,28 @@ else:
import tomli as tomllib
RC_NAMES = (Path("pylintrc"), Path(".pylintrc"))
-CONFIG_NAMES = RC_NAMES + (Path("pyproject.toml"), Path("setup.cfg"))
+PYPROJECT_NAME = Path("pyproject.toml")
+CONFIG_NAMES = (*RC_NAMES, PYPROJECT_NAME, Path("setup.cfg"))
+
+
+def _find_pyproject() -> Path:
+ """Search for file pyproject.toml in the parent directories recursively.
+
+ It resolves symlinks, so if there is any symlink up in the tree, it does not respect them
+ """
+ current_dir = Path.cwd().resolve()
+ is_root = False
+ while not is_root:
+ if (current_dir / PYPROJECT_NAME).is_file():
+ return current_dir / PYPROJECT_NAME
+ is_root = (
+ current_dir == current_dir.parent
+ or (current_dir / ".git").is_dir()
+ or (current_dir / ".hg").is_dir()
+ )
+ current_dir = current_dir.parent
+
+ return current_dir
def _toml_has_config(path: Path | str) -> bool:
@@ -101,6 +121,13 @@ def find_default_config_files() -> Iterator[Path]:
pass
try:
+ parent_pyproject = _find_pyproject()
+ if parent_pyproject.is_file() and _toml_has_config(parent_pyproject):
+ yield parent_pyproject.resolve()
+ except OSError:
+ pass
+
+ try:
yield from _find_config_in_home_or_environment()
except OSError:
pass
@@ -110,21 +137,3 @@ def find_default_config_files() -> Iterator[Path]:
yield Path("/etc/pylintrc").resolve()
except OSError:
pass
-
-
-def find_pylintrc() -> str | None:
- """Search the pylint rc file and return its path if it finds it, else return
- None.
- """
- # TODO: 3.0: Remove deprecated function
- warnings.warn(
- "find_pylintrc and the PYLINTRC constant have been deprecated. "
- "Use find_default_config_files if you want access to pylint's configuration file "
- "finding logic.",
- DeprecationWarning,
- stacklevel=2,
- )
- for config_file in find_default_config_files():
- if str(config_file).endswith("pylintrc"):
- return str(config_file)
- return None
diff --git a/pylint/config/help_formatter.py b/pylint/config/help_formatter.py
index 5e517d944..78d43d178 100644
--- a/pylint/config/help_formatter.py
+++ b/pylint/config/help_formatter.py
@@ -1,13 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import argparse
from pylint.config.callback_actions import _CallbackAction
-from pylint.constants import DEFAULT_PYLINT_HOME, OLD_DEFAULT_PYLINT_HOME
+from pylint.constants import DEFAULT_PYLINT_HOME
class _HelpFormatter(argparse.RawDescriptionHelpFormatter):
@@ -35,8 +35,7 @@ class _HelpFormatter(argparse.RawDescriptionHelpFormatter):
Environment variables:
The following environment variables are used:
* PYLINTHOME Path to the directory where persistent data for the run will
- be stored. If not found, it defaults to '{DEFAULT_PYLINT_HOME}'
- or '{OLD_DEFAULT_PYLINT_HOME}' (in the current working directory).
+ be stored. If not found, it defaults to '{DEFAULT_PYLINT_HOME}'.
* PYLINTRC Path to the configuration file. See the documentation for the method used
to search for configuration file.
diff --git a/pylint/config/option.py b/pylint/config/option.py
deleted file mode 100644
index 74e4d45d8..000000000
--- a/pylint/config/option.py
+++ /dev/null
@@ -1,239 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-from __future__ import annotations
-
-import copy
-import optparse # pylint: disable=deprecated-module
-import pathlib
-import re
-import warnings
-from collections.abc import Callable, Sequence
-from re import Pattern
-from typing import Any
-
-from pylint import utils
-
-
-# pylint: disable=unused-argument
-def _csv_validator(
- _: Any, name: str, value: str | list[str] | tuple[str]
-) -> Sequence[str]:
- return utils._check_csv(value)
-
-
-# pylint: disable=unused-argument
-def _regexp_validator(
- _: Any, name: str, value: str | re.Pattern[str]
-) -> re.Pattern[str]:
- if hasattr(value, "pattern"):
- return value # type: ignore[return-value]
- return re.compile(value)
-
-
-# pylint: disable=unused-argument
-def _regexp_csv_validator(
- _: Any, name: str, value: str | list[str]
-) -> list[re.Pattern[str]]:
- return [_regexp_validator(_, name, val) for val in _csv_validator(_, name, value)]
-
-
-def _regexp_paths_csv_validator(
- _: Any, name: str, value: str | list[Pattern[str]]
-) -> list[Pattern[str]]:
- if isinstance(value, list):
- return value
- patterns = []
- for val in _csv_validator(_, name, value):
- patterns.append(
- re.compile(
- str(pathlib.PureWindowsPath(val)).replace("\\", "\\\\")
- + "|"
- + pathlib.PureWindowsPath(val).as_posix()
- )
- )
- return patterns
-
-
-def _choice_validator(choices: list[Any], name: str, value: Any) -> Any:
- if value not in choices:
- msg = "option %s: invalid value: %r, should be in %s"
- raise optparse.OptionValueError(msg % (name, value, choices))
- return value
-
-
-def _yn_validator(opt: str, _: str, value: Any) -> bool:
- if isinstance(value, int):
- return bool(value)
- if isinstance(value, str):
- value = value.lower()
- if value in {"y", "yes", "true"}:
- return True
- 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))
-
-
-def _multiple_choice_validator(choices: list[Any], name: str, value: Any) -> Any:
- values = utils._check_csv(value)
- for csv_value in values:
- if csv_value not in choices:
- msg = "option %s: invalid value: %r, should be in %s"
- raise optparse.OptionValueError(msg % (name, csv_value, choices))
- return values
-
-
-def _non_empty_string_validator( # pragma: no cover # Unused
- opt: Any, _: str, value: str
-) -> str:
- if not value:
- msg = "indent string can't be empty."
- raise optparse.OptionValueError(msg)
- return utils._unquote(value)
-
-
-def _multiple_choices_validating_option( # pragma: no cover # Unused
- opt: optparse.Option, name: str, value: Any
-) -> Any:
- return _multiple_choice_validator(
- opt.choices, name, value # type: ignore[attr-defined]
- )
-
-
-def _py_version_validator(_: Any, name: str, value: Any) -> tuple[int, int, int]:
- if not isinstance(value, tuple):
- try:
- value = tuple(int(val) for val in value.split("."))
- except (ValueError, AttributeError):
- raise optparse.OptionValueError(
- f"Invalid format for {name}, should be version string. E.g., '3.8'"
- ) from None
- return value # type: ignore[no-any-return]
-
-
-VALIDATORS: dict[str, Callable[[Any, str, Any], Any] | Callable[[Any], Any]] = {
- "string": utils._unquote,
- "int": int,
- "float": float,
- "glob_paths_csv": _csv_validator,
- "regexp": lambda pattern: re.compile(pattern or ""),
- "regexp_csv": _regexp_csv_validator,
- "regexp_paths_csv": _regexp_paths_csv_validator,
- "csv": _csv_validator,
- "yn": _yn_validator,
- "choice": lambda opt, name, value: _choice_validator(opt["choices"], name, value),
- "confidence": lambda opt, name, value: _multiple_choice_validator(
- opt["choices"], name, value
- ),
- "multiple_choice": lambda opt, name, value: _multiple_choice_validator(
- opt["choices"], name, value
- ),
- "non_empty_string": _non_empty_string_validator,
- "py_version": _py_version_validator,
-}
-
-
-def _call_validator(opttype: str, optdict: Any, option: str, value: Any) -> Any:
- if opttype not in VALIDATORS:
- raise TypeError(f'Unsupported type "{opttype}"')
- try:
- return VALIDATORS[opttype](optdict, option, value) # type: ignore[call-arg]
- except TypeError:
- try:
- return VALIDATORS[opttype](value) # type: ignore[call-arg]
- except Exception as e:
- raise optparse.OptionValueError(
- f"{option} value ({value!r}) should be of type {opttype}"
- ) from e
-
-
-def _validate(value: Any, optdict: Any, name: str = "") -> Any:
- """Return a validated value for an option according to its type.
-
- optional argument name is only used for error message formatting
- """
- try:
- _type = optdict["type"]
- except KeyError:
- return value
- return _call_validator(_type, optdict, name, value)
-
-
-# pylint: disable=no-member
-class Option(optparse.Option):
- TYPES = optparse.Option.TYPES + (
- "glob_paths_csv",
- "regexp",
- "regexp_csv",
- "regexp_paths_csv",
- "csv",
- "yn",
- "confidence",
- "multiple_choice",
- "non_empty_string",
- "py_version",
- )
- ATTRS = optparse.Option.ATTRS + ["hide", "level"]
- TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
- TYPE_CHECKER["glob_paths_csv"] = _csv_validator
- TYPE_CHECKER["regexp"] = _regexp_validator
- TYPE_CHECKER["regexp_csv"] = _regexp_csv_validator
- TYPE_CHECKER["regexp_paths_csv"] = _regexp_paths_csv_validator
- TYPE_CHECKER["csv"] = _csv_validator
- TYPE_CHECKER["yn"] = _yn_validator
- TYPE_CHECKER["confidence"] = _multiple_choices_validating_option
- TYPE_CHECKER["multiple_choice"] = _multiple_choices_validating_option
- TYPE_CHECKER["non_empty_string"] = _non_empty_string_validator
- TYPE_CHECKER["py_version"] = _py_version_validator
-
- def __init__(self, *opts: Any, **attrs: Any) -> None:
- # TODO: 3.0: Remove deprecated class
- warnings.warn(
- "Option has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__(*opts, **attrs)
- if hasattr(self, "hide") and self.hide:
- self.help = optparse.SUPPRESS_HELP
-
- def _check_choice(self) -> None:
- if self.type in {"choice", "multiple_choice", "confidence"}:
- if self.choices is None: # type: ignore[attr-defined]
- raise optparse.OptionError(
- "must supply a list of choices for type 'choice'", self
- )
- if not isinstance(self.choices, (tuple, list)): # type: ignore[attr-defined]
- raise optparse.OptionError(
- # pylint: disable-next=consider-using-f-string
- "choices must be a list of strings ('%s' supplied)"
- % str(type(self.choices)).split("'")[1], # type: ignore[attr-defined]
- self,
- )
- elif self.choices is not None: # type: ignore[attr-defined]
- raise optparse.OptionError(
- f"must not supply choices for type {self.type!r}", self
- )
-
- optparse.Option.CHECK_METHODS[2] = _check_choice # type: ignore[index]
-
- def process( # pragma: no cover # Argparse
- self, opt: Any, value: Any, values: Any, parser: Any
- ) -> int:
- assert isinstance(self.dest, str)
- if self.callback and self.callback.__module__ == "pylint.lint.run":
- return 1
- # First, convert the value(s) to the right type. Howl if any
- # value(s) are bogus.
- value = self.convert_value(opt, value)
- if self.type == "named":
- existent = getattr(values, self.dest)
- if existent:
- existent.update(value)
- value = existent
- # And then take whatever action is expected of us.
- # This is a separate method to make life easier for
- # subclasses to add new actions.
- return self.take_action(self.action, self.dest, opt, value, values, parser)
diff --git a/pylint/config/option_manager_mixin.py b/pylint/config/option_manager_mixin.py
deleted file mode 100644
index c468f494f..000000000
--- a/pylint/config/option_manager_mixin.py
+++ /dev/null
@@ -1,372 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-
-# pylint: disable=duplicate-code
-
-from __future__ import annotations
-
-import collections
-import configparser
-import contextlib
-import copy
-import optparse # pylint: disable=deprecated-module
-import os
-import sys
-import warnings
-from collections.abc import Iterator
-from pathlib import Path
-from typing import TYPE_CHECKING, Any, TextIO
-
-from pylint import utils
-from pylint.config.option import Option
-from pylint.config.option_parser import OptionParser # type: ignore[attr-defined]
-from pylint.typing import OptionDict
-
-if TYPE_CHECKING:
- from pylint.config.options_provider_mixin import ( # type: ignore[attr-defined]
- OptionsProviderMixin,
- )
-
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
-
-
-def _expand_default(self: optparse.HelpFormatter, option: Option) -> str:
- """Patch OptionParser.expand_default with custom behaviour.
-
- This will handle defaults to avoid overriding values in the
- configuration file.
- """
- if self.parser is None or not self.default_tag:
- return str(option.help)
- optname = option._long_opts[0][2:]
- try:
- provider = self.parser.options_manager._all_options[optname] # type: ignore[attr-defined]
- except KeyError:
- value = None
- else:
- optdict = provider.get_option_def(optname)
- optname = provider.option_attrname(optname, optdict)
- value = getattr(provider.config, optname, optdict)
- value = utils._format_option_value(optdict, value)
- if value is optparse.NO_DEFAULT or not value:
- value = self.NO_DEFAULT_VALUE
- return option.help.replace(self.default_tag, str(value)) # type: ignore[union-attr]
-
-
-@contextlib.contextmanager
-def _patch_optparse() -> Iterator[None]:
- # pylint: disable = redefined-variable-type
- orig_default = optparse.HelpFormatter
- try:
- optparse.HelpFormatter.expand_default = _expand_default # type: ignore[assignment]
- yield
- finally:
- optparse.HelpFormatter.expand_default = orig_default # type: ignore[assignment]
-
-
-class OptionsManagerMixIn:
- """Handle configuration from both a configuration file and command line options."""
-
- def __init__(self, usage: str) -> None:
- # TODO: 3.0: Remove deprecated class
- warnings.warn(
- "OptionsManagerMixIn has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- self.reset_parsers(usage)
- # list of registered options providers
- self.options_providers: list[OptionsProviderMixin] = []
- # dictionary associating option name to checker
- self._all_options: collections.OrderedDict[Any, Any] = collections.OrderedDict()
- self._short_options: dict[Any, Any] = {}
- self._nocallback_options: dict[Any, Any] = {}
- self._mygroups: dict[Any, Any] = {}
- # verbosity
- self._maxlevel = 0
-
- def reset_parsers(self, usage: str = "") -> None:
- # configuration file parser
- self.cfgfile_parser = configparser.ConfigParser(
- inline_comment_prefixes=("#", ";")
- )
- # command line parser
- self.cmdline_parser = OptionParser(Option, usage=usage)
- self.cmdline_parser.options_manager = self
- self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS)
-
- def register_options_provider(
- self, provider: OptionsProviderMixin, own_group: bool = True
- ) -> None:
- """Register an options provider."""
- self.options_providers.append(provider)
- non_group_spec_options = [
- option for option in provider.options if "group" not in option[1]
- ]
- groups = getattr(provider, "option_groups", ())
- if own_group and non_group_spec_options:
- self.add_option_group(
- provider.name.upper(),
- provider.__doc__,
- non_group_spec_options,
- provider,
- )
- else:
- for opt, optdict in non_group_spec_options:
- self.add_optik_option(provider, self.cmdline_parser, opt, optdict)
- for gname, gdoc in groups:
- gname = gname.upper()
- goptions = [
- option
- for option in provider.options
- if option[1].get("group", "").upper() == gname
- ]
- self.add_option_group(gname, gdoc, goptions, provider)
-
- def add_option_group(
- self, group_name: str, _: Any, options: Any, provider: OptionsProviderMixin
- ) -> None:
- # add option group to the command line parser
- if group_name in self._mygroups:
- group = self._mygroups[group_name]
- else:
- group = optparse.OptionGroup(
- self.cmdline_parser, title=group_name.capitalize()
- )
- self.cmdline_parser.add_option_group(group)
- self._mygroups[group_name] = group
- # add section to the config file
- if (
- group_name != "DEFAULT"
- and group_name not in self.cfgfile_parser._sections # type: ignore[attr-defined]
- ):
- self.cfgfile_parser.add_section(group_name)
- # add provider's specific options
- for opt, optdict in options:
- if not isinstance(optdict.get("action", "store"), str):
- optdict["action"] = "callback"
- self.add_optik_option(provider, group, opt, optdict)
-
- def add_optik_option(
- self,
- provider: OptionsProviderMixin,
- optikcontainer: Any,
- opt: str,
- optdict: OptionDict,
- ) -> None:
- args, optdict = self.optik_option(provider, opt, optdict)
- option = optikcontainer.add_option(*args, **optdict)
- self._all_options[opt] = provider
- self._maxlevel = max(self._maxlevel, option.level or 0)
-
- def optik_option(
- self, provider: OptionsProviderMixin, opt: str, optdict: OptionDict
- ) -> tuple[list[str], OptionDict]:
- """Get our personal option definition and return a suitable form for
- use with optik/optparse.
- """
- optdict = copy.copy(optdict)
- if "action" in optdict:
- self._nocallback_options[provider] = opt
- else:
- optdict["action"] = "callback"
- optdict["callback"] = self.cb_set_provider_option
- # default is handled here and *must not* be given to optik if you
- # want the whole machinery to work
- if "default" in optdict:
- if (
- "help" in optdict
- and optdict.get("default") is not None
- and optdict["action"] not in ("store_true", "store_false")
- ):
- optdict["help"] += " [current: %default]" # type: ignore[operator]
- del optdict["default"]
- args = ["--" + str(opt)]
- if "short" in optdict:
- self._short_options[optdict["short"]] = opt
- args.append("-" + optdict["short"]) # type: ignore[operator]
- del optdict["short"]
- # cleanup option definition dict before giving it to optik
- for key in list(optdict.keys()):
- if key not in self._optik_option_attrs:
- optdict.pop(key)
- return args, optdict
-
- def cb_set_provider_option(
- self, option: Option, opt: str, value: Any, parser: Any
- ) -> None:
- """Optik callback for option setting."""
- if opt.startswith("--"):
- # remove -- on long option
- opt = opt[2:]
- else:
- # short option, get its long equivalent
- opt = self._short_options[opt[1:]]
- # trick since we can't set action='store_true' on options
- if value is None:
- value = 1
- self.global_set_option(opt, value)
-
- def global_set_option(self, opt: str, value: Any) -> None:
- """Set option on the correct option provider."""
- self._all_options[opt].set_option(opt, value)
-
- def generate_config(
- self, stream: TextIO | None = None, skipsections: tuple[str, ...] = ()
- ) -> None:
- """Write a configuration file according to the current configuration
- into the given stream or stdout.
- """
- options_by_section: dict[str, list[tuple[str, OptionDict, Any]]] = {}
- sections = []
- for provider in self.options_providers:
- for section, options in provider.options_by_section():
- if section is None:
- section = provider.name
- if section in skipsections:
- continue
- options = [
- (n, d, v)
- for (n, d, v) in options
- if d.get("type") is not None and not d.get("deprecated")
- ]
- if not options:
- continue
- if section not in sections:
- sections.append(section)
- all_options = options_by_section.setdefault(section, [])
- all_options += options
- stream = stream or sys.stdout
- printed = False
- for section in sections:
- if printed:
- print("\n", file=stream)
- utils.format_section(
- stream, section.upper(), sorted(options_by_section[section])
- )
- printed = True
-
- def load_provider_defaults(self) -> None:
- """Initialize configuration using default values."""
- for provider in self.options_providers:
- provider.load_defaults()
-
- def read_config_file(
- self, config_file: Path | None = None, verbose: bool = False
- ) -> None:
- """Read the configuration file but do not load it (i.e. dispatching
- values to each option's provider).
- """
- if config_file:
- config_file = Path(os.path.expandvars(config_file)).expanduser()
- if not config_file.exists():
- raise OSError(f"The config file {str(config_file)} doesn't exist!")
-
- parser = self.cfgfile_parser
- if config_file.suffix == ".toml":
- try:
- self._parse_toml(config_file, parser)
- except tomllib.TOMLDecodeError:
- pass
- else:
- # Use this encoding in order to strip the BOM marker, if any.
- with open(config_file, encoding="utf_8_sig") as fp:
- parser.read_file(fp)
- # normalize each section's title
- for sect, values in list(parser._sections.items()): # type: ignore[attr-defined]
- if sect.startswith("pylint."):
- sect = sect[len("pylint.") :]
- if not sect.isupper() and values:
- parser._sections[sect.upper()] = values # type: ignore[attr-defined]
-
- if not verbose:
- return
- if config_file and config_file.exists():
- msg = f"Using config file '{config_file}'"
- else:
- msg = "No config file found, using default configuration"
- print(msg, file=sys.stderr)
-
- def _parse_toml(self, config_file: Path, parser: configparser.ConfigParser) -> None:
- """Parse and handle errors of a toml configuration file."""
- with open(config_file, mode="rb") as fp:
- content = tomllib.load(fp)
- try:
- sections_values = content["tool"]["pylint"]
- except KeyError:
- return
- for section, values in sections_values.items():
- section_name = section.upper()
- # TOML has rich types, convert values to
- # strings as ConfigParser expects.
- if not isinstance(values, dict):
- # This class is a mixin: add_message comes from the `PyLinter` class
- self.add_message( # type: ignore[attr-defined]
- "bad-configuration-section", line=0, args=(section, values)
- )
- continue
- for option, value in values.items():
- if isinstance(value, bool):
- values[option] = "yes" if value else "no"
- elif isinstance(value, list):
- values[option] = ",".join(value)
- else:
- values[option] = str(value)
- for option, value in values.items():
- try:
- parser.set(section_name, option, value=value)
- except configparser.NoSectionError:
- parser.add_section(section_name)
- parser.set(section_name, option, value=value)
-
- def load_config_file(self) -> None:
- """Dispatch values previously read from a configuration file to each
- option's provider.
- """
- parser = self.cfgfile_parser
- for section in parser.sections():
- for option, value in parser.items(section):
- try:
- self.global_set_option(option, value)
- except (KeyError, optparse.OptionError):
- continue
-
- def load_configuration(self, **kwargs: Any) -> None:
- """Override configuration according to given parameters."""
- return self.load_configuration_from_config(kwargs)
-
- def load_configuration_from_config(self, config: dict[str, Any]) -> None:
- for opt, opt_value in config.items():
- opt = opt.replace("_", "-")
- provider = self._all_options[opt]
- provider.set_option(opt, opt_value)
-
- def load_command_line_configuration(
- self, args: list[str] | None = None
- ) -> list[str]:
- """Override configuration according to command line parameters.
-
- return additional arguments
- """
- with _patch_optparse():
- args = sys.argv[1:] if args is None else list(args)
- (options, args) = self.cmdline_parser.parse_args(args=args)
- for provider in self._nocallback_options:
- config = provider.config
- for attr in config.__dict__.keys():
- value = getattr(options, attr, None)
- if value is None:
- continue
- setattr(config, attr, value)
- return args # type: ignore[return-value]
-
- def help(self, level: int = 0) -> str:
- """Return the usage string for available options."""
- self.cmdline_parser.formatter.output_level = level
- with _patch_optparse():
- return str(self.cmdline_parser.format_help())
diff --git a/pylint/config/option_parser.py b/pylint/config/option_parser.py
deleted file mode 100644
index 9ffb80116..000000000
--- a/pylint/config/option_parser.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-# type: ignore # Deprecated module.
-
-import optparse # pylint: disable=deprecated-module
-import warnings
-
-from pylint.config.option import Option
-
-
-def _level_options(group, outputlevel):
- return [
- option
- for option in group.option_list
- if (getattr(option, "level", 0) or 0) <= outputlevel
- and option.help is not optparse.SUPPRESS_HELP
- ]
-
-
-class OptionParser(optparse.OptionParser):
- def __init__(self, option_class, *args, **kwargs):
- # TODO: 3.0: Remove deprecated class
- warnings.warn(
- "OptionParser has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__(option_class=Option, *args, **kwargs) # noqa: B026
-
- def format_option_help(self, formatter=None):
- if formatter is None:
- formatter = self.formatter
- outputlevel = getattr(formatter, "output_level", 0)
- formatter.store_option_strings(self)
- result = [formatter.format_heading("Options")]
- formatter.indent()
- if self.option_list:
- result.append(optparse.OptionContainer.format_option_help(self, formatter))
- result.append("\n")
- for group in self.option_groups:
- if group.level <= outputlevel and (
- group.description or _level_options(group, outputlevel)
- ):
- result.append(group.format_help(formatter))
- result.append("\n")
- formatter.dedent()
- # Drop the last "\n", or the header if no options or option groups:
- return "".join(result[:-1])
-
- def _match_long_opt(self, opt): # pragma: no cover # Unused
- """Disable abbreviations."""
- if opt not in self._long_opt:
- raise optparse.BadOptionError(opt)
- return opt
diff --git a/pylint/config/options_provider_mixin.py b/pylint/config/options_provider_mixin.py
deleted file mode 100644
index 67f64ee0a..000000000
--- a/pylint/config/options_provider_mixin.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-# type: ignore # Deprecated module.
-
-import optparse # pylint: disable=deprecated-module
-import warnings
-
-from pylint.config.callback_actions import _CallbackAction
-from pylint.config.option import _validate
-from pylint.typing import Options
-
-
-class UnsupportedAction(Exception):
- """Raised by set_option when it doesn't know what to do for an action."""
-
-
-class OptionsProviderMixIn:
- """Mixin to provide options to an OptionsManager."""
-
- # those attributes should be overridden
- name = "default"
- options: Options = ()
- level = 0
-
- def __init__(self):
- # TODO: 3.0: Remove deprecated class
- warnings.warn(
- "OptionsProviderMixIn has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- self.config = optparse.Values()
- self.load_defaults()
-
- def load_defaults(self):
- """Initialize the provider using default values."""
- for opt, optdict in self.options:
- action = optdict.get("action")
- if action != "callback":
- # callback action have no default
- if optdict is None:
- optdict = self.get_option_def(opt)
- default = optdict.get("default")
- self.set_option(opt, default, action, optdict)
-
- def option_attrname(self, opt, optdict=None):
- """Get the config attribute corresponding to opt."""
- if optdict is None:
- optdict = self.get_option_def(opt)
- return optdict.get("dest", opt.replace("-", "_"))
-
- def option_value(self, opt):
- """Get the current value for the given option."""
- return getattr(self.config, self.option_attrname(opt), None)
-
- def set_option(self, optname, value, action=None, optdict=None):
- """Method called to set an option (registered in the options list)."""
- if optdict is None:
- optdict = self.get_option_def(optname)
- if value is not None:
- value = _validate(value, optdict, optname)
- if action is None:
- action = optdict.get("action", "store")
- if action == "store":
- setattr(self.config, self.option_attrname(optname, optdict), value)
- elif action in {"store_true", "count"}:
- setattr(self.config, self.option_attrname(optname, optdict), value)
- elif action == "store_false":
- setattr(self.config, self.option_attrname(optname, optdict), value)
- elif action == "append":
- optname = self.option_attrname(optname, optdict)
- _list = getattr(self.config, optname, None)
- if _list is None:
- if isinstance(value, (list, tuple)):
- _list = value
- elif value is not None:
- _list = [value]
- setattr(self.config, optname, _list)
- elif isinstance(_list, tuple):
- setattr(self.config, optname, _list + (value,))
- else:
- _list.append(value)
- elif (
- action == "callback"
- or (not isinstance(action, str))
- and issubclass(action, _CallbackAction)
- ):
- return
- else:
- raise UnsupportedAction(action)
-
- def get_option_def(self, opt):
- """Return the dictionary defining an option given its name."""
- assert self.options
- for option in self.options:
- if option[0] == opt:
- return option[1]
- raise optparse.OptionError(
- f"no such option {opt} in section {self.name!r}", opt
- )
-
- def options_by_section(self):
- """Return an iterator on options grouped by section.
-
- (section, [list of (optname, optdict, optvalue)])
- """
- sections = {}
- for optname, optdict in self.options:
- sections.setdefault(optdict.get("group"), []).append(
- (optname, optdict, self.option_value(optname))
- )
- if None in sections:
- yield None, sections.pop(None)
- for section, options in sorted(sections.items()):
- yield section.upper(), options
-
- def options_and_values(self, options=None):
- if options is None:
- options = self.options
- for optname, optdict in options:
- yield optname, optdict, self.option_value(optname)
diff --git a/pylint/config/utils.py b/pylint/config/utils.py
index d7cbd7c07..91e4ff86f 100644
--- a/pylint/config/utils.py
+++ b/pylint/config/utils.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utils for arguments/options parsing and handling."""
from __future__ import annotations
import re
-import warnings
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Any
@@ -39,14 +38,6 @@ def _convert_option_to_argument(
| _ExtendArgument
):
"""Convert an optdict to an Argument class instance."""
- if "level" in optdict and "hide" not in optdict:
- warnings.warn(
- "The 'level' key in optdicts has been deprecated. "
- "Use 'hide' with a boolean to hide an option from the help message. "
- f"optdict={optdict}",
- DeprecationWarning,
- )
-
# Get the long and short flags
flags = [f"--{opt}"]
if "short" in optdict:
@@ -74,17 +65,9 @@ def _convert_option_to_argument(
section=optdict.get("group", None),
metavar=optdict.get("metavar", None),
)
- try:
- default = optdict["default"]
- except KeyError:
- warnings.warn(
- "An option dictionary should have a 'default' key to specify "
- "the option's default value. This key will be required in pylint "
- "3.0. It is not required for 'store_true' and callable actions. "
- f"optdict={optdict}",
- DeprecationWarning,
- )
- default = None
+
+ default = optdict["default"]
+
if action == "extend":
return _ExtendArgument(
flags=flags,
diff --git a/pylint/constants.py b/pylint/constants.py
index de29fb9b8..5bf4abf5c 100644
--- a/pylint/constants.py
+++ b/pylint/constants.py
@@ -1,14 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import os
-import pathlib
import platform
import sys
-from datetime import datetime
import astroid
import platformdirs
@@ -49,9 +47,6 @@ MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1}
# on all project using [MAIN] in their rcfile.
MAIN_CHECKER_NAME = "main"
-USER_HOME = os.path.expanduser("~")
-# TODO: 3.0: Remove in 3.0 with all the surrounding code
-OLD_DEFAULT_PYLINT_HOME = ".pylint.d"
DEFAULT_PYLINT_HOME = platformdirs.user_cache_dir("pylint")
DEFAULT_IGNORE_LIST = ("CVS",)
@@ -101,55 +96,10 @@ INCOMPATIBLE_WITH_USELESS_SUPPRESSION = frozenset(
)
-def _warn_about_old_home(pylint_home: pathlib.Path) -> None:
- """Warn users about the old pylint home being deprecated.
-
- The spam prevention mechanism is due to pylint being used in parallel by
- pre-commit, and the message being spammy in this context
- Also if you work with an old version of pylint that recreates the
- old pylint home, you can get the old message for a long time.
- """
- prefix_spam_prevention = "pylint_warned_about_old_cache_already"
- spam_prevention_file = pathlib.Path(pylint_home) / datetime.now().strftime(
- prefix_spam_prevention + "_%Y-%m-%d.temp"
- )
- old_home = pathlib.Path(USER_HOME) / OLD_DEFAULT_PYLINT_HOME
-
- if old_home.exists() and not spam_prevention_file.exists():
- print(
- f"PYLINTHOME is now '{pylint_home}' but obsolescent '{old_home}' is found; "
- "you can safely remove the latter",
- file=sys.stderr,
- )
-
- # Remove old spam prevention file
- if pylint_home.exists():
- for filename in pylint_home.iterdir():
- if prefix_spam_prevention in str(filename):
- try:
- os.remove(pylint_home / filename)
- except OSError: # pragma: no cover
- pass
-
- # Create spam prevention file for today
- try:
- pylint_home.mkdir(parents=True, exist_ok=True)
- with open(spam_prevention_file, "w", encoding="utf8") as f:
- f.write("")
- except Exception as exc: # pragma: no cover # pylint: disable=broad-except
- print(
- "Can't write the file that was supposed to "
- f"prevent 'pylint.d' deprecation spam in {pylint_home} because of {exc}."
- )
-
-
def _get_pylint_home() -> str:
"""Return the pylint home."""
if "PYLINTHOME" in os.environ:
return os.environ["PYLINTHOME"]
-
- _warn_about_old_home(pathlib.Path(DEFAULT_PYLINT_HOME))
-
return DEFAULT_PYLINT_HOME
diff --git a/pylint/epylint.py b/pylint/epylint.py
deleted file mode 100755
index dd23b450b..000000000
--- a/pylint/epylint.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4
-# -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4
-
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Emacs and Flymake compatible Pylint.
-
-This script is for integration with Emacs and is compatible with Flymake mode.
-
-epylint walks out of python packages before invoking pylint. This avoids
-reporting import errors that occur when a module within a package uses the
-absolute import path to get another module within this package.
-
-For example:
- - Suppose a package is structured as
-
- a/__init__.py
- a/b/x.py
- a/c/y.py
-
- - Then if y.py imports x as "from a.b import x" the following produces pylint
- errors
-
- cd a/c; pylint y.py
-
- - The following obviously doesn't
-
- pylint a/c/y.py
-
- - As this script will be invoked by Emacs within the directory of the file
- we are checking we need to go out of it to avoid these false positives.
-
-You may also use py_run to run pylint with desired options and get back (or not)
-its output.
-"""
-
-from __future__ import annotations
-
-import os
-import shlex
-import sys
-import warnings
-from collections.abc import Sequence
-from io import StringIO
-from subprocess import PIPE, Popen
-from typing import NoReturn, TextIO, overload
-
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
-
-def _get_env() -> dict[str, str]:
- """Extracts the environment PYTHONPATH and appends the current 'sys.path'
- to it.
- """
- env = dict(os.environ)
- env["PYTHONPATH"] = os.pathsep.join(sys.path)
- return env
-
-
-def lint(filename: str, options: Sequence[str] = ()) -> int:
- """Pylint the given file.
-
- When run from Emacs we will be in the directory of a file, and passed its
- filename. If this file is part of a package and is trying to import other
- modules from within its own package or another package rooted in a directory
- below it, pylint will classify it as a failed import.
-
- To get around this, we traverse down the directory tree to find the root of
- the package this module is in. We then invoke pylint from this directory.
-
- Finally, we must correct the filenames in the output generated by pylint so
- Emacs doesn't become confused (it will expect just the original filename,
- while pylint may extend it with extra directories if we've traversed down
- the tree)
- """
- # traverse downwards until we are out of a python package
- full_path = os.path.abspath(filename)
- parent_path = os.path.dirname(full_path)
- child_path = os.path.basename(full_path)
-
- while parent_path != "/" and os.path.exists(
- os.path.join(parent_path, "__init__.py")
- ):
- child_path = os.path.join(os.path.basename(parent_path), child_path)
- parent_path = os.path.dirname(parent_path)
-
- # Start pylint
- # Ensure we use the python and pylint associated with the running epylint
- run_cmd = "import sys; from pylint.lint import Run; Run(sys.argv[1:])"
- cmd = (
- [sys.executable, "-c", run_cmd]
- + [
- "--msg-template",
- "{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}",
- "-r",
- "n",
- child_path,
- ]
- + list(options)
- )
-
- with Popen(
- cmd, stdout=PIPE, cwd=parent_path, env=_get_env(), universal_newlines=True
- ) as process:
- for line in process.stdout: # type: ignore[union-attr]
- # remove pylintrc warning
- if line.startswith("No config file found"):
- continue
-
- # modify the file name that's put out to reverse the path traversal we made
- parts = line.split(":")
- if parts and parts[0] == child_path:
- line = ":".join([filename] + parts[1:])
- print(line, end=" ")
-
- process.wait()
- return process.returncode
-
-
-@overload
-def py_run(
- command_options: str = ...,
- return_std: Literal[False] = ...,
- stdout: TextIO | int | None = ...,
- stderr: TextIO | int | None = ...,
-) -> None:
- ...
-
-
-@overload
-def py_run(
- command_options: str,
- return_std: Literal[True],
- stdout: TextIO | int | None = ...,
- stderr: TextIO | int | None = ...,
-) -> tuple[StringIO, StringIO]:
- ...
-
-
-def py_run(
- command_options: str = "",
- return_std: bool = False,
- stdout: TextIO | int | None = None,
- stderr: TextIO | int | None = None,
-) -> tuple[StringIO, StringIO] | None:
- """Run pylint from python.
-
- ``command_options`` is a string containing ``pylint`` command line options;
- ``return_std`` (boolean) indicates return of created standard output
- and error (see below);
- ``stdout`` and ``stderr`` are 'file-like' objects in which standard output
- could be written.
-
- Calling agent is responsible for stdout/err management (creation, close).
- Default standard output and error are those from sys,
- or standalone ones (``subprocess.PIPE``) are used
- if they are not set and ``return_std``.
-
- If ``return_std`` is set to ``True``, this function returns a 2-uple
- containing standard output and error related to created process,
- as follows: ``(stdout, stderr)``.
-
- To silently run Pylint on a module, and get its standard output and error:
- >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True)
- """
- warnings.warn(
- "'epylint' will be removed in pylint 3.0, use https://github.com/emacsorphanage/pylint instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- # Detect if we use Python as executable or not, else default to `python`
- executable = sys.executable if "python" in sys.executable else "python"
-
- # Create command line to call pylint
- epylint_part = [executable, "-c", "from pylint import epylint;epylint.Run()"]
- options = shlex.split(command_options, posix=not sys.platform.startswith("win"))
- cli = epylint_part + options
-
- # Providing standard output and/or error if not set
- if stdout is None:
- stdout = PIPE if return_std else sys.stdout
- if stderr is None:
- stderr = PIPE if return_std else sys.stderr
- # Call pylint in a sub-process
- with Popen(
- cli,
- shell=False,
- stdout=stdout,
- stderr=stderr,
- env=_get_env(),
- universal_newlines=True,
- ) as process:
- proc_stdout, proc_stderr = process.communicate()
- # Return standard output and error
- if return_std:
- return StringIO(proc_stdout), StringIO(proc_stderr)
- return None
-
-
-def Run(argv: Sequence[str] | None = None) -> NoReturn:
- warnings.warn(
- "'epylint' will be removed in pylint 3.0, use https://github.com/emacsorphanage/pylint instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- if not argv and len(sys.argv) == 1:
- print(f"Usage: {sys.argv[0]} <filename> [options]")
- sys.exit(1)
-
- argv = argv or sys.argv[1:]
- if not os.path.exists(argv[0]):
- print(f"{argv[0]} does not exist")
- sys.exit(1)
- else:
- sys.exit(lint(argv[0], argv[1:]))
-
-
-if __name__ == "__main__":
- Run()
diff --git a/pylint/exceptions.py b/pylint/exceptions.py
index 766eb9327..2bfbfa8cc 100644
--- a/pylint/exceptions.py
+++ b/pylint/exceptions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Exception classes raised by various operations within pylint."""
diff --git a/pylint/extensions/__init__.py b/pylint/extensions/__init__.py
index 01b978e4b..e9e2b0d1b 100644
--- a/pylint/extensions/__init__.py
+++ b/pylint/extensions/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py
index 33edc95e9..dddc577d6 100644
--- a/pylint/extensions/_check_docs_utils.py
+++ b/pylint/extensions/_check_docs_utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utility methods for docstring checking."""
diff --git a/pylint/extensions/bad_builtin.py b/pylint/extensions/bad_builtin.py
index 904b2a394..22ef2b95e 100644
--- a/pylint/extensions/bad_builtin.py
+++ b/pylint/extensions/bad_builtin.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checker for deprecated builtins."""
diff --git a/pylint/extensions/broad_try_clause.py b/pylint/extensions/broad_try_clause.py
index 2291d32d4..762055a25 100644
--- a/pylint/extensions/broad_try_clause.py
+++ b/pylint/extensions/broad_try_clause.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Looks for try/except statements with too much code in the try clause."""
diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py
index b584ea35e..de20ed8ec 100644
--- a/pylint/extensions/check_elif.py
+++ b/pylint/extensions/check_elif.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/code_style.py b/pylint/extensions/code_style.py
index 262a7f0c4..5ce1ae476 100644
--- a/pylint/extensions/code_style.py
+++ b/pylint/extensions/code_style.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/comparetozero.py b/pylint/extensions/comparetozero.py
deleted file mode 100644
index 116bf229a..000000000
--- a/pylint/extensions/comparetozero.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Looks for comparisons to zero."""
-
-from __future__ import annotations
-
-import itertools
-from typing import TYPE_CHECKING
-
-import astroid
-from astroid import nodes
-
-from pylint import checkers
-from pylint.checkers import utils
-from pylint.interfaces import HIGH
-
-if TYPE_CHECKING:
- from pylint.lint import PyLinter
-
-
-def _is_constant_zero(node: str | nodes.NodeNG) -> bool:
- # We have to check that node.value is not False because node.value == 0 is True
- # when node.value is False
- return (
- isinstance(node, astroid.Const) and node.value == 0 and node.value is not False
- )
-
-
-class CompareToZeroChecker(checkers.BaseChecker):
- """Checks for comparisons to zero.
-
- Most of the time you should use the fact that integers with a value of 0 are false.
- An exception to this rule is when 0 is allowed in the program and has a
- different meaning than None!
- """
-
- # configuration section name
- name = "compare-to-zero"
- msgs = {
- "C2001": (
- '"%s" can be simplified to "%s" as 0 is falsey',
- "compare-to-zero",
- "Used when Pylint detects comparison to a 0 constant.",
- )
- }
-
- options = ()
-
- @utils.only_required_for_messages("compare-to-zero")
- def visit_compare(self, node: nodes.Compare) -> None:
- # pylint: disable=duplicate-code
- _operators = ["!=", "==", "is not", "is"]
- # note: astroid.Compare has the left most operand in node.left
- # while the rest are a list of tuples in node.ops
- # the format of the tuple is ('compare operator sign', node)
- # here we squash everything into `ops` to make it easier for processing later
- ops: list[tuple[str, nodes.NodeNG]] = [("", node.left)]
- ops.extend(node.ops)
- iter_ops = iter(ops)
- all_ops = list(itertools.chain(*iter_ops))
-
- for ops_idx in range(len(all_ops) - 2):
- op_1 = all_ops[ops_idx]
- op_2 = all_ops[ops_idx + 1]
- op_3 = all_ops[ops_idx + 2]
- error_detected = False
-
- # 0 ?? X
- if _is_constant_zero(op_1) and op_2 in _operators:
- error_detected = True
- op = op_3
- # X ?? 0
- elif op_2 in _operators and _is_constant_zero(op_3):
- error_detected = True
- op = op_1
-
- if error_detected:
- original = f"{op_1.as_string()} {op_2} {op_3.as_string()}"
- suggestion = (
- op.as_string()
- if op_2 in {"!=", "is not"}
- else f"not {op.as_string()}"
- )
- self.add_message(
- "compare-to-zero",
- args=(original, suggestion),
- node=node,
- confidence=HIGH,
- )
-
-
-def register(linter: PyLinter) -> None:
- linter.register_checker(CompareToZeroChecker(linter))
diff --git a/pylint/extensions/comparison_placement.py b/pylint/extensions/comparison_placement.py
index df7cc9890..f7ecceae3 100644
--- a/pylint/extensions/comparison_placement.py
+++ b/pylint/extensions/comparison_placement.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checks for yoda comparisons (variable before constant)
See https://en.wikipedia.org/wiki/Yoda_conditions.
diff --git a/pylint/extensions/confusing_elif.py b/pylint/extensions/confusing_elif.py
index ada8b5ab1..546b644b3 100644
--- a/pylint/extensions/confusing_elif.py
+++ b/pylint/extensions/confusing_elif.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/consider_refactoring_into_while_condition.py b/pylint/extensions/consider_refactoring_into_while_condition.py
index b4b53d8fa..b7e905e8a 100644
--- a/pylint/extensions/consider_refactoring_into_while_condition.py
+++ b/pylint/extensions/consider_refactoring_into_while_condition.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Looks for try/except statements with too much code in the try clause."""
diff --git a/pylint/extensions/consider_ternary_expression.py b/pylint/extensions/consider_ternary_expression.py
index 0e9444662..83046ce38 100644
--- a/pylint/extensions/consider_ternary_expression.py
+++ b/pylint/extensions/consider_ternary_expression.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for if / assign blocks that can be rewritten with if-expressions."""
diff --git a/pylint/extensions/dict_init_mutate.py b/pylint/extensions/dict_init_mutate.py
index fb4c83647..4977e234b 100644
--- a/pylint/extensions/dict_init_mutate.py
+++ b/pylint/extensions/dict_init_mutate.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for use of dictionary mutation after initialization."""
from __future__ import annotations
diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py
index 0c2e4e9e3..6e173c39e 100644
--- a/pylint/extensions/docparams.py
+++ b/pylint/extensions/docparams.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings."""
diff --git a/pylint/extensions/docstyle.py b/pylint/extensions/docstyle.py
index 1ca2885e9..c54ab93b2 100644
--- a/pylint/extensions/docstyle.py
+++ b/pylint/extensions/docstyle.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/dunder.py b/pylint/extensions/dunder.py
index e0e9af316..ad3e272aa 100644
--- a/pylint/extensions/dunder.py
+++ b/pylint/extensions/dunder.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/empty_comment.py b/pylint/extensions/empty_comment.py
index e8a914708..61e257ffd 100644
--- a/pylint/extensions/empty_comment.py
+++ b/pylint/extensions/empty_comment.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/emptystring.py b/pylint/extensions/emptystring.py
deleted file mode 100644
index f96a980f5..000000000
--- a/pylint/extensions/emptystring.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Looks for comparisons to empty string."""
-
-from __future__ import annotations
-
-import itertools
-from typing import TYPE_CHECKING
-
-from astroid import nodes
-
-from pylint import checkers
-from pylint.checkers import utils
-from pylint.interfaces import HIGH
-
-if TYPE_CHECKING:
- from pylint.lint import PyLinter
-
-
-class CompareToEmptyStringChecker(checkers.BaseChecker):
- name = "compare-to-empty-string"
- msgs = {
- "C1901": (
- '"%s" can be simplified to "%s" as an empty string is falsey',
- "compare-to-empty-string",
- "Used when Pylint detects comparison to an empty string constant.",
- )
- }
-
- options = ()
-
- @utils.only_required_for_messages("compare-to-empty-string")
- def visit_compare(self, node: nodes.Compare) -> None:
- """Checks for comparisons to empty string.
-
- Most of the time you should use the fact that empty strings are false.
- An exception to this rule is when an empty string value is allowed in the program
- and has a different meaning than None!
- """
- _operators = {"!=", "==", "is not", "is"}
- # note: astroid.Compare has the left most operand in node.left while the rest
- # are a list of tuples in node.ops the format of the tuple is
- # ('compare operator sign', node) here we squash everything into `ops`
- # to make it easier for processing later
- ops: list[tuple[str, nodes.NodeNG | None]] = [("", node.left)]
- ops.extend(node.ops)
- iter_ops = iter(ops)
- ops = list(itertools.chain(*iter_ops)) # type: ignore[arg-type]
- for ops_idx in range(len(ops) - 2):
- op_1: nodes.NodeNG | None = ops[ops_idx]
- op_2: str = ops[ops_idx + 1] # type: ignore[assignment]
- op_3: nodes.NodeNG | None = ops[ops_idx + 2]
- error_detected = False
- if op_1 is None or op_3 is None or op_2 not in _operators:
- continue
- node_name = ""
- # x ?? ""
- if utils.is_empty_str_literal(op_1):
- error_detected = True
- node_name = op_3.as_string()
- # '' ?? X
- elif utils.is_empty_str_literal(op_3):
- error_detected = True
- node_name = op_1.as_string()
- if error_detected:
- suggestion = f"not {node_name}" if op_2 in {"==", "is"} else node_name
- self.add_message(
- "compare-to-empty-string",
- args=(node.as_string(), suggestion),
- node=node,
- confidence=HIGH,
- )
-
-
-def register(linter: PyLinter) -> None:
- linter.register_checker(CompareToEmptyStringChecker(linter))
diff --git a/pylint/extensions/eq_without_hash.py b/pylint/extensions/eq_without_hash.py
index b0d0f01bd..5f39dfa3e 100644
--- a/pylint/extensions/eq_without_hash.py
+++ b/pylint/extensions/eq_without_hash.py
@@ -1,12 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""This is the remnant of the python3 checker.
It was removed because the transition from python 2 to python3 is
behind us, but some checks are still useful in python3 after all.
-See https://github.com/PyCQA/pylint/issues/5025
+See https://github.com/pylint-dev/pylint/issues/5025
"""
from astroid import nodes
diff --git a/pylint/extensions/for_any_all.py b/pylint/extensions/for_any_all.py
index bc7dd9c48..2369a595d 100644
--- a/pylint/extensions/for_any_all.py
+++ b/pylint/extensions/for_any_all.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for use of for loops that only check for a condition."""
diff --git a/pylint/extensions/magic_value.py b/pylint/extensions/magic_value.py
index 7cfb410ae..c69711f85 100644
--- a/pylint/extensions/magic_value.py
+++ b/pylint/extensions/magic_value.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Checks for magic values instead of literals."""
diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py
index ea64d2ebf..604360fb7 100644
--- a/pylint/extensions/mccabe.py
+++ b/pylint/extensions/mccabe.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Module to add McCabe checker class for pylint."""
diff --git a/pylint/extensions/no_self_use.py b/pylint/extensions/no_self_use.py
index 0fd38877f..8b9b8aa9b 100644
--- a/pylint/extensions/no_self_use.py
+++ b/pylint/extensions/no_self_use.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/overlapping_exceptions.py b/pylint/extensions/overlapping_exceptions.py
index 93d225137..ddcf24e7c 100644
--- a/pylint/extensions/overlapping_exceptions.py
+++ b/pylint/extensions/overlapping_exceptions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Looks for overlapping exceptions."""
diff --git a/pylint/extensions/private_import.py b/pylint/extensions/private_import.py
index df08c7116..a9e537c1b 100644
--- a/pylint/extensions/private_import.py
+++ b/pylint/extensions/private_import.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for imports on private external modules and names."""
diff --git a/pylint/extensions/redefined_loop_name.py b/pylint/extensions/redefined_loop_name.py
index df333fab9..d03b80be3 100644
--- a/pylint/extensions/redefined_loop_name.py
+++ b/pylint/extensions/redefined_loop_name.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Optional checker to warn when loop variables are overwritten in the loop's body."""
diff --git a/pylint/extensions/redefined_variable_type.py b/pylint/extensions/redefined_variable_type.py
index 8d88d856e..ba5af3136 100644
--- a/pylint/extensions/redefined_variable_type.py
+++ b/pylint/extensions/redefined_variable_type.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/set_membership.py b/pylint/extensions/set_membership.py
index f267e046f..b72f5aa18 100644
--- a/pylint/extensions/set_membership.py
+++ b/pylint/extensions/set_membership.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py
index 264d0c383..2956465cf 100644
--- a/pylint/extensions/typing.py
+++ b/pylint/extensions/typing.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/extensions/while_used.py b/pylint/extensions/while_used.py
index 6f9612196..da1f9d59c 100644
--- a/pylint/extensions/while_used.py
+++ b/pylint/extensions/while_used.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Check for use of while loops."""
diff --git a/pylint/graph.py b/pylint/graph.py
index 5cffca615..4112fadfa 100644
--- a/pylint/graph.py
+++ b/pylint/graph.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Graph manipulation utilities.
diff --git a/pylint/interfaces.py b/pylint/interfaces.py
index 221084fab..a93a40ceb 100644
--- a/pylint/interfaces.py
+++ b/pylint/interfaces.py
@@ -1,28 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Interfaces for Pylint objects."""
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-import warnings
-from tokenize import TokenInfo
-from typing import TYPE_CHECKING, NamedTuple
-
-from astroid import nodes
-
-if TYPE_CHECKING:
- from pylint.checkers import BaseChecker
- from pylint.message import Message
- from pylint.reporters.ureports.nodes import Section
+from typing import NamedTuple
__all__ = (
- "IRawChecker",
- "IAstroidChecker",
- "ITokenChecker",
- "IReporter",
- "IChecker",
"HIGH",
"CONTROL_FLOW",
"INFERENCE",
@@ -51,87 +35,3 @@ UNDEFINED = Confidence("UNDEFINED", "Warning without any associated confidence l
CONFIDENCE_LEVELS = [HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED]
CONFIDENCE_LEVEL_NAMES = [i.name for i in CONFIDENCE_LEVELS]
-
-
-class Interface:
- """Base class for interfaces."""
-
- def __init__(self) -> None:
- warnings.warn(
- "Interface and all of its subclasses have been deprecated "
- "and will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
-
- @classmethod
- def is_implemented_by(
- cls: type[Interface] | tuple[type[Interface], ...], instance: BaseChecker
- ) -> bool:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- return implements(instance, cls)
-
-
-def implements(
- obj: BaseChecker,
- interface: type[Interface] | tuple[type[Interface], ...],
-) -> bool:
- """Does the given object (maybe an instance or class) implement the interface."""
- # TODO: 3.0: Remove deprecated function
- warnings.warn(
- "implements has been deprecated in favour of using basic "
- "inheritance patterns without using __implements__.",
- DeprecationWarning,
- stacklevel=2,
- )
- implements_ = getattr(obj, "__implements__", ())
- if not isinstance(implements_, (list, tuple)):
- implements_ = (implements_,)
- return any(issubclass(i, interface) for i in implements_)
-
-
-class IChecker(Interface):
- """Base interface, to be used only for sub interfaces definition."""
-
- def open(self) -> None:
- """Called before visiting project (i.e. set of modules)."""
-
- def close(self) -> None:
- """Called after visiting project (i.e. set of modules)."""
-
-
-class IRawChecker(IChecker):
- """Interface for checker which need to parse the raw file."""
-
- def process_module(self, node: nodes.Module) -> None:
- """Process a module.
-
- The module's content is accessible via ``astroid.stream``
- """
-
-
-class ITokenChecker(IChecker):
- """Interface for checkers that need access to the token list."""
-
- def process_tokens(self, tokens: list[TokenInfo]) -> None:
- """Process a module.
-
- Tokens is a list of all source code tokens in the file.
- """
-
-
-class IAstroidChecker(IChecker):
- """Interface for checker which prefers receive events according to
- statement type.
- """
-
-
-class IReporter(Interface):
- """Reporter collect messages and display results encapsulated in a layout."""
-
- def handle_message(self, msg: Message) -> None:
- """Handle the given message object."""
-
- def display_reports(self, layout: Section) -> None:
- """Display results encapsulated in the layout tree."""
diff --git a/pylint/lint/__init__.py b/pylint/lint/__init__.py
index 573d9c262..adc920708 100644
--- a/pylint/lint/__init__.py
+++ b/pylint/lint/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Pylint [options] modules_or_packages.
@@ -27,12 +27,7 @@ from pylint.lint.report_functions import (
report_total_messages_stats,
)
from pylint.lint.run import Run
-from pylint.lint.utils import (
- _augment_sys_path,
- _patch_sys_path,
- augmented_sys_path,
- fix_import_path,
-)
+from pylint.lint.utils import _augment_sys_path, augmented_sys_path
__all__ = [
"check_parallel",
@@ -42,8 +37,6 @@ __all__ = [
"report_total_messages_stats",
"Run",
"ArgumentPreprocessingError",
- "_patch_sys_path",
- "fix_import_path",
"_augment_sys_path",
"augmented_sys_path",
"discover_package_path",
diff --git a/pylint/lint/base_options.py b/pylint/lint/base_options.py
index b7c81991a..ee811db4e 100644
--- a/pylint/lint/base_options.py
+++ b/pylint/lint/base_options.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Functions that creates the basic options for the Run and PyLinter classes."""
diff --git a/pylint/lint/caching.py b/pylint/lint/caching.py
index 8ea8a2236..97c4503d1 100644
--- a/pylint/lint/caching.py
+++ b/pylint/lint/caching.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -45,6 +45,7 @@ def load_results(
"You're using an old pylint cache with invalid data following "
f"an upgrade, please delete '{data_file}'.",
UserWarning,
+ stacklevel=2,
)
raise TypeError
return data
diff --git a/pylint/lint/expand_modules.py b/pylint/lint/expand_modules.py
index bb25986e4..1e8fd032f 100644
--- a/pylint/lint/expand_modules.py
+++ b/pylint/lint/expand_modules.py
@@ -1,12 +1,11 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import os
import sys
-import warnings
from collections.abc import Sequence
from re import Pattern
@@ -24,18 +23,6 @@ def _modpath_from_file(filename: str, is_namespace: bool, path: list[str]) -> li
)
-def get_python_path(filepath: str) -> str:
- # TODO: Remove deprecated function
- warnings.warn(
- "get_python_path has been deprecated because assumption that there's always an __init__.py "
- "is not true since python 3.3 and is causing problems, particularly with PEP 420."
- "Use discover_package_path and pass source root(s).",
- DeprecationWarning,
- stacklevel=2,
- )
- return discover_package_path(filepath, [])
-
-
def discover_package_path(modulepath: str, source_roots: Sequence[str]) -> str:
"""Discover package path from one its modules and source roots."""
dirname = os.path.realpath(os.path.expanduser(modulepath))
@@ -101,7 +88,7 @@ def expand_modules(
):
continue
module_package_path = discover_package_path(something, source_roots)
- additional_search_path = [".", module_package_path] + path
+ additional_search_path = [".", module_package_path, *path]
if os.path.exists(something):
# this is a file or a directory
try:
diff --git a/pylint/lint/message_state_handler.py b/pylint/lint/message_state_handler.py
index ddeeaa7bc..26028f0fa 100644
--- a/pylint/lint/message_state_handler.py
+++ b/pylint/lint/message_state_handler.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-import sys
import tokenize
from collections import defaultdict
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
from pylint import exceptions, interfaces
from pylint.constants import (
@@ -27,12 +26,6 @@ from pylint.utils.pragma_parser import (
parse_pragma,
)
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
-
if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter
@@ -55,9 +48,8 @@ class _MessageStateHandler:
"enable-msg": self._options_methods["enable"],
}
self._pragma_lineno: dict[str, int] = {}
- # TODO: 3.0: Update key type to str when current_name is always str
self._stashed_messages: defaultdict[
- tuple[str | None, str], list[tuple[str | None, str]]
+ tuple[str, str], list[tuple[str | None, str]]
] = defaultdict(list)
"""Some messages in the options (for --enable and --disable) are encountered
too early to warn about them.
diff --git a/pylint/lint/parallel.py b/pylint/lint/parallel.py
index 544a256d3..e57c7e8ab 100644
--- a/pylint/lint/parallel.py
+++ b/pylint/lint/parallel.py
@@ -1,11 +1,10 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import functools
-import warnings
from collections import defaultdict
from collections.abc import Iterable, Sequence
from typing import TYPE_CHECKING, Any
@@ -42,7 +41,7 @@ def _worker_initialize(
"""Function called to initialize a worker for a Process within a concurrent Pool.
:param linter: A linter-class (PyLinter) instance pickled with dill
- :param extra_packages_paths: Extra entries to be added to sys.path
+ :param extra_packages_paths: Extra entries to be added to `sys.path`
"""
global _worker_linter # pylint: disable=global-statement
_worker_linter = dill.loads(linter)
@@ -61,10 +60,9 @@ def _worker_check_single_file(
file_item: FileItem,
) -> tuple[
int,
- # TODO: 3.0: Make this only str after deprecation has been removed
- str | None,
str,
- str | None,
+ str,
+ str,
list[Message],
LinterStats,
int,
@@ -82,14 +80,6 @@ def _worker_check_single_file(
msgs = _worker_linter.reporter.messages
assert isinstance(_worker_linter.reporter, reporters.CollectingReporter)
_worker_linter.reporter.reset()
- if _worker_linter.current_name is None:
- warnings.warn(
- (
- "In pylint 3.0 the current_name attribute of the linter object should be a string. "
- "If unknown it should be initialized as an empty string."
- ),
- DeprecationWarning,
- )
return (
id(multiprocessing.current_process()),
_worker_linter.current_name,
@@ -175,4 +165,4 @@ def check_parallel(
linter.msg_status |= msg_status
_merge_mapreduce_data(linter, all_mapreduce_data)
- linter.stats = merge_stats([linter.stats] + all_stats)
+ linter.stats = merge_stats([linter.stats, *all_stats])
diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py
index 863076f8f..ed607aca5 100644
--- a/pylint/lint/pylinter.py
+++ b/pylint/lint/pylinter.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -12,14 +12,13 @@ import os
import sys
import tokenize
import traceback
-import warnings
from collections import defaultdict
from collections.abc import Callable, Iterator, Sequence
from io import TextIOWrapper
from pathlib import Path
from re import Pattern
from types import ModuleType
-from typing import Any
+from typing import Any, Protocol
import astroid
from astroid import nodes
@@ -69,12 +68,6 @@ from pylint.typing import (
)
from pylint.utils import ASTWalker, FileState, LinterStats, utils
-if sys.version_info >= (3, 8):
- from typing import Protocol
-else:
- from typing_extensions import Protocol
-
-
MANAGER = astroid.MANAGER
@@ -139,26 +132,38 @@ MSGS: dict[str, MessageDefinitionTuple] = {
"raw-checker-failed",
"Used to inform that a built-in module has not been checked "
"using the raw checkers.",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0010": (
"Unable to consider inline option %r",
"bad-inline-option",
"Used when an inline option is either badly formatted or can't "
"be used inside modules.",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0011": (
"Locally disabling %s (%s)",
"locally-disabled",
"Used when an inline option disables a message or a messages category.",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0013": (
"Ignoring entire file",
"file-ignored",
"Used to inform that the file will not be checked",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0020": (
"Suppressed %s (from line %d)",
@@ -167,14 +172,20 @@ MSGS: dict[str, MessageDefinitionTuple] = {
"by a disable= comment in the file. This message is not "
"generated for messages that are ignored due to configuration "
"settings.",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0021": (
"Useless suppression of %s",
"useless-suppression",
"Reported when a message is explicitly disabled for a line or "
"a block of code, but never triggered.",
- {"scope": WarningScope.LINE},
+ {
+ "scope": WarningScope.LINE,
+ "default_enabled": False,
+ },
),
"I0022": (
'Pragma "%s" is deprecated, use "%s" instead',
@@ -185,6 +196,7 @@ MSGS: dict[str, MessageDefinitionTuple] = {
{
"old_names": [("I0014", "deprecated-disable-all")],
"scope": WarningScope.LINE,
+ "default_enabled": False,
},
),
"E0001": (
@@ -311,7 +323,8 @@ class PyLinter(
self.options: Options = options + _make_linter_options(self)
for opt_group in option_groups:
self.option_groups_descs[opt_group[0]] = opt_group[1]
- self._option_groups: tuple[tuple[str, str], ...] = option_groups + (
+ self._option_groups: tuple[tuple[str, str], ...] = (
+ *option_groups,
("Messages control", "Options controlling analysis messages"),
("Reports", "Options related to output formatting and reporting"),
)
@@ -339,32 +352,13 @@ class PyLinter(
# Attributes related to visiting files
self.file_state = FileState("", self.msgs_store, is_base_filestate=True)
- self.current_name: str | None = None
+ self.current_name: str = ""
self.current_file: str | None = None
self._ignore_file = False
self._ignore_paths: list[Pattern[str]] = []
self.register_checker(self)
- @property
- def option_groups(self) -> tuple[tuple[str, str], ...]:
- # TODO: 3.0: Remove deprecated attribute
- warnings.warn(
- "The option_groups attribute has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- return self._option_groups
-
- @option_groups.setter
- def option_groups(self, value: tuple[tuple[str, str], ...]) -> None:
- warnings.warn(
- "The option_groups attribute has been deprecated and will be removed in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- self._option_groups = value
-
def load_default_plugins(self) -> None:
checkers.initialize(self)
reporters.initialize(self)
@@ -655,23 +649,12 @@ class PyLinter(
else:
yield something
- def check(self, files_or_modules: Sequence[str] | str) -> None:
+ def check(self, files_or_modules: Sequence[str]) -> None:
"""Main checking entry: check a list of files or modules from their name.
files_or_modules is either a string or list of strings presenting modules to check.
"""
- # 1) Initialize
self.initialize()
-
- # 2) Gather all files
- if not isinstance(files_or_modules, (list, tuple)):
- # TODO: 3.0: Remove deprecated typing and update docstring
- warnings.warn(
- "In pylint 3.0, the checkers check function will only accept sequence of string",
- DeprecationWarning,
- stacklevel=2,
- )
- files_or_modules = (files_or_modules,) # type: ignore[assignment]
if self.config.recursive:
files_or_modules = tuple(self._discover_files(files_or_modules))
if self.config.from_stdin:
@@ -687,7 +670,7 @@ class PyLinter(
}
)
- # TODO: Move the parallel invocation into step 5 of the checking process
+ # TODO: Move the parallel invocation into step 3 of the checking process
if not self.config.from_stdin and self.config.jobs > 1:
original_sys_path = sys.path[:]
check_parallel(
@@ -699,7 +682,7 @@ class PyLinter(
sys.path = original_sys_path
return
- # 3) Get all FileItems
+ # 1) Get all FileItems
with augmented_sys_path(extra_packages_paths):
if self.config.from_stdin:
fileitems = self._get_file_descr_from_stdin(files_or_modules[0])
@@ -711,10 +694,10 @@ class PyLinter(
# The contextmanager also opens all checkers and sets up the PyLinter class
with augmented_sys_path(extra_packages_paths):
with self._astroid_module_checker() as check_astroid_module:
- # 4) Get the AST for each FileItem
+ # 2) Get the AST for each FileItem
ast_per_fileitem = self._get_asts(fileitems, data)
- # 5) Lint each ast
+ # 3) Lint each ast
self._lint_files(ast_per_fileitem, check_astroid_module)
def _get_asts(
@@ -743,15 +726,6 @@ class PyLinter(
return ast_per_fileitem
- def check_single_file(self, name: str, filepath: str, modname: str) -> None:
- warnings.warn(
- "In pylint 3.0, the checkers check_single_file function will be removed. "
- "Use check_single_file_item instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- self.check_single_file_item(FileItem(name, filepath, modname))
-
def check_single_file_item(self, file: FileItem) -> None:
"""Check single file item.
@@ -919,26 +893,13 @@ class PyLinter(
self.add_message(key, args=message)
return result
- def set_current_module(
- self, modname: str | None, filepath: str | None = None
- ) -> None:
+ def set_current_module(self, modname: str, filepath: str | None = None) -> None:
"""Set the name of the currently analyzed module and
init statistics for it.
"""
if not modname and filepath is None:
return
self.reporter.on_set_current_module(modname or "", filepath)
- if modname is None:
- # TODO: 3.0: Remove all modname or ""'s in this method
- warnings.warn(
- (
- "In pylint 3.0 modname should be a string so that it can be used to "
- "correctly set the current_name attribute of the linter instance. "
- "If unknown it should be initialized as an empty string."
- ),
- DeprecationWarning,
- stacklevel=2,
- )
self.current_name = modname
self.current_file = filepath or modname
self.stats.init_single_module(modname or "")
@@ -976,41 +937,9 @@ class PyLinter(
tokencheckers = [
c for c in _checkers if isinstance(c, checkers.BaseTokenChecker)
]
- # TODO: 3.0: Remove deprecated for-loop
- for c in _checkers:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- if (
- interfaces.implements(c, interfaces.ITokenChecker)
- and c not in tokencheckers
- and c is not self
- ):
- tokencheckers.append(c) # type: ignore[arg-type] # pragma: no cover
- warnings.warn( # pragma: no cover
- "Checkers should subclass BaseTokenChecker "
- "instead of using the __implements__ mechanism. Use of __implements__ "
- "will no longer be supported in pylint 3.0",
- DeprecationWarning,
- )
rawcheckers = [
c for c in _checkers if isinstance(c, checkers.BaseRawFileChecker)
]
- # TODO: 3.0: Remove deprecated if-statement
- for c in _checkers:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- if (
- interfaces.implements(c, interfaces.IRawChecker)
- and c not in rawcheckers
- ):
- rawcheckers.append(c) # type: ignore[arg-type] # pragma: no cover
- warnings.warn( # pragma: no cover
- "Checkers should subclass BaseRawFileChecker "
- "instead of using the __implements__ mechanism. Use of __implements__ "
- "will no longer be supported in pylint 3.0",
- DeprecationWarning,
- )
- # notify global begin
for checker in _checkers:
checker.open()
walker.add_checker(checker)
@@ -1085,10 +1014,6 @@ class PyLinter(
retval = self._check_astroid_module(
ast_node, walker, rawcheckers, tokencheckers
)
-
- # TODO: 3.0: Remove unnecessary assertion
- assert self.current_name
-
self.stats.by_module[self.current_name]["statement"] = (
walker.nbstatements - before_check_statements
)
@@ -1154,12 +1079,7 @@ class PyLinter(
"""
# Display whatever messages are left on the reporter.
self.reporter.display_messages(report_nodes.Section())
-
- # TODO: 3.0: Remove second half of if-statement
- if (
- not self.file_state._is_base_filestate
- and self.file_state.base_name is not None
- ):
+ if not self.file_state._is_base_filestate:
# load previous results if any
previous_stats = load_results(self.file_state.base_name)
self.reporter.on_close(self.stats, previous_stats)
@@ -1181,11 +1101,9 @@ class PyLinter(
def _report_evaluation(self) -> int | None:
"""Make the global evaluation report."""
- # check with at least check 1 statements (usually 0 when there is a
+ # check with at least a statement (usually 0 when there is a
# syntax error preventing pylint from further processing)
note = None
- # TODO: 3.0: Remove assertion
- assert self.file_state.base_name is not None
previous_stats = load_results(self.file_state.base_name)
if self.stats.statement == 0:
return note
@@ -1270,12 +1188,7 @@ class PyLinter(
msg_cat = MSG_TYPES[message_definition.msgid[0]]
self.msg_status |= MSG_TYPES_STATUS[message_definition.msgid[0]]
self.stats.increase_single_message_count(msg_cat, 1)
- # TODO: 3.0 Should be removable after https://github.com/PyCQA/pylint/pull/5580
- self.stats.increase_single_module_message_count(
- self.current_name, # type: ignore[arg-type]
- msg_cat,
- 1,
- )
+ self.stats.increase_single_module_message_count(self.current_name, msg_cat, 1)
try:
self.stats.by_msg[message_definition.symbol] += 1
except KeyError:
diff --git a/pylint/lint/report_functions.py b/pylint/lint/report_functions.py
index 7d1674977..da7ab5fbc 100644
--- a/pylint/lint/report_functions.py
+++ b/pylint/lint/report_functions.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/lint/run.py b/pylint/lint/run.py
index 49b807f87..776803f2e 100644
--- a/pylint/lint/run.py
+++ b/pylint/lint/run.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -9,7 +9,7 @@ import sys
import warnings
from collections.abc import Sequence
from pathlib import Path
-from typing import Any, ClassVar
+from typing import ClassVar
from pylint import config
from pylint.checkers.utils import clear_lru_caches
@@ -97,9 +97,6 @@ def _cpu_count() -> int:
return cpu_count
-UNUSED_PARAM_SENTINEL = object()
-
-
class Run:
"""Helper class to use as main for pylint with 'run(*sys.argv[1:])'."""
@@ -123,7 +120,6 @@ group are mutually exclusive.",
args: Sequence[str],
reporter: BaseReporter | None = None,
exit: bool = True, # pylint: disable=redefined-builtin
- do_exit: Any = UNUSED_PARAM_SENTINEL,
) -> None:
# Immediately exit if user asks for version
if "--version" in args:
@@ -158,9 +154,6 @@ group are mutually exclusive.",
# load command line plugins
linter.load_plugin_modules(self._plugins)
- linter.disable("I")
- linter.enable("c-extension-no-member")
-
# Register the options needed for 'pylint-config'
# By not registering them by default they don't show up in the normal usage message
if self._is_pylint_config:
@@ -175,6 +168,7 @@ group are mutually exclusive.",
warnings.warn(
"NOTE: The 'pylint-config' command is experimental and usage can change",
UserWarning,
+ stacklevel=2,
)
code = _handle_pylint_config_commands(linter)
if exit:
@@ -214,14 +208,6 @@ group are mutually exclusive.",
else:
linter.check(args)
score_value = linter.generate_reports()
-
- if do_exit is not UNUSED_PARAM_SENTINEL:
- warnings.warn(
- "do_exit is deprecated and it is going to be removed in a future version.",
- DeprecationWarning,
- )
- exit = do_exit
-
if linter.config.clear_cache_post_run:
clear_lru_caches()
MANAGER.clear_cache()
diff --git a/pylint/lint/utils.py b/pylint/lint/utils.py
index 98fb8087a..a7fbfd0bc 100644
--- a/pylint/lint/utils.py
+++ b/pylint/lint/utils.py
@@ -1,19 +1,18 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import contextlib
+import platform
import sys
import traceback
-import warnings
from collections.abc import Iterator, Sequence
from datetime import datetime
from pathlib import Path
-from pylint.config import PYLINT_HOME
-from pylint.lint.expand_modules import discover_package_path
+from pylint.constants import PYLINT_HOME, full_version
def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> Path:
@@ -26,18 +25,20 @@ def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) ->
if not issue_template_path.exists():
template = """\
First, please verify that the bug is not already filled:
-https://github.com/PyCQA/pylint/issues/
+https://github.com/pylint-dev/pylint/issues/
-Then create a new crash issue:
-https://github.com/PyCQA/pylint/issues/new?assignees=&labels=crash%2Cneeds+triage&template=BUG-REPORT.yml
+Then create a new issue:
+https://github.com/pylint-dev/pylint/issues/new?labels=Crash 💥%2CNeeds triage 📥
-"""
- template += f"""\
+"""
+ template += f"""
Issue title:
Crash ``{ex}`` (if possible, be more specific about what made pylint crash)
-Content:
-When parsing the following file:
+
+### Bug description
+
+When parsing the following ``a.py``:
<!--
If sharing the code is not an option, please state so,
@@ -48,11 +49,49 @@ When parsing the following file:
{file_content}
```
-pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace:
+### Command used
+
+```shell
+pylint a.py
```
+
+### Pylint output
+
+<details open>
+ <summary>
+ pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace:
+ </summary>
+
+```python
"""
template += traceback.format_exc()
- template += "```\n"
+ template += f"""
+```
+
+
+</details>
+
+### Expected behavior
+
+No crash.
+
+### Pylint version
+
+```shell
+{full_version}
+```
+
+### OS / Environment
+
+{sys.platform} ({platform.system()})
+
+### Additional dependencies
+
+<!--
+Please remove this part if you're not using any of
+your dependencies in the example.
+ -->
+"""
try:
with open(issue_template_path, "a", encoding="utf8") as f:
f.write(template)
@@ -73,19 +112,6 @@ def get_fatal_error_message(filepath: str, issue_template_path: Path) -> str:
)
-def _patch_sys_path(args: Sequence[str]) -> list[str]:
- # TODO: Remove deprecated function
- warnings.warn(
- "_patch_sys_path has been deprecated because it relies on auto-magic package path "
- "discovery which is implemented by get_python_path that is deprecated. "
- "Use _augment_sys_path and pass additional sys.path entries as an argument obtained from "
- "discover_package_path.",
- DeprecationWarning,
- stacklevel=2,
- )
- return _augment_sys_path([discover_package_path(arg, []) for arg in args])
-
-
def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]:
original = list(sys.path)
changes = []
@@ -100,28 +126,6 @@ def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]:
@contextlib.contextmanager
-def fix_import_path(args: Sequence[str]) -> Iterator[None]:
- """Prepare 'sys.path' for running the linter checks.
-
- Within this context, each of the given arguments is importable.
- Paths are added to 'sys.path' in corresponding order to the arguments.
- We avoid adding duplicate directories to sys.path.
- `sys.path` is reset to its original value upon exiting this context.
- """
- # TODO: Remove deprecated function
- warnings.warn(
- "fix_import_path has been deprecated because it relies on auto-magic package path "
- "discovery which is implemented by get_python_path that is deprecated. "
- "Use augmented_sys_path and pass additional sys.path entries as an argument obtained from "
- "discover_package_path.",
- DeprecationWarning,
- stacklevel=2,
- )
- with augmented_sys_path([discover_package_path(arg, []) for arg in args]):
- yield
-
-
-@contextlib.contextmanager
def augmented_sys_path(additional_paths: Sequence[str]) -> Iterator[None]:
"""Augment 'sys.path' by adding non-existent entries from additional_paths."""
original = _augment_sys_path(additional_paths)
diff --git a/pylint/message/__init__.py b/pylint/message/__init__.py
index 11d2b17dc..6fa8e44b7 100644
--- a/pylint/message/__init__.py
+++ b/pylint/message/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""All the classes related to Message handling."""
diff --git a/pylint/message/_deleted_message_ids.py b/pylint/message/_deleted_message_ids.py
index 4f361c96a..60289e805 100644
--- a/pylint/message/_deleted_message_ids.py
+++ b/pylint/message/_deleted_message_ids.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -18,7 +18,7 @@ DELETED_MSGID_PREFIXES: list[int] = []
DELETED_MESSAGES_IDS = {
# Everything until the next comment is from the PY3K+ checker
- "https://github.com/PyCQA/pylint/pull/4942": [
+ "https://github.com/pylint-dev/pylint/pull/4942": [
DeletedMessage("W1601", "apply-builtin"),
DeletedMessage("E1601", "print-statement"),
DeletedMessage("E1602", "parameter-unpacking"),
@@ -95,10 +95,10 @@ DELETED_MESSAGES_IDS = {
DeletedMessage("W1661", "exception-escape"),
DeletedMessage("W1662", "comprehension-escape"),
],
- "https://github.com/PyCQA/pylint/pull/3578": [
+ "https://github.com/pylint-dev/pylint/pull/3578": [
DeletedMessage("W0312", "mixed-indentation"),
],
- "https://github.com/PyCQA/pylint/pull/3577": [
+ "https://github.com/pylint-dev/pylint/pull/3577": [
DeletedMessage(
"C0326",
"bad-whitespace",
@@ -109,7 +109,7 @@ DELETED_MESSAGES_IDS = {
],
),
],
- "https://github.com/PyCQA/pylint/pull/3571": [
+ "https://github.com/pylint-dev/pylint/pull/3571": [
DeletedMessage("C0330", "bad-continuation")
],
"https://pylint.readthedocs.io/en/latest/whatsnew/1/1.4.html#what-s-new-in-pylint-1-4-3": [
@@ -117,10 +117,10 @@ DELETED_MESSAGES_IDS = {
DeletedMessage("R0922", "abstract-class-little-used"),
DeletedMessage("W0142", "star-args"),
],
- "https://github.com/PyCQA/pylint/issues/2409": [
+ "https://github.com/pylint-dev/pylint/issues/2409": [
DeletedMessage("W0232", "no-init"),
],
- "https://github.com/PyCQA/pylint/pull/6421": [
+ "https://github.com/pylint-dev/pylint/pull/6421": [
DeletedMessage("W0111", "assign-to-new-keyword"),
],
}
diff --git a/pylint/message/message.py b/pylint/message/message.py
index 23dd6c082..6ee8c5f78 100644
--- a/pylint/message/message.py
+++ b/pylint/message/message.py
@@ -1,11 +1,10 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
from dataclasses import asdict, dataclass
-from warnings import warn
from pylint.constants import MSG_TYPES
from pylint.interfaces import UNDEFINED, Confidence
@@ -35,27 +34,10 @@ class Message: # pylint: disable=too-many-instance-attributes
self,
msg_id: str,
symbol: str,
- location: tuple[str, str, str, str, int, int] | MessageLocationTuple,
+ location: MessageLocationTuple,
msg: str,
confidence: Confidence | None,
) -> None:
- if not isinstance(location, MessageLocationTuple):
- warn(
- "In pylint 3.0, Messages will only accept a MessageLocationTuple as location parameter",
- DeprecationWarning,
- stacklevel=2,
- )
- location = MessageLocationTuple(
- location[0],
- location[1],
- location[2],
- location[3],
- location[4],
- location[5],
- None,
- None,
- )
-
self.msg_id = msg_id
self.symbol = symbol
self.msg = msg
diff --git a/pylint/message/message_definition.py b/pylint/message/message_definition.py
index 25aa87d92..29a8780cb 100644
--- a/pylint/message/message_definition.py
+++ b/pylint/message/message_definition.py
@@ -1,11 +1,10 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import sys
-import warnings
from typing import TYPE_CHECKING, Any
from astroid import nodes
@@ -73,21 +72,8 @@ class MessageDefinition:
def __str__(self) -> str:
return f"{repr(self)}:\n{self.msg} {self.description}"
- def may_be_emitted(
- self,
- py_version: tuple[int, ...] | sys._version_info | None = None,
- ) -> bool:
- """Return True if message may be emitted using the configured py_version."""
- if py_version is None:
- py_version = sys.version_info
- warnings.warn(
- "'py_version' will be a required parameter of "
- "'MessageDefinition.may_be_emitted' in pylint 3.0. The most likely "
- "solution is to use 'linter.config.py_version' if you need to keep "
- "using this function, or to use 'MessageDefinition.is_message_enabled'"
- " instead.",
- DeprecationWarning,
- )
+ def may_be_emitted(self, py_version: tuple[int, ...] | sys._version_info) -> bool:
+ """May the message be emitted using the configured py_version?"""
if self.minversion is not None and self.minversion > py_version:
return False
if self.maxversion is not None and self.maxversion <= py_version:
diff --git a/pylint/message/message_definition_store.py b/pylint/message/message_definition_store.py
index 7bbc70a51..4921be212 100644
--- a/pylint/message/message_definition_store.py
+++ b/pylint/message/message_definition_store.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -58,7 +58,7 @@ class MessageDefinitionStore:
# Since MessageDefinitionStore is only initialized once
# and the arguments are relatively small we do not run the
# risk of creating a large memory leak.
- # See discussion in: https://github.com/PyCQA/pylint/pull/5673
+ # See discussion in: https://github.com/pylint-dev/pylint/pull/5673
@functools.lru_cache( # pylint: disable=method-cache-max-size-none # noqa: B019
maxsize=None
)
diff --git a/pylint/message/message_id_store.py b/pylint/message/message_id_store.py
index d1810bd2b..273522d3d 100644
--- a/pylint/message/message_id_store.py
+++ b/pylint/message/message_id_store.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/py.typed b/pylint/py.typed
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/pylint/py.typed
diff --git a/pylint/pyreverse/__init__.py b/pylint/pyreverse/__init__.py
index 458c0f35d..175e9cb67 100644
--- a/pylint/pyreverse/__init__.py
+++ b/pylint/pyreverse/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Pyreverse.extensions."""
diff --git a/pylint/pyreverse/diadefslib.py b/pylint/pyreverse/diadefslib.py
index 85b23052e..3b7694823 100644
--- a/pylint/pyreverse/diadefslib.py
+++ b/pylint/pyreverse/diadefslib.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Handle diagram generation options for class diagram or default diagrams."""
@@ -12,6 +12,7 @@ from typing import Any
import astroid
from astroid import nodes
+from astroid.modutils import is_stdlib_module
from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram
from pylint.pyreverse.inspector import Linker, Project
@@ -67,10 +68,14 @@ class DiaDefGenerator:
return self.anc_level, self.association_level
def show_node(self, node: nodes.ClassDef) -> bool:
- """True if builtins and not show_builtins."""
- if self.config.show_builtin:
- return True
- return node.root().name != "builtins" # type: ignore[no-any-return]
+ """Determine if node should be shown based on config."""
+ if node.root().name == "builtins":
+ return self.config.show_builtin # type: ignore[no-any-return]
+
+ if is_stdlib_module(node.root().name):
+ return self.config.show_stdlib # type: ignore[no-any-return]
+
+ return True
def add_class(self, node: nodes.ClassDef) -> None:
"""Visit one class and add it to diagram."""
diff --git a/pylint/pyreverse/diagrams.py b/pylint/pyreverse/diagrams.py
index 4437d3c4e..01bce7dc3 100644
--- a/pylint/pyreverse/diagrams.py
+++ b/pylint/pyreverse/diagrams.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Diagram objects."""
@@ -13,7 +13,7 @@ import astroid
from astroid import nodes, util
from pylint.checkers.utils import decorated_with_property
-from pylint.pyreverse.utils import FilterMixIn, is_interface
+from pylint.pyreverse.utils import FilterMixIn
class Figure:
@@ -50,7 +50,13 @@ class DiagramEntity(Figure):
) -> None:
super().__init__()
self.title = title
- self.node: nodes.NodeNG = node if node else nodes.NodeNG()
+ self.node: nodes.NodeNG = node or nodes.NodeNG(
+ lineno=None,
+ col_offset=None,
+ end_lineno=None,
+ end_col_offset=None,
+ parent=None,
+ )
self.shape = self.default_shape
@@ -195,11 +201,7 @@ class ClassDiagram(Figure, FilterMixIn):
node = obj.node
obj.attrs = self.get_attrs(node)
obj.methods = self.get_methods(node)
- # shape
- if is_interface(node):
- obj.shape = "interface"
- else:
- obj.shape = "class"
+ obj.shape = "class"
# inheritance link
for par_node in node.ancestors(recurs=False):
try:
@@ -207,13 +209,6 @@ class ClassDiagram(Figure, FilterMixIn):
self.add_relationship(obj, par_obj, "specialization")
except KeyError:
continue
- # implements link
- for impl_node in node.implements:
- try:
- impl_obj = self.object_from_node(impl_node)
- self.add_relationship(obj, impl_obj, "implements")
- except KeyError:
- continue
# associations & aggregations links
for name, values in list(node.aggregations_type.items()):
diff --git a/pylint/pyreverse/dot_printer.py b/pylint/pyreverse/dot_printer.py
index 99cb17e97..edaea2384 100644
--- a/pylint/pyreverse/dot_printer.py
+++ b/pylint/pyreverse/dot_printer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in dot format and image formats supported by Graphviz."""
@@ -25,13 +25,11 @@ class HTMLLabels(Enum):
ALLOWED_CHARSETS: frozenset[str] = frozenset(("utf-8", "iso-8859-1", "latin1"))
SHAPES: dict[NodeType, str] = {
NodeType.PACKAGE: "box",
- NodeType.INTERFACE: "record",
NodeType.CLASS: "record",
}
# pylint: disable-next=consider-using-namedtuple-or-dataclass
ARROWS: dict[EdgeType, dict[str, str]] = {
EdgeType.INHERITS: {"arrowtail": "none", "arrowhead": "empty"},
- EdgeType.IMPLEMENTS: {"arrowtail": "node", "arrowhead": "empty", "style": "dashed"},
EdgeType.ASSOCIATION: {
"fontcolor": "green",
"arrowtail": "none",
diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py
index 523ff8171..0cabe9473 100644
--- a/pylint/pyreverse/inspector.py
+++ b/pylint/pyreverse/inspector.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Visitor doing some post-processing on the astroid tree.
@@ -12,13 +12,11 @@ from __future__ import annotations
import collections
import os
import traceback
-import warnings
from abc import ABC, abstractmethod
-from collections.abc import Generator
-from typing import Any, Callable, Optional
+from typing import Callable, Optional
import astroid
-from astroid import nodes, util
+from astroid import nodes
from pylint import constants
from pylint.pyreverse import utils
@@ -39,27 +37,6 @@ def _astroid_wrapper(
return None
-def interfaces(node: nodes.ClassDef) -> Generator[Any, None, None]:
- """Return an iterator on interfaces implemented by the given class node."""
- try:
- implements = astroid.bases.Instance(node).getattr("__implements__")[0]
- except astroid.exceptions.NotFoundError:
- return
- if implements.frame(future=True) is not node:
- return
- found = set()
- missing = False
- for iface in nodes.unpack_infer(implements):
- if isinstance(iface, util.UninferableBase):
- missing = True
- continue
- if iface not in found:
- found.add(iface)
- yield iface
- if missing:
- raise astroid.exceptions.InferenceError()
-
-
class IdGeneratorMixIn:
"""Mixin adding the ability to generate integer uid."""
@@ -129,9 +106,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
* aggregations_type
as instance_attrs_type but for aggregations relationships
-
- * implements,
- list of implemented interface _objects_ (only on astroid.Class nodes)
"""
def __init__(self, project: Project, tag: bool = False) -> None:
@@ -172,7 +146,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
"""Visit an astroid.Class node.
* set the locals_type and instance_attrs_type mappings
- * set the implements list and build it
* optionally tag the node with a unique id
"""
if hasattr(node, "locals_type"):
@@ -194,24 +167,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
if not isinstance(assignattr, nodes.Unknown):
self.associations_handler.handle(assignattr, node)
self.handle_assignattr_type(assignattr, node)
- # resolve implemented interface
- try:
- ifaces = interfaces(node)
- if ifaces is not None:
- node.implements = list(ifaces)
- if node.implements:
- # TODO: 3.0: Remove support for __implements__
- warnings.warn(
- "pyreverse will drop support for resolving and displaying "
- "implemented interfaces in pylint 3.0. The implementation "
- "relies on the '__implements__' attribute proposed in PEP 245"
- ", which was rejected in 2006.",
- DeprecationWarning,
- )
- else:
- node.implements = []
- except astroid.InferenceError:
- node.implements = []
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
"""Visit an astroid.Function node.
diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py
index a2dc94734..58128bb57 100644
--- a/pylint/pyreverse/main.py
+++ b/pylint/pyreverse/main.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Create UML diagrams for classes and modules in <packages>."""
@@ -27,7 +27,6 @@ from pylint.typing import Options
DIRECTLY_SUPPORTED_FORMATS = (
"dot",
- "vcg",
"puml",
"plantuml",
"mmd",
@@ -35,23 +34,16 @@ DIRECTLY_SUPPORTED_FORMATS = (
)
DEFAULT_COLOR_PALETTE = (
- "aliceblue",
- "antiquewhite",
- "aquamarine",
- "burlywood",
- "cadetblue",
- "chartreuse",
- "chocolate",
- "coral",
- "cornflowerblue",
- "cyan",
- "darkgoldenrod",
- "darkseagreen",
- "dodgerblue",
- "forestgreen",
- "gold",
- "hotpink",
- "mediumspringgreen",
+ # colorblind scheme taken from https://personal.sron.nl/~pault/
+ "#77AADD", # light blue
+ "#99DDFF", # light cyan
+ "#44BB99", # mint
+ "#BBCC33", # pear
+ "#AAAA00", # olive
+ "#EEDD88", # light yellow
+ "#EE8866", # orange
+ "#FFAABB", # pink
+ "#DDDDDD", # pale grey
)
OPTIONS: Options = (
@@ -138,6 +130,15 @@ OPTIONS: Options = (
},
),
(
+ "show-stdlib",
+ {
+ "short": "L",
+ "action": "store_true",
+ "default": False,
+ "help": "include standard library objects in representation of classes",
+ },
+ ),
+ (
"module-names",
{
"short": "m",
@@ -157,6 +158,14 @@ OPTIONS: Options = (
},
),
(
+ "no-standalone",
+ {
+ "action": "store_true",
+ "default": False,
+ "help": "only show nodes with connections",
+ },
+ ),
+ (
"output",
{
"short": "o",
diff --git a/pylint/pyreverse/mermaidjs_printer.py b/pylint/pyreverse/mermaidjs_printer.py
index a8f3c576b..61d0d7948 100644
--- a/pylint/pyreverse/mermaidjs_printer.py
+++ b/pylint/pyreverse/mermaidjs_printer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in mermaidjs format."""
@@ -17,12 +17,10 @@ class MermaidJSPrinter(Printer):
NODES: dict[NodeType, str] = {
NodeType.CLASS: "class",
- NodeType.INTERFACE: "class",
NodeType.PACKAGE: "class",
}
ARROWS: dict[EdgeType, str] = {
EdgeType.INHERITS: "--|>",
- EdgeType.IMPLEMENTS: "..|>",
EdgeType.ASSOCIATION: "--*",
EdgeType.AGGREGATION: "--o",
EdgeType.USES: "-->",
@@ -46,7 +44,6 @@ class MermaidJSPrinter(Printer):
# pylint: disable=duplicate-code
if properties is None:
properties = NodeProperties(label=name)
- stereotype = "~~Interface~~" if type_ is NodeType.INTERFACE else ""
nodetype = self.NODES[type_]
body = []
if properties.attrs:
@@ -60,7 +57,7 @@ class MermaidJSPrinter(Printer):
line += f" {get_annotation_label(func.returns)}"
body.append(line)
name = name.split(".")[-1]
- self.emit(f"{nodetype} {name}{stereotype} {{")
+ self.emit(f"{nodetype} {name} {{")
self._inc_indent()
for line in body:
self.emit(line)
diff --git a/pylint/pyreverse/plantuml_printer.py b/pylint/pyreverse/plantuml_printer.py
index de3f983b7..5f703b62e 100644
--- a/pylint/pyreverse/plantuml_printer.py
+++ b/pylint/pyreverse/plantuml_printer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in dot format and image formats supported by Graphviz."""
@@ -17,12 +17,10 @@ class PlantUmlPrinter(Printer):
NODES: dict[NodeType, str] = {
NodeType.CLASS: "class",
- NodeType.INTERFACE: "class",
NodeType.PACKAGE: "package",
}
ARROWS: dict[EdgeType, str] = {
EdgeType.INHERITS: "--|>",
- EdgeType.IMPLEMENTS: "..|>",
EdgeType.ASSOCIATION: "--*",
EdgeType.AGGREGATION: "--o",
EdgeType.USES: "-->",
@@ -56,10 +54,9 @@ class PlantUmlPrinter(Printer):
"""
if properties is None:
properties = NodeProperties(label=name)
- stereotype = " << interface >>" if type_ is NodeType.INTERFACE else ""
nodetype = self.NODES[type_]
if properties.color and properties.color != self.DEFAULT_COLOR:
- color = f" #{properties.color}"
+ color = f" #{properties.color.lstrip('#')}"
else:
color = ""
body = []
@@ -76,7 +73,7 @@ class PlantUmlPrinter(Printer):
label = properties.label if properties.label is not None else name
if properties.fontcolor and properties.fontcolor != self.DEFAULT_COLOR:
label = f"<color:{properties.fontcolor}>{label}</color>"
- self.emit(f'{nodetype} "{label}" as {name}{stereotype}{color} {{')
+ self.emit(f'{nodetype} "{label}" as {name}{color} {{')
self._inc_indent()
for line in body:
self.emit(line)
diff --git a/pylint/pyreverse/printer.py b/pylint/pyreverse/printer.py
index cdbf7e3c8..f08c74602 100644
--- a/pylint/pyreverse/printer.py
+++ b/pylint/pyreverse/printer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Base class defining the interface for a printer."""
@@ -17,13 +17,11 @@ from pylint.pyreverse.utils import get_annotation_label
class NodeType(Enum):
CLASS = "class"
- INTERFACE = "interface"
PACKAGE = "package"
class EdgeType(Enum):
INHERITS = "inherits"
- IMPLEMENTS = "implements"
ASSOCIATION = "association"
AGGREGATION = "aggregation"
USES = "uses"
diff --git a/pylint/pyreverse/printer_factory.py b/pylint/pyreverse/printer_factory.py
index 41e8b46c8..fdbe480ed 100644
--- a/pylint/pyreverse/printer_factory.py
+++ b/pylint/pyreverse/printer_factory.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -8,10 +8,8 @@ from pylint.pyreverse.dot_printer import DotPrinter
from pylint.pyreverse.mermaidjs_printer import HTMLMermaidJSPrinter, MermaidJSPrinter
from pylint.pyreverse.plantuml_printer import PlantUmlPrinter
from pylint.pyreverse.printer import Printer
-from pylint.pyreverse.vcg_printer import VCGPrinter
filetype_to_printer: dict[str, type[Printer]] = {
- "vcg": VCGPrinter,
"plantuml": PlantUmlPrinter,
"puml": PlantUmlPrinter,
"mmd": MermaidJSPrinter,
diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py
index 078bc1b7e..6294773b2 100644
--- a/pylint/pyreverse/utils.py
+++ b/pylint/pyreverse/utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Generic classes/functions for pyreverse core/extensions."""
@@ -72,11 +72,6 @@ def get_visibility(name: str) -> str:
return visibility
-def is_interface(node: nodes.ClassDef) -> bool:
- # bw compatibility
- return node.type == "interface" # type: ignore[no-any-return]
-
-
def is_exception(node: nodes.ClassDef) -> bool:
# bw compatibility
return node.type == "exception" # type: ignore[no-any-return]
diff --git a/pylint/pyreverse/vcg_printer.py b/pylint/pyreverse/vcg_printer.py
deleted file mode 100644
index b9e2e94f3..000000000
--- a/pylint/pyreverse/vcg_printer.py
+++ /dev/null
@@ -1,303 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Functions to generate files readable with George Sander's vcg
-(Visualization of Compiler Graphs).
-
-You can download vcg at https://rw4.cs.uni-sb.de/~sander/html/gshome.html
-Note that vcg exists as a debian package.
-See vcg's documentation for explanation about the different values that
-maybe used for the functions parameters.
-"""
-
-from __future__ import annotations
-
-from collections.abc import Mapping
-from typing import Any
-
-from pylint.pyreverse.printer import EdgeType, Layout, NodeProperties, NodeType, Printer
-
-ATTRS_VAL = {
- "algos": (
- "dfs",
- "tree",
- "minbackward",
- "left_to_right",
- "right_to_left",
- "top_to_bottom",
- "bottom_to_top",
- "maxdepth",
- "maxdepthslow",
- "mindepth",
- "mindepthslow",
- "mindegree",
- "minindegree",
- "minoutdegree",
- "maxdegree",
- "maxindegree",
- "maxoutdegree",
- ),
- "booleans": ("yes", "no"),
- "colors": (
- "black",
- "white",
- "blue",
- "red",
- "green",
- "yellow",
- "magenta",
- "lightgrey",
- "cyan",
- "darkgrey",
- "darkblue",
- "darkred",
- "darkgreen",
- "darkyellow",
- "darkmagenta",
- "darkcyan",
- "gold",
- "lightblue",
- "lightred",
- "lightgreen",
- "lightyellow",
- "lightmagenta",
- "lightcyan",
- "lilac",
- "turquoise",
- "aquamarine",
- "khaki",
- "purple",
- "yellowgreen",
- "pink",
- "orange",
- "orchid",
- ),
- "shapes": ("box", "ellipse", "rhomb", "triangle"),
- "textmodes": ("center", "left_justify", "right_justify"),
- "arrowstyles": ("solid", "line", "none"),
- "linestyles": ("continuous", "dashed", "dotted", "invisible"),
-}
-
-# meaning of possible values:
-# O -> string
-# 1 -> int
-# list -> value in list
-GRAPH_ATTRS = {
- "title": 0,
- "label": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "bordercolor": ATTRS_VAL["colors"],
- "width": 1,
- "height": 1,
- "borderwidth": 1,
- "textmode": ATTRS_VAL["textmodes"],
- "shape": ATTRS_VAL["shapes"],
- "shrink": 1,
- "stretch": 1,
- "orientation": ATTRS_VAL["algos"],
- "vertical_order": 1,
- "horizontal_order": 1,
- "xspace": 1,
- "yspace": 1,
- "layoutalgorithm": ATTRS_VAL["algos"],
- "late_edge_labels": ATTRS_VAL["booleans"],
- "display_edge_labels": ATTRS_VAL["booleans"],
- "dirty_edge_labels": ATTRS_VAL["booleans"],
- "finetuning": ATTRS_VAL["booleans"],
- "manhattan_edges": ATTRS_VAL["booleans"],
- "smanhattan_edges": ATTRS_VAL["booleans"],
- "port_sharing": ATTRS_VAL["booleans"],
- "edges": ATTRS_VAL["booleans"],
- "nodes": ATTRS_VAL["booleans"],
- "splines": ATTRS_VAL["booleans"],
-}
-NODE_ATTRS = {
- "title": 0,
- "label": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "bordercolor": ATTRS_VAL["colors"],
- "width": 1,
- "height": 1,
- "borderwidth": 1,
- "textmode": ATTRS_VAL["textmodes"],
- "shape": ATTRS_VAL["shapes"],
- "shrink": 1,
- "stretch": 1,
- "vertical_order": 1,
- "horizontal_order": 1,
-}
-EDGE_ATTRS = {
- "sourcename": 0,
- "targetname": 0,
- "label": 0,
- "linestyle": ATTRS_VAL["linestyles"],
- "class": 1,
- "thickness": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "arrowcolor": ATTRS_VAL["colors"],
- "backarrowcolor": ATTRS_VAL["colors"],
- "arrowsize": 1,
- "backarrowsize": 1,
- "arrowstyle": ATTRS_VAL["arrowstyles"],
- "backarrowstyle": ATTRS_VAL["arrowstyles"],
- "textmode": ATTRS_VAL["textmodes"],
- "priority": 1,
- "anchor": 1,
- "horizontal_order": 1,
-}
-SHAPES: dict[NodeType, str] = {
- NodeType.PACKAGE: "box",
- NodeType.CLASS: "box",
- NodeType.INTERFACE: "ellipse",
-}
-# pylint: disable-next=consider-using-namedtuple-or-dataclass
-ARROWS: dict[EdgeType, dict[str, str | int]] = {
- EdgeType.USES: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "backarrowsize": 0,
- },
- EdgeType.INHERITS: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "backarrowsize": 10,
- },
- EdgeType.IMPLEMENTS: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "linestyle": "dotted",
- "backarrowsize": 10,
- },
- EdgeType.ASSOCIATION: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "textcolor": "green",
- },
- EdgeType.AGGREGATION: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "textcolor": "green",
- },
-}
-ORIENTATION: dict[Layout, str] = {
- Layout.LEFT_TO_RIGHT: "left_to_right",
- Layout.RIGHT_TO_LEFT: "right_to_left",
- Layout.TOP_TO_BOTTOM: "top_to_bottom",
- Layout.BOTTOM_TO_TOP: "bottom_to_top",
-}
-
-# Misc utilities ###############################################################
-
-
-class VCGPrinter(Printer):
- def _open_graph(self) -> None:
- """Emit the header lines."""
- self.emit("graph:{\n")
- self._inc_indent()
- self._write_attributes(
- GRAPH_ATTRS,
- title=self.title,
- layoutalgorithm="dfs",
- late_edge_labels="yes",
- port_sharing="no",
- manhattan_edges="yes",
- )
- if self.layout:
- self._write_attributes(GRAPH_ATTRS, orientation=ORIENTATION[self.layout])
-
- def _close_graph(self) -> None:
- """Emit the lines needed to properly close the graph."""
- self._dec_indent()
- self.emit("}")
-
- def emit_node(
- self,
- name: str,
- type_: NodeType,
- properties: NodeProperties | None = None,
- ) -> None:
- """Create a new node.
-
- Nodes can be classes, packages, participants etc.
- """
- if properties is None:
- properties = NodeProperties(label=name)
- elif properties.label is None:
- properties.label = name
- self.emit(f'node: {{title:"{name}"', force_newline=False)
- self._write_attributes(
- NODE_ATTRS,
- label=self._build_label_for_node(properties),
- shape=SHAPES[type_],
- )
- self.emit("}")
-
- @staticmethod
- def _build_label_for_node(properties: NodeProperties) -> str:
- fontcolor = "\f09" if properties.fontcolor == "red" else ""
- label = rf"\fb{fontcolor}{properties.label}\fn"
- if properties.attrs is None and properties.methods is None:
- # return a compact form which only displays the classname in a box
- return label
- attrs = properties.attrs or []
- methods = properties.methods or []
- method_names = [func.name for func in methods]
- # box width for UML like diagram
- maxlen = max(len(name) for name in [properties.label] + method_names + attrs)
- line = "_" * (maxlen + 2)
- label = rf"{label}\n\f{line}"
- for attr in attrs:
- label = rf"{label}\n\f08{attr}"
- if attrs:
- label = rf"{label}\n\f{line}"
- for func in method_names:
- label = rf"{label}\n\f10{func}()"
- return label
-
- def emit_edge(
- self,
- from_node: str,
- to_node: str,
- type_: EdgeType,
- label: str | None = None,
- ) -> None:
- """Create an edge from one node to another to display relationships."""
- self.emit(
- f'edge: {{sourcename:"{from_node}" targetname:"{to_node}"',
- force_newline=False,
- )
- attributes = ARROWS[type_]
- if label:
- attributes["label"] = label
- self._write_attributes(
- EDGE_ATTRS,
- **attributes,
- )
- self.emit("}")
-
- def _write_attributes(
- self, attributes_dict: Mapping[str, Any], **args: Any
- ) -> None:
- """Write graph, node or edge attributes."""
- for key, value in args.items():
- try:
- _type = attributes_dict[key]
- except KeyError as e:
- raise AttributeError(
- f"no such attribute {key}\npossible attributes are {attributes_dict.keys()}"
- ) from e
-
- if not _type:
- self.emit(f'{key}:"{value}"\n')
- elif _type == 1:
- self.emit(f"{key}:{int(value)}\n")
- elif value in _type:
- self.emit(f"{key}:{value}\n")
- else:
- raise ValueError(
- f"value {value} isn't correct for attribute {key} correct values are {type}"
- )
diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py
index d92f4e2e5..58e967115 100644
--- a/pylint/pyreverse/writer.py
+++ b/pylint/pyreverse/writer.py
@@ -1,8 +1,8 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
-"""Utilities for creating VCG and Dot diagrams."""
+"""Utilities for creating diagrams."""
from __future__ import annotations
@@ -57,6 +57,12 @@ class DiagramWriter:
# sorted to get predictable (hence testable) results
for module in sorted(diagram.modules(), key=lambda x: x.title):
module.fig_id = module.node.qname()
+ if self.config.no_standalone and not any(
+ module in (rel.from_object, rel.to_object)
+ for rel in diagram.get_relationships("depends")
+ ):
+ continue
+
self.printer.emit_node(
module.fig_id,
type_=NodeType.PACKAGE,
@@ -75,9 +81,17 @@ class DiagramWriter:
# sorted to get predictable (hence testable) results
for obj in sorted(diagram.objects, key=lambda x: x.title): # type: ignore[no-any-return]
obj.fig_id = obj.node.qname()
- type_ = NodeType.INTERFACE if obj.shape == "interface" else NodeType.CLASS
+ if self.config.no_standalone and not any(
+ obj in (rel.from_object, rel.to_object)
+ for rel_type in ("specialization", "association", "aggregation")
+ for rel in diagram.get_relationships(rel_type)
+ ):
+ continue
+
self.printer.emit_node(
- obj.fig_id, type_=type_, properties=self.get_class_properties(obj)
+ obj.fig_id,
+ type_=NodeType.CLASS,
+ properties=self.get_class_properties(obj),
)
# inheritance links
for rel in diagram.get_relationships("specialization"):
@@ -86,13 +100,6 @@ class DiagramWriter:
rel.to_object.fig_id,
type_=EdgeType.INHERITS,
)
- # implementation links
- for rel in diagram.get_relationships("implements"):
- self.printer.emit_edge(
- rel.from_object.fig_id,
- rel.to_object.fig_id,
- type_=EdgeType.IMPLEMENTS,
- )
# generate associations
for rel in diagram.get_relationships("association"):
self.printer.emit_edge(
diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py
index f22530de1..cf7b57576 100644
--- a/pylint/reporters/__init__.py
+++ b/pylint/reporters/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utilities methods and classes for reporters."""
diff --git a/pylint/reporters/base_reporter.py b/pylint/reporters/base_reporter.py
index 3df970d80..d370b1910 100644
--- a/pylint/reporters/base_reporter.py
+++ b/pylint/reporters/base_reporter.py
@@ -1,14 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import os
import sys
-import warnings
from typing import TYPE_CHECKING, TextIO
-from warnings import warn
from pylint.message import Message
from pylint.reporters.ureports.nodes import Text
@@ -31,13 +29,6 @@ class BaseReporter:
"""Name of the reporter."""
def __init__(self, output: TextIO | None = None) -> None:
- if getattr(self, "__implements__", None):
- warnings.warn(
- "Using the __implements__ inheritance pattern for BaseReporter is no "
- "longer supported. Child classes should only inherit BaseReporter",
- DeprecationWarning,
- stacklevel=2,
- )
self.linter: PyLinter
self.section = 0
self.out: TextIO = output or sys.stdout
@@ -49,16 +40,6 @@ class BaseReporter:
"""Handle a new message triggered on the current file."""
self.messages.append(msg)
- def set_output(self, output: TextIO | None = None) -> None:
- """Set output stream."""
- # TODO: 3.0: Remove deprecated method
- warn(
- "'set_output' will be removed in 3.0, please use 'reporter.out = stream' instead",
- DeprecationWarning,
- stacklevel=2,
- )
- self.out = output or sys.stdout
-
def writeln(self, string: str = "") -> None:
"""Write a line in the output buffer."""
print(string, file=self.out)
diff --git a/pylint/reporters/collecting_reporter.py b/pylint/reporters/collecting_reporter.py
index ca9170253..943a74d55 100644
--- a/pylint/reporters/collecting_reporter.py
+++ b/pylint/reporters/collecting_reporter.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/reporters/json_reporter.py b/pylint/reporters/json_reporter.py
index 29df6ba07..176946e72 100644
--- a/pylint/reporters/json_reporter.py
+++ b/pylint/reporters/json_reporter.py
@@ -1,25 +1,19 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""JSON reporter."""
from __future__ import annotations
import json
-import sys
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING, Optional, TypedDict
from pylint.interfaces import UNDEFINED
from pylint.message import Message
from pylint.reporters.base_reporter import BaseReporter
from pylint.typing import MessageLocationTuple
-if sys.version_info >= (3, 8):
- from typing import TypedDict
-else:
- from typing_extensions import TypedDict
-
if TYPE_CHECKING:
from pylint.lint.pylinter import PyLinter
from pylint.reporters.ureports.nodes import Section
@@ -75,7 +69,7 @@ class JSONReporter(BaseJSONReporter):
TODO: 3.0: Remove this JSONReporter in favor of the new one handling abs-path
and confidence.
- TODO: 2.16: Add a new JSONReporter handling abs-path, confidence and scores.
+ TODO: 3.0: Add a new JSONReporter handling abs-path, confidence and scores.
(Ultimately all other breaking change related to json for 3.0).
"""
diff --git a/pylint/reporters/multi_reporter.py b/pylint/reporters/multi_reporter.py
index 8bf0828c4..0c27293b7 100644
--- a/pylint/reporters/multi_reporter.py
+++ b/pylint/reporters/multi_reporter.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/reporters/reports_handler_mix_in.py b/pylint/reporters/reports_handler_mix_in.py
index 32a7190e7..95d45ba91 100644
--- a/pylint/reporters/reports_handler_mix_in.py
+++ b/pylint/reporters/reports_handler_mix_in.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/reporters/text.py b/pylint/reporters/text.py
index c25b31f7e..462ea48fe 100644
--- a/pylint/reporters/text.py
+++ b/pylint/reporters/text.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Plain text reporters:.
@@ -15,12 +15,11 @@ import re
import sys
import warnings
from dataclasses import asdict, fields
-from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, TextIO, cast, overload
+from typing import TYPE_CHECKING, Dict, NamedTuple, TextIO
from pylint.message import Message
from pylint.reporters import BaseReporter
from pylint.reporters.ureports.text_writer import TextWriter
-from pylint.utils import _splitstrip
if TYPE_CHECKING:
from pylint.lint import PyLinter
@@ -37,6 +36,34 @@ class MessageStyle(NamedTuple):
style: tuple[str, ...] = ()
"""Tuple of style strings (see `ANSI_COLORS` for available values)."""
+ def __get_ansi_code(self) -> str:
+ """Return ANSI escape code corresponding to color and style.
+
+ :raise KeyError: if a nonexistent color or style identifier is given
+
+ :return: the built escape code
+ """
+ ansi_code = [ANSI_STYLES[effect] for effect in self.style]
+ if self.color:
+ if self.color.isdigit():
+ ansi_code.extend(["38", "5"])
+ ansi_code.append(self.color)
+ else:
+ ansi_code.append(ANSI_COLORS[self.color])
+ if ansi_code:
+ return ANSI_PREFIX + ";".join(ansi_code) + ANSI_END
+ return ""
+
+ def _colorize_ansi(self, msg: str) -> str:
+ if self.color is None and len(self.style) == 0:
+ # If both color and style are not defined, then leave the text as is.
+ return msg
+ escape_code = self.__get_ansi_code()
+ # If invalid (or unknown) color, don't wrap msg with ANSI codes
+ if escape_code:
+ return f"{escape_code}{msg}{ANSI_RESET}"
+ return msg
+
ColorMappingDict = Dict[str, MessageStyle]
@@ -70,85 +97,9 @@ MESSAGE_FIELDS = {i.name for i in fields(Message)}
"""All fields of the Message class."""
-def _get_ansi_code(msg_style: MessageStyle) -> str:
- """Return ANSI escape code corresponding to color and style.
-
- :param msg_style: the message style
-
- :raise KeyError: if a nonexistent color or style identifier is given
-
- :return: the built escape code
- """
- ansi_code = [ANSI_STYLES[effect] for effect in msg_style.style]
- if msg_style.color:
- if msg_style.color.isdigit():
- ansi_code.extend(["38", "5"])
- ansi_code.append(msg_style.color)
- else:
- ansi_code.append(ANSI_COLORS[msg_style.color])
- if ansi_code:
- return ANSI_PREFIX + ";".join(ansi_code) + ANSI_END
- return ""
-
-
-@overload
-def colorize_ansi(
- msg: str,
- msg_style: MessageStyle | None = ...,
-) -> str:
- ...
-
-
-@overload
-def colorize_ansi(
- msg: str,
- msg_style: str | None = ...,
- style: str = ...,
- *,
- color: str | None = ...,
-) -> str:
- # Remove for pylint 3.0
- ...
-
-
-def colorize_ansi(
- msg: str,
- msg_style: MessageStyle | str | None = None,
- style: str = "",
- **kwargs: str | None,
-) -> str:
- r"""colorize message by wrapping it with ANSI escape codes
-
- :param msg: the message string to colorize
-
- :param msg_style: the message style
- or color (for backwards compatibility): the color of the message style
-
- :param style: the message's style elements, this will be deprecated
-
- :param \**kwargs: used to accept `color` parameter while it is being deprecated
-
- :return: the ANSI escaped string
- """
- # TODO: 3.0: Remove deprecated typing and only accept MessageStyle as parameter
- if not isinstance(msg_style, MessageStyle):
- warnings.warn(
- "In pylint 3.0, the colorize_ansi function of Text reporters will only accept a "
- "MessageStyle parameter",
- DeprecationWarning,
- stacklevel=2,
- )
- color = kwargs.get("color")
- style_attrs = tuple(_splitstrip(style))
- msg_style = MessageStyle(color or msg_style, style_attrs)
- # If both color and style are not defined, then leave the text as is
- if msg_style.color is None and len(msg_style.style) == 0:
- return msg
- escape_code = _get_ansi_code(msg_style)
- # If invalid (or unknown) color, don't wrap msg with ANSI codes
- if escape_code:
- return f"{escape_code}{msg}{ANSI_RESET}"
- return msg
+def colorize_ansi(msg: str, msg_style: MessageStyle) -> str:
+ """Colorize message by wrapping it with ANSI escape codes."""
+ return msg_style._colorize_ansi(msg)
def make_header(msg: Message) -> str:
@@ -186,7 +137,8 @@ class TextReporter(BaseReporter):
if argument[0] not in MESSAGE_FIELDS:
warnings.warn(
f"Don't recognize the argument '{argument[0]}' in the --msg-template. "
- "Are you sure it is supported on the current version of pylint?"
+ "Are you sure it is supported on the current version of pylint?",
+ stacklevel=2,
)
template = re.sub(r"\{" + argument[0] + r"(:.*?)?\}", "", template)
self._fixed_template = template
@@ -269,30 +221,9 @@ class ColorizedTextReporter(TextReporter):
def __init__(
self,
output: TextIO | None = None,
- color_mapping: (
- ColorMappingDict | dict[str, tuple[str | None, str]] | None
- ) = None,
+ color_mapping: ColorMappingDict | None = None,
) -> None:
super().__init__(output)
- # TODO: 3.0: Remove deprecated typing and only accept ColorMappingDict as
- # color_mapping parameter
- if color_mapping and not isinstance(
- list(color_mapping.values())[0], MessageStyle
- ):
- warnings.warn(
- "In pylint 3.0, the ColorizedTextReporter will only accept ColorMappingDict as "
- "color_mapping parameter",
- DeprecationWarning,
- stacklevel=2,
- )
- temp_color_mapping: ColorMappingDict = {}
- for key, value in color_mapping.items():
- color = value[0]
- style_attrs = tuple(_splitstrip(value[1])) # type: ignore[arg-type]
- temp_color_mapping[key] = MessageStyle(color, style_attrs)
- color_mapping = temp_color_mapping
- else:
- color_mapping = cast(Optional[ColorMappingDict], color_mapping)
self.color_mapping = color_mapping or ColorizedTextReporter.COLOR_MAPPING
ansi_terms = ["xterm-16color", "xterm-256color"]
if os.environ.get("TERM") not in ansi_terms:
diff --git a/pylint/reporters/ureports/__init__.py b/pylint/reporters/ureports/__init__.py
index a6a0946af..b87c3c319 100644
--- a/pylint/reporters/ureports/__init__.py
+++ b/pylint/reporters/ureports/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
__all__ = ("BaseWriter",)
diff --git a/pylint/reporters/ureports/base_writer.py b/pylint/reporters/ureports/base_writer.py
index e4bd7e710..9a12123cb 100644
--- a/pylint/reporters/ureports/base_writer.py
+++ b/pylint/reporters/ureports/base_writer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Universal report objects and some formatting drivers.
diff --git a/pylint/reporters/ureports/nodes.py b/pylint/reporters/ureports/nodes.py
index 8b6bf32c6..59443996d 100644
--- a/pylint/reporters/ureports/nodes.py
+++ b/pylint/reporters/ureports/nodes.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Micro reports objects.
@@ -72,7 +72,7 @@ class BaseLayout(VNode):
assert self.parent is not self
if self.parent is None:
return []
- return [self.parent] + self.parent.parents()
+ return [self.parent, *self.parent.parents()]
def add_text(self, text: str) -> None:
"""Shortcut to add text data."""
diff --git a/pylint/reporters/ureports/text_writer.py b/pylint/reporters/ureports/text_writer.py
index 13c675aab..5dd6a5d08 100644
--- a/pylint/reporters/ureports/text_writer.py
+++ b/pylint/reporters/ureports/text_writer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Text formatting drivers for ureports."""
diff --git a/pylint/testutils/__init__.py b/pylint/testutils/__init__.py
index e9b8de1de..0ff9b773b 100644
--- a/pylint/testutils/__init__.py
+++ b/pylint/testutils/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Functional/non regression tests for pylint."""
diff --git a/pylint/testutils/_primer/__init__.py b/pylint/testutils/_primer/__init__.py
index 17c854572..2c40f561b 100644
--- a/pylint/testutils/_primer/__init__.py
+++ b/pylint/testutils/_primer/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
__all__ = ["PackageToLint", "PRIMER_DIRECTORY_PATH"]
diff --git a/pylint/testutils/_primer/package_to_lint.py b/pylint/testutils/_primer/package_to_lint.py
index d59f93ed5..1f596f02b 100644
--- a/pylint/testutils/_primer/package_to_lint.py
+++ b/pylint/testutils/_primer/package_to_lint.py
@@ -1,22 +1,17 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import logging
-import sys
from pathlib import Path
+from typing import Literal
from git import GitCommandError
from git.cmd import Git
from git.repo import Repo
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
PRIMER_DIRECTORY_PATH = Path("tests") / ".pylint_primer_tests"
diff --git a/pylint/testutils/_primer/primer.py b/pylint/testutils/_primer/primer.py
index 7d08f1df5..abe6bc4dd 100644
--- a/pylint/testutils/_primer/primer.py
+++ b/pylint/testutils/_primer/primer.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/testutils/_primer/primer_command.py b/pylint/testutils/_primer/primer_command.py
index bbc930913..817c1a0d3 100644
--- a/pylint/testutils/_primer/primer_command.py
+++ b/pylint/testutils/_primer/primer_command.py
@@ -1,23 +1,17 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import abc
import argparse
-import sys
from pathlib import Path
-from typing import Dict
+from typing import Dict, TypedDict
from pylint.reporters.json_reporter import OldJsonExport
from pylint.testutils._primer import PackageToLint
-if sys.version_info >= (3, 8):
- from typing import TypedDict
-else:
- from typing_extensions import TypedDict
-
class PackageData(TypedDict):
commit: str
diff --git a/pylint/testutils/_primer/primer_compare_command.py b/pylint/testutils/_primer/primer_compare_command.py
index acc1c9562..b405d7577 100644
--- a/pylint/testutils/_primer/primer_compare_command.py
+++ b/pylint/testutils/_primer/primer_compare_command.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import json
diff --git a/pylint/testutils/_primer/primer_prepare_command.py b/pylint/testutils/_primer/primer_prepare_command.py
index e69e55b95..27e216bd5 100644
--- a/pylint/testutils/_primer/primer_prepare_command.py
+++ b/pylint/testutils/_primer/primer_prepare_command.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import sys
@@ -15,20 +15,21 @@ class PrepareCommand(PrimerCommand):
def run(self) -> None:
commit_string = ""
version_string = ".".join(str(x) for x in sys.version_info[:2])
+ # Shorten the SHA to avoid exceeding GitHub's 512 char ceiling
if self.config.clone:
for package, data in self.packages.items():
local_commit = data.lazy_clone()
print(f"Cloned '{package}' at commit '{local_commit}'.")
- commit_string += local_commit + "_"
+ commit_string += local_commit[:8] + "_"
elif self.config.check:
for package, data in self.packages.items():
local_commit = Repo(data.clone_directory).head.object.hexsha
print(f"Found '{package}' at commit '{local_commit}'.")
- commit_string += local_commit + "_"
+ commit_string += local_commit[:8] + "_"
elif self.config.make_commit_string:
for package, data in self.packages.items():
remote_sha1_commit = (
- Git().ls_remote(data.url, data.branch).split("\t")[0]
+ Git().ls_remote(data.url, data.branch).split("\t")[0][:8]
)
print(f"'{package}' remote is at commit '{remote_sha1_commit}'.")
commit_string += remote_sha1_commit + "_"
diff --git a/pylint/testutils/_primer/primer_run_command.py b/pylint/testutils/_primer/primer_run_command.py
index cd17d6b1d..7fb4a9ea2 100644
--- a/pylint/testutils/_primer/primer_run_command.py
+++ b/pylint/testutils/_primer/primer_run_command.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -77,7 +77,8 @@ class RunCommand(PrimerCommand):
# Duplicate code takes too long and is relatively safe
# TODO: Find a way to allow cyclic-import and compare output correctly
disables = ["--disable=duplicate-code,cyclic-import"]
- arguments = data.pylint_args + enables + disables
+ additional = ["--clear-cache-post-run=y"]
+ arguments = data.pylint_args + enables + disables + additional
output = StringIO()
reporter = JSONReporter(output)
print(f"Running 'pylint {', '.join(arguments)}'")
@@ -96,6 +97,7 @@ class RunCommand(PrimerCommand):
if fatal_msgs:
warnings.warn(
f"Encountered fatal errors while priming {package_name} !\n"
- f"{self._print_msgs(fatal_msgs)}\n\n"
+ f"{self._print_msgs(fatal_msgs)}\n\n",
+ stacklevel=2,
)
return messages, fatal_msgs
diff --git a/pylint/testutils/_run.py b/pylint/testutils/_run.py
index 0ad68868f..e0a19deca 100644
--- a/pylint/testutils/_run.py
+++ b/pylint/testutils/_run.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Classes and functions used to mimic normal pylint runs.
@@ -10,10 +10,8 @@ This module is considered private and can change at any time.
from __future__ import annotations
from collections.abc import Sequence
-from typing import Any
from pylint.lint import Run as LintRun
-from pylint.lint.run import UNUSED_PARAM_SENTINEL
from pylint.reporters.base_reporter import BaseReporter
from pylint.testutils.lint_module_test import PYLINTRC
@@ -39,7 +37,6 @@ class _Run(LintRun):
args: Sequence[str],
reporter: BaseReporter | None = None,
exit: bool = True, # pylint: disable=redefined-builtin
- do_exit: Any = UNUSED_PARAM_SENTINEL,
) -> None:
args = _add_rcfile_default_pylintrc(list(args))
- super().__init__(args, reporter, exit, do_exit)
+ super().__init__(args, reporter, exit)
diff --git a/pylint/testutils/checker_test_case.py b/pylint/testutils/checker_test_case.py
index 291f52002..343eb8254 100644
--- a/pylint/testutils/checker_test_case.py
+++ b/pylint/testutils/checker_test_case.py
@@ -1,11 +1,10 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import contextlib
-import warnings
from collections.abc import Generator, Iterator
from typing import Any
@@ -78,23 +77,8 @@ class CheckerTestCase:
assert expected_msg.line == gotten_msg.line, msg
assert expected_msg.col_offset == gotten_msg.col_offset, msg
if PY38_PLUS and not IS_PYPY or PY39_PLUS:
- # TODO: 3.0: Remove deprecated missing arguments and remove the warning
- if not expected_msg.end_line == gotten_msg.end_line:
- warnings.warn( # pragma: no cover
- f"The end_line attribute of {gotten_msg} does not match "
- f"the expected value in {expected_msg}. In pylint 3.0 correct end_line "
- "attributes will be required for MessageTest.",
- DeprecationWarning,
- stacklevel=2,
- )
- if not expected_msg.end_col_offset == gotten_msg.end_col_offset:
- warnings.warn( # pragma: no cover
- f"The end_col_offset attribute of {gotten_msg} does not match "
- f"the expected value in {expected_msg}. In pylint 3.0 correct end_col_offset "
- "attributes will be required for MessageTest.",
- DeprecationWarning,
- stacklevel=2,
- )
+ assert expected_msg.end_line == gotten_msg.end_line, msg
+ assert expected_msg.end_col_offset == gotten_msg.end_col_offset, msg
def walk(self, node: nodes.NodeNG) -> None:
"""Recursive walk on the given node."""
diff --git a/pylint/testutils/configuration_test.py b/pylint/testutils/configuration_test.py
index 8b921ceff..5ddb2c03b 100644
--- a/pylint/testutils/configuration_test.py
+++ b/pylint/testutils/configuration_test.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Utility functions for configuration testing."""
diff --git a/pylint/testutils/constants.py b/pylint/testutils/constants.py
index 1c3c69d79..0d93b7e87 100644
--- a/pylint/testutils/constants.py
+++ b/pylint/testutils/constants.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
import operator
import re
diff --git a/pylint/testutils/decorator.py b/pylint/testutils/decorator.py
index ab99cbdd8..c20692132 100644
--- a/pylint/testutils/decorator.py
+++ b/pylint/testutils/decorator.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/testutils/functional/__init__.py b/pylint/testutils/functional/__init__.py
index 4840e1ba6..c1c1c5139 100644
--- a/pylint/testutils/functional/__init__.py
+++ b/pylint/testutils/functional/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
__all__ = [
"FunctionalTestFile",
diff --git a/pylint/testutils/functional/find_functional_tests.py b/pylint/testutils/functional/find_functional_tests.py
index 200cee7ec..f2e636687 100644
--- a/pylint/testutils/functional/find_functional_tests.py
+++ b/pylint/testutils/functional/find_functional_tests.py
@@ -1,18 +1,20 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import os
+from collections.abc import Iterator
from pathlib import Path
from pylint.testutils.functional.test_file import FunctionalTestFile
-REASONABLY_DISPLAYABLE_VERTICALLY = 48
-"""'Wet finger' number of files that are reasonable to display by an IDE."""
-SHOULD_BE_IN_THE_SAME_DIRECTORY = 5
-"""'Wet finger' as in 'in my settings there are precisely this many'."""
+REASONABLY_DISPLAYABLE_VERTICALLY = 49
+"""'Wet finger' number of files that are reasonable to display by an IDE.
+
+'Wet finger' as in 'in my settings there are precisely this many'.
+"""
IGNORED_PARENT_DIRS = {
"deprecated_relative_import",
@@ -32,11 +34,12 @@ IGNORED_PARENT_PARENT_DIRS = {
def get_functional_test_files_from_directory(
input_dir: Path | str,
+ max_file_per_directory: int = REASONABLY_DISPLAYABLE_VERTICALLY,
) -> list[FunctionalTestFile]:
"""Get all functional tests in the input_dir."""
suite = []
- _check_functional_tests_structure(Path(input_dir))
+ _check_functional_tests_structure(Path(input_dir), max_file_per_directory)
for dirpath, dirnames, filenames in os.walk(input_dir):
if dirpath.endswith("__pycache__"):
@@ -49,39 +52,88 @@ def get_functional_test_files_from_directory(
return suite
-def _check_functional_tests_structure(directory: Path) -> None:
- """Check if test directories follow correct file/folder structure."""
- # Ignore underscored directories
+def _check_functional_tests_structure(
+ directory: Path, max_file_per_directory: int
+) -> None:
+ """Check if test directories follow correct file/folder structure.
+
+ Ignore underscored directories or files.
+ """
if Path(directory).stem.startswith("_"):
return
files: set[Path] = set()
dirs: set[Path] = set()
+ def _get_files_from_dir(
+ path: Path, violations: list[tuple[Path, int]]
+ ) -> list[Path]:
+ """Return directories and files from a directory and handles violations."""
+ files_without_leading_underscore = list(
+ p for p in path.iterdir() if not p.stem.startswith("_")
+ )
+ if len(files_without_leading_underscore) > max_file_per_directory:
+ violations.append((path, len(files_without_leading_underscore)))
+ return files_without_leading_underscore
+
+ def walk(path: Path) -> Iterator[Path]:
+ violations: list[tuple[Path, int]] = []
+ violations_msgs: set[str] = set()
+ parent_dir_files = _get_files_from_dir(path, violations)
+ error_msg = (
+ "The following directory contains too many functional tests files:\n"
+ )
+ for _file_or_dir in parent_dir_files:
+ if _file_or_dir.is_dir():
+ _files = _get_files_from_dir(_file_or_dir, violations)
+ yield _file_or_dir.resolve()
+ try:
+ yield from walk(_file_or_dir)
+ except AssertionError as e:
+ violations_msgs.add(str(e).replace(error_msg, ""))
+ else:
+ yield _file_or_dir.resolve()
+ if violations or violations_msgs:
+ _msg = error_msg
+ for offending_file, number in violations:
+ _msg += f"- {offending_file}: {number} when the max is {max_file_per_directory}\n"
+ for error_msg in violations_msgs:
+ _msg += error_msg
+ raise AssertionError(_msg)
+
# Collect all sub-directories and files in directory
- for file_or_dir in directory.iterdir():
- if file_or_dir.is_file():
- if file_or_dir.suffix == ".py" and not file_or_dir.stem.startswith("_"):
- files.add(file_or_dir)
- elif file_or_dir.is_dir():
+ for file_or_dir in walk(directory):
+ if file_or_dir.is_dir():
dirs.add(file_or_dir)
- _check_functional_tests_structure(file_or_dir)
-
- assert len(files) <= REASONABLY_DISPLAYABLE_VERTICALLY, (
- f"{directory} contains too many functional tests files "
- + f"({len(files)} > {REASONABLY_DISPLAYABLE_VERTICALLY})."
- )
+ elif file_or_dir.suffix == ".py":
+ files.add(file_or_dir)
+ directory_does_not_exists: list[tuple[Path, Path]] = []
+ misplaced_file: list[Path] = []
for file in files:
possible_dir = file.parent / file.stem.split("_")[0]
- assert not possible_dir.exists(), f"{file} should go in {possible_dir}."
-
+ if possible_dir.exists():
+ directory_does_not_exists.append((file, possible_dir))
# Exclude some directories as they follow a different structure
if (
not len(file.parent.stem) == 1 # First letter sub-directories
and file.parent.stem not in IGNORED_PARENT_DIRS
and file.parent.parent.stem not in IGNORED_PARENT_PARENT_DIRS
):
- assert file.stem.startswith(
- file.parent.stem
- ), f"{file} should not go in {file.parent}"
+ if not file.stem.startswith(file.parent.stem):
+ misplaced_file.append(file)
+
+ if directory_does_not_exists or misplaced_file:
+ msg = "The following functional tests are disorganized:\n"
+ for file, possible_dir in directory_does_not_exists:
+ msg += (
+ f"- In '{directory}', '{file.relative_to(directory)}' "
+ f"should go in '{possible_dir.relative_to(directory)}'\n"
+ )
+ for file in misplaced_file:
+ msg += (
+ f"- In '{directory}', {file.relative_to(directory)} should go in a directory"
+ f" that starts with the first letters"
+ f" of '{file.stem}' (not '{file.parent.stem}')\n"
+ )
+ raise AssertionError(msg)
diff --git a/pylint/testutils/functional/lint_module_output_update.py b/pylint/testutils/functional/lint_module_output_update.py
index e4f88b3ef..8855fd1aa 100644
--- a/pylint/testutils/functional/lint_module_output_update.py
+++ b/pylint/testutils/functional/lint_module_output_update.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/testutils/functional/test_file.py b/pylint/testutils/functional/test_file.py
index 85a72daa9..16593b5c4 100644
--- a/pylint/testutils/functional/test_file.py
+++ b/pylint/testutils/functional/test_file.py
@@ -1,13 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import configparser
-import sys
from collections.abc import Callable
from os.path import basename, exists, join
+from typing import TypedDict
def parse_python_version(ver_str: str) -> tuple[int, ...]:
@@ -19,12 +19,6 @@ class NoFileError(Exception):
pass
-if sys.version_info >= (3, 8):
- from typing import TypedDict
-else:
- from typing_extensions import TypedDict
-
-
class TestFileOptions(TypedDict):
min_pyver: tuple[int, ...]
max_pyver: tuple[int, ...]
@@ -62,7 +56,7 @@ class FunctionalTestFile:
def __init__(self, directory: str, filename: str) -> None:
self._directory = directory
self.base = filename.replace(".py", "")
- # TODO: 2.x: Deprecate FunctionalTestFile.options and related code
+ # TODO: 3.0: Deprecate FunctionalTestFile.options and related code
# We should just parse these options like a normal configuration file.
self.options: TestFileOptions = {
"min_pyver": (2, 5),
diff --git a/pylint/testutils/functional_test_file.py b/pylint/testutils/functional_test_file.py
deleted file mode 100644
index e2bd7f59b..000000000
--- a/pylint/testutils/functional_test_file.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-__all__ = [
- "FunctionalTestFile",
- "NoFileError",
- "parse_python_version",
-]
-
-import warnings
-
-from pylint.testutils.functional import (
- FunctionalTestFile,
- NoFileError,
- parse_python_version,
-)
-
-warnings.warn(
- "'pylint.testutils.functional_test_file' will be accessible from"
- " the 'pylint.testutils.functional' namespace in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
-)
diff --git a/pylint/testutils/get_test_info.py b/pylint/testutils/get_test_info.py
index a91b1ecd2..eb2c78cfd 100644
--- a/pylint/testutils/get_test_info.py
+++ b/pylint/testutils/get_test_info.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/testutils/global_test_linter.py b/pylint/testutils/global_test_linter.py
index d1ffac599..2e0d3d170 100644
--- a/pylint/testutils/global_test_linter.py
+++ b/pylint/testutils/global_test_linter.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from pylint import checkers
from pylint.lint import PyLinter
diff --git a/pylint/testutils/lint_module_test.py b/pylint/testutils/lint_module_test.py
index e139af12b..b578e3162 100644
--- a/pylint/testutils/lint_module_test.py
+++ b/pylint/testutils/lint_module_test.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -266,7 +266,7 @@ class LintModuleTest:
expected_messages: MessageCounter,
actual_output: list[OutputLine],
) -> str:
- msg = [f'Wrong results for file "{self._test_file.base}":']
+ msg = [f'Wrong message(s) raised for "{Path(self._test_file.source).name}":']
missing, unexpected = self.multiset_difference(
expected_messages, actual_messages
)
@@ -289,7 +289,7 @@ class LintModuleTest:
) -> str:
missing = set(expected_lines) - set(received_lines)
unexpected = set(received_lines) - set(expected_lines)
- error_msg = f"Wrong output for '{self._test_file.base}.txt':"
+ error_msg = f'Wrong output for "{Path(self._test_file.expected_output).name}":'
sort_by_line_number = operator.attrgetter("lineno")
if missing:
error_msg += "\n- Missing lines:\n"
diff --git a/pylint/testutils/output_line.py b/pylint/testutils/output_line.py
index 7465fce9d..95f24cc12 100644
--- a/pylint/testutils/output_line.py
+++ b/pylint/testutils/output_line.py
@@ -1,10 +1,9 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-import warnings
from collections.abc import Sequence
from typing import Any, NamedTuple, TypeVar
@@ -13,7 +12,6 @@ from astroid import nodes
from pylint.constants import PY38_PLUS
from pylint.interfaces import UNDEFINED, Confidence
from pylint.message.message import Message
-from pylint.testutils.constants import UPDATE_OPTION
_T = TypeVar("_T")
@@ -89,60 +87,22 @@ class OutputLine(NamedTuple):
"""
if isinstance(row, str):
row = row.split(",")
- # noinspection PyBroadException
- # pylint: disable = too-many-try-statements
try:
+ line = int(row[1])
column = cls._get_column(row[2])
- if len(row) == 5:
- warnings.warn(
- "In pylint 3.0 functional tests expected output should always include the "
- "expected confidence level, expected end_line and expected end_column. "
- "An OutputLine should thus have a length of 8.",
- DeprecationWarning,
- stacklevel=2,
- )
- return cls(
- row[0],
- int(row[1]),
- column,
- None,
- None,
- row[3],
- row[4],
- UNDEFINED.name,
- )
- if len(row) == 6:
- warnings.warn(
- "In pylint 3.0 functional tests expected output should always include the "
- "expected end_line and expected end_column. An OutputLine should thus have "
- "a length of 8.",
- DeprecationWarning,
- stacklevel=2,
- )
- return cls(
- row[0], int(row[1]), column, None, None, row[3], row[4], row[5]
- )
- if len(row) == 8:
- end_line = cls._get_py38_none_value(row[3], check_endline)
- end_column = cls._get_py38_none_value(row[4], check_endline)
- return cls(
- row[0],
- int(row[1]),
- column,
- cls._value_to_optional_int(end_line),
- cls._value_to_optional_int(end_column),
- row[5],
- row[6],
- row[7],
- )
- raise IndexError
- except Exception: # pylint: disable=broad-except
- warnings.warn(
- "Expected 'msg-symbolic-name:42:27:MyClass.my_function:The message:"
- f"CONFIDENCE' but we got '{':'.join(row)}'. Try updating the expected"
- f" output with:\npython tests/test_functional.py {UPDATE_OPTION}",
- UserWarning,
+ end_line = cls._value_to_optional_int(
+ cls._get_py38_none_value(row[3], check_endline)
+ )
+ end_column = cls._value_to_optional_int(
+ cls._get_py38_none_value(row[4], check_endline)
)
+ # symbol, line, column, end_line, end_column, node, msg, confidences
+ assert len(row) == 8
+ return cls(
+ row[0], line, column, end_line, end_column, row[5], row[6], row[7]
+ )
+ except Exception: # pylint: disable=broad-except
+ # We need this to not fail for the update script to work.
return cls("", 0, 0, None, None, "", "", "")
def to_csv(self) -> tuple[str, str, str, str, str, str, str, str]:
diff --git a/pylint/testutils/pyreverse.py b/pylint/testutils/pyreverse.py
index 20dce84dd..c621f9e7a 100644
--- a/pylint/testutils/pyreverse.py
+++ b/pylint/testutils/pyreverse.py
@@ -1,23 +1,17 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import argparse
import configparser
import shlex
-import sys
from pathlib import Path
-from typing import NamedTuple
+from typing import NamedTuple, TypedDict
from pylint.pyreverse.main import DEFAULT_COLOR_PALETTE
-if sys.version_info >= (3, 8):
- from typing import TypedDict
-else:
- from typing_extensions import TypedDict
-
# This class could and should be replaced with a simple dataclass when support for Python < 3.7 is dropped.
# A NamedTuple is not possible as some tests need to modify attributes during the test.
@@ -37,7 +31,9 @@ class PyreverseConfig(
all_ancestors: bool | None = None,
show_associated: int | None = None,
all_associated: bool | None = None,
+ no_standalone: bool = False,
show_builtin: bool = False,
+ show_stdlib: bool = False,
module_names: bool | None = None,
only_classnames: bool = False,
output_format: str = "dot",
@@ -58,7 +54,9 @@ class PyreverseConfig(
self.all_ancestors = all_ancestors
self.show_associated = show_associated
self.all_associated = all_associated
+ self.no_standalone = no_standalone
self.show_builtin = show_builtin
+ self.show_stdlib = show_stdlib
self.module_names = module_names
self.only_classnames = only_classnames
self.output_format = output_format
diff --git a/pylint/testutils/reporter_for_tests.py b/pylint/testutils/reporter_for_tests.py
index 62d70a38d..d3c06eecd 100644
--- a/pylint/testutils/reporter_for_tests.py
+++ b/pylint/testutils/reporter_for_tests.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -20,7 +20,7 @@ class GenericTestReporter(BaseReporter):
out: StringIO
- def __init__( # pylint: disable=super-init-not-called # See https://github.com/PyCQA/pylint/issues/4941
+ def __init__( # pylint: disable=super-init-not-called # See https://github.com/pylint-dev/pylint/issues/4941
self,
) -> None:
self.path_strip_prefix: str = getcwd() + sep
diff --git a/pylint/testutils/testing_pylintrc b/pylint/testutils/testing_pylintrc
index ba5318b51..9429b858f 100644
--- a/pylint/testutils/testing_pylintrc
+++ b/pylint/testutils/testing_pylintrc
@@ -7,3 +7,7 @@ disable=
suppressed-message,
locally-disabled,
useless-suppression,
+
+enable=
+ deprecated-pragma,
+ use-symbolic-message-instead,
diff --git a/pylint/testutils/tokenize_str.py b/pylint/testutils/tokenize_str.py
index 13295fe51..dc9ada72a 100644
--- a/pylint/testutils/tokenize_str.py
+++ b/pylint/testutils/tokenize_str.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/testutils/unittest_linter.py b/pylint/testutils/unittest_linter.py
index a519680f1..a19afec56 100644
--- a/pylint/testutils/unittest_linter.py
+++ b/pylint/testutils/unittest_linter.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
# pylint: disable=duplicate-code
from __future__ import annotations
-import sys
-from typing import Any
+from typing import Any, Literal
from astroid import nodes
@@ -15,11 +14,6 @@ from pylint.interfaces import UNDEFINED, Confidence
from pylint.lint import PyLinter
from pylint.testutils.output_line import MessageTest
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
class UnittestLinter(PyLinter):
"""A fake linter class to capture checker messages."""
diff --git a/pylint/testutils/utils.py b/pylint/testutils/utils.py
index 292e991c2..1ff999b28 100644
--- a/pylint/testutils/utils.py
+++ b/pylint/testutils/utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/typing.py b/pylint/typing.py
index d62618605..da11112b4 100644
--- a/pylint/typing.py
+++ b/pylint/typing.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""A collection of typing utilities."""
from __future__ import annotations
import argparse
-import sys
from pathlib import Path
from typing import (
TYPE_CHECKING,
@@ -15,19 +14,17 @@ from typing import (
Callable,
Dict,
Iterable,
+ Literal,
NamedTuple,
Optional,
Pattern,
+ Protocol,
Tuple,
Type,
+ TypedDict,
Union,
)
-if sys.version_info >= (3, 8):
- from typing import Literal, Protocol, TypedDict
-else:
- from typing_extensions import Literal, Protocol, TypedDict
-
if TYPE_CHECKING:
from pylint.config.callback_actions import _CallbackAction
from pylint.pyreverse.inspector import Project
diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py
index bc5011db9..61833a598 100644
--- a/pylint/utils/__init__.py
+++ b/pylint/utils/__init__.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Some various utilities and helper classes, most of them used in the
main pylint class.
@@ -14,13 +14,11 @@ from pylint.utils.utils import (
HAS_ISORT_5,
IsortDriver,
_check_csv,
- _format_option_value,
_splitstrip,
_unquote,
decoding_stream,
diff_string,
format_section,
- get_global_option,
get_module_and_frameid,
get_rst_section,
get_rst_title,
@@ -34,14 +32,12 @@ __all__ = [
"HAS_ISORT_5",
"IsortDriver",
"_check_csv",
- "_format_option_value",
"_splitstrip",
"_unquote",
"decoding_stream",
"diff_string",
"FileState",
"format_section",
- "get_global_option",
"get_module_and_frameid",
"get_rst_section",
"get_rst_title",
diff --git a/pylint/utils/ast_walker.py b/pylint/utils/ast_walker.py
index cf0f13fd1..338caf57a 100644
--- a/pylint/utils/ast_walker.py
+++ b/pylint/utils/ast_walker.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/utils/docs.py b/pylint/utils/docs.py
index ebd7cc8c3..ba592c4a4 100644
--- a/pylint/utils/docs.py
+++ b/pylint/utils/docs.py
@@ -1,13 +1,12 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Various helper functions to create the docs of a linter object."""
from __future__ import annotations
import sys
-import warnings
from typing import TYPE_CHECKING, Any, TextIO
from pylint.constants import MAIN_CHECKER_NAME
@@ -25,20 +24,16 @@ def _get_checkers_infos(linter: PyLinter) -> dict[str, dict[str, Any]]:
if name != MAIN_CHECKER_NAME:
try:
by_checker[name]["checker"] = checker
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- by_checker[name]["options"] += checker.options_and_values()
+ by_checker[name]["options"] += checker._options_and_values()
by_checker[name]["msgs"].update(checker.msgs)
by_checker[name]["reports"] += checker.reports
except KeyError:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- by_checker[name] = {
- "checker": checker,
- "options": list(checker.options_and_values()),
- "msgs": dict(checker.msgs),
- "reports": list(checker.reports),
- }
+ by_checker[name] = {
+ "checker": checker,
+ "options": list(checker._options_and_values()),
+ "msgs": dict(checker.msgs),
+ "reports": list(checker.reports),
+ }
return by_checker
@@ -51,16 +46,14 @@ Pylint provides global options and switches.
"""
for checker in linter.get_checkers():
if checker.name == MAIN_CHECKER_NAME and checker.options:
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=DeprecationWarning)
- for section, options in checker.options_by_section():
- if section is None:
- title = "General options"
- else:
- title = f"{section.capitalize()} options"
- result += get_rst_title(title, "~")
- assert isinstance(options, list)
- result += f"{get_rst_section(None, options)}\n"
+ for section, options in checker._options_by_section():
+ if section is None:
+ title = "General options"
+ else:
+ title = f"{section.capitalize()} options"
+ result += get_rst_title(title, "~")
+ assert isinstance(options, list)
+ result += f"{get_rst_section(None, options)}\n"
return result
diff --git a/pylint/utils/file_state.py b/pylint/utils/file_state.py
index 19122b373..9ae5fb077 100644
--- a/pylint/utils/file_state.py
+++ b/pylint/utils/file_state.py
@@ -1,15 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import collections
-import sys
-import warnings
from collections import defaultdict
from collections.abc import Iterator
-from typing import TYPE_CHECKING, Dict
+from typing import TYPE_CHECKING, Dict, Literal
from astroid import nodes
@@ -19,11 +17,6 @@ from pylint.constants import (
WarningScope,
)
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
from pylint.message import MessageDefinition, MessageDefinitionStore
@@ -36,26 +29,12 @@ class FileState:
def __init__(
self,
- modname: str | None = None,
- msg_store: MessageDefinitionStore | None = None,
+ modname: str,
+ msg_store: MessageDefinitionStore,
node: nodes.Module | None = None,
*,
is_base_filestate: bool = False,
) -> None:
- if modname is None:
- warnings.warn(
- "FileState needs a string as modname argument. "
- "This argument will be required in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- if msg_store is None:
- warnings.warn(
- "FileState needs a 'MessageDefinitionStore' as msg_store argument. "
- "This argument will be required in pylint 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
self.base_name = modname
self._module_msgs_state: MessageStateDict = {}
self._raw_module_msgs_state: MessageStateDict = {}
@@ -74,25 +53,6 @@ class FileState:
PyLinter.
"""
- def collect_block_lines(
- self, msgs_store: MessageDefinitionStore, module_node: nodes.Module
- ) -> None:
- """Walk the AST to collect block level options line numbers."""
- warnings.warn(
- "'collect_block_lines' has been deprecated and will be removed in pylint 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- for msg, lines in self._module_msgs_state.items():
- self._raw_module_msgs_state[msg] = lines.copy()
- orig_state = self._module_msgs_state.copy()
- self._module_msgs_state = {}
- self._suppression_mapping = {}
- self._effective_max_line_number = module_node.tolineno
- for msgid, lines in orig_state.items():
- for msgdef in msgs_store.get_message_definitions(msgid):
- self._set_state_on_block_lines(msgs_store, module_node, msgdef, lines)
-
def _set_state_on_block_lines(
self,
msgs_store: MessageDefinitionStore,
@@ -230,10 +190,6 @@ class FileState:
) -> None:
"""Set status (enabled/disable) for a given message at a given line."""
assert line > 0
- assert self._module
- # TODO: 3.0: Remove unnecessary assertion
- assert self._msgs_store
-
if scope != "line":
# Expand the status to cover all relevant block lines
self._set_state_on_block_lines(
diff --git a/pylint/utils/linterstats.py b/pylint/utils/linterstats.py
index f9a6c6c3c..e7a088b7b 100644
--- a/pylint/utils/linterstats.py
+++ b/pylint/utils/linterstats.py
@@ -1,19 +1,13 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
-import sys
-from typing import cast
+from typing import Literal, TypedDict, cast
from pylint.typing import MessageTypesFullName
-if sys.version_info >= (3, 8):
- from typing import Literal, TypedDict
-else:
- from typing_extensions import Literal, TypedDict
-
class BadNames(TypedDict):
"""TypedDict to store counts of node types with bad names."""
diff --git a/pylint/utils/pragma_parser.py b/pylint/utils/pragma_parser.py
index df3627380..12513e284 100644
--- a/pylint/utils/pragma_parser.py
+++ b/pylint/utils/pragma_parser.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py
index 054d307bc..543d7cbb3 100644
--- a/pylint/utils/utils.py
+++ b/pylint/utils/utils.py
@@ -1,6 +1,6 @@
# 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
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -28,12 +28,12 @@ from typing import (
TYPE_CHECKING,
Any,
List,
+ Literal,
Pattern,
TextIO,
Tuple,
TypeVar,
Union,
- overload,
)
from astroid import Module, modutils, nodes
@@ -41,13 +41,7 @@ from astroid import Module, modutils, nodes
from pylint.constants import PY_EXTS
from pylint.typing import OptionDict
-if sys.version_info >= (3, 8):
- from typing import Literal
-else:
- from typing_extensions import Literal
-
if TYPE_CHECKING:
- from pylint.checkers.base_checker import BaseChecker
from pylint.lint import PyLinter
DEFAULT_LINE_LENGTH = 79
@@ -215,77 +209,6 @@ def register_plugins(linter: PyLinter, directory: str) -> None:
imported[base] = 1
-@overload
-def get_global_option(
- checker: BaseChecker, option: GLOBAL_OPTION_BOOL, default: bool | None = ...
-) -> bool:
- ...
-
-
-@overload
-def get_global_option(
- checker: BaseChecker, option: GLOBAL_OPTION_INT, default: int | None = ...
-) -> int:
- ...
-
-
-@overload
-def get_global_option(
- checker: BaseChecker,
- option: GLOBAL_OPTION_LIST,
- default: list[str] | None = ...,
-) -> list[str]:
- ...
-
-
-@overload
-def get_global_option(
- checker: BaseChecker,
- option: GLOBAL_OPTION_PATTERN,
- default: Pattern[str] | None = ...,
-) -> Pattern[str]:
- ...
-
-
-@overload
-def get_global_option(
- checker: BaseChecker,
- option: GLOBAL_OPTION_PATTERN_LIST,
- default: list[Pattern[str]] | None = ...,
-) -> list[Pattern[str]]:
- ...
-
-
-@overload
-def get_global_option(
- checker: BaseChecker,
- option: GLOBAL_OPTION_TUPLE_INT,
- default: tuple[int, ...] | None = ...,
-) -> tuple[int, ...]:
- ...
-
-
-def get_global_option(
- checker: BaseChecker,
- option: GLOBAL_OPTION_NAMES,
- default: T_GlobalOptionReturnTypes | None = None, # pylint: disable=unused-argument
-) -> T_GlobalOptionReturnTypes | None | Any:
- """DEPRECATED: Retrieve an option defined by the given *checker* or
- by all known option providers.
-
- It will look in the list of all options providers
- until the given *option* will be found.
- If the option wasn't found, the *default* value will be returned.
- """
- warnings.warn(
- "get_global_option has been deprecated. You can use "
- "checker.linter.config to get all global options instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return getattr(checker.linter.config, option.replace("-", "_"))
-
-
def _splitstrip(string: str, sep: str = ",") -> list[str]:
"""Return a list of stripped string by splitting the string given as
argument on `sep` (',' by default), empty strings are discarded.
@@ -340,7 +263,7 @@ def _comment(string: str) -> str:
def _format_option_value(optdict: OptionDict, value: Any) -> str:
"""Return the user input's value from a 'compiled' value.
- TODO: 3.0: Remove deprecated function
+ TODO: Refactor the code to not use this deprecated function
"""
if optdict.get("type", None) == "py_version":
value = ".".join(str(item) for item in value)