summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2022-04-13 21:45:47 +0200
committerGitHub <noreply@github.com>2022-04-13 21:45:47 +0200
commit443802995bf0cf4921e51a5ef2f2807c22153fab (patch)
tree9743416df8a3dbe58903d9ce45960c3cb495ac7b
parent00321dc4fe3af28541cd24bf8dd3017f7b074dbc (diff)
downloadpylint-git-443802995bf0cf4921e51a5ef2f2807c22153fab.tar.gz
Add a ``--generate-toml-config`` option (#6199)
-rw-r--r--ChangeLog4
-rw-r--r--doc/faq.rst8
-rw-r--r--doc/intro.rst5
-rw-r--r--doc/tutorial.rst5
-rw-r--r--doc/user_guide/run.rst4
-rw-r--r--doc/whatsnew/2.14.rst4
-rw-r--r--pylint/config/arguments_manager.py75
-rw-r--r--pylint/config/callback_actions.py14
-rw-r--r--pylint/lint/run.py29
-rw-r--r--setup.cfg4
-rw-r--r--tests/test_self.py56
11 files changed, 197 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 98e002195..0c2ca34e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -85,6 +85,10 @@ Release date: TBA
Closes #5205
+* Added the ``generate-toml-config`` option.
+
+ Ref #5462
+
* Added new checker ``unnecessary-list-index-lookup`` for indexing into a list while
iterating over ``enumerate()``.
diff --git a/doc/faq.rst b/doc/faq.rst
index 20e56b18e..0992cd7d6 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -100,16 +100,16 @@ localized using the following rules:
* ".pylint.d" directory in the current directory
-3.3 How do I find the option name (for pylintrc) corresponding to a specific command line option?
+3.3 How do I find the option name corresponding to a specific command line option?
--------------------------------------------------------------------------------------------------------
-You can generate a sample pylintrc file with --generate-rcfile
+You can generate a sample configuration file with ``--generate-toml-config``.
Every option present on the command line before this will be included in
-the rc file
+the toml file
For example::
- pylint --disable=bare-except,invalid-name --class-rgx='[A-Z][a-z]+' --generate-rcfile
+ pylint --disable=bare-except,invalid-name --class-rgx='[A-Z][a-z]+' --generate-toml-config
3.4 I'd rather not run Pylint from the command line. Can I integrate it with my editor?
---------------------------------------------------------------------------------------
diff --git a/doc/intro.rst b/doc/intro.rst
index 02e6ec792..f1c58fcaf 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -53,5 +53,6 @@ The best way to tackle pylint's verboseness is to:
all convention messages is simple as a ``--disable=C`` option added to pylint
command.
-* create a custom configuration file, tailored to your needs. You can generate
- one using pylint's command ``--generate-rcfile``.
+* manage the configuration through a configuration file. With the option
+ ``generate-toml-config`` you can create a piece of ``.toml`` text to put
+ in your ``pyproject.toml`` file.
diff --git a/doc/tutorial.rst b/doc/tutorial.rst
index 57eea6af8..173b255cd 100644
--- a/doc/tutorial.rst
+++ b/doc/tutorial.rst
@@ -59,7 +59,7 @@ A couple of the options that we'll focus on here are: ::
Commands:
--help-msg=<msg-id>
- --generate-rcfile
+ --generate-toml-config
Messages control:
--disable=<msg-ids>
Reports:
@@ -273,7 +273,8 @@ example but go ahead and `read up`_ on them if you want.
It would really be a pain to specify that regex on the command line all the time, particularly if we're using many other options.
That's what a configuration file is for. We can configure our Pylint to
store our options for us so we don't have to declare them on the command line. Using a configuration file is a nice way of formalizing your rules and
- quickly sharing them with others. Invoking ``pylint --generate-rcfile`` will create a sample rcfile with all the options set and explained in comments.
+ quickly sharing them with others. Invoking ``pylint --generate-toml-config`` will create a sample ``.toml`` section with all the options set and explained in comments.
+ This can then be added to your ``pyproject.toml`` file or any other ``.toml`` file pointed to with the ``--rcfile`` option.
That's it for the basic intro. More tutorials will follow.
diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst
index 898a1b732..f9b8b8537 100644
--- a/doc/user_guide/run.rst
+++ b/doc/user_guide/run.rst
@@ -178,12 +178,12 @@ configuration file in the following order and uses the first one it finds:
#. ``/etc/pylintrc``
-The ``--generate-rcfile`` option will generate a commented configuration file
+The ``--generate-toml-config`` option will generate a commented configuration file
on standard output according to the current configuration and exit. This
includes:
* Any configuration file found as explained above
-* Options appearing before ``--generate-rcfile`` on the Pylint command line
+* Options appearing before ``--generate-toml-config`` on the Pylint command line
Of course you can also start with the default values and hand-tune the
configuration.
diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst
index 3e31f447a..525caa7e6 100644
--- a/doc/whatsnew/2.14.rst
+++ b/doc/whatsnew/2.14.rst
@@ -95,6 +95,10 @@ Other Changes
Ref #5392
+* Added the ``generate-toml-config`` option.
+
+ Ref #5462
+
* Fix false negative for ``bad-string-format-type`` if the value to be formatted is passed in
as a variable holding a constant.
diff --git a/pylint/config/arguments_manager.py b/pylint/config/arguments_manager.py
index 2bdd344b4..a4c865dfa 100644
--- a/pylint/config/arguments_manager.py
+++ b/pylint/config/arguments_manager.py
@@ -10,11 +10,15 @@ import configparser
import copy
import optparse # pylint: disable=deprecated-module
import os
+import re
import sys
+import textwrap
import warnings
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, List, Optional, TextIO, Tuple, Union
+import tomlkit
+
from pylint import utils
from pylint.config.argument import (
_Argument,
@@ -595,3 +599,74 @@ class _ArgumentsManager:
def global_set_option(self, opt, value):
"""Set option on the correct option provider."""
self._all_options[opt].set_option(opt, value)
+
+ def _generate_config_file(self) -> None:
+ """Write a configuration file according to the current configuration into stdout."""
+ toml_doc = tomlkit.document()
+ pylint_tool_table = tomlkit.table(is_super_table=True)
+ toml_doc.add(tomlkit.key(["tool", "pylint"]), pylint_tool_table)
+
+ for group in sorted(
+ self._arg_parser._action_groups,
+ key=lambda x: (x.title != "Master", x.title),
+ ):
+ # Skip the options section with the --help option
+ if group.title == "options":
+ continue
+
+ # Skip sections without options such as "positional arguments"
+ if not group._group_actions:
+ continue
+
+ group_table = tomlkit.table()
+ for action in sorted(
+ group._group_actions, key=lambda x: x.option_strings[0][2:]
+ ):
+ optname = action.option_strings[0][2:]
+
+ # We skip old name options that don't have their own optdict
+ try:
+ optdict = self._option_dicts[optname]
+ except KeyError:
+ continue
+
+ if optdict.get("hide_from_config_file"):
+ continue
+
+ # Add help comment
+ help_msg = optdict.get("help", "")
+ assert isinstance(help_msg, str)
+ help_text = textwrap.wrap(help_msg, width=79)
+ for line in help_text:
+ group_table.add(tomlkit.comment(line))
+
+ # Get current value of option
+ value = getattr(self.namespace, optname.replace("-", "_"))
+
+ # Create a comment if the option has no value
+ if not value:
+ group_table.add(tomlkit.comment(f"{optname} ="))
+ group_table.add(tomlkit.nl())
+ continue
+
+ # Tomlkit doesn't support regular expressions
+ if isinstance(value, re.Pattern):
+ value = value.pattern
+ elif isinstance(value, (list, tuple)) and isinstance(
+ value[0], re.Pattern
+ ):
+ value = [i.pattern for i in value]
+
+ # Add to table
+ group_table.add(optname, value)
+ group_table.add(tomlkit.nl())
+
+ assert group.title
+ pylint_tool_table.add(group.title.lower(), group_table)
+
+ toml_string = tomlkit.dumps(toml_doc)
+
+ # Make sure the string we produce is valid toml and can be parsed
+ tomllib.loads(toml_string)
+
+ print(toml_string)
diff --git a/pylint/config/callback_actions.py b/pylint/config/callback_actions.py
index a3904c503..0d87ee122 100644
--- a/pylint/config/callback_actions.py
+++ b/pylint/config/callback_actions.py
@@ -245,6 +245,20 @@ class _GenerateRCFileAction(_AccessRunObjectAction):
sys.exit(0)
+class _GenerateConfigFileAction(_AccessRunObjectAction):
+ """Generate a .toml format configuration file."""
+
+ def __call__(
+ self,
+ parser: argparse.ArgumentParser,
+ namespace: argparse.Namespace,
+ values: Union[str, Sequence[Any], None],
+ option_string: Optional[str] = "--generate-toml-config",
+ ) -> None:
+ self.run.linter._generate_config_file()
+ sys.exit(0)
+
+
class _ErrorsOnlyModeAction(_AccessRunObjectAction):
"""Turn on errors-only mode.
diff --git a/pylint/lint/run.py b/pylint/lint/run.py
index 0a17c1420..07336f78c 100644
--- a/pylint/lint/run.py
+++ b/pylint/lint/run.py
@@ -12,6 +12,7 @@ from pylint.config.callback_actions import (
_DoNothingAction,
_ErrorsOnlyModeAction,
_FullDocumentationAction,
+ _GenerateConfigFileAction,
_GenerateRCFileAction,
_ListCheckGroupsAction,
_ListConfidenceLevelsAction,
@@ -104,6 +105,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"group": "Commands",
"help": "Specify a configuration file to load.",
+ "hide_from_config_file": True,
},
),
(
@@ -114,6 +116,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"group": "Commands",
"help": "Specify an output file.",
+ "hide_from_config_file": True,
},
),
(
@@ -135,6 +138,7 @@ group are mutually exclusive.",
"group": "Commands",
"help": "Display a help message for the given message id and "
"exit. The value may be a comma separated list of message ids.",
+ "hide_from_config_file": True,
},
),
(
@@ -146,6 +150,7 @@ group are mutually exclusive.",
"group": "Commands",
"help": "Display a list of all pylint's messages divided by whether "
"they are emittable with the given interpreter.",
+ "hide_from_config_file": True,
},
),
(
@@ -157,6 +162,7 @@ group are mutually exclusive.",
"group": "Commands",
"help": "Display a list of what messages are enabled, "
"disabled and non-emittable with the given configuration.",
+ "hide_from_config_file": True,
},
),
(
@@ -167,6 +173,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"group": "Commands",
"help": "List pylint's message groups.",
+ "hide_from_config_file": True,
},
),
(
@@ -177,6 +184,7 @@ group are mutually exclusive.",
"kwargs": {"Run": self},
"group": "Commands",
"help": "Generate pylint's confidence levels.",
+ "hide_from_config_file": True,
},
),
(
@@ -187,6 +195,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"group": "Commands",
"help": "List available extensions.",
+ "hide_from_config_file": True,
},
),
(
@@ -197,6 +206,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"group": "Commands",
"help": "Generate pylint's full documentation.",
+ "hide_from_config_file": True,
},
),
(
@@ -210,6 +220,21 @@ group are mutually exclusive.",
"the current configuration. You can put other options "
"before this one to get them in the generated "
"configuration.",
+ "hide_from_config_file": True,
+ },
+ ),
+ (
+ "generate-toml-config",
+ {
+ "action": _GenerateConfigFileAction,
+ "kwargs": {"Run": self},
+ "callback": Run._not_implemented_callback,
+ "group": "Commands",
+ "help": "Generate a sample configuration file according to "
+ "the current configuration. You can put other options "
+ "before this one to get them in the generated "
+ "configuration. The config is in the .toml format.",
+ "hide_from_config_file": True,
},
),
(
@@ -222,6 +247,7 @@ group are mutually exclusive.",
"help": "In error mode, checkers without error messages are "
"disabled and for others, only the ERROR messages are "
"displayed, and no reports are done by default.",
+ "hide_from_config_file": True,
},
),
(
@@ -233,6 +259,7 @@ group are mutually exclusive.",
"short": "v",
"help": "In verbose mode, extra non-checker-related info "
"will be displayed.",
+ "hide_from_config_file": True,
},
),
(
@@ -243,6 +270,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"help": "Load and enable all available extensions. "
"Use --list-extensions to see a list all available extensions.",
+ "hide_from_config_file": True,
},
),
(
@@ -253,6 +281,7 @@ group are mutually exclusive.",
"callback": Run._not_implemented_callback,
"help": "Show more verbose help.",
"group": "Commands",
+ "hide_from_config_file": True,
},
),
),
diff --git a/setup.cfg b/setup.cfg
index 79faf0fef..94a5f197f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -51,6 +51,7 @@ install_requires =
isort>=4.2.5,<6
mccabe>=0.6,<0.8
tomli>=1.1.0;python_version<"3.11"
+ tomlkit>=0.10.1
colorama;sys_platform=="win32"
typing-extensions>=3.10.0;python_version<"3.10"
python_requires = >=3.7.2
@@ -135,6 +136,9 @@ ignore_missing_imports = True
[mypy-git.*]
ignore_missing_imports = True
+[mypy-tomlkit]
+ignore_missing_imports = True
+
[mypy-sphinx.*]
ignore_missing_imports = True
diff --git a/tests/test_self.py b/tests/test_self.py
index c8c943e10..24b564ede 100644
--- a/tests/test_self.py
+++ b/tests/test_self.py
@@ -15,7 +15,7 @@ import sys
import textwrap
import warnings
from copy import copy
-from io import StringIO
+from io import BytesIO, StringIO
from os.path import abspath, dirname, join
from pathlib import Path
from typing import TYPE_CHECKING, Any, Generator, Iterator, List, Optional, TextIO
@@ -34,6 +34,12 @@ from pylint.reporters import JSONReporter
from pylint.reporters.text import BaseReporter, ColorizedTextReporter, TextReporter
from pylint.utils import utils
+if sys.version_info >= (3, 11):
+ import tomllib
+else:
+ import tomli as tomllib
+
+
if TYPE_CHECKING:
from pylint.reporters.ureports.nodes import Section
@@ -1366,6 +1372,54 @@ class TestCallbackOptions:
assert "suppressed-message" in messages
@staticmethod
+ def test_generate_toml_config() -> None:
+ """Test the --generate-toml-config flag."""
+ process = subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "pylint",
+ "--preferred-modules=a:b",
+ "--generate-toml-config",
+ ],
+ capture_output=True,
+ encoding="utf-8",
+ check=False,
+ )
+ assert "[tool.pylint.master]" in process.stdout
+ assert '"positional arguments"' not in process.stdout
+ assert 'preferred-modules = ["a:b"]' in process.stdout
+
+ process_two = subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "pylint",
+ "--preferred-modules=a:b",
+ "--generate-toml-config",
+ ],
+ capture_output=True,
+ encoding="utf-8",
+ check=False,
+ )
+ assert process.stdout == process_two.stdout
+
+ @staticmethod
+ def test_generate_toml_config_disable_symbolic_names() -> None:
+ """Test that --generate-toml-config puts symbolic names in the --disable option."""
+ out = StringIO()
+ with _patch_streams(out):
+ with pytest.raises(SystemExit):
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ Run(["--generate-toml-config"])
+
+ bytes_out = BytesIO(out.getvalue().encode("utf-8"))
+ content = tomllib.load(bytes_out)
+ messages = content["tool"]["pylint"]["messages control"]["disable"]
+ assert "invalid-name" in messages, out.getvalue()
+
+ @staticmethod
def test_errors_only() -> None:
"""Test the --errors-only flag."""
with pytest.raises(SystemExit):