properties.py 5.19 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Copyright 2016 ZEROFAIL
#
# This file is part of Goblin.
#
# Goblin is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Goblin 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Goblin.  If not, see <http://www.gnu.org/licenses/>.

18
"""Classes to handle properties and data type definitions"""
19

20
21
import logging

22
from goblin import abc, cardinality, exception
23

24

25
logger = logging.getLogger(__name__)
26

davebshow's avatar
davebshow committed
27
28

class PropertyDescriptor:
29
30
31
32
    """
    Descriptor that validates user property input and gets/sets properties
    as instance attributes. Not instantiated by user.
    """
davebshow's avatar
davebshow committed
33

davebshow's avatar
davebshow committed
34
    def __init__(self, name, prop):
35
        self._prop_name = name
36
        self._name = '_' + name
davebshow's avatar
davebshow committed
37
38
        self._data_type = prop.data_type
        self._default = prop.default
davebshow's avatar
davebshow committed
39
40

    def __get__(self, obj, objtype):
41
        if obj is None:
42
            return getattr(objtype.__mapping__, self._prop_name)
davebshow's avatar
davebshow committed
43
        return getattr(obj, self._name, self._default)
davebshow's avatar
davebshow committed
44
45

    def __set__(self, obj, val):
46
        val = self._data_type.validate(val)
47
        setattr(obj, self._name, val)
davebshow's avatar
davebshow committed
48
49

    def __delete__(self, obj):
davebshow's avatar
davebshow committed
50
51
52
53
54
55
        # hmmm what is the best approach here
        attr = getattr(obj, self._name, None)
        if attr:
            del attr


56
class Property(abc.BaseProperty):
57
58
59
60
61
    """
    API class used to define properties. Replaced with
    :py:class:`PropertyDescriptor` by :py:class:`goblin.element.ElementMeta`.

    :param goblin.abc.DataType data_type: Str or class of data type
davebshow's avatar
davebshow committed
62
    :param str db_name: User defined custom name for property in db
63
64
    :param default: Default value for this property.
    """
davebshow's avatar
davebshow committed
65

davebshow's avatar
davebshow committed
66
67
    __descriptor__ = PropertyDescriptor

68
    def __init__(self, data_type, *, db_name=None, default=None):
davebshow's avatar
davebshow committed
69
70
71
        if isinstance(data_type, type):
            data_type = data_type()
        self._data_type = data_type
72
        self._db_name = db_name
davebshow's avatar
davebshow committed
73
74
75
76
77
78
        self._default = default

    @property
    def data_type(self):
        return self._data_type

79
80
81
82
    @property
    def db_name(self):
        return self._db_name

davebshow's avatar
davebshow committed
83
84
85
86
87
    @property
    def default(self):
        return self._default


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class IdPropertyDescriptor:

    def __init__(self, name, prop):
        assert name == 'id', 'ID properties must be named "id"'
        self._data_type = prop.data_type

    def __get__(self, obj, objtype=None):
        if obj is None:
            raise exception.ElementError(
                "Only instantiated elements have ID property")
        return obj._id

    def __set__(self, obj, val):
        raise exception.ElementError('ID should not be set manually')


class IdProperty(abc.BaseProperty):

    __descriptor__ = IdPropertyDescriptor

    def __init__(self, data_type):
        if isinstance(data_type, type):
            data_type = data_type()
        self._data_type = data_type

    @property
    def data_type(self):
        return self._data_type


davebshow's avatar
davebshow committed
118
# Data types
119
120
121
122
123
124
125
126
127
128
129
130
class Generic(abc.DataType):

    def validate(self, val):
        return super().validate(val)

    def to_db(self, val=None):
        return super().to_db(val=val)

    def to_ogm(self, val):
        return super().to_ogm(val)


131
class String(abc.DataType):
132
    """Simple string datatype"""
davebshow's avatar
davebshow committed
133
134
135

    def validate(self, val):
        if val is not None:
136
137
            try:
                return str(val)
davebshow's avatar
davebshow committed
138
139
            except ValueError as e:
                raise exception.ValidationError(
140
                    'Not a valid string: {}'.format(val)) from e
davebshow's avatar
davebshow committed
141

142
143
    def to_db(self, val=None):
        return super().to_db(val=val)
davebshow's avatar
davebshow committed
144

davebshow's avatar
davebshow committed
145
146
    def to_ogm(self, val):
        return super().to_ogm(val)
davebshow's avatar
davebshow committed
147
148
149


class Integer(abc.DataType):
150
    """Simple integer datatype"""
davebshow's avatar
davebshow committed
151
152
153
154
155

    def validate(self, val):
        if val is not None:
            try:
                return int(val)
156
            except (ValueError, TypeError) as e:
davebshow's avatar
davebshow committed
157
                raise exception.ValidationError(
158
                    'Not a valid integer: {}'.format(val)) from e
davebshow's avatar
davebshow committed
159

160
161
    def to_db(self, val=None):
        return super().to_db(val=val)
davebshow's avatar
davebshow committed
162
163
164
165
166
167

    def to_ogm(self, val):
        return super().to_ogm(val)


class Float(abc.DataType):
davebshow's avatar
davebshow committed
168
    """Simple float datatype"""
169
170
    def validate(self, val):
        try:
171
            val = float(val)
172
        except ValueError:
173
174
            raise exception.ValidationError(
                "Not a valid float: {}".format(val)) from e
175
        return val
176

177
178
    def to_db(self, val=None):
        return super().to_db(val=val)
179
180

    def to_ogm(self, val):
181
        return super().to_ogm(val)
davebshow's avatar
davebshow committed
182
183


184
class Boolean(abc.DataType):
davebshow's avatar
davebshow committed
185
    """Simple boolean datatype"""
186
    def validate(self, val):
187
188
189
        try:
            val = bool(val)
        except ValueError:
190
191
            raise exception.ValidationError(
                "Not a valid boolean: {val}".format(val)) from e
192
193
        return val

194
195
    def to_db(self, val=None):
        return super().to_db(val=val)
196
197

    def to_ogm(self, val):
198
        return super().to_ogm(val)