DebugClients/Python3/AsyncFile.py

changeset 0
de9c2efb9d02
child 13
1af94a91f439
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an asynchronous file like socket interface for the debugger.
8 """
9
10 import socket
11 import sys
12
13 from DebugProtocol import EOT, RequestOK
14
15
16 def AsyncPendingWrite(file):
17 """
18 Module function to check for data to be written.
19
20 @param file The file object to be checked (file)
21 @return Flag indicating if there is data wating (int)
22 """
23 try:
24 pending = file.pendingWrite()
25 except:
26 pending = 0
27
28 return pending
29
30
31 class AsyncFile(object):
32 """
33 Class wrapping a socket object with a file interface.
34 """
35 maxtries = 10
36 maxbuffersize = 1024 * 1024 * 4
37
38 def __init__(self, sock, mode, name):
39 """
40 Constructor
41
42 @param sock the socket object being wrapped
43 @param mode mode of this file (string)
44 @param name name of this file (string)
45 """
46 # Initialise the attributes.
47 self.__closed = False
48 self.sock = sock
49 self.mode = mode
50 self.name = name
51 self.nWriteErrors = 0
52
53 self.wpending = ''
54
55 def __checkMode(self, mode):
56 """
57 Private method to check the mode.
58
59 This method checks, if an operation is permitted according to
60 the mode of the file. If it is not, an IOError is raised.
61
62 @param mode the mode to be checked (string)
63 """
64 if mode != self.mode:
65 raise IOError((9, '[Errno 9] Bad file descriptor'))
66
67 def __nWrite(self, n):
68 """
69 Private method to write a specific number of pending bytes.
70
71 @param n the number of bytes to be written (int)
72 """
73 if n:
74 try :
75 buf = "{0!s}{1!s}".format(self.wpending[:n], EOT)
76 try:
77 buf = buf.encode('utf8', 'backslashreplace')
78 except (UnicodeEncodeError, UnicodeDecodeError):
79 pass
80 self.sock.sendall(buf)
81 self.wpending = self.wpending[n:]
82 self.nWriteErrors = 0
83 except socket.error:
84 self.nWriteErrors += 1
85 if self.nWriteErrors > self.maxtries:
86 self.wpending = '' # delete all output
87
88 def pendingWrite(self):
89 """
90 Public method that returns the number of bytes waiting to be written.
91
92 @return the number of bytes to be written (int)
93 """
94 return self.wpending.rfind('\n') + 1
95
96 def close(self, closeit = False):
97 """
98 Public method to close the file.
99
100 @param closeit flag to indicate a close ordered by the debugger code (boolean)
101 """
102 if closeit and not self.__closed:
103 self.flush()
104 self.sock.close()
105 self.__closed = True
106
107 def flush(self):
108 """
109 Public method to write all pending bytes.
110 """
111 self.__nWrite(len(self.wpending))
112
113 def isatty(self):
114 """
115 Public method to indicate whether a tty interface is supported.
116
117 @return always false
118 """
119 return False
120
121 def fileno(self):
122 """
123 Public method returning the file number.
124
125 @return file number (int)
126 """
127 return self.sock.fileno()
128
129 def read_p(self, size = -1):
130 """
131 Public method to read bytes from this file.
132
133 @param size maximum number of bytes to be read (int)
134 @return the bytes read (any)
135 """
136 self.__checkMode('r')
137
138 if size < 0:
139 size = 20000
140
141 return self.sock.recv(size).decode('utf8')
142
143 def read(self, size = -1):
144 """
145 Public method to read bytes from this file.
146
147 @param size maximum number of bytes to be read (int)
148 @return the bytes read (any)
149 """
150 self.__checkMode('r')
151
152 buf = input()
153 if size >= 0:
154 buf = buf[:size]
155 return buf
156
157 def readline_p(self, size = -1):
158 """
159 Public method to read a line from this file.
160
161 <b>Note</b>: This method will not block and may return
162 only a part of a line if that is all that is available.
163
164 @param size maximum number of bytes to be read (int)
165 @return one line of text up to size bytes (string)
166 """
167 self.__checkMode('r')
168
169 if size < 0:
170 size = 20000
171
172 # The integration of the debugger client event loop and the connection
173 # to the debugger relies on the two lines of the debugger command being
174 # delivered as two separate events. Therefore we make sure we only
175 # read a line at a time.
176 line = self.sock.recv(size, socket.MSG_PEEK)
177
178 eol = line.find(b'\n')
179
180 if eol >= 0:
181 size = eol + 1
182 else:
183 size = len(line)
184
185 # Now we know how big the line is, read it for real.
186 return self.sock.recv(size).decode('utf8')
187
188 def readlines(self, sizehint = -1):
189 """
190 Public method to read all lines from this file.
191
192 @param sizehint hint of the numbers of bytes to be read (int)
193 @return list of lines read (list of strings)
194 """
195 self.__checkMode('r')
196
197 lines = []
198 room = sizehint
199
200 line = self.readline_p(room)
201 linelen = len(line)
202
203 while linelen > 0:
204 lines.append(line)
205
206 if sizehint >= 0:
207 room = room - linelen
208
209 if room <= 0:
210 break
211
212 line = self.readline_p(room)
213 linelen = len(line)
214
215 return lines
216
217 def readline(self, sizehint = -1):
218 """
219 Public method to read one line from this file.
220
221 @param sizehint hint of the numbers of bytes to be read (int)
222 @return one line read (string)
223 """
224 self.__checkMode('r')
225
226 line = input() + '\n'
227 if sizehint >= 0:
228 line = line[:sizehint]
229 return line
230
231 def seek(self, offset, whence = 0):
232 """
233 Public method to move the filepointer.
234
235 @param offset offset to move the filepointer to (integer)
236 @param whence position the offset relates to
237 @exception IOError This method is not supported and always raises an
238 IOError.
239 """
240 raise IOError((29, '[Errno 29] Illegal seek'))
241
242 def tell(self):
243 """
244 Public method to get the filepointer position.
245
246 @exception IOError This method is not supported and always raises an
247 IOError.
248 """
249 raise IOError((29, '[Errno 29] Illegal seek'))
250
251 def truncate(self, size = -1):
252 """
253 Public method to truncate the file.
254
255 @param size size to truncaze to (integer)
256 @exception IOError This method is not supported and always raises an
257 IOError.
258 """
259 raise IOError((29, '[Errno 29] Illegal seek'))
260
261 def write(self, s):
262 """
263 Public method to write a string to the file.
264
265 @param s bytes to be written (string)
266 """
267 self.__checkMode('w')
268 tries = 0
269 if not self.wpending:
270 self.wpending = s
271 elif len(self.wpending) + len(s) > self.maxbuffersize:
272 # flush wpending so that different string types are not concatenated
273 while self.wpending:
274 # if we have a persistent error in sending the data, an exception
275 # will be raised in __nWrite
276 self.flush()
277 tries += 1
278 if tries > self.maxtries:
279 raise socket.error("Too many attempts to send data")
280 self.wpending = s
281 else:
282 self.wpending += s
283 self.__nWrite(self.pendingWrite())
284
285 def writelines(self, list):
286 """
287 Public method to write a list of strings to the file.
288
289 @param list the list to be written (list of string)
290 """
291 for l in list:
292 self.write(l)

eric ide

mercurial