aboutsummaryrefslogtreecommitdiffstats
path: root/tagit/external/setproperty/setproperty.pyx
blob: 21bacbba37734930f8d7d65df379ad09e3477682 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
118
119
120
121
122
123
124
125

from weakref import ref

cdef inline void observable_set_dispatch(object self) except *:
    cdef Property prop = self.prop
    obj = self.obj()
    if obj is not None:
        prop.dispatch(obj)


class ObservableSet(set):
    # Internal class to observe changes inside a native python set.
    def __init__(self, *largs):
        self.prop = largs[0]
        self.obj = ref(largs[1])
        super(ObservableSet, self).__init__(*largs[2:])

    def __iand__(self, *largs):
        set.__iand__(self, *largs)
        observable_set_dispatch(self)

    def __ior__(self, *largs):
        set.__ior__(self, *largs)
        observable_set_dispatch(self)

    def __isub__(self, *largs):
        set.__isub__(self, *largs)
        observable_set_dispatch(self)

    def __ixor__(self, *largs):
        set.__ixor__(self, *largs)
        observable_set_dispatch(self)

    def add(self, *largs):
        set.add(self, *largs)
        observable_set_dispatch(self)

    def clear(self):
        set.clear(self)
        observable_set_dispatch(self)

    def difference_update(self, *largs):
        set.difference_update(self, *largs)
        observable_set_dispatch(self)

    def discard(self, *largs):
        set.discard(self, *largs)
        observable_set_dispatch(self)

    def intersection_update(self, *largs):
        set.intersection_update(self, *largs)
        observable_set_dispatch(self)

    def pop(self, *largs):
        cdef object result = set.pop(self, *largs)
        observable_set_dispatch(self)
        return result

    def remove(self, *largs):
        set.remove(self, *largs)
        observable_set_dispatch(self)

    def symmetric_difference_update(self, *largs):
        set.symmetric_difference_update(self, *largs)
        observable_set_dispatch(self)

    def update(self, *largs):
        set.update(self, *largs)
        observable_set_dispatch(self)


cdef class SetProperty(Property):
    '''Property that represents a set.

    :Parameters:
        `defaultvalue`: set, defaults to set()
            Specifies the default value of the property.

    .. warning::

        When assigning a set to a :class:`SetProperty`, the set stored in
        the property is a shallow copy of the set and not the original set. This can
        be demonstrated with the following example::

            >>> class MyWidget(Widget):
            >>>     my_set = SetProperty([])

            >>> widget = MyWidget()
            >>> my_set = {1, 5, {'hi': 'hello'}}
            >>> widget.my_set = my_set
            >>> print(my_set is widget.my_set)
            False
            >>> my_set.add(10)
            >>> print(my_set, widget.my_set)
            {1, 5, {'hi': 'hello'}, 10} {1, 5, {'hi': 'hello'}}

        However, changes to nested levels will affect the property as well,
        since the property uses a shallow copy of my_set.

    '''
    def __init__(self, defaultvalue=0, **kw):
        defaultvalue = set() if defaultvalue == 0 else defaultvalue

        super(SetProperty, self).__init__(defaultvalue, **kw)

    cpdef PropertyStorage link(self, EventDispatcher obj, str name):
        Property.link(self, obj, name)
        cdef PropertyStorage ps = obj.__storage[self._name]
        if ps.value is not None:
            ps.value = ObservableSet(self, obj, ps.value)

    cdef check(self, EventDispatcher obj, value, PropertyStorage property_storage):
        if Property.check(self, obj, value, property_storage):
            return True
        if type(value) is not ObservableSet:
            raise ValueError('%s.%s accept only ObservableSet' % (
                obj.__class__.__name__,
                self.name))

    cpdef set(self, EventDispatcher obj, value):
        if value is not None:
            value = ObservableSet(self, obj, value)
        Property.set(self, obj, value)