diff options
author | Fergal Hainey <fergal.hainey@skyscanner.net> | 2018-03-25 00:53:02 +0000 |
---|---|---|
committer | Ashley Whetter <ashley@awhetter.co.uk> | 2019-02-09 13:40:54 -0800 |
commit | b5a2fc72b786ab8475ccbfd69532900e08944ce1 (patch) | |
tree | 4f6c40777726255eb5539af3260efe86115b0f00 | |
parent | cd41c2b82500220e77a70f7c3143350ada6c9f25 (diff) | |
download | pylint-git-b5a2fc72b786ab8475ccbfd69532900e08944ce1.tar.gz |
Add help comment writing in INI files
-rw-r--r-- | pylint/config.py | 31 | ||||
-rw-r--r-- | pylint/test/unittest_config.py | 55 | ||||
-rw-r--r-- | pylint/utils.py | 56 |
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: |