# -*- coding: utf-8 -*-
# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>
# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
#
"""
Module implementing an asynchronous interface for the debugger.
"""
import socket
from qt import SIGNAL, PYSIGNAL, QObject, QSocketNotifier
def AsyncPendingWrite(file):
"""
Module function to check for data to be written.
Arguments
file -- the file object to be checked (file)
Returns
flag indicating if there is data wating (int)
"""
try:
pending = file.pendingWrite()
except:
pending = 0
return pending
class AsyncFile:
"""
Class wrapping a socket object with a file interface.
"""
def __init__(self,sock,mode,name):
"""
Constructor
Arguments
sock -- the socket object being wrapped
mode -- mode of this file (string)
name -- name of this file (string)
"""
# Initialise the attributes.
self.closed = 0
self.sock = sock
self.mode = mode
self.name = name
self.softspace = 0
self.wpending = ''
def checkMode(self,mode):
"""
Private method to check the mode.
This method checks, if an operation is permitted according to
the mode of the file. If it is not, an IOError is raised.
Arguments
mode -- the mode to be checked (string)
"""
if mode != self.mode:
raise IOError, '[Errno 9] Bad file descriptor'
def nWrite(self,n):
"""
Private method to write a specific number of pending bytes.
Arguments
n -- the number of bytes to be written (int)
"""
if n:
sent = self.sock.send(self.wpending[:n])
self.wpending = self.wpending[sent:]
def pendingWrite(self):
"""
Public method that returns the number of bytes waiting to be written.
Returns
the number of bytes to be written (int)
"""
return self.wpending.rfind('\n') + 1
def close(self):
"""
Public method to close the file.
"""
if not self.closed:
self.flush()
self.sock.close()
self.closed = 1
def flush(self):
"""
Public method to write all pending bytes.
"""
self.nWrite(len(self.wpending))
def isatty(self):
"""
Public method to indicate whether a tty interface is supported.
Returns
always false
"""
return 0
def fileno(self):
"""
Public method returning the file number.
Returns
file number (int)
"""
return self.sock.fileno()
def read(self,size=-1):
"""
Public method to read bytes from this file.
Arguments
size -- maximum number of bytes to be read (int)
Returns
the bytes read (any)
"""
self.checkMode('r')
if size < 0:
size = 20000
return self.sock.recv(size)
def readline(self,size=-1):
"""
Public method to read a line from this file.
Arguments
size -- maximum number of bytes to be read (int)
Returns
one line of text up to size bytes (string)
Note
This method will not block and may return only a part of a
line if that is all that is available.
"""
self.checkMode('r')
if size < 0:
size = 20000
# The integration of the debugger client event loop and the connection
# to the debugger relies on the two lines of the debugger command being
# delivered as two separate events. Therefore we make sure we only
# read a line at a time.
line = self.sock.recv(size,socket.MSG_PEEK)
eol = line.find('\n')
if eol >= 0:
size = eol + 1
else:
size = len(line)
# Now we know how big the line is, read it for real.
return self.sock.recv(size)
def readlines(self,sizehint=-1):
"""
Public method to read all lines from this file.
Arguments
sizehint -- hint of the numbers of bytes to be read (int)
Returns
list of lines read (list of strings)
"""
lines = []
room = sizehint
line = self.readline(room)
linelen = len(line)
while linelen > 0:
lines.append(line)
if sizehint >= 0:
room = room - linelen
if room <= 0:
break
line = self.readline(room)
linelen = len(line)
return lines
def seek(self,offset,whence=0):
"""
Public method to move the filepointer.
This method is not supported and always raises an
IOError.
"""
raise IOError, '[Errno 29] Illegal seek'
def tell(self):
"""
Public method to get the filepointer position.
This method is not supported and always raises an
IOError.
"""
raise IOError, '[Errno 29] Illegal seek'
def truncate(self,size=-1):
"""
Public method to truncate the file.
This method is not supported and always raises an
IOError.
"""
raise IOError, '[Errno 29] Illegal seek'
def write(self,str):
"""
Public method to write a string to the file.
Arguments
str -- bytes to be written (string)
"""
self.checkMode('w')
self.wpending = self.wpending + str
self.nWrite(self.pendingWrite())
def writelines(self,list):
"""
Public method to write a list of strings to the file.
Arguments
list -- the list to be written (list of string)
"""
map(self.write,list)
class AsyncIO(QObject):
"""
Class implementing asynchronous reading and writing.
It implements asynchronous reading and writing using the Qt event
loop.
Signals
lineReady -- emitted when a complete line has been read
gotEOF -- emitted if EOF was read
"""
def __init__(self,parent=None):
"""
Constructor
Arguments
parent -- the optional parent of this object (QObject)
"""
QObject.__init__(self,parent)
# There is no connection yet.
self.disconnect()
def disconnect(self):
"""
Public method to disconnect any current connection.
"""
self.readsn = None
self.readfd = None
self.writesn = None
self.writefd = None
def setDescriptors(self,rfd,wfd):
"""
Public method called to set the descriptors for the connection.
Arguments
rfd -- file descriptor of the input file (int)
wfd -- file descriptor of the output file (int)
"""
self.rbuf = ''
self.readfd = rfd
self.wbuf = ''
self.writefd = wfd
def setNotifiers(self):
"""
Public method to set up the socket notifiers for the Qt event loop.
"""
self.readsn = QSocketNotifier(self.readfd.fileno(),QSocketNotifier.Read)
self.readsn.connect(self.readsn,SIGNAL('activated(int)'),self.readReady)
self.writesn = QSocketNotifier(self.writefd.fileno(),QSocketNotifier.Write)
self.writesn.connect(self.writesn,SIGNAL('activated(int)'),self.writeReady)
self.setWriteNotifier()
def readReady(self,fd):
"""
Protected method called when there is data ready to be read.
Arguments
fd -- file descriptor of the file that has data to be read (int)
"""
# There seems to be a problem under Windows that the QSocketNotifier
# says that there is data from the server, but the Python socket module
# complains that the recv() on the socket would block and raises an
# exception. However, everything goes on to work as expected so we
# just catch the exception and ignore it.
try:
got = self.readfd.readline()
except:
return
if len(got) == 0:
self.emit(PYSIGNAL('gotEOF'),())
return
self.rbuf = self.rbuf + got
# Emit a signal for the line if it is complete.
eol = self.rbuf.find('\n')
while eol >= 0:
s = self.rbuf[:eol + 1]
self.rbuf = self.rbuf[eol + 1:]
self.emit(PYSIGNAL('lineReady'),(s,))
eol = self.rbuf.find('\n')
def writeReady(self,fd):
"""
Protected method called when we are ready to write data.
Arguments
fd -- file descriptor of the file that has data to be written (int)
"""
self.writefd.write(self.wbuf)
self.writefd.flush()
self.wbuf = ''
if self.writesn is not None:
self.setWriteNotifier()
def setWriteNotifier(self):
"""
Private method called to disable the write notifier.
If there is no data to be written, the write notifier
will be diabled.
"""
if not AsyncPendingWrite(self.writefd):
self.writesn.setEnabled(0)
def write(self,s):
"""
Public method to write a string.
Arguments
s -- the data to be written (string)
"""
# Make sure any write notifier is enabled.
if self.writesn is not None:
self.writesn.setEnabled(1)
self.wbuf = self.wbuf + s
|