terom@0: """ terom@0: Generic configuration file output terom@0: """ terom@0: terom@1: import os, os.path, tempfile, shutil terom@0: terom@1: class ConfObject (object) : terom@0: """ terom@0: An object that can be written to a ConfFile, as multiple lines of text. terom@0: """ terom@0: terom@3: def __init__ (self, comments=None) : terom@3: """ terom@3: Initialize with the given list of comments. Comments that are None should be ignore. terom@3: """ terom@3: terom@3: # init the comments list terom@3: self.comments = comments or [] terom@3: terom@3: def add_comment (self, comment) : terom@3: """ terom@3: Add a comment to be rendered in the output. terom@3: """ terom@3: terom@3: # add it terom@3: self.comments.append(comment) terom@3: terom@0: def fmt_lines (self) : terom@0: """ terom@0: Yield a series of lines to be output in the file terom@0: """ terom@0: terom@0: abstract terom@0: terom@1: class File (ConfObject) : terom@0: """ terom@0: A single configuration file on the filesystem. terom@0: terom@1: Configuration files are themselves ConfObject's, although this must be implemented in the inheriting class. terom@0: """ terom@0: terom@0: def __init__ (self, name, path, backup_suffix='.bak', mode=0644) : terom@0: """ terom@0: Initialize the config file, but don't open it yet terom@0: terom@0: @param name the human-readable friendly name for the config file terom@0: @param path the full filesystem path for the file terom@0: @param backup_suffix rename the old file by adding this suffix when replacing it terom@0: @param mode the permission bits for the new file terom@0: """ terom@0: terom@0: self.name = name terom@0: self.path = path terom@0: self.backup_suffix = backup_suffix terom@0: self.mode = mode terom@0: terom@1: def write_file (self, file) : terom@0: """ terom@1: Write out this config's stuff into the given file terom@0: """ terom@0: terom@0: writer = Writer(file) terom@1: writer.write_obj(self) terom@0: terom@1: def write (self) : terom@0: """ terom@1: Write out a new config file with this config's stuff using the following procedure: terom@4: terom@0: * lock the real file terom@0: * open a new temporary file, and write out the objects into that, closing it terom@0: * move the real file out of the way terom@0: * move the temporary file into the real file's place terom@0: * unlock the real file terom@0: """ terom@0: terom@0: # XXX: how to aquire the lock? terom@0: # XXX: this needs checking over terom@0: terom@0: # open the new temporary file terom@0: # XXX: ensure fd is closed terom@0: tmp_fd, tmp_path = tempfile.mkstemp(prefix=self.name) terom@0: terom@0: # ...as a file terom@0: tmp_file = os.fdopen(tmp_fd, "w") terom@0: terom@0: # fix the permissions terom@0: os.chmod(tmp_path, self.mode) terom@0: terom@0: # write it terom@1: self.write_file(tmp_file) terom@0: terom@0: # close it terom@0: tmp_file.close() terom@0: terom@0: # move the old file out of the way terom@1: if os.path.exists(self.path) : terom@1: os.rename(self.path, self.path + self.backup_suffix) terom@0: terom@0: # move the new file in terom@0: shutil.move(tmp_path, self.path) terom@0: terom@0: class Writer (object) : terom@0: """ terom@0: A conf.Writer is used to write out a new conf.File (as a temporary file) terom@0: """ terom@0: terom@0: def __init__ (self, file) : terom@0: """ terom@0: @param file the temporary file object terom@0: """ terom@0: terom@0: self.file = file terom@0: terom@1: def write_line (self, line) : terom@1: """ terom@1: Write a single line to the file terom@1: """ terom@1: terom@1: self.file.write("%s\n" % (line, )) terom@1: terom@1: def write_lines (self, lines) : terom@1: """ terom@1: Write a series of lines into the file terom@1: """ terom@1: terom@1: for line in lines : terom@1: self.write_line(line) terom@1: terom@0: def write_obj (self, obj) : terom@0: """ terom@0: Write a single object to the file terom@0: """ terom@0: terom@0: # just write out all the lines terom@1: self.write_lines(obj.fmt_lines()) terom@0: