eric6/DebugClients/Python/AsyncFile.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7360
9190402e4505
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an asynchronous file like socket interface for the
8 debugger.
9 """
10
11 import socket
12
13 from DebugUtilities import prepareJsonCommand
14
15 try:
16 unicode
17 except NameError:
18 unicode = str
19 raw_input = input
20
21
22 def AsyncPendingWrite(file):
23 """
24 Module function to check for data to be written.
25
26 @param file The file object to be checked
27 @type file
28 @return Flag indicating if there is data waiting
29 @rtype int
30 """
31 try:
32 pending = file.pendingWrite()
33 except Exception:
34 pending = 0
35
36 return pending
37
38
39 class AsyncFile(object):
40 """
41 Class wrapping a socket object with a file interface.
42 """
43 maxtries = 10
44
45 def __init__(self, sock, mode, name):
46 """
47 Constructor
48
49 @param sock the socket object being wrapped
50 @type socket
51 @param mode mode of this file
52 @type str
53 @param name name of this file
54 @type str
55 """
56 # Initialise the attributes.
57 self.closed = False
58 self.sock = sock
59 self.mode = mode
60 self.name = name
61 self.nWriteErrors = 0
62 self.encoding = "utf-8"
63 self.errors = None
64 self.newlines = None
65 self.line_buffering = False
66
67 self.wpending = []
68
69 def __checkMode(self, mode):
70 """
71 Private method to check the mode.
72
73 This method checks, if an operation is permitted according to
74 the mode of the file. If it is not, an IOError is raised.
75
76 @param mode the mode to be checked
77 @type string
78 @exception IOError raised to indicate a bad file descriptor
79 """
80 if mode != self.mode:
81 raise IOError((9, '[Errno 9] Bad file descriptor'))
82
83 def pendingWrite(self):
84 """
85 Public method that returns the number of strings waiting to be written.
86
87 @return the number of strings to be written
88 @rtype int
89 """
90 return len(self.wpending)
91
92 def close(self, closeit=False):
93 """
94 Public method to close the file.
95
96 @param closeit flag to indicate a close ordered by the debugger code
97 @type bool
98 """
99 if closeit and not self.closed:
100 self.flush()
101 self.sock.close()
102 self.closed = True
103
104 def flush(self):
105 """
106 Public method to write all pending entries.
107 """
108 while self.wpending:
109 try:
110 buf = self.wpending.pop(0)
111 except IndexError:
112 break
113
114 try:
115 try:
116 buf = buf.encode('utf-8', 'backslashreplace')
117 except (UnicodeEncodeError, UnicodeDecodeError):
118 pass
119 self.sock.sendall(buf)
120 self.nWriteErrors = 0
121 except socket.error:
122 self.nWriteErrors += 1
123 if self.nWriteErrors > self.maxtries:
124 self.wpending = [] # delete all output
125
126 def isatty(self):
127 """
128 Public method to indicate whether a tty interface is supported.
129
130 @return always false
131 @rtype bool
132 """
133 return False
134
135 def fileno(self):
136 """
137 Public method returning the file number.
138
139 @return file number
140 @rtype int
141 """
142 try:
143 return self.sock.fileno()
144 except socket.error:
145 return -1
146
147 def readable(self):
148 """
149 Public method to check, if the stream is readable.
150
151 @return flag indicating a readable stream
152 @rtype bool
153 """
154 return self.mode == "r"
155
156 def read_p(self, size=-1):
157 """
158 Public method to read bytes from this file.
159
160 @param size maximum number of bytes to be read
161 @type int
162 @return the bytes read
163 @rtype str
164 """
165 self.__checkMode('r')
166
167 if size < 0:
168 size = 20000
169
170 return self.sock.recv(size).decode('utf8', 'backslashreplace')
171
172 def read(self, size=-1):
173 """
174 Public method to read bytes from this file.
175
176 @param size maximum number of bytes to be read
177 @type int
178 @return the bytes read
179 @rtype str
180 """
181 self.__checkMode('r')
182
183 buf = raw_input()
184 if size >= 0:
185 buf = buf[:size]
186 return buf
187
188 def readCommand(self):
189 """
190 Public method to read a length prefixed command string.
191
192 @return command string
193 @rtype str
194 """
195 # The command string is prefixed by a 9 character long length field.
196 length = self.sock.recv(9)
197 length = int(length)
198 data = b''
199 while len(data) < length:
200 newData = self.sock.recv(length - len(data))
201 data += newData
202
203 # step 2: convert the data
204 return data.decode('utf8', 'backslashreplace')
205
206 def readline_p(self, size=-1):
207 """
208 Public method to read a line from this file.
209
210 <b>Note</b>: This method will not block and may return
211 only a part of a line if that is all that is available.
212
213 @param size maximum number of bytes to be read
214 @type int
215 @return one line of text up to size bytes
216 @rtype str
217 """
218 self.__checkMode('r')
219
220 if size < 0:
221 size = 20000
222
223 # The integration of the debugger client event loop and the connection
224 # to the debugger relies on the two lines of the debugger command being
225 # delivered as two separate events. Therefore we make sure we only
226 # read a line at a time.
227 line = self.sock.recv(size, socket.MSG_PEEK)
228
229 eol = line.find(b'\n')
230
231 if eol >= 0:
232 size = eol + 1
233 else:
234 size = len(line)
235
236 # Now we know how big the line is, read it for real.
237 return self.sock.recv(size).decode('utf8', 'backslashreplace')
238
239 def readlines(self, sizehint=-1):
240 """
241 Public method to read all lines from this file.
242
243 @param sizehint hint of the numbers of bytes to be read
244 @type int
245 @return list of lines read
246 @rtype list of str
247 """
248 self.__checkMode('r')
249
250 lines = []
251 room = sizehint
252
253 line = self.readline_p(room)
254 linelen = len(line)
255
256 while linelen > 0:
257 lines.append(line)
258
259 if sizehint >= 0:
260 room = room - linelen
261
262 if room <= 0:
263 break
264
265 line = self.readline_p(room)
266 linelen = len(line)
267
268 return lines
269
270 def readline(self, sizehint=-1):
271 """
272 Public method to read one line from this file.
273
274 @param sizehint hint of the numbers of bytes to be read
275 @type int
276 @return one line read
277 @rtype str
278 """
279 self.__checkMode('r')
280
281 line = raw_input() + '\n'
282 if sizehint >= 0:
283 line = line[:sizehint]
284 return line
285
286 def seekable(self):
287 """
288 Public method to check, if the stream is seekable.
289
290 @return flag indicating a seekable stream
291 @rtype bool
292 """
293 return False
294
295 def seek(self, offset, whence=0):
296 """
297 Public method to move the filepointer.
298
299 @param offset offset to move the filepointer to
300 @type int
301 @param whence position the offset relates to
302 @type int
303 @exception IOError This method is not supported and always raises an
304 IOError.
305 """
306 raise IOError((29, '[Errno 29] Illegal seek'))
307
308 def tell(self):
309 """
310 Public method to get the filepointer position.
311
312 @exception IOError This method is not supported and always raises an
313 IOError.
314 """
315 raise IOError((29, '[Errno 29] Illegal seek'))
316
317 def truncate(self, size=-1):
318 """
319 Public method to truncate the file.
320
321 @param size size to truncate to
322 @type int
323 @exception IOError This method is not supported and always raises an
324 IOError.
325 """
326 raise IOError((29, '[Errno 29] Illegal seek'))
327
328 def writable(self):
329 """
330 Public method to check, if a stream is writable.
331
332 @return flag indicating a writable stream
333 @rtype bool
334 """
335 return self.mode == "w"
336
337 def write(self, s):
338 """
339 Public method to write a string to the file.
340
341 @param s text to be written
342 @type str
343 """
344 self.__checkMode('w')
345
346 cmd = prepareJsonCommand("ClientOutput", {
347 "text": s,
348 })
349 self.wpending.append(cmd)
350 self.flush()
351
352 def write_p(self, s):
353 """
354 Public method to write a json-rpc 2.0 coded string to the file.
355
356 @param s text to be written
357 @type str
358 """
359 self.__checkMode('w')
360
361 self.wpending.append(s)
362 self.flush()
363
364 def writelines(self, lines):
365 """
366 Public method to write a list of strings to the file.
367
368 @param lines list of texts to be written
369 @type list of str
370 """
371 self.write("".join(lines))
372
373 #
374 # eflag: noqa = M702

eric ide

mercurial