def write()

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()