X-Git-Url: https://git.madduck.net/etc/taskwarrior.git/blobdiff_plain/0110af91cb5bf3804efc35cb50c8430419af4313..d2e4a33719c765e528de9c22e00e30de2754d8f5:/tasklib/task.py diff --git a/tasklib/task.py b/tasklib/task.py index 138ea7c..0666b0f 100644 --- a/tasklib/task.py +++ b/tasklib/task.py @@ -28,6 +28,37 @@ class TaskWarriorException(Exception): pass +class ReadOnlyDictView(object): + """ + Provides simplified read-only view upon dict object. + """ + + def __init__(self, viewed_dict): + self.viewed_dict = viewed_dict + + def __getitem__(self, key): + return copy.deepcopy(self.viewed_dict.__getitem__(key)) + + def __contains__(self, k): + return self.viewed_dict.__contains__(k) + + def __iter__(self): + for value in self.viewed_dict: + yield copy.deepcopy(value) + + def __len__(self): + return len(self.viewed_dict) + + def get(self, key, default=None): + return copy.deepcopy(self.viewed_dict.get(key, default)) + + def items(self): + return [copy.deepcopy(v) for v in self.viewed_dict.items()] + + def values(self): + return [copy.deepcopy(v) for v in self.viewed_dict.values()] + + class SerializingObject(object): """ Common ancestor for TaskResource & TaskFilter, since they both @@ -44,6 +75,15 @@ class SerializingObject(object): not export empty-valued attributes) if the attribute is not iterable (e.g. list or set), in which case a empty iterable should be used. + + Normalizing methods should hold the following contract: + - They are used to validate and normalize the user input. + Any attribute value that comes from the user (during Task + initialization, assignign values to Task attributes, or + filtering by user-provided values of attributes) is first + validated and normalized using the normalize_{key} method. + - If validation or normalization fails, normalizer is expected + to raise ValueError. """ def _deserialize(self, key, value): @@ -63,6 +103,10 @@ class SerializingObject(object): or entered as a value of Task attribute. """ + # None value should not be converted by normalizer + if value is None: + return None + normalize_func = getattr(self, 'normalize_{0}'.format(key), lambda x: x) @@ -193,13 +237,14 @@ class SerializingObject(object): # If the value is already localized, there is no need to change # time zone at this point. Also None is a valid value too. localized = value - + return localized def normalize_uuid(self, value): # Enforce sane UUID if not isinstance(value, six.text_type) or value == '': - raise ValueError("UUID must be a valid non-empty string.") + raise ValueError("UUID must be a valid non-empty string, " + "not: {}".format(value)) return value @@ -390,6 +435,9 @@ class Task(TaskResource): for (key, value) in six.iteritems(kwargs)) self._original_data = copy.deepcopy(self._data) + # Provide read only access to the original data + self.original = ReadOnlyDictView(self._original_data) + def __unicode__(self): return self['description']