in python/packages/mysql_gadgets/common/config_parser.py [0:0]
def write(self, backup_file_path=None):
"""Write configurations to the option file.
If configurations were modified it saves those changes to file,
the order of the sections/options is preserved as well as existing
comments (except inline comments of deleted options or sections).
:param backup_file_path: if provided, we try to create a create a
backup of the original configuration file on
the provided path. It must be an absolute
path.
:raises GadgetConfigParserError: If the user does not have permissions
to overwrite the option file or to create a backup file (if asked to)
or if an error occurred while parsing the configuration file.
"""
in_memory_sections = set(self._main_opt_parser.sections())
read_sections = set()
read_options = set()
drop_section = False
cursect = None
line = ""
output_file = self.filename
def optval_tostr(opt, val):
"""Auxiliary function used to obtain a formatted string with
an option and option value.
"""
if val:
return u"{0} = {1}".format(opt, val)
return opt
if not self.modified and self.output_option_filename is None:
# if we did not do any modifications to what was read from file,
# we can simply exit.
return
try:
with open(fs_encode(self.filename), 'r') as f:
lines = f.readlines()
except IOError as err:
raise GadgetConfigParserError(
u"Option file '{0}' is not readable."
u"".format(self.filename),
cause=err)
# Create a backup file if provided but no output_option file was
# specified and if original file was not empty
if (backup_file_path and os.stat(self.filename).st_size != 0 and
not self.output_option_filename):
if not os.path.isabs(backup_file_path):
raise GadgetConfigParserError(
u"'{0}' is not an absolute path. Please provide an "
u"absolute path to the backup file".format(
backup_file_path))
else:
orig_perms = stat.S_IMODE(os.stat(self.filename).st_mode)
# ensure that permissions are at most 640 but respect
# original ones if they are tighter
backup_perms = orig_perms & (
stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
# set flag to create file in write only mode
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
try:
fd = os.open(backup_file_path, flags, backup_perms)
with os.fdopen(fd, 'w') as bf:
bf.writelines(lines)
except Exception as err:
raise GadgetConfigParserError(
"Backup file '{0}' is not writable."
"".format(backup_file_path),
cause=err)
f = None
if self.output_option_filename:
# if output_file was provided create it if not exists
prefix, fname = os.path.split(self.output_option_filename)
create_option_file(None, fname, prefix, replace=True)
# create a copy of the configuration file to output_file
shutil.copyfile(self.filename, self.output_option_filename)
output_file = self.output_option_filename
try:
f = open(fs_encode(output_file), 'w')
for line in lines:
line = fs_decode(line)
# empty lines or comment lines are returned unmodified
if line.strip() == '' or line.strip().startswith("#") or \
line.strip().startswith(";"):
f.write(fs_encode(line))
else:
# is it a section header?
mo = self._main_opt_parser.SECTCRE.match(line)
if mo:
if cursect is None: # First section being read
cursect = mo.group('header').lower()
# Flag section to be dropped if it is not in the
# ConfigParser object
drop_section = cursect not in in_memory_sections
read_sections.add(cursect)
else:
# we are going into another section, add any
# missing options to the previous section unless
# it was removed
if not drop_section:
parser_items = self._main_opt_parser.items(
cursect)
written_new_options = False
for opt, val in parser_items:
if opt not in read_options:
written_new_options = True
f.write(fs_encode(u"{0}\n".format(
optval_tostr(opt,
val))))
if written_new_options:
# add a new line to the end of the new
# options
f.write("")
# get the name of the new section
cursect = mo.group('header').lower()
# Flag section to be dropped if it is not in the
# ConfigParser object
drop_section = cursect not in in_memory_sections
read_sections.add(cursect)
# Reset options read from previous section
read_options = set()
# write section line if section is not meant to be
# removed
if not drop_section:
f.write(fs_encode(line))
# an option line?
else:
mo = self._main_opt_parser._optcre.match(line)
# if an option inside a section
if mo and cursect:
if drop_section:
# if option belongs to a section to be dropped,
# remove it
continue
optname, _, optval = mo.group('option', 'vi',
'value')
x_optname = self._main_opt_parser.optionxform(
optname.rstrip())
read_options.add(x_optname)
# replace option value if it needs replacing
try:
new_value = self._main_opt_parser.get(
cursect, x_optname)
except NoOptionError:
# option was removed. Remove it from file
continue
if new_value == optval:
# if value remains the same, leave line as is
f.write(fs_encode(line))
continue
new_str = optval_tostr(optname, new_value)
if not optval:
old_str = optname
else:
# calculate index where option name ends
options_ends_at = line.find(optname) + \
len(optname)
# calculate index where value ends
matched_value_index = line.find(
optval, options_ends_at)
replace_until_index = matched_value_index + \
len(optval)
# we need to replace the old line up until
# the value (if exists)
old_str = line[:replace_until_index]
f.write(
fs_encode(line.replace(old_str, new_str)))
else:
# line format is not valid (neither a section nor
# an option nor a comment line)
raise GadgetConfigParserError(
u"Write operation failed. File '{0}' could "
u"not be parsed correctly, parsing error at "
u"line '{1}'.".format(output_file, line))
# add missing options for last section (if a section was read)
if not drop_section and cursect is not None:
parser_items = self._main_opt_parser.items(cursect)
written_new_options = False
for opt, val in parser_items:
if opt not in read_options:
# if last line read doesn't have a '\n' at the end,
# add it manually.
if not line.endswith("\n") and not written_new_options:
f.write("\n")
written_new_options = True
f.write(fs_encode(u"{0}\n".format(
optval_tostr(opt, val))))
if written_new_options:
# add a new line in case any new options were added to the
# last section
f.write("")
# add new sections and respective options
parser_sections = self._main_opt_parser.sections()
for s in parser_sections:
if s not in read_sections:
# if this section was not yet read in the file, it is
# a new section. Write it along with its options.
f.write(fs_encode(u"[{0}]\n".format(s)))
for opt, val in self._main_opt_parser.items(s):
f.write(fs_encode(u"{0}\n".format(
optval_tostr(opt, val))))
except IOError as err:
raise GadgetConfigParserError(
u"Option file '{0}' is not writable."
u"".format(output_file),
cause=err)
else:
# we've written changes to file, reset modified flag
self.modified = False
finally:
# close the file
if f:
f.close()