src/eric7/DebugClients/Python/AsyncFile.py

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

eric ide

mercurial