From 6ede495b94501a4cbdfd985dcdf4be4f582bbb9b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 25 Nov 2014 15:04:26 +0100 Subject: Serializable Models + Soledad Adaptor --- src/leap/mail/adaptors/models.py | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/leap/mail/adaptors/models.py (limited to 'src/leap/mail/adaptors/models.py') diff --git a/src/leap/mail/adaptors/models.py b/src/leap/mail/adaptors/models.py new file mode 100644 index 0000000..1648059 --- /dev/null +++ b/src/leap/mail/adaptors/models.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +# models.py +# Copyright (C) 2014 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Generic Models to be used by the Document Adaptors. +""" +import copy + + +class SerializableModel(object): + """ + A Generic document model, that can be serialized into a dictionary. + + Subclasses of this `SerializableModel` are meant to be added as class + attributes of classes inheriting from DocumentWrapper. + + A subclass __meta__ of this SerializableModel might exist, and contain info + relative to particularities of this model. + + For instance, the use of `__meta__.index` marks the existence of a primary + index in the model, which will be used to do unique queries (in which case + all the other indexed fields in the underlying document will be filled with + the default info contained in the model definition). + """ + + @classmethod + def serialize(klass): + """ + Get a dictionary representation of the public attributes in the model + class. To avoid collisions with builtin functions, any occurrence of an + attribute ended in '_' (like 'type_') will be normalized by removing + the trailing underscore. + + This classmethod is used from within the serialized method of a + DocumentWrapper instance: it provides defaults for the + empty document. + """ + assert isinstance(klass, type) + return _normalize_dict(klass.__dict__) + + +class DocumentWrapper(object): + """ + A Wrapper object that can be manipulated, passed around, and serialized in + a format that the store understands. + It is related to a SerializableModel, which must be specified as the + ``model`` class attribute. The instance of this DocumentWrapper will not + allow any other *public* attributes than those defined in the corresponding + model. + """ + # TODO we could do some very basic type checking here + # TODO set a dirty flag (on __setattr__, whenever the value is != from + # before) + # TODO we could enforce the existence of a correct "model" attribute + # in some other way (other than in the initializer) + + def __init__(self, **kwargs): + if not getattr(self, 'model', None): + raise RuntimeError( + 'DocumentWrapper class needs a model attribute') + + defaults = self.model.serialize() + + if kwargs: + values = copy.deepcopy(defaults) + values.update(kwargs) + else: + values = defaults + + for k, v in values.items(): + k = k.replace('-', '_') + setattr(self, k, v) + + def __setattr__(self, attr, value): + normalized = _normalize_dict(self.model.__dict__) + if not attr.startswith('_') and attr not in normalized: + raise RuntimeError( + "Cannot set attribute because it's not defined " + "in the model: %s" % attr) + object.__setattr__(self, attr, value) + + def serialize(self): + return _normalize_dict(self.__dict__) + + def create(self): + raise NotImplementedError() + + def update(self): + raise NotImplementedError() + + def delete(self): + raise NotImplementedError() + + @classmethod + def get_or_create(self): + raise NotImplementedError() + + @classmethod + def get_all(self): + raise NotImplementedError() + + +def _normalize_dict(_dict): + items = _dict.items() + not_callable = lambda (k, v): not callable(v) + not_private = lambda(k, v): not k.startswith('_') + for cond in not_callable, not_private: + items = filter(cond, items) + items = [(k, v) if not k.endswith('_') else (k[:-1], v) + for (k, v) in items] + items = [(k.replace('-', '_'), v) for (k, v) in items] + return dict(items) -- cgit v1.2.3 From 84888155b09b3af6a755262b28728de2f851c8cb Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Sun, 4 Jan 2015 03:37:18 -0400 Subject: tests for mail.mail module: Message --- src/leap/mail/adaptors/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/mail/adaptors/models.py') diff --git a/src/leap/mail/adaptors/models.py b/src/leap/mail/adaptors/models.py index 1648059..88e0e4e 100644 --- a/src/leap/mail/adaptors/models.py +++ b/src/leap/mail/adaptors/models.py @@ -89,7 +89,7 @@ class DocumentWrapper(object): if not attr.startswith('_') and attr not in normalized: raise RuntimeError( "Cannot set attribute because it's not defined " - "in the model: %s" % attr) + "in the model %s: %s" % (self.__class__, attr)) object.__setattr__(self, attr, value) def serialize(self): -- cgit v1.2.3 From 11d4373226c8ab32c31fa92beed8aedb962dd756 Mon Sep 17 00:00:00 2001 From: Bruno Wagner Date: Tue, 21 Jul 2015 19:56:43 -0300 Subject: Transformed assigned lambdas to functions in models and test_models because of pep8 --- src/leap/mail/adaptors/models.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/leap/mail/adaptors/models.py') diff --git a/src/leap/mail/adaptors/models.py b/src/leap/mail/adaptors/models.py index 88e0e4e..c5b838a 100644 --- a/src/leap/mail/adaptors/models.py +++ b/src/leap/mail/adaptors/models.py @@ -115,10 +115,8 @@ class DocumentWrapper(object): def _normalize_dict(_dict): items = _dict.items() - not_callable = lambda (k, v): not callable(v) - not_private = lambda(k, v): not k.startswith('_') - for cond in not_callable, not_private: - items = filter(cond, items) + items = filter(lambda k, v: not callable(v), items) + items = filter(lambda k, v: not k.startswith('_')) items = [(k, v) if not k.endswith('_') else (k[:-1], v) for (k, v) in items] items = [(k.replace('-', '_'), v) for (k, v) in items] -- cgit v1.2.3 From 5027dd2dd3c7679a7eea025d838a7d472c355623 Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Wed, 29 Jul 2015 16:11:33 +0200 Subject: [bug] fixed syntax error in models.py The lambdas take two args, so it needs to be a tuple. Furthermore filter needs a collection. --- src/leap/mail/adaptors/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/mail/adaptors/models.py') diff --git a/src/leap/mail/adaptors/models.py b/src/leap/mail/adaptors/models.py index c5b838a..2bf9e60 100644 --- a/src/leap/mail/adaptors/models.py +++ b/src/leap/mail/adaptors/models.py @@ -115,8 +115,8 @@ class DocumentWrapper(object): def _normalize_dict(_dict): items = _dict.items() - items = filter(lambda k, v: not callable(v), items) - items = filter(lambda k, v: not k.startswith('_')) + items = filter(lambda (k, v): not callable(v), items) + items = filter(lambda (k, v): not k.startswith('_'), items) items = [(k, v) if not k.endswith('_') else (k[:-1], v) for (k, v) in items] items = [(k.replace('-', '_'), v) for (k, v) in items] -- cgit v1.2.3 From 490effb589ad94f2f6f520b385399864b08def91 Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Tue, 11 Aug 2015 13:42:24 +0200 Subject: [bug] Fix missing _normailize_dict in DocumentWrapper constructor. In the constructor values already is normalized (i.e. with underscores), while kwargs contains items that are not normalized (i.e. with dashes). Joining the dicts resulted in two entries that only differed by dash or underscores. The setattr then set the value that occurred later in items, thereby sometimes overriding the correct value with the default one. --- src/leap/mail/adaptors/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/mail/adaptors/models.py') diff --git a/src/leap/mail/adaptors/models.py b/src/leap/mail/adaptors/models.py index 2bf9e60..49460f7 100644 --- a/src/leap/mail/adaptors/models.py +++ b/src/leap/mail/adaptors/models.py @@ -76,7 +76,7 @@ class DocumentWrapper(object): if kwargs: values = copy.deepcopy(defaults) - values.update(kwargs) + values.update(_normalize_dict(kwargs)) else: values = defaults -- cgit v1.2.3