element.py 5.73 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/>.

davebshow's avatar
davebshow committed
18
19
import logging

davebshow's avatar
davebshow committed
20
21
import inflection

22
from goblin import abc, cardinality, exception, mapper, properties
davebshow's avatar
davebshow committed
23
24
25
26
27
28


logger = logging.getLogger(__name__)


class ElementMeta(type):
29
30
31
32
33
    """
    Metaclass for graph elements. Responsible for creating the
    :py:class:`Mapping<mapper.Mapping>` object and replacing user defined
    :py:class:`property.Property` with :py:class:`property.PropertyDescriptor`.
    """
davebshow's avatar
davebshow committed
34
    def __new__(cls, name, bases, namespace, **kwds):
davebshow's avatar
davebshow committed
35
36
37
        if name == 'VertexProperty':
            element_type = name.lower()
        elif bases:
38
            element_type = bases[0].__name__.lower()
davebshow's avatar
davebshow committed
39
40
41
        else:
            element_type = name.lower()
        namespace['__type__'] = element_type
davebshow's avatar
davebshow committed
42
43
        if not namespace.get('__label__', None):
            namespace['__label__'] = inflection.underscore(name)
davebshow's avatar
davebshow committed
44
45
46
47
        props = {}
        new_namespace = {}
        for k, v in namespace.items():
            if isinstance(v, abc.BaseProperty):
48
                if element_type == 'edge' and hasattr(v, 'cardinality'):
49
                    raise exception.MappingError(
50
                        'Edge property cannot have set/list cardinality')
davebshow's avatar
davebshow committed
51
52
53
54
55
56
57
58
59
60
                props[k] = v
                v = v.__descriptor__(k, v)
            new_namespace[k] = v
        new_namespace['__mapping__'] = mapper.create_mapping(namespace,
                                                             props)
        result = type.__new__(cls, name, bases, new_namespace)
        return result


class Element(metaclass=ElementMeta):
61
    """Base class for classes that implement the Element property interface"""
62
    id = properties.IdProperty(properties.Generic)
davebshow's avatar
davebshow committed
63
64
65
66
67
68
69


class Vertex(Element):
    """Base class for user defined Vertex classes"""
    pass


davebshow's avatar
davebshow committed
70
class GenericVertex(Vertex):
71
72
73
74
    """
    Class used to build vertices when user defined vertex class is not
    available. Generally not instantiated by end user.
    """
davebshow's avatar
davebshow committed
75
76
77
    pass


davebshow's avatar
davebshow committed
78
class Edge(Element):
79
80
    """
    Base class for user defined Edge classes.
davebshow's avatar
davebshow committed
81

82
83
84
    :param Vertex source: Source (outV) vertex
    :param Vertex target: Target (inV) vertex
    """
davebshow's avatar
davebshow committed
85
    def __init__(self, source=None, target=None):
davebshow's avatar
davebshow committed
86
87
        self._source = source
        self._target = target
davebshow's avatar
davebshow committed
88
89
90
91

    def getsource(self):
        return self._source

92
93
94
    def setsource(self, vertex):
        assert isinstance(vertex, Vertex) or vertex is None
        self._source = vertex
davebshow's avatar
davebshow committed
95
96
97
98
99
100
101
102
103

    def delsource(self):
        del self._source

    source = property(getsource, setsource, delsource)

    def gettarget(self):
        return self._target

104
105
106
    def settarget(self, vertex):
        assert isinstance(vertex, Vertex) or vertex is None
        self._target = vertex
davebshow's avatar
davebshow committed
107
108
109
110
111
112
113

    def deltarget(self):
        del self._target

    target = property(gettarget, settarget, deltarget)


davebshow's avatar
davebshow committed
114
class GenericEdge(Edge):
115
116
117
118
    """
    Class used to build edges when user defined edges class is not available.
    Generally not instantiated by end user.
    """
119
    pass
davebshow's avatar
davebshow committed
120

davebshow's avatar
davebshow committed
121
class VertexPropertyDescriptor:
122
123
124
125
    """
    Descriptor that validates user property input and gets/sets properties
    as instance attributes.
    """
davebshow's avatar
davebshow committed
126
127

    def __init__(self, name, vertex_property):
128
        self._prop_name = name
davebshow's avatar
davebshow committed
129
130
131
132
        self._name = '_' + name
        self._vertex_property = vertex_property.__class__
        self._data_type = vertex_property.data_type
        self._default = vertex_property.default
133
        self._cardinality = vertex_property._cardinality
davebshow's avatar
davebshow committed
134
135
136

    def __get__(self, obj, objtype):
        if obj is None:
137
            return getattr(objtype.__mapping__, self._prop_name)
davebshow's avatar
davebshow committed
138
139
        default = self._default
        if default:
140
141
142
            default = self._data_type.validate_vertex_prop(
                default, self._cardinality, self._vertex_property,
                self._data_type)
davebshow's avatar
davebshow committed
143
144
145
        return getattr(obj, self._name, default)

    def __set__(self, obj, val):
146
147
148
149
        if val:
            val = self._data_type.validate_vertex_prop(
                val, self._cardinality, self._vertex_property, self._data_type)
        setattr(obj, self._name, val)
davebshow's avatar
davebshow committed
150
151


152
class VertexProperty(Vertex, abc.BaseProperty):
davebshow's avatar
davebshow committed
153
    """Base class for user defined vertex properties."""
davebshow's avatar
davebshow committed
154
155
156

    __descriptor__ = VertexPropertyDescriptor

davebshow's avatar
davebshow committed
157
    def __init__(self, data_type, *, default=None, db_name=None,
158
                 card=None):
davebshow's avatar
davebshow committed
159
160
161
162
        if isinstance(data_type, type):
            data_type = data_type()
        self._data_type = data_type
        self._default = default
163
        self._db_name = db_name
164
165
166
        if card is None:
            card = cardinality.Cardinality.single
        self._cardinality = card
davebshow's avatar
davebshow committed
167
168
169
170
171
172
173
174
175

    @property
    def default(self):
        self._default

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

176
    def getvalue(self):
177
        return self._val
davebshow's avatar
davebshow committed
178

179
180
181
    def setvalue(self, val):
        self._val = val

davebshow's avatar
davebshow committed
182
183
    value = property(getvalue, setvalue)

184
185
186
187
188
189
190
191
    @property
    def db_name(self):
        return self._db_name

    @property
    def cardinality(self):
        return self._cardinality

davebshow's avatar
davebshow committed
192
193
194
    def __repr__(self):
        return '<{}(type={}, value={})'.format(self.__class__.__name__,
                                               self._data_type, self.value)