config: Add postprocessor for converting config back.

Idea forward from here is that once we have a config sub command that we expose
a setting config values which will:

1. Run the preprocessor on the file to edit.
2. Load it into config parser.
3. Modify the value.
4. Write it to a io.ByteString
5. Run the postprocessor
6. Save the file with comments etc intact.
This commit is contained in:
Thomas Adamcik 2013-10-27 12:30:02 +01:00
parent d5cb4282d9
commit 73f91710e1
2 changed files with 90 additions and 15 deletions

View File

@ -147,7 +147,7 @@ def _format(config, comments, schemas, display):
return b'\n'.join(output)
def _preprocess(string):
def _preprocess(config_string):
"""Convert a raw config into a form that preserves comments etc."""
results = ['[__COMMENTS__]']
counter = itertools.count(0)
@ -173,7 +173,7 @@ def _preprocess(string):
return '%s\n__SECTION%d__ = %s' % (
match.group(1), next(counter), match.group(2))
for line in string.splitlines():
for line in config_string.splitlines():
line = blank_line_re.sub(newlines, line)
line = section_re.sub(sections, line)
line = comment_re.sub(comments, line)
@ -182,6 +182,18 @@ def _preprocess(string):
return '\n'.join(results)
def _postprocess(config_string):
"""Converts a preprocessed config back to original form."""
flags = re.IGNORECASE | re.MULTILINE
result = re.sub(r'^\[__COMMENTS__\](\n|$)', '', config_string, flags=flags)
result = re.sub(r'\n__INLINE\d+__ =(.*)$', ' ;\g<1>', result, flags=flags)
result = re.sub(r'^__HASH\d+__ =(.*)$', '#\g<1>', result, flags=flags)
result = re.sub(r'^__SEMICOLON\d+__ =(.*)$', ';\g<1>', result, flags=flags)
result = re.sub(r'\n__SECTION\d+__ =(.*)$', '\g<1>', result, flags=flags)
result = re.sub(r'^__BLANK\d+__ =$', '', result, flags=flags)
return result
class Proxy(collections.Mapping):
def __init__(self, data):
self._data = data

View File

@ -117,8 +117,7 @@ this-should-equal-everything = baz # as this is not a comment
# this is also a comment ; and the next line should be a blank comment.
;
# foo # = should all be treated as a comment.
"""
# foo # = should all be treated as a comment."""
PROCESSED_CONFIG = """[__COMMENTS__]
__HASH0__ = comments before first section should work
@ -137,20 +136,20 @@ __SEMICOLON9__ =
__HASH10__ = foo # = should all be treated as a comment."""
class ProcessorTest(unittest.TestCase):
maxDiff = None # Show entire diff.
class PreProcessorTest(unittest.TestCase):
maxDiff = None # Show entire diff.
def test_preprocessor_empty_config(self):
def test_empty_config(self):
result = config._preprocess('')
self.assertEqual(result, '[__COMMENTS__]')
def test_preprocessor_plain_section(self):
def test_plain_section(self):
result = config._preprocess('[section]\nfoo = bar')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'foo = bar')
def test_preprocessor_initial_comments(self):
def test_initial_comments(self):
result = config._preprocess('; foobar')
self.assertEqual(result, '[__COMMENTS__]\n'
'__SEMICOLON0__ = foobar')
@ -164,41 +163,105 @@ class ProcessorTest(unittest.TestCase):
'__SEMICOLON0__ = foo\n'
'__HASH1__ = bar')
def test_preprocessor_initial_comment_inline_handling(self):
def test_initial_comment_inline_handling(self):
result = config._preprocess('; foo ; bar ; baz')
self.assertEqual(result, '[__COMMENTS__]\n'
'__SEMICOLON0__ = foo\n'
'__INLINE1__ = bar\n'
'__INLINE2__ = baz')
def test_preprocessor_inline_semicolon_comment(self):
def test_inline_semicolon_comment(self):
result = config._preprocess('[section]\nfoo = bar ; baz')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'foo = bar\n'
'__INLINE0__ = baz')
def test_preprocessor_no_inline_hash_comment(self):
def test_no_inline_hash_comment(self):
result = config._preprocess('[section]\nfoo = bar # baz')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'foo = bar # baz')
def test_preprocessor_section_extra_text(self):
def test_section_extra_text(self):
result = config._preprocess('[section] foobar')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'__SECTION0__ = foobar')
def test_preprocessor_section_extra_text_inline_semicolon(self):
def test_section_extra_text_inline_semicolon(self):
result = config._preprocess('[section] foobar ; baz')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'__SECTION0__ = foobar\n'
'__INLINE1__ = baz')
def test_preprocessor_conversion(self):
def test_conversion(self):
"""Tests all of the above cases at once."""
result = config._preprocess(INPUT_CONFIG)
self.assertEqual(result, PROCESSED_CONFIG)
class PostProcessorTest(unittest.TestCase):
maxDiff = None # Show entire diff.
def test_empty_config(self):
result = config._postprocess('[__COMMENTS__]')
self.assertEqual(result, '')
def test_plain_section(self):
result = config._postprocess('[__COMMENTS__]\n'
'[section]\n'
'foo = bar')
self.assertEqual(result, '[section]\nfoo = bar')
def test_initial_comments(self):
result = config._postprocess('[__COMMENTS__]\n'
'__SEMICOLON0__ = foobar')
self.assertEqual(result, '; foobar')
result = config._postprocess('[__COMMENTS__]\n'
'__HASH0__ = foobar')
self.assertEqual(result, '# foobar')
result = config._postprocess('[__COMMENTS__]\n'
'__SEMICOLON0__ = foo\n'
'__HASH1__ = bar')
self.assertEqual(result, '; foo\n# bar')
def test_initial_comment_inline_handling(self):
result = config._postprocess('[__COMMENTS__]\n'
'__SEMICOLON0__ = foo\n'
'__INLINE1__ = bar\n'
'__INLINE2__ = baz')
self.assertEqual(result, '; foo ; bar ; baz')
def test_inline_semicolon_comment(self):
result = config._postprocess('[__COMMENTS__]\n'
'[section]\n'
'foo = bar\n'
'__INLINE0__ = baz')
self.assertEqual(result, '[section]\nfoo = bar ; baz')
def test_no_inline_hash_comment(self):
result = config._preprocess('[section]\nfoo = bar # baz')
self.assertEqual(result, '[__COMMENTS__]\n'
'[section]\n'
'foo = bar # baz')
def test_section_extra_text(self):
result = config._postprocess('[__COMMENTS__]\n'
'[section]\n'
'__SECTION0__ = foobar')
self.assertEqual(result, '[section] foobar')
def test_section_extra_text_inline_semicolon(self):
result = config._postprocess('[__COMMENTS__]\n'
'[section]\n'
'__SECTION0__ = foobar\n'
'__INLINE1__ = baz')
self.assertEqual(result, '[section] foobar ; baz')
def test_conversion(self):
result = config._postprocess(PROCESSED_CONFIG)
self.assertEqual(result, INPUT_CONFIG)