root/trunk/RBFoundation/RBFoundation/SmartSelect.py

Revision 754, 8.2 kB (checked in by brian, 5 years ago)

Documentation fix

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 """Simple select.select handle management.
23
24 A set of classes wrapping select.select, making the management of N handles as
25 simple as managing a single handle.  In order to implement SmartSelect for your
26 socket or other handle(s), simply derive from SmartSelectBase, and implement the
27 abstract methods from ClientBase.
28
29 Well read pythoneers will note that is module is very similar to asyncore, and are
30 probably wondering why.  Well, my reason is interface.  Asyncore was great inspiration,
31 but I wanted to have less assumption that the objects were sockets; simply objects
32 compatible with select.select.  Secondly, objects derived from SmartSelectBase should
33 be as easily run as if they were collected into a larger group.  Finally, I wanted the
34 collections to feel like just that: collections of selectable objects.  (dict, list, or single)
35 """
36
37 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38 #~ Imports                                           
39 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40
41 import select
42 import socket
43 import time
44
45 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46 #~ Classes
47 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48
49 class ClientBase(object):
50     """An abstract base class for creating "Smart Sockets" or other select.select able objects"""
51     def _IsComplete(self): return 0
52     #def fileno(self): return 0 ## override this, returning something that works with select.select, like socket.fileno
53     def _NeedsRead(self): return 0
54     #def _ProcessRead(self): pass  ## If there is no definition, then these will raise if not "overridden"
55     def _NeedsWrite(self): return 0
56     #def _ProcessWrite(self): pass  ## If there is no definition, then these will raise if not "overridden"
57     def _NeedsError(self): return 0
58     #def _ProcessError(self): pass  ## If there is no definition, then these will raise if not "overridden"
59
60 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
61
62 class SmartSelectBase(object):
63     """Base class for waiting upon a set of select.select able objects"""
64
65     ProcessState = "Initial", None
66
67     def Process(self, timeout=None):
68         """Uses select.select to wait on several file (proxy) handles in an object-oriented way. 
69         ProcessState contains the state and, if applicable, the handle being processed,
70         allowing for interogation in the case of an exception."""
71         if not self:
72             self.ProcessState = "Idle", None
73             return 0
74
75         self.FilterLists()
76
77         self.ProcessState = "Waiting", None
78         ReadList, WriteList, ErrorList = self.ReadList, self.WriteList, self.ErrorList
79         if ReadList or WriteList or ErrorList:
80             ReadList, WriteList, ErrorList = select.select(ReadList, WriteList, ErrorList, timeout)
81
82             for each in ReadList:
83                 self.ProcessState = "Read", each
84                 each._ProcessRead()
85             for each in WriteList:
86                 self.ProcessState = "Write", each
87                 each._ProcessWrite()
88             for each in ErrorList:
89                 self.ProcessState = "Error", each
90                 each._ProcessError()
91
92             self.ProcessState = "Complete", None
93             return (ReadList or WriteList or ErrorList) and 1 or 0
94         else:
95             if timeout is not None:
96                 time.sleep(timeout)
97             self.ProcessState = "Complete", None
98             return 0
99
100     def ProcessPending(self, timeout=0.0):
101         """Calls Process until a timeout occurs."""
102         while self.Process(timeout):
103             pass
104         return 0
105
106 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107
108 class SmartSelectClientBase(ClientBase, SmartSelectBase):
109     """Allows base classes of a smart select to be able to run "by themselves" as if they were their own SmartSelectors"""
110     def FilterLists(self, *args, **kw): pass
111
112     def _getReadList(self): return self._NeedsRead() and [self] or []
113     ReadList = property(_getReadList)
114
115     def _getWriteList(self): return self._NeedsWrite() and [self] or []
116     WriteList = property(_getWriteList)
117
118     def _getErrorList(self): return self._NeedsError() and [self] or []
119     ErrorList = property(_getErrorList)
120
121 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122
123 class SmartSelectCollection(SmartSelectBase):
124     """Allows base classes of a smart select to be able to run "by themselves" as if they were their own SmartSelectors"""
125     def FilterLists(self, *args, **kw):
126         return self.SelectCollection.FilterLists(*args, **kw)
127
128     def _getReadList(self):
129         return self.SelectCollection._getReadList()
130     ReadList = property(_getReadList)
131
132     def _getWriteList(self):
133         return self.SelectCollection._getWriteList()
134     WriteList = property(_getWriteList)
135
136     def _getErrorList(self):
137         return self.SelectCollection._getErrorList()
138     ErrorList = property(_getErrorList)
139    
140 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141
142 class SmartSelectList(SmartSelectBase, list):
143     """A list collection of Smart Sockets to aid in pseudo "non-blocking" socket programming. 
144     Note, this is very simalr to asyncore.dispatcher."""
145
146     def __call__(self, *args, **kw):
147         return self.Process(*args, **kw)
148        
149     def FilterLists(self, *args, **kw):
150         self[:] = [x for x in self if not x._IsComplete()]
151
152     def _getReadList(self): return [x for x in self if x._NeedsRead()]
153     ReadList = property(_getReadList)
154
155     def _getWriteList(self): return [x for x in self if x._NeedsWrite()]
156     WriteList = property(_getWriteList)
157
158     def _getErrorList(self): return [x for x in self if x._NeedsError()]
159     ErrorList = property(_getErrorList)
160
161
162 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
163
164 class SmartSelectDict(SmartSelectBase, dict):
165     """A dictionary collection of Smart Sockets to aid in pseudo "non-blocking" socket programming. 
166     Note, this is very simalr to asyncore.dispatcher."""
167
168     def __call__(self, *args, **kw):
169         return self.Process(*args, **kw)
170        
171     def FilterLists(self, *args, **kw):
172         for key, each in self.items():
173             if not each._IsComplete():
174                 del self[key]
175
176     def _getReadList(self): return [x for x in self.itervalues() if x._NeedsRead()]
177     ReadList = property(_getReadList)
178
179     def _getWriteList(self): return [x for x in self.itervalues() if x._NeedsWrite()]
180     WriteList = property(_getWriteList)
181
182     def _getErrorList(self): return [x for x in self.itervalues() if x._NeedsError()]
183     ErrorList = property(_getErrorList)
184
185 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186
187 class SmartSelectDictList(SmartSelectBase, dict):
188     """A dictionary collection of lists of Smart Sockets to aid in pseudo "non-blocking" socket programming. 
189     Note, this is very simalr to asyncore.dispatcher."""
190
191     def __call__(self, *args, **kw):
192         return self.Process(*args, **kw)
193        
194     def FilterLists(self, *args, **kw):
195         for key, each in self.items():
196             if each:
197                 each[:] = [x for x in each if not x._IsComplete()]
198             else:
199                 del self[key]
200
201     def _getReadList(self): return [x for y in self.itervalues() for x in y if x._NeedsRead()]
202     ReadList = property(_getReadList)
203
204     def _getWriteList(self): return [x for y in self.itervalues() for x in y if x._NeedsWrite()]
205     WriteList = property(_getWriteList)
206
207     def _getErrorList(self): return [x for y in self.itervalues() for x in y if x._NeedsError()]
208     ErrorList = property(_getErrorList)
209
210 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
211
212 SmartSelect = SmartSelectList
Note: See TracBrowser for help on using the browser.