Last active
July 6, 2025 02:44
-
-
Save Jip-Hop/d82781da424724b4018bdfc5a2f1318b to your computer and use it in GitHub Desktop.
Revisions
-
Jip-Hop revised this gist
Jun 29, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -24,7 +24,7 @@ def __init__(self, *args, **kwargs): # Template to store comments as key value pair self._comment_template = "#{0} " + delimiter + " {1}" # Regex to match the comment prefix self._comment_regex = re.compile(r"^#\d+\s*" + re.escape(delimiter) + r"[^\S\n]*") # Regex to match cosmetic newlines (skips newlines in multiline values): # consecutive whitespace from start of line followed by a line not starting with whitespace self._cosmetic_newlines_regex = re.compile(r"^(\s+)(?=^\S)", re.MULTILINE) -
Jip-Hop revised this gist
Jun 29, 2024 . 1 changed file with 7 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,14 +2,15 @@ See the example usage inside `configparser.py`. Output when running the `configp ``` # Comments may appear before the first section [Simple Values] key = value spaces in keys = allowed spaces in values = allowed as well spaces around the delimiter = obviously you can also use = to delimit keys from values # We will update some values in this section [All Values Are Strings] values like this = 2000000 or this = 2 @@ -34,6 +35,10 @@ empty string value here = [You can use comments] # like this ; or this # 1 empty line above and 2 below, also preserved # 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. @@ -47,6 +52,7 @@ multiline_values = #!/usr/bin/env bash # You can even write a little multiline bash script echo 'This script snippet is not mangled!' echo "added some $RANDOM stuff from python" # Did I mention we can NOT indent comments, either? # Indenting is reserved to handle multiline values we can add keys = with some value -
Jip-Hop revised this gist
Feb 25, 2024 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -19,10 +19,12 @@ def __init__(self, *args, **kwargs): self._comment_prefixes = () # Starting point for the comment IDs self._comment_id = 0 # Default delimiter to use delimiter = self._delimiters[0] # Template to store comments as key value pair self._comment_template = "#{0} " + delimiter + " {1}" # Regex to match the comment prefix self._comment_regex = re.compile(f"^#\d+\s*{re.escape(delimiter)}[^\S\n]*") # Regex to match cosmetic newlines (skips newlines in multiline values): # consecutive whitespace from start of line followed by a line not starting with whitespace self._cosmetic_newlines_regex = re.compile(r"^(\s+)(?=^\S)", re.MULTILINE) -
Jip-Hop revised this gist
Feb 25, 2024 . 1 changed file with 5 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,8 @@ def __init__(self, *args, **kwargs): self._comment_prefixes_backup = self._comment_prefixes # Unset _comment_prefixes so comments won't be skipped self._comment_prefixes = () # Starting point for the comment IDs self._comment_id = 0 # Template to store comments as key value pair self._comment_template = "#{0} = {1}" # Regex to match the comment prefix @@ -53,8 +55,9 @@ def _read(self, fp, fpname): elif i in cosmetic_newline_indices or line.startswith( self._comment_prefixes_backup ): # Store cosmetic newline or comment with unique key lines[i] = self._comment_template.format(self._comment_id, line) self._comment_id += 1 # Feed the preprocessed file to the original _read method return super()._read(io.StringIO("".join(lines)), fpname) -
Jip-Hop revised this gist
Feb 25, 2024 . 1 changed file with 5 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -79,6 +79,11 @@ def write(self, fp, space_around_delimiters=True): fp.write("".join(self._top_comments + lines).rstrip()) def clear(self): # Also clear the _top_comments self._top_comments = [] super().clear() # Example usage: -
Jip-Hop revised this gist
Feb 24, 2024 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -21,9 +21,9 @@ def __init__(self, *args, **kwargs): self._comment_template = "#{0} = {1}" # Regex to match the comment prefix self._comment_regex = re.compile(r"^#\d+\s*=[^\S\n]*") # Regex to match cosmetic newlines (skips newlines in multiline values): # consecutive whitespace from start of line followed by a line not starting with whitespace self._cosmetic_newlines_regex = re.compile(r"^(\s+)(?=^\S)", re.MULTILINE) # List to store comments above the first section self._top_comments = [] -
Jip-Hop revised this gist
Feb 24, 2024 . 2 changed files with 38 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -19,26 +19,42 @@ def __init__(self, *args, **kwargs): self._comment_prefixes = () # Template to store comments as key value pair self._comment_template = "#{0} = {1}" # Regex to match the comment prefix self._comment_regex = re.compile(r"^#\d+\s*=[^\S\n]*") # Regex to match cosmetic newlines (to filter out newlines in multiline values): # consecutive empty lines followed by a line which does not start with whitespace self._cosmetic_newlines_regex = re.compile(r"^(\s*\n)+(?=\S)", re.MULTILINE) # List to store comments above the first section self._top_comments = [] def _find_cosmetic_newlines(self, text): # Indices of the lines containing cosmetic newlines cosmetic_newline_indices = set() for match in re.finditer(self._cosmetic_newlines_regex, text): start_index = text.count("\n", 0, match.start()) end_index = start_index + text.count("\n", match.start(), match.end()) cosmetic_newline_indices.update(range(start_index, end_index)) return cosmetic_newline_indices def _read(self, fp, fpname): lines = fp.readlines() cosmetic_newline_indices = self._find_cosmetic_newlines("".join(lines)) above_first_section = True # Preprocess config file to preserve comments for i, line in enumerate(lines): if line.startswith("["): above_first_section = False elif above_first_section: # Remove this line for now lines[i] = "" self._top_comments.append(line) elif i in cosmetic_newline_indices or line.startswith( self._comment_prefixes_backup ): # Store cosmetic newline or comment with unique key based on line index lines[i] = self._comment_template.format(i, line) # Feed the preprocessed file to the original _read method return super()._read(io.StringIO("".join(lines)), fpname) @@ -51,11 +67,17 @@ def write(self, fp, space_around_delimiters=True): sfile.seek(0) lines = sfile.readlines() cosmetic_newline_indices = self._find_cosmetic_newlines("".join(lines)) for i, line in enumerate(lines): if i in cosmetic_newline_indices: # Remove newlines added below each section by .write() lines[i] = "" continue # Remove the comment prefix (if regex matches) lines[i] = self._comment_regex.sub("", line, 1) fp.write("".join(self._top_comments + lines).rstrip()) # Example usage: This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ # Comments may appear before the first section [Simple Values] key=value spaces in keys=allowed @@ -32,6 +33,9 @@ empty string value here = # like this ; or this # 1 empty line above and 2 below, also preserved # 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. -
Jip-Hop renamed this gist
Feb 24, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
Jip-Hop renamed this gist
Feb 24, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
Jip-Hop created this gist
Feb 22, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,53 @@ See the example usage inside `configparser.py`. Output when running the `configparser.py` file: ``` # Comments may appear before the first section [Simple Values] key = value spaces in keys = allowed spaces in values = allowed as well spaces around the delimiter = obviously you can also use = to delimit keys from values # We will update some values in this section [All Values Are Strings] values like this = 2000000 or this = 2 # The comments will remain are they treated as numbers? = no # We can even use duplicate comments # We can even use duplicate comments integers, floats and booleans are held as = strings # cOmMeNt cAsInG Is pReSeRvEd can use the api to get converted values directly = true // We can even use the // prefix to write comments [Multiline Values] # We can even use duplicate comments chorus = I'm a lumberjack, and I'm okay I sleep all night and I work all day [No Values] key_without_value 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. # That being said, this can be customized. [Sections Can NOT Be Indented] can_values_be_as_well = False multiline_values = #!/usr/bin/env bash set -euo pipefail # You can even write a little multiline bash script echo 'This script snippet is not mangled!' echo "added some $RANDOM stuff from python" # Did I mention we can NOT indent comments, either? # Indenting is reserved to handle multiline values we can add keys = with some value ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,49 @@ # Comments may appear before the first section [Simple Values] key=value spaces in keys=allowed spaces in values=allowed as well spaces around the delimiter = obviously you can also use : to delimit keys from values # We will update some values in this section [All Values Are Strings] values like this: 1000000 or this: 3.14159265359 # The comments will remain are they treated as numbers? : no # We can even use duplicate comments # We can even use duplicate comments integers, floats and booleans are held as: strings # cOmMeNt cAsInG Is pReSeRvEd can use the API to get converted values directly: true // We can even use the // prefix to write comments [Multiline Values] # We can even use duplicate comments chorus: I'm a lumberjack, and I'm okay I sleep all night and I work all day [No Values] key_without_value 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. # That being said, this can be customized. [Sections Can NOT Be Indented] can_values_be_as_well = False multiline_values = #!/usr/bin/env bash set -euo pipefail # You can even write a little multiline bash script echo 'This script snippet is not mangled!' # Did I mention we can NOT indent comments, either? # Indenting is reserved to handle multiline values This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,86 @@ #!/usr/bin/env python3 import configparser import io import re class CommentConfigParser(configparser.ConfigParser): """Comment preserving ConfigParser. Limitation: No support for indenting section headers, comments and keys. They should have no leading whitespace. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Backup _comment_prefixes self._comment_prefixes_backup = self._comment_prefixes # Unset _comment_prefixes so comments won't be skipped self._comment_prefixes = () # Template to store comments as key value pair self._comment_template = "#{0} = {1}" # Regex to match the comment id prefix self._comment_regex = re.compile(r"^#\d+\s*=\s*") # List to store comments above the first section self._top_comments = [] def _read(self, fp, fpname): lines = fp.readlines() above_first_section = True # Preprocess config file to preserve comments for i, line in enumerate(lines): if line.startswith("["): above_first_section = False elif line.startswith(self._comment_prefixes_backup): if above_first_section: # Remove this line for now lines[i] = "" self._top_comments.append(line) else: # Store comment as value with unique key based on line number lines[i] = self._comment_template.format(i, line) # Feed the preprocessed file to the original _read method return super()._read(io.StringIO("".join(lines)), fpname) def write(self, fp, space_around_delimiters=True): # Write the config to an in-memory file with io.StringIO() as sfile: super().write(sfile, space_around_delimiters) # Start from the beginning of sfile sfile.seek(0) lines = sfile.readlines() for i, line in enumerate(lines): # Remove the comment id prefix lines[i] = self._comment_regex.sub("", line, 1) fp.write("".join(self._top_comments + lines)) # Example usage: # Instantiate the comment preserving config parser parser = CommentConfigParser( interpolation=None, allow_no_value=True, comment_prefixes=("//", "#", ";"), ) parser.read("./config") section = parser["All Values Are Strings"] section["values like this"] = str( parser.getint("All Values Are Strings", "values like this") * 2 ) section["or this"] = "2" section = parser["Sections Can NOT Be Indented"] section["we can add keys"] = "with some value" section["multiline_values"] += '\necho "added some $RANDOM stuff from python"' # Print the modified config file to stdout with comments preserved with io.StringIO() as sfile: parser.write(sfile) # Start from the beginning of sfile sfile.seek(0) print(sfile.read(), end="")