Bug fix: closing the file
[codimension.git] / src / debugger / client / asyncfile_cdm_dbg.py
blob00f3b2278a389a02395f3939bd5f40172e73fbd4
2 # -*- coding: utf-8 -*-
4 # codimension - graphics python two-way code editor and analyzer
5 # Copyright (C) 2010 Sergey Satskiy <sergey.satskiy@gmail.com>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # $Id$
24 # The file was taken from eric 4 and adopted for codimension.
25 # Original copyright:
26 # Copyright (c) 2002 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
29 """
30 Module implementing an asynchronous
31 file like socket interface for the debugger.
32 """
34 import socket
36 from protocol_cdm_dbg import EOT
39 def AsyncPendingWrite( fileObj ):
40 """
41 Checks for data to be written
43 @param fileObj The file object to be checked (file)
44 @return Flag indicating if there is data wating (int)
45 """
47 try:
48 pending = fileObj.pendingWrite()
49 except:
50 pending = 0
51 return pending
54 class AsyncFile( object ):
55 " Wrapps a socket object with a file interface "
57 maxtries = 10
58 maxbuffersize = 1024 * 1024 * 4
60 def __init__( self, sock, mode, name ):
61 """
62 @param sock the socket object being wrapped
63 @param mode mode of this file (string)
64 @param name name of this file (string)
65 """
67 # Initialise the attributes.
68 self.closed = 0
69 self.sock = sock
70 self.mode = mode
71 self.name = name
72 self.nWriteErrors = 0
74 self.wpending = u''
75 return
77 def __checkMode( self, mode ):
78 """
79 Checks the mode
81 This method checks, if an operation is permitted according to
82 the mode of the file. If it is not, an IOError is raised.
84 @param mode the mode to be checked (string)
85 """
87 if mode != self.mode:
88 raise IOError, '[Errno 9] Bad file descriptor'
89 return
91 def __nWrite( self, n ):
92 """
93 Writes a specific number of pending bytes
95 @param n the number of bytes to be written (int)
96 """
98 if n:
99 try :
100 buf = "%s%s" % ( self.wpending[ : n ], EOT )
101 try:
102 buf = buf.encode( 'utf8' )
103 except ( UnicodeEncodeError, UnicodeDecodeError ):
104 pass
105 self.sock.sendall( buf )
106 self.wpending = self.wpending[ n : ]
107 self.nWriteErrors = 0
108 except socket.error:
109 self.nWriteErrors += 1
110 if self.nWriteErrors > self.maxtries:
111 self.wpending = u'' # delete all output
112 return
114 def pendingWrite( self ):
116 Returns the number of bytes waiting to be written
118 @return the number of bytes to be written (int)
120 return len( self.wpending )
121 # return self.wpending.rfind( '\n' ) + 1
123 def close( self, closeit = 0 ):
125 Closes the file
127 @param closeit flag to indicate a close ordered by
128 the debugger code (boolean)
131 if closeit and not self.closed:
132 self.flush()
133 self.sock.close()
134 self.closed = 1
135 return
137 def flush( self ):
138 " Writes all pending bytes "
140 self.__nWrite( len( self.wpending ) )
141 return
143 def isatty( self ):
145 Indicates whether a tty interface is supported.
147 @return always false
149 return 0
151 def fileno( self ):
153 Public method returning the file number.
155 @return file number (int)
157 try:
158 return self.sock.fileno()
159 except socket.error:
160 return -1
162 def read_p( self, size = -1 ):
164 Reads bytes from this file
166 @param size maximum number of bytes to be read (int)
167 @return the bytes read (any)
169 self.__checkMode( 'r' )
171 if size < 0:
172 size = 20000
174 return self.sock.recv(size).decode( 'utf8' )
176 def read( self, size = -1 ):
178 Reads bytes from this file
180 @param size maximum number of bytes to be read (int)
181 @return the bytes read (any)
183 self.__checkMode( 'r' )
185 buf = raw_input()
186 if size >= 0:
187 buf = buf[ : size ]
188 return buf
190 def readline_p( self, size = -1 ):
192 Reads a line from this file.
194 <b>Note</b>: This method will not block and may return
195 only a part of a line if that is all that is available.
197 @param size maximum number of bytes to be read (int)
198 @return one line of text up to size bytes (string)
200 self.__checkMode( 'r' )
202 if size < 0:
203 size = 20000
205 # The integration of the debugger client event loop and the connection
206 # to the debugger relies on the two lines of the debugger command being
207 # delivered as two separate events. Therefore we make sure we only
208 # read a line at a time.
209 line = self.sock.recv( size, socket.MSG_PEEK )
211 eol = line.find( '\n' )
213 if eol >= 0:
214 size = eol + 1
215 else:
216 size = len( line )
218 # Now we know how big the line is, read it for real.
219 return self.sock.recv( size ).decode( 'utf8' )
221 def readlines( self, sizehint = -1 ):
223 Reads all lines from this file.
225 @param sizehint hint of the numbers of bytes to be read (int)
226 @return list of lines read (list of strings)
228 self.__checkMode( 'r' )
230 lines = []
231 room = sizehint
233 line = self.readline_p( room )
234 linelen = len( line )
236 while linelen > 0:
237 lines.append( line )
239 if sizehint >= 0:
240 room = room - linelen
242 if room <= 0:
243 break
245 line = self.readline_p( room )
246 linelen = len( line )
248 return lines
250 def readline( self, sizehint = -1 ):
252 Reads one line from this file.
254 @param sizehint hint of the numbers of bytes to be read (int)
255 @return one line read (string)
257 self.__checkMode( 'r' )
259 line = raw_input() + '\n'
260 if sizehint >= 0:
261 line = line[ : sizehint ]
262 return line
264 def seek( self, offset, whence = 0 ):
266 Moves the filepointer
268 @exception IOError This method is not supported and always raises an
269 IOError.
271 raise IOError, '[Errno 29] Illegal seek'
273 def tell( self ):
275 Provides the filepointer position
277 @exception IOError This method is not supported and always raises an
278 IOError.
280 raise IOError, '[Errno 29] Illegal seek'
282 def truncate( self, size = -1 ):
284 Truncates the file
286 @exception IOError This method is not supported and always raises an
287 IOError.
289 raise IOError, '[Errno 29] Illegal seek'
291 def write( self, s ):
293 Writes a string to the file
295 @param s bytes to be written (string)
298 self.__checkMode( 'w' )
299 tries = 0
300 if not self.wpending:
301 self.wpending = s
302 elif type( self.wpending ) != type( s ) or \
303 len( self.wpending ) + len( s ) > self.maxbuffersize:
304 # flush wpending so that different string types
305 # are not concatenated
306 while self.wpending:
307 # if we have a persistent error in sending the data,
308 # an exception will be raised in __nWrite
309 self.flush()
310 tries += 1
311 if tries > self.maxtries:
312 raise socket.error( "Too many attempts to send data" )
313 self.wpending = s
314 else:
315 self.wpending += s
316 self.__nWrite( self.pendingWrite() )
317 return
319 def writelines( self, lines ):
321 Writes a list of strings to the file.
323 @param lines the list to be written (list of string)
325 map( self.write, lines )
326 return