pvl/config/file.py
changeset 8 46d36bc33086
parent 7 0f9cae2d7147
equal deleted inserted replaced
7:0f9cae2d7147 8:46d36bc33086
       
     1 """
       
     2     Generic configuration file output
       
     3 """
       
     4 
       
     5 import os, os.path, tempfile, shutil
       
     6 
       
     7 class Item (object) :
       
     8     """
       
     9         An object that can be represented as a series of lines of text.
       
    10         
       
    11         The structure of these objects/lines is not defined, but as a convenience, a method for prepending lines of
       
    12         comments is provided.
       
    13     """
       
    14     
       
    15     # the prefix char for comments, must be defined
       
    16     COMMENT_PREFIX = None
       
    17 
       
    18     @classmethod
       
    19     def filter_comments (self, comments) :
       
    20         """
       
    21             Return the filtered list of comments
       
    22         """
       
    23 
       
    24         return [comment for comment in comments if comment]
       
    25 
       
    26     def __init__ (self, comments=()) :
       
    27         """
       
    28             Initialize with the given list of comments, which will be filtered before use.
       
    29         """
       
    30         
       
    31         # init the comments list
       
    32         self.comments = self.filter_comments(comments)
       
    33     
       
    34     def add_comment (self, comment) :
       
    35         """
       
    36             Add the given comment
       
    37         """
       
    38 
       
    39         self.comments += self.filter_comments([comment])
       
    40 
       
    41     def iter_comments (self) :
       
    42         """
       
    43             Formats comments for output
       
    44         """
       
    45 
       
    46         for comment in self.comments :
       
    47             yield "%s %s" % (self.COMMENT_PREFIX, comment)
       
    48 
       
    49     def iter_lines (self, indent='\t') :
       
    50         """
       
    51             Yield a series of lines to be output in the file
       
    52         """
       
    53         
       
    54         abstract
       
    55     
       
    56     def render_lines (self, encoding=None, newline='\n', **opts) :
       
    57         """
       
    58             Render lines as a series of objects, possibly encoded.
       
    59         """
       
    60 
       
    61         for line in self.iter_lines(**opts) :
       
    62             if encoding :
       
    63                 line = line.encode(encoding)
       
    64             
       
    65             elif encoding is False :
       
    66                 line = unicode(line)
       
    67 
       
    68             yield line + newline
       
    69 
       
    70     def render_unicode (self, **opts) :
       
    71         """
       
    72             Render lines as a single unicode object
       
    73         """
       
    74 
       
    75         return u''.join(self.render_lines(encoding=False, **opts))
       
    76 
       
    77     def render_str (self, encoding='utf-8', **opts) :
       
    78         """
       
    79             Render lines as a single str object
       
    80         """
       
    81 
       
    82         return ''.join(self.render_lines(encoding=encoding, **opts))
       
    83 
       
    84     def render_out (self, file, encoding='utf-8', **opts) :
       
    85         """
       
    86             Render lines and write out to given file object
       
    87         """
       
    88 
       
    89         for line in self.render_lines(encoding=encoding, **opts) :
       
    90             file.write(line)
       
    91 
       
    92 class Contents (Item) :
       
    93     """
       
    94         A series of Items
       
    95     """
       
    96 
       
    97     def __init__ (self, *items) :
       
    98         self.items = items
       
    99     
       
   100     def add_item (self, item) :
       
   101         self.items.append(item)
       
   102 
       
   103     def iter_lines (self, **opts) :
       
   104         for item in self.items :
       
   105             for line in item.iter_lines(**opts) :
       
   106                 yield line
       
   107 
       
   108 class File (object) :
       
   109     """
       
   110         A configuration file on the filesystem.
       
   111 
       
   112         Configuration files can be written out with some given contents.
       
   113             
       
   114             
       
   115         XXX: better logic for backup file
       
   116     """
       
   117 
       
   118     def __init__ (self, name, path, backup_suffix='.bak', mode=0644) :
       
   119         """
       
   120             Initialize the config file, but don't open it yet
       
   121 
       
   122             @param name the human-readable friendly name for the config file
       
   123             @param path the full filesystem path for the file
       
   124             @param backup_suffix rename the old file by adding this suffix when replacing it
       
   125             @param mode the permission bits for the new file
       
   126         """
       
   127   
       
   128         self.name = name
       
   129         self.path = path
       
   130         self.backup_suffix = backup_suffix
       
   131         self.mode = mode
       
   132     
       
   133     def write (self, data) :
       
   134         """
       
   135             Write out a new config file with the given Item data using the following procedure:
       
   136 
       
   137                 * lock the real file
       
   138                 * open a new temporary file, and write out the objects into that, closing it
       
   139                 * move the real file out of the way
       
   140                 * move the temporary file into the real file's place
       
   141                 * unlock the real file
       
   142         """
       
   143 
       
   144         # XXX: how to aquire the lock?
       
   145         # XXX: this needs checking over
       
   146 
       
   147         # open the new temporary file
       
   148         # XXX: ensure OS fd is closed
       
   149         tmp_fd, tmp_path = tempfile.mkstemp(prefix=self.name)
       
   150 
       
   151         # ...as a file
       
   152         tmp_file = os.fdopen(tmp_fd, "w")
       
   153         
       
   154         # fix the permissions
       
   155         os.chmod(tmp_path, self.mode)
       
   156 
       
   157         # write the Item out into the file
       
   158         data.render_out(tmp_file)
       
   159 
       
   160         # close it
       
   161         tmp_file.close()
       
   162         
       
   163         # move the old file out of the way
       
   164         if os.path.exists(self.path) :
       
   165             # XXX: use shutil.move?
       
   166             os.rename(self.path, self.path + self.backup_suffix)
       
   167 
       
   168         # move the new file in
       
   169         shutil.move(tmp_path, self.path)
       
   170