From ede35ffd73b63c2cbf6dd4d07bb77861fb1b6770 Mon Sep 17 00:00:00 2001 From: Steven Armstrong Date: Fri, 14 Oct 2011 01:15:08 +0200 Subject: [PATCH] completely rewrite file based property handling Signed-off-by: Steven Armstrong --- lib/cdist/util/fsproperty.py | 225 ++++++++++++----------------------- 1 file changed, 76 insertions(+), 149 deletions(-) diff --git a/lib/cdist/util/fsproperty.py b/lib/cdist/util/fsproperty.py index 9ac7017f..5b8aa708 100644 --- a/lib/cdist/util/fsproperty.py +++ b/lib/cdist/util/fsproperty.py @@ -98,55 +98,6 @@ class FileList(collections.MutableSequence): self.__write(lines) -class FileListProperty(FileList): - - def __init__(self, path): - """ - :param path: string or callable - - Usage: - - class Foo(object): - parameters = DirectoryDictProperty(lambda obj: os.path.join(obj.absolute_path, 'parameter')) - other_dict = DirectoryDictProperty('/tmp/folder') - - def __init__(self): - self.absolute_path = '/tmp/foo' - - """ - self.path = None - self.__path = path - - def _set_path(self, *args, **kwargs): - if self.path is None: - path = self.__path - if callable(path): - path = path(*args, **kwargs) - if not os.path.isabs(path): - raise AbsolutePathRequiredError(path) - self.path = path - - # Descriptor Protocol - def __get__(self, obj, objtype=None): - if obj is None: - return self.__class__ - self._set_path(obj) - return self - - def __set__(self, obj, value): - self._set_path(obj) - try: - os.unlink(self.path) - except EnvironmentError: - # ignored - pass - for item in value: - self.append(item) - - def __delete__(self, obj): - raise AttributeError("can't delete attribute") - - class DirectoryDict(collections.MutableMapping): """A dict that stores it's items as files in a directory. @@ -178,7 +129,10 @@ class DirectoryDict(collections.MutableMapping): fd.write(str(value)) def __delitem__(self, key): - os.remove(os.path.join(self.path, key)) + try: + os.remove(os.path.join(self.path, key)) + except EnvironmentError: + raise KeyError(key) def __iter__(self): return iter(os.listdir(self.path)) @@ -187,95 +141,98 @@ class DirectoryDict(collections.MutableMapping): return len(os.listdir(self.path)) -class DirectoryDictProperty(DirectoryDict): +class FileBasedProperty(object): + attribute_class = None def __init__(self, path): """ :param path: string or callable - Usage: + Abstract super class. Subclass and set the class member attribute_class accordingly. + + Usage with a sublcass: class Foo(object): - parameters = DirectoryDictProperty(lambda obj: os.path.join(obj.absolute_path, 'parameter')) - other_dict = DirectoryDictProperty('/tmp/folder') + # note that the actual DirectoryDict is stored as __parameters on the instance + parameters = DirectoryDictProperty(lambda instance: os.path.join(instance.absolute_path, 'parameter')) + # note that the actual DirectoryDict is stored as __other_dict on the instance + other_dict = DirectoryDictProperty('/tmp/other_dict') def __init__(self): self.absolute_path = '/tmp/foo' """ - self.path = None - self.__path = path + self.path = path - def _set_path(self, *args, **kwargs): - #print("_set_path: self: %s" % self) - print("_set_path: args: %s" % args) - print("_set_path: kwargs: %s" % kwargs) - print("_set_path: self.path: %s" % self.path) - if self.path is None: - path = self.__path - if callable(path): - path = path(*args, **kwargs) - print("_set_path: %s" % path) - if not os.path.isabs(path): - raise AbsolutePathRequiredError(path) - # create directory if it doesn't exist - print("os.path.isdir(%s): %s" % (path, os.path.isdir(path))) - if not os.path.isdir(path): - os.mkdir(path) - self.path = path + def _get_path(self, instance): + path = self.path + if callable(path): + path = path(instance) + return path - # Descriptor Protocol - def __get__(self, obj, objtype=None): - if obj is None: - return self.__class__ - self._set_path(obj) - return self + def _get_property_name(self, owner): + for name, prop in owner.__dict__.items(): + if self == prop: + return name - def __set__(self, obj, value): - self._set_path(obj) - if value is not None: - for name in self.keys(): - del self[name] - self.update(value) + def _get_attribute(self, instance, owner): + name = self._get_property_name(owner) + attribute_name = '__%s' % name + if not hasattr(instance, attribute_name): + path = self._get_path(instance) + attribute_instance = self.attribute_class(path) + setattr(instance, attribute_name, attribute_instance) + return getattr(instance, attribute_name) - def __delete__(self, obj): + def __get__(self, instance, owner): + if instance is None: + return self + return self._get_attribute(instance, owner) + + def __delete__(self, instance): raise AttributeError("can't delete attribute") -class FileBooleanProperty(object): - def __init__(self, path): - """ - :param path: string or callable +class DirectoryDictProperty(FileBasedProperty): + attribute_class = DirectoryDict - Usage: + def __set__(self, instance, value): + attribute_instance = self._get_attribute(instance, instance.__class__) + for name in attribute_instance.keys(): + del attribute_instance[name] + attribute_instance.update(value) - class Foo(object): - changed = FileBoolean(lambda obj: os.path.join(obj.absolute_path, 'changed')) - other_boolean = FileBoolean('/tmp/other_boolean') - def __init__(self): - self.absolute_path = '/tmp/foo_boolean' +class FileListProperty(FileBasedProperty): + attribute_class = FileList - """ - self._path = path + def __set__(self, instance, value): + path = self._get_path(instance) + try: + os.unlink(path) + except EnvironmentError: + # ignored + pass + attribute_instance = self._get_attribute(instance, instance.__class__) + for item in value: + attribute_instance.append(item) - def _get_path(self, *args, **kwargs): - path = self._path - if callable(path): - return path(*args, **kwargs) - if not os.path.isabs(path): - raise AbsolutePathRequiredError(path) - return path +class FileBooleanProperty(FileBasedProperty): + """A boolean property which uses a file to represent its value. + + File exists -> True + File does not exists -> False + """ # Descriptor Protocol - def __get__(self, obj, objtype=None): - if obj is None: - return self.__class__ - path = self._get_path(obj) + def __get__(self, instance, owner): + if instance is None: + return self + path = self._get_path(instance) return os.path.isfile(path) - def __set__(self, obj, value): - path = self._get_path(obj) + def __set__(self, instance, value): + path = self._get_path(instance) if value: open(path, "w").close() else: @@ -285,42 +242,15 @@ class FileBooleanProperty(object): # ignore pass - def __delete__(self, obj): - raise AttributeError("can't delete attribute") - -class FileStringProperty(object): +class FileStringProperty(FileBasedProperty): """A string property which stores its value in a file. """ - def __init__(self, path): - """ - :param path: string or callable - - Usage: - - class Foo(object): - source = FileStringProperty(lambda obj: os.path.join(obj.absolute_path, 'source')) - other = FileStringProperty('/tmp/other') - - def __init__(self): - self.absolute_path = '/tmp/foo_boolean' - - """ - self._path = path - - def _get_path(self, *args, **kwargs): - path = self._path - if callable(path): - return path(*args, **kwargs) - if not os.path.isabs(path): - raise AbsolutePathRequiredError(path) - return path - # Descriptor Protocol - def __get__(self, obj, objtype=None): - if obj is None: - return self.__class__ - path = self._get_path(obj) + def __get__(self, instance, owner): + if instance is None: + return self + path = self._get_path(instance) value = "" try: with open(path, "r") as fd: @@ -329,16 +259,13 @@ class FileStringProperty(object): pass return value - def __set__(self, obj, value): - path = self._get_path(obj) + def __set__(self, instance, value): + path = self._get_path(instance) if value: with open(path, "w") as fd: fd.write(str(value)) else: try: - os.unlink(path) + os.remove(path) except EnvironmentError: pass - - def __delete__(self, obj): - raise AttributeError("Can't delete attribute. Set it's value to an empty string to remove the underlying file.")