root/trunk/RBJabber/RBJabber/SubjectObserver/Subject.py

Revision 528, 5.4 kB (checked in by sholloway, 5 years ago)

Depreciating WeakBind?, LazyProperty?
General cleanup tasks
Demo cleanups

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ License
4 ##~
5 ##- The RuneBlade Foundation library is intended to ease some
6 ##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
7 ##- applications, while providing the flexibility to modularly change the
8 ##- architecture. Enjoy.
9 ##~
10 ##~ Copyright (C) 2002  TechGame Networks, LLC.
11 ##~
12 ##~ This library is free software; you can redistribute it and/or
13 ##~ modify it under the terms of the BSD style License as found in the
14 ##~ LICENSE file included with this distribution.
15 ##~
16 ##~ TechGame Networks, LLC can be reached at:
17 ##~ 3578 E. Hartsel Drive #211
18 ##~ Colorado Springs, Colorado, USA, 80920
19 ##~
20 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 """Maintains a list of montioring observers that get updated when
23 UpdateObservers or UpdateObserversEx get called, unless the subject is Locked.
24 A lock can be obtained by calling Lock, and it maintained until the lock object
25 no longer exisits or is explicitly released.
26
27 BindCallable module is used extensively to prevent reference chains keeping objects in
28 memory unnecessarily."""
29
30 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 #~ Imports
32 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33
34 from RBFoundation import BindCallable
35 import bisect
36
37 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38 #~ Definitions
39 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40
41 class Subject(object):
42     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43     #~ Special
44     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
46     def __init__(self):
47         self._observers = []
48         self._cachedUpdates = {}
49         self._locked = []
50
51     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52     #~ Management
53     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54    
55     def AddObserver(self, observer, priority=0):
56         """Adds observer to the internal collection monitoring this subject
57         Observer is assumed to be a callable object."""
58         result = BindCallable.BindCallable(observer)
59         bisect.insort(self._observers, (-priority, result))
60         return self
61
62     def RemoveObserver(self, observer):
63         """Removes observer from the internal collection monitoring this subject. 
64         Observer should be the same object that was passed to AddObserver."""
65         result = BindCallable.BindCallable(observer)
66         self._observers[:] = [x for x in self._observers if x[-1] != result]
67         return self
68
69     def ClearObservers(self):
70         """Removes all observers from the internal collection"""
71         self._observers[:] = []
72         return self
73
74     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75     #~ Update Callbacks
76     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
77    
78     def _ObserverList(self):
79         """Returns the internal observer collection pruned of invalid weakref objects as a list"""
80         self._observers[:] = [x for x in self._observers if x[-1]]
81         return [x[-1] for x in self._observers]
82     Observers = property(_ObserverList)
83
84     def UpdateObservers(self, **kw):
85         """Updates all observers of this subject with data in **kw.  See also UpdateObserversEx."""
86         return self.UpdateObserversEx(kw)
87
88     def UpdateObserversEx(self, kw):
89         """Updates all observers of this subject with data in kw.  (kw is assumed to be a dictonary.)  See also UpdateObservers."""
90         self._cachedUpdates.update(kw)
91         if self._cachedUpdates and not self.Locked():
92             _cachedUpdates, self._cachedUpdates = self._cachedUpdates, {}
93             lock = self.Lock()
94             for each in self.Observers:
95                 self.UpdateObserver(each, _cachedUpdates)
96             lock.SetLock(None)
97             return 1
98         else: return 0
99
100     def UpdateObserver(self, observer, UpdateDict):
101         """Updates observer with UpdateDict.  (UpdateDict is assumed to be a dictonary.)"""
102         if observer: observer(self, **UpdateDict)
103
104     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105     #~ Subject Locking
106     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107
108     class _SubjectLock:
109         """Maintains a lock on subject while instance exists"""
110         subject = None
111         def __init__(self, lockedsubject):
112             self.SetLock(lockedsubject)
113         def __del__(self):
114             try:
115                 self.SetLock(None)
116             except:
117                 # XXX: Is there a good way to pass the exception along, and not have it
118                 # be absorbed by the python __del__ exception condition?
119                 import traceback
120                 traceback.print_exc()
121         def SetLock(self, lockedsubject):
122             """Obtains a  lock on lockedsubject.  If lockedsubject is different than the
123             current subject, then the current subject is unlocked as well."""
124             if self.subject and id(self) in self.subject._locked:
125                 self.subject._locked.remove(id(self))
126                 if self.subject._cachedUpdates:
127                     self.subject.UpdateObservers()
128             self.subject = lockedsubject
129             if self.subject:
130                 self.subject._locked.append(id(self))
131
132     def Lock(self):
133         """Prevents observers of subject from being updated while a lock is held on the subject."""
134         return self._SubjectLock(self)
135
136     def Locked(self):
137         """Returns 1 if the subject is locked, and 0 otherwise"""
138         return self._locked and 1 or 0
Note: See TracBrowser for help on using the browser.