Created
June 29, 2015 20:23
-
-
Save mfm24/d4e8ea3afe2140374bb5 to your computer and use it in GitHub Desktop.
This file contains 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 characters
# config_sectioner | |
# | |
# Treats a text file as a python file object, only | |
# inserting, adding or removing text in a marked off section. | |
# If the section doesn't exist it is appended to the file. | |
# Include information about the last date the file was written | |
# and by which programs. All blocks have a unique key, so that | |
# a file can contain multiple ConfigSections with different keys. | |
from __future__ import division, print_function | |
from cStringIO import StringIO | |
import time | |
import logging | |
__PROGRAM__ = "ConfigSection" | |
__VERSION__ = "0.01" | |
class ConfigSection(object): | |
def __init__(self, key, inner_stream, append=False, line_comment='#'): | |
self.key = key | |
self.stream = inner_stream | |
if append: | |
self.mid_io = StringIO(self._get_mid_data()) | |
else: | |
self.mid_io = StringIO() | |
self.mid_io.seek(0) | |
self.section_text = "" | |
self.line_comment = line_comment | |
def read(self, n=-1): | |
# NB docs say: | |
# When size is omitted or negative, the entire | |
# contents of the file will be read and returned | |
return self.mid_io.read(n) | |
def write(self, data): | |
self.mid_io.write(data) | |
def seek(self, pos): | |
self.mid_io.seek(pos) | |
def tell(self): | |
self.mid_io.tell() | |
def flush(self): | |
self._write_mid_data(self.mid_io) | |
# we'll flush if we can | |
try: | |
self.stream.flush() | |
except AttributeError: | |
pass | |
def get_start_header(self): | |
return "{} ----- BEGIN {} -----".format(self.line_comment, self.key) | |
def get_end_header(self): | |
return "{} ----- END {} -----".format(self.line_comment, self.key) | |
def get_banner(self): | |
dat = """# | |
# Auto-generated section. | |
# This section of the file generated by {PROGRAM} v{VERSION} on {TIME}. | |
# WARNING: DO NOT EDIT ANY TEXT BETWEEN THE "BEGIN {KEY}" LINE ABOVE | |
# AND THE "END {KEY}" LINE BELOW! These lines have been automatically | |
# generated by a tool and any text contained between these lines may be | |
# regenerated or deleted at any time! | |
""" | |
t = time.asctime() | |
ret = "\n".join( | |
l.strip().replace('#', self.line_comment).format( | |
PROGRAM=__PROGRAM__, | |
VERSION=__VERSION__, | |
TIME=t, | |
KEY=self.key) | |
for l in dat.splitlines() | |
) | |
return ret | |
@staticmethod | |
def split(stream, delim, maxsplit=None): | |
""" Returns a list of StringIOs splitting stream with delim. | |
If maxplit is specified, we split that many times.""" | |
ret = [StringIO()] | |
for d in stream: | |
i = d.find(delim) | |
if i >= 0 and (maxsplit is None or len(ret) <= maxsplit): | |
ret[-1].write(d[:i]) | |
ret.append(StringIO()) | |
ret[-1].write(d[i+len(delim):]) | |
else: | |
ret[-1].write(d) | |
return ret | |
def _write_mid_data(self, mid_data): | |
"""We search for our section delimiters in the file, and | |
overwrite them with the start header, the banner, the wanted text, | |
then the end banner, followed by the rest of the file.""" | |
start_header = self.get_start_header() | |
end_header = self.get_end_header() | |
self.stream.seek(0) | |
pre_mid = self.split(self.stream, start_header) | |
try: | |
pre, mid = pre_mid | |
except ValueError: | |
pre = pre_mid[0] | |
# add line ffeds separating the block: | |
start_header = "\n" + start_header | |
end_header = end_header + "\n" | |
post = StringIO() | |
else: | |
mid.seek(0) | |
mid_post = self.split(mid, end_header) | |
try: | |
mid, post = mid_post | |
except ValueError: | |
# no end block found. In this case we err on side of caution | |
# and leave everything intact | |
logging.warn( | |
"No end block found! Keeping all text after start block!") | |
post = mid_post[0] | |
self.stream.seek(0) | |
for s in [pre, | |
StringIO(start_header+"\n"+self.get_banner()), | |
mid_data, | |
StringIO("\n"+end_header), | |
post]: | |
s.seek(0) | |
self.stream.write(s.read()) | |
def _get_mid_data(self): | |
s = self.get_start_header() | |
e = self.get_end_header() | |
self.stream.seek(0) | |
pre, mid = self.split(self.stream, s) | |
mid.seek(0) | |
mid, post = self.split(mid, e) | |
mid.seek(0) | |
return "\n".join(x for x in mid if not x.startswith(self.line_comment)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment