@@ -41,7 +41,8 @@ def on_foo_value(self, instance, value, **kwargs):
4141
4242PY2 = sys .version_info < (3 ,)
4343
44- __all__ = ['Property' , 'ListProperty' , 'DictProperty' ]
44+ __all__ = ['Property' , 'AliasProperty' , 'ListProperty' , 'DictProperty' ]
45+
4546
4647class Property (object ):
4748 """Defined on the class level to create an observable attribute
@@ -56,36 +57,44 @@ class Property(object):
5657 :class:`~pydispatch.dispatch.Dispatcher` instance.
5758
5859 """
60+
5961 def __init__ (self , default = None ):
6062 self ._name = ''
6163 self .default = default
6264 self .__storage = {}
6365 self .__weakrefs = InformativeWVDict (del_callback = self ._on_weakref_fin )
66+
6467 @property
6568 def name (self ):
6669 return self ._name
70+
6771 @name .setter
6872 def name (self , value ):
6973 if self ._name != '' :
7074 return
7175 self ._name = value
76+
7277 def _add_instance (self , obj , default = None ):
7378 if default is None :
7479 default = self .default
7580 self .__storage [id (obj )] = self .default
7681 self .__weakrefs [id (obj )] = obj
82+
7783 def _del_instance (self , obj ):
7884 del self .__storage [id (obj )]
85+
7986 def _on_weakref_fin (self , obj_id ):
8087 if obj_id in self .__storage :
8188 del self .__storage [obj_id ]
89+
8290 def __get__ (self , obj , objcls = None ):
8391 if obj is None :
8492 return self
8593 obj_id = id (obj )
8694 if obj_id not in self .__storage :
8795 self ._add_instance (obj )
8896 return self .__storage [obj_id ]
97+
8998 def __set__ (self , obj , value ):
9099 obj_id = id (obj )
91100 if obj_id not in self .__storage :
@@ -95,6 +104,7 @@ def __set__(self, obj, value):
95104 return
96105 self .__storage [obj_id ] = value
97106 self ._on_change (obj , current , value )
107+
98108 def _on_change (self , obj , old , value , ** kwargs ):
99109 """Called internally to emit changes from the instance object
100110
@@ -114,11 +124,58 @@ def _on_change(self, obj, old, value, **kwargs):
114124 """
115125 kwargs ['property' ] = self
116126 obj .emit (self .name , obj , value , old = old , ** kwargs )
127+
117128 def __repr__ (self ):
118129 return '<{}: {}>' .format (self .__class__ , self )
130+
119131 def __str__ (self ):
120132 return self .name
121133
134+
135+ class AliasProperty (Property ):
136+ """Property with a getter method and optional setter method. Behaves similar to Pythons builtin properties.
137+
138+ Args:
139+ getter : method used to provide the property value
140+ setter (Optional): method used to set the property value. If this method returns False change events will
141+ not be emitted.
142+ """
143+
144+ def __init__ (self , getter , setter = None , bind = None ):
145+ super ().__init__ ()
146+ self .__getter = getter
147+ self .__setter = setter
148+ self .__bindings = dict ((prop , self ._on_change ) for prop in bind ) if bind is not None else {}
149+
150+ def _on_change (self , obj , * args , ** kwargs ):
151+ property = kwargs .get ('property' , None )
152+ if property is None :
153+ return super ()._on_change (obj , * args , ** kwargs )
154+ old = super ().__get__ (obj )
155+ value = self .__get__ (obj )
156+ if old != value :
157+ super ()._on_change (obj , old , value )
158+
159+ def _add_instance (self , obj , default = None ):
160+ super ()._add_instance (obj , default )
161+ obj .bind (** self .__bindings )
162+
163+ def __get__ (self , obj , objcls = None ):
164+ if obj is None :
165+ return self
166+ value = self ._Property__storage [id (obj )] = self .__getter (obj )
167+ return value
168+
169+ def __set__ (self , obj , value ):
170+ current = self .__getter (obj )
171+ if current == value :
172+ return
173+ if self .__setter is None :
174+ raise AttributeError ("can't set attribute" )
175+ if self .__setter (obj , value ) is None :
176+ super ().__set__ (obj , value )
177+
178+
122179class ListProperty (Property ):
123180 """Property with a :class:`list` type value
124181
@@ -134,18 +191,22 @@ class ListProperty(Property):
134191 Changes to the contents of the list are able to be observed through
135192 :class:`ObservableList`.
136193 """
194+
137195 def __init__ (self , default = None , copy_on_change = False ):
138196 if default is None :
139197 default = []
140198 self .copy_on_change = copy_on_change
141199 super (ListProperty , self ).__init__ (default )
200+
142201 def _add_instance (self , obj ):
143202 default = self .default [:]
144203 default = ObservableList (default , obj = obj , property = self )
145204 super (ListProperty , self )._add_instance (obj , default )
205+
146206 def __set__ (self , obj , value ):
147207 value = ObservableList (value , obj = obj , property = self )
148208 super (ListProperty , self ).__set__ (obj , value )
209+
149210 def __get__ (self , obj , objcls = None ):
150211 if obj is None :
151212 return self
@@ -155,6 +216,7 @@ def __get__(self, obj, objcls=None):
155216 self ._Property__storage [id (obj )] = value
156217 return value
157218
219+
158220class DictProperty (Property ):
159221 """Property with a :class:`dict` type value
160222
@@ -170,18 +232,22 @@ class DictProperty(Property):
170232 Changes to the contents of the dict are able to be observed through
171233 :class:`ObservableDict`.
172234 """
235+
173236 def __init__ (self , default = None , copy_on_change = False ):
174237 if default is None :
175238 default = {}
176239 self .copy_on_change = copy_on_change
177240 super (DictProperty , self ).__init__ (default )
241+
178242 def _add_instance (self , obj ):
179243 default = self .default .copy ()
180244 default = ObservableDict (default , obj = obj , property = self )
181245 super (DictProperty , self )._add_instance (obj , default )
246+
182247 def __set__ (self , obj , value ):
183248 value = ObservableDict (value , obj = obj , property = self )
184249 super (DictProperty , self ).__set__ (obj , value )
250+
185251 def __get__ (self , obj , objcls = None ):
186252 if obj is None :
187253 return self
@@ -191,6 +257,7 @@ def __get__(self, obj, objcls=None):
191257 self ._Property__storage [id (obj )] = value
192258 return value
193259
260+
194261class Observable (object ):
195262 """Mixin used by :class:`ObservableList` and :class:`ObservableDict`
196263 to emit changes and build other observables
@@ -202,19 +269,22 @@ class Observable(object):
202269 copied and replaced by another :class:`ObservableDict`. This allows nested
203270 containers to be observed and their changes to be tracked.
204271 """
272+
205273 def _build_observable (self , item ):
206274 if isinstance (item , list ):
207275 item = ObservableList (item , parent = self )
208276 elif isinstance (item , dict ):
209277 item = ObservableDict (item , parent = self )
210278 return item
279+
211280 def _get_copy_or_none (self ):
212281 p = self .parent_observable
213282 if p is not None :
214283 return p ._get_copy_or_none ()
215284 if not self .copy_on_change :
216285 return None
217286 return self ._deepcopy ()
287+
218288 def _deepcopy (self ):
219289 o = self .copy ()
220290 if isinstance (self , list ):
@@ -225,6 +295,7 @@ def _deepcopy(self):
225295 if isinstance (item , Observable ):
226296 o [key ] = item ._deepcopy ()
227297 return o
298+
228299 def _emit_change (self , ** kwargs ):
229300 if not self ._init_complete :
230301 return
@@ -235,12 +306,14 @@ def _emit_change(self, **kwargs):
235306 return
236307 self .property ._on_change (self .obj , old , self , ** kwargs )
237308
309+
238310class ObservableList (list , Observable ):
239311 """A :class:`list` subclass that tracks changes to its contents
240312
241313 Note:
242314 This class is for internal use and not intended to be used directly
243315 """
316+
244317 def __init__ (self , initlist = None , ** kwargs ):
245318 self ._init_complete = False
246319 super (ObservableList , self ).__init__ ()
@@ -254,20 +327,24 @@ def __init__(self, initlist=None, **kwargs):
254327 if initlist is not None :
255328 self .extend (initlist )
256329 self ._init_complete = True
330+
257331 def __setitem__ (self , key , item ):
258332 old = self ._get_copy_or_none ()
259333 item = self ._build_observable (item )
260334 super (ObservableList , self ).__setitem__ (key , item )
261335 self ._emit_change (keys = [key ], old = old )
336+
262337 def __delitem__ (self , key ):
263338 old = self ._get_copy_or_none ()
264339 super (ObservableList , self ).__delitem__ (key )
265340 self ._emit_change (old = old )
341+
266342 if PY2 :
267343 def __setslice__ (self , * args ):
268344 old = self ._get_copy_or_none ()
269345 super (ObservableList , self ).__setslice__ (* args )
270346 self ._emit_change (old = old )
347+
271348 def __delslice__ (self , * args ):
272349 old = self ._get_copy_or_none ()
273350 super (ObservableList , self ).__delslice__ (* args )
@@ -280,15 +357,18 @@ def clear(self):
280357 if not hasattr (list , 'copy' ):
281358 def copy (self ):
282359 return self [:]
360+
283361 def __iadd__ (self , other ):
284362 other = self ._build_observable (other )
285363 self .extend (other )
286364 return self
365+
287366 def append (self , item ):
288367 old = self ._get_copy_or_none ()
289368 item = self ._build_observable (item )
290369 super (ObservableList , self ).append (item )
291370 self ._emit_change (old = old )
371+
292372 def extend (self , other ):
293373 old = self ._get_copy_or_none ()
294374 init = self ._init_complete
@@ -298,17 +378,20 @@ def extend(self, other):
298378 if init :
299379 self ._init_complete = True
300380 self ._emit_change (old = old )
381+
301382 def remove (self , * args ):
302383 old = self ._get_copy_or_none ()
303384 super (ObservableList , self ).remove (* args )
304385 self ._emit_change (old = old )
305386
387+
306388class ObservableDict (dict , Observable ):
307389 """A :class:`dict` subclass that tracks changes to its contents
308390
309391 Note:
310392 This class is for internal use and not intended to be used directly
311393 """
394+
312395 def __init__ (self , initdict = None , ** kwargs ):
313396 self ._init_complete = False
314397 super (ObservableDict , self ).__init__ ()
@@ -322,15 +405,18 @@ def __init__(self, initdict=None, **kwargs):
322405 if initdict is not None :
323406 self .update (initdict )
324407 self ._init_complete = True
408+
325409 def __setitem__ (self , key , item ):
326410 old = self ._get_copy_or_none ()
327411 item = self ._build_observable (item )
328412 super (ObservableDict , self ).__setitem__ (key , item )
329413 self ._emit_change (keys = [key ], old = old )
414+
330415 def __delitem__ (self , key ):
331416 old = self ._get_copy_or_none ()
332417 super (ObservableDict , self ).__delitem__ (key )
333418 self ._emit_change (old = old )
419+
334420 def update (self , other ):
335421 old = self ._get_copy_or_none ()
336422 init = self ._init_complete
@@ -344,15 +430,20 @@ def update(self, other):
344430 if init :
345431 self ._init_complete = True
346432 self ._emit_change (keys = list (keys ), old = old )
433+
347434 def clear (self ):
348435 old = self ._get_copy_or_none ()
349436 super (ObservableDict , self ).clear ()
350437 self ._emit_change (old = old )
438+
351439 def pop (self , * args ):
352440 old = self ._get_copy_or_none ()
353441 super (ObservableDict , self ).pop (* args )
354442 self ._emit_change (old = old )
443+
355444 def setdefault (self , * args ):
356445 old = self ._get_copy_or_none ()
357446 super (ObservableDict , self ).setdefault (* args )
358447 self ._emit_change (old = old )
448+
449+
0 commit comments