Source code for layeredconfig.plistfile

from datetime import datetime
import codecs
import plistlib
import sys

from six import text_type as str
from six import binary_type as bytes

from . import DictSource


[docs]class PListFile(DictSource): def __init__(self, plistfilename=None, writable=True, **kwargs): """Loads and optionally saves configuration files in PList format. Since PList has some support for typed values (supports numbers, lists, bools, datetimes *but not dates*), data from this source are sometimes typed, sometimes only available as strings. :param plistfile: The name of a PList file. Nested sections are turned into nested config objects. :type plistfile: str :param writable: Whether changes to the LayeredConfig object that has this PListFile object amongst its sources should be saved in the PList file. :type writable: bool """ if sys.version_info >= (3,4): self.reader = plistlib.load self.writer = plistlib.dump else: self.reader = plistlib.readPlist self.writer = plistlib.writePlist super(PListFile, self).__init__(**kwargs) if plistfilename == None and 'parent' in kwargs and hasattr(kwargs['parent'], 'plistfilename'): plistfilename = kwargs['parent'].plistfilename if 'defaults' in kwargs: self.source = kwargs['defaults'] elif kwargs.get('empty', False): self.source = {} else: with open(plistfilename, "rb") as fp: self.source = self.reader(fp) self.plistfilename = plistfilename self.dirty = False self.encoding = "utf-8" # I hope this is a sensible default self.writable = writable
[docs] def set(self, key, value): # plist natively supports some types but not all (notably not date) if not isinstance(value, (str, bool, int, list, datetime)): value = str(value) super(PListFile, self).set(key, value)
[docs] def get(self, key): ret = super(PListFile, self).get(key) if isinstance(ret, bytes): ret = ret.decode(self.encoding) # same with individual elements of lists elif isinstance(ret, list): for idx, val in enumerate(ret): if isinstance(ret[idx], bytes): ret[idx] = ret[idx].decode(self.encoding) return ret
[docs] def typed(self, key): # if the value is anything other than a string, we can be sure # that it contains useful type information. return self.has(key) and not isinstance(self.get(key), str)
[docs] def keys(self): for k in super(PListFile, self).keys(): if isinstance(k, bytes): k = k.decode(self.encoding) yield k
[docs] def subsections(self): for k in super(PListFile, self).subsections(): if isinstance(k, bytes): k = k.decode(self.encoding) yield k
[docs] def save(self): assert not self.parent, "save() should only be called on root objects" if self.plistfilename: with open(self.plistfilename, "wb") as fp: self.writer(self.source, fp)