diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 9dee73e1e7..313394bda8 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -57,7 +57,7 @@ hint of what Pylint is going to ``pick on``: :: further processing. When Pylint is first run on a fresh piece of code, a common complaint is that it -is too ``noisy``. The default configuration enforce a lot of warnings. +is too ``noisy``. The default configuration enforces a lot of warnings. We'll use some of the options we noted above to make it suit your preferences a bit better. diff --git a/pylint/__init__.py b/pylint/__init__.py index 3752a020f3..e74be3e898 100644 --- a/pylint/__init__.py +++ b/pylint/__init__.py @@ -6,7 +6,9 @@ __all__ = [ "__version__", + "core", "modify_sys_path", + "recommended", "run_pylint", "run_pyreverse", "run_symilar", @@ -19,6 +21,7 @@ from typing import NoReturn from pylint.__pkginfo__ import __version__ +from pylint.message_sets import core, recommended # pylint: disable=import-outside-toplevel diff --git a/pylint/config/arguments_manager.py b/pylint/config/arguments_manager.py index e830047562..764bd4fe7b 100644 --- a/pylint/config/arguments_manager.py +++ b/pylint/config/arguments_manager.py @@ -239,6 +239,8 @@ def _generate_config( """Write a configuration file according to the current configuration into the given stream or stdout. """ + # If --message-sets is absent, maybe just go ahead and + # set --message-sets=pylint.recommended? or wait until pylint 5? options_by_section = {} sections = [] for group in sorted( diff --git a/pylint/config/callback_actions.py b/pylint/config/callback_actions.py index bf2decd3c6..38839d5363 100644 --- a/pylint/config/callback_actions.py +++ b/pylint/config/callback_actions.py @@ -382,6 +382,24 @@ def __call__( raise NotImplementedError # pragma: no cover +class _MessageSetsAction(_AccessLinterObjectAction): + """Callback action for setting message sets.""" + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: str | Sequence[Any] | None, + option_string: str | None = "--disable", + ) -> None: + for msg_set in values or (): + actions = utils.utils._import_attribute(msg_set) + for enable in actions["enable"]: + self.linter.enable(enable) + for disable in actions["disable"]: + self.linter.disable(disable) + + class _DisableAction(_XableAction): """Callback action for disabling a message.""" diff --git a/pylint/lint/base_options.py b/pylint/lint/base_options.py index 4ec84d2e9e..74f0dd85d0 100644 --- a/pylint/lint/base_options.py +++ b/pylint/lint/base_options.py @@ -26,6 +26,7 @@ _ListMessagesEnabledAction, _LongHelpAction, _MessageHelpAction, + _MessageSetsAction, _OutputFormatAction, ) from pylint.typing import Options @@ -181,6 +182,19 @@ def _make_linter_options(linter: PyLinter) -> Options: f" Leave empty to show all. Valid levels: {', '.join(interfaces.CONFIDENCE_LEVEL_NAMES)}.", }, ), + ( + "message-sets", + { + "action": _MessageSetsAction, + "callback": lambda x1, x2, x3, x4: x1, + "default": ("pylint.core"), # pylint.recommended in v5 + "metavar": "", + "short": "msg-sets", + "group": "Messages control", + "help": "", + "kwargs": {"linter": linter}, + }, + ), ( "enable", { diff --git a/pylint/message_sets.py b/pylint/message_sets.py new file mode 100644 index 0000000000..b91155c1a3 --- /dev/null +++ b/pylint/message_sets.py @@ -0,0 +1,16 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt + +core: dict[str, set[str]] = { + "enable": set(), + "disable": set(), +} + +recommended: dict[str, set[str]] = { + "enable": set(), + "disable": { + "duplicate-code", + # add others from issue... + }, +} diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index 4b2aee4757..4cd82a96cb 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -24,6 +24,7 @@ import warnings from collections import deque from collections.abc import Iterable, Sequence +from importlib import import_module from io import BufferedReader, BytesIO from re import Pattern from typing import TYPE_CHECKING, Any, Literal, TextIO, TypeVar @@ -264,6 +265,18 @@ def _check_regexp_csv(value: list[str] | tuple[str] | str) -> Iterable[str]: yield from ("".join(regexp).strip() for regexp in regexps if regexp is not None) +def _import_attribute(dotted_path: str) -> Any: + try: + module_path, attribute_name = dotted_path.rsplit(".", 1) + except ValueError as err: + raise ImportError(f"{dotted_path} doesn't look like a module path") from err + + if not (module := sys.modules.get(module_path)): + module = import_module(module_path) + + return getattr(module, attribute_name) + + def _comment(string: str) -> str: """Return string as a comment.""" lines = [line.strip() for line in string.splitlines()] diff --git a/pylintrc b/pylintrc index fe653a092c..b0ee1d195a 100644 --- a/pylintrc +++ b/pylintrc @@ -76,6 +76,8 @@ clear-cache-post-run=no # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED # confidence= +message-sets=pylint.recommended + # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where