diff --git a/README.md b/README.md index d16f95c..ab0684d 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,12 @@ --- -A custom ConfigParser class that preserves comments and option casing when writing loaded config out. +A custom ConfigParser class that preserves comments and most formatting when +writing loaded config out. -This library gives you a custom class of the standard library's `configparser.ConfigParger` which will preserve the comments of a loaded config file when writing that file back out. +This library gives you a custom class of the standard library's +`configparser.ConfigParger` which will preserve the comments of a loaded config +file when writing that file back out. --- @@ -58,7 +61,12 @@ with open("myconfig.ini", "w") as savefile: ## Results -We favor the line spacing choices of the `ConfigParser` class so the input format may not be preserved completely. However, the comments will be preserved. +There is the attempt to retain the original format. Known formats that will not +be preserved: + +1. Indents will not be preserved outside of multi-line values +2. Spacing around assignments will be normalized. +3. Casing of all options will be written as lowercase. ### Before @@ -83,7 +91,6 @@ multi-line= value03 closing=0 # Trailing comment - ``` ### After @@ -97,9 +104,10 @@ foo = bar trace = false logging = true ; This is a comment as well + # so we need to track all of them -; and many could be between things +; and many could be between things [NEW SECTION] # Another comment multi-line = @@ -108,5 +116,4 @@ multi-line = value03 closing = 0 # Trailing comment - ``` diff --git a/src/commentedconfigparser/commentedconfigparser.py b/src/commentedconfigparser/commentedconfigparser.py index 5ad4a61..6e7db8f 100644 --- a/src/commentedconfigparser/commentedconfigparser.py +++ b/src/commentedconfigparser/commentedconfigparser.py @@ -17,6 +17,7 @@ __all__ = ["CommentedConfigParser"] _COMMENT_PATTERN = re.compile(r"^\s*[#|;]\s*(.*)$") +_BLANK_LINE_PATTERN = re.compile(r"^\s*$") _COMMENT_OPTION_PATTERN = re.compile(r"^(\s*)?__comment_\d+\s?[=|:]\s?(.*)$") _KEY_PATTERN = re.compile(r"^(.+?)\s?[=|:].*$") _SECTION_PATTERN = re.compile(r"^\s*\[(.+)\]\s*$") @@ -98,6 +99,11 @@ def _translate_comments(self, content: list[str]) -> str: # are handled by the parent and retain order of insertion. line = f"__comment_{self.__file_index}{idx}={line.lstrip()}" + elif _BLANK_LINE_PATTERN.match(line): + # Blank lines cannot be stripped or the newline char will + # be lost which causes issues downstream. + line = f"__comment_{self.__file_index}{idx}={line}" + elif _KEY_PATTERN.match(line) or _SECTION_PATTERN.match(line): # Strip the left whitespace from sections and keys. This will # leave only multiline values with leading whitespace preventing @@ -121,13 +127,14 @@ def _restore_comments(self, content: str) -> str: rendered += self.__header_block for line in content.splitlines(): + if not line: + # Skip the provided empty lines and use our own instead + continue + comment_match = _COMMENT_OPTION_PATTERN.match(line) if comment_match: line = comment_match.group(2) rendered.append(line + "\n") - # Remove extra trailing newline - rendered[-1] = rendered[-1].rstrip() - return "".join(rendered) diff --git a/tests/empty_comments_expected.ini b/tests/empty_comments_expected.ini index ea13022..ded4be4 100644 --- a/tests/empty_comments_expected.ini +++ b/tests/empty_comments_expected.ini @@ -4,11 +4,14 @@ [DEFAULT] foo = bar + # Comment here # bar = foo ; ; Comment here too + [BIZ] baz = bar + diff --git a/tests/empty_comments_input.ini b/tests/empty_comments_input.ini index 5bea392..5f887d0 100644 --- a/tests/empty_comments_input.ini +++ b/tests/empty_comments_input.ini @@ -14,3 +14,4 @@ bar = foo [BIZ] baz=bar + diff --git a/tests/pydocs_expected.ini b/tests/pydocs_expected.ini index dcb500b..498d440 100644 --- a/tests/pydocs_expected.ini +++ b/tests/pydocs_expected.ini @@ -25,6 +25,7 @@ empty string value here = [You can use comments] # like this ; or this + # By default only in an empty line. # Inline comments can be harmful because they prevent users # from using the delimiting characters as parts of values. diff --git a/tests/regression_original_expected.ini b/tests/regression_original_expected.ini index f4da53f..2868c1e 100644 --- a/tests/regression_original_expected.ini +++ b/tests/regression_original_expected.ini @@ -6,9 +6,10 @@ foo = bar trace = false logging = true ; This is a comment as well + # so we need to track all of them -; and many could be between things +; and many could be between things [NEW SECTION] foo = bar # Unique foo