summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFergal Hainey <fergal.hainey@skyscanner.net>2018-03-25 00:53:02 +0000
committerAshley Whetter <ashley@awhetter.co.uk>2019-02-09 13:40:54 -0800
commitb5a2fc72b786ab8475ccbfd69532900e08944ce1 (patch)
tree4f6c40777726255eb5539af3260efe86115b0f00
parentcd41c2b82500220e77a70f7c3143350ada6c9f25 (diff)
downloadpylint-git-b5a2fc72b786ab8475ccbfd69532900e08944ce1.tar.gz
Add help comment writing in INI files
-rw-r--r--pylint/config.py31
-rw-r--r--pylint/test/unittest_config.py55
-rw-r--r--pylint/utils.py56
3 files changed, 83 insertions, 59 deletions
diff --git a/pylint/config.py b/pylint/config.py
index c1e302f61..54ac0486e 100644
--- a/pylint/config.py
+++ b/pylint/config.py
@@ -42,6 +42,7 @@ import os
import pickle
import re
import sys
+import textwrap
import configparser
@@ -534,6 +535,29 @@ class FileParser(ConfigParser, metaclass=abc.ABCMeta):
pass
+def make_commenter(description):
+ """Make a function to add comments from re.sub."""
+
+ def make_comment(match):
+ """Make the replacement string including commented desciption."""
+ comment = "\n".join("# {}".format(line) for line in textwrap.wrap(description))
+ return match.expand(r"{}\n\g<0>".format(comment))
+
+ return make_comment
+
+
+def make_commented_config_text(option_definitions, config_text):
+ """Make config ini text with descriptions added as comments."""
+ for name, definition in option_definitions.items():
+ config_text = re.sub(
+ r"^{}[ =]".format(re.escape(name)),
+ make_commenter(definition["help"]),
+ config_text,
+ flags=re.MULTILINE,
+ )
+ return config_text
+
+
class IniFileParser(FileParser):
"""Parses a config files into config objects."""
@@ -599,8 +623,11 @@ class IniFileParser(FileParser):
config.set_option(option, value)
def write(self, stream=sys.stdout):
- # TODO: Check if option descriptions are written out
- self._parser.write(stream)
+ with six.StringIO() as temp_stream:
+ self._parser.write(temp_stream)
+ config_text = temp_stream.getvalue()
+ config_text = make_commented_config_text(self._option_definitions, config_text)
+ stream.write(config_text)
class LongHelpFormatter(argparse.HelpFormatter):
diff --git a/pylint/test/unittest_config.py b/pylint/test/unittest_config.py
index 327870640..0ef6c331e 100644
--- a/pylint/test/unittest_config.py
+++ b/pylint/test/unittest_config.py
@@ -12,9 +12,10 @@
import re
import sre_constants
-from pylint import config
import pytest
+from pylint import config
+
RE_PATTERN_TYPE = getattr(re, "Pattern", getattr(re, "_pattern_type", None))
@@ -60,3 +61,55 @@ def test__regexp_csv_validator_invalid():
pattern_strings = ["test_.*", "foo\\.bar", "^baz)$"]
with pytest.raises(sre_constants.error):
config.VALIDATORS["regexp_csv"](",".join(pattern_strings))
+
+
+@pytest.mark.comments
+def test_make_commenter():
+ """Test make_commenter."""
+ commenter = config.make_commenter(
+ (
+ "This is a long description, I am trying to test wrapping "
+ "works OK. I hope this test passes."
+ )
+ )
+ match = re.match("^check ", "check = hey")
+ assert commenter(match) == (
+ "# This is a long description, I am trying to test "
+ "wrapping works OK. I\n"
+ "# hope this test passes.\n"
+ "check "
+ )
+
+
+@pytest.mark.comments
+def test_make_commented_config_text():
+ """Test make_commented_config_text."""
+ assert config.make_commented_config_text(
+ {
+ "check": {"help": "Check something."},
+ "validate": {"help": "Validate a thing."},
+ },
+ "check = 1\nvalidate=2\nother = 3",
+ ) == (
+ "# Check something.\n"
+ "check = 1\n"
+ "# Validate a thing.\n"
+ "validate=2\n"
+ "other = 3"
+ )
+
+
+@pytest.mark.comments
+def test_write(tmpdir):
+ """Test IniFileParser.write."""
+ parser = config.IniFileParser()
+ parser.add_option_definitions(
+ (("check", {"help": "Check something.", "group": "checks"}),)
+ )
+ config_path = tmpdir.join("config.cfg")
+ config_path.write("[checks]\ncheck = 1")
+ parser.parse(str(config_path), config.Configuration())
+ output_path = tmpdir.join("output.cfg")
+ with output_path.open("w") as output:
+ parser.write(stream=output)
+ assert output_path.read() == "[CHECKS]\n# Check something.\ncheck = 1\n\n"
diff --git a/pylint/utils.py b/pylint/utils.py
index 88c1100c5..9c32546cd 100644
--- a/pylint/utils.py
+++ b/pylint/utils.py
@@ -1416,62 +1416,6 @@ def _check_csv(value):
return _splitstrip(value)
-def _comment(string):
- """return string as a comment"""
- lines = [line.strip() for line in string.splitlines()]
- return "# " + ("%s# " % os.linesep).join(lines)
-
-
-def _format_option_value(optdict, value):
- """return the user input's value from a 'compiled' value"""
- if isinstance(value, (list, tuple)):
- value = ",".join(_format_option_value(optdict, item) for item in value)
- elif isinstance(value, dict):
- value = ",".join("%s:%s" % (k, v) for k, v in value.items())
- elif hasattr(value, "match"): # optdict.get('type') == 'regexp'
- # compiled regexp
- value = value.pattern
- elif optdict.get("type") == "yn":
- value = "yes" if value else "no"
- elif isinstance(value, str) and value.isspace():
- value = "'%s'" % value
- return value
-
-
-def _ini_format_section(stream, section, options, doc=None):
- """format an options section using the INI format"""
- if doc:
- print(_comment(doc), file=stream)
- print("[%s]" % section, file=stream)
- _ini_format(stream, options)
-
-
-def _ini_format(stream, options):
- """format options using the INI format"""
- for optname, optdict, value in options:
- value = _format_option_value(optdict, value)
- help_opt = optdict.get("help")
- if help_opt:
- help_opt = _normalize_text(help_opt, line_len=79, indent="# ")
- print(file=stream)
- print(help_opt, file=stream)
- else:
- print(file=stream)
- if value is None:
- print("#%s=" % optname, file=stream)
- else:
- value = str(value).strip()
- if re.match(r"^([\w-]+,)+[\w-]+$", str(value)):
- separator = "\n " + " " * len(optname)
- value = separator.join(x + "," for x in str(value).split(","))
- # remove trailing ',' from last element of the list
- value = value[:-1]
- print("%s=%s" % (optname, value), file=stream)
-
-
-format_section = _ini_format_section
-
-
def _rest_format_section(stream, section, options, doc=None):
"""format an options section using as ReST formatted output"""
if section: