[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-65]

add settimeout/gettimeout/setblocking, some bugfixes.
hide the command and response codes in sftp so they aren't exported.
add settimeout/gettimeout/setblocking that just wrap calls to the underlying
socket or channel.  fix _read_all to not catch timeout exceptions.
This commit is contained in:
Robey Pointer 2004-08-27 00:28:33 +00:00
parent 32afce8d49
commit 34d975b972
1 changed files with 82 additions and 52 deletions

View File

@ -25,15 +25,16 @@ from channel import Channel
from message import Message from message import Message
from file import BufferedFile from file import BufferedFile
CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, CMD_FSTAT, CMD_SETSTAT, \ _CMD_INIT, _CMD_VERSION, _CMD_OPEN, _CMD_CLOSE, _CMD_READ, _CMD_WRITE, _CMD_LSTAT, _CMD_FSTAT, \
CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, CMD_REMOVE, CMD_MKDIR, CMD_RMDIR, CMD_REALPATH, \ _CMD_SETSTAT, _CMD_FSETSTAT, _CMD_OPENDIR, _CMD_READDIR, _CMD_REMOVE, _CMD_MKDIR, \
CMD_STAT, CMD_RENAME, CMD_READLINK, CMD_SYMLINK = range(1, 21) _CMD_RMDIR, _CMD_REALPATH, _CMD_STAT, _CMD_RENAME, _CMD_READLINK, _CMD_SYMLINK \
CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106) = range(1, 21)
CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202) _CMD_STATUS, _CMD_HANDLE, _CMD_DATA, _CMD_NAME, _CMD_ATTRS = range(101, 106)
_CMD_EXTENDED, _CMD_EXTENDED_REPLY = range(200, 202)
FX_OK = 0 _FX_OK = 0
FX_EOF, FX_NO_SUCH_FILE, FX_PERMISSION_DENIED, FX_FAILURE, FX_BAD_MESSAGE, FX_NO_CONNECTION, \ _FX_EOF, _FX_NO_SUCH_FILE, _FX_PERMISSION_DENIED, _FX_FAILURE, _FX_BAD_MESSAGE, \
FX_CONNECTION_LOST, FX_OP_UNSUPPORTED = range(1, 9) _FX_NO_CONNECTION, _FX_CONNECTION_LOST, _FX_OP_UNSUPPORTED = range(1, 9)
VERSION = 3 VERSION = 3
@ -133,8 +134,8 @@ class SFTPFile (BufferedFile):
BufferedFile._set_mode(self, mode, bufsize) BufferedFile._set_mode(self, mode, bufsize)
def _get_size(self): def _get_size(self):
t, msg = self.sftp._request(CMD_FSTAT, self.handle) t, msg = self.sftp._request(_CMD_FSTAT, self.handle)
if t != CMD_ATTRS: if t != _CMD_ATTRS:
raise SFTPError('Expected attrs') raise SFTPError('Expected attrs')
attr = SFTPAttributes() attr = SFTPAttributes()
attr.unpack(msg) attr.unpack(msg)
@ -145,12 +146,12 @@ class SFTPFile (BufferedFile):
def close(self): def close(self):
BufferedFile.close(self) BufferedFile.close(self)
self.sftp._request(CMD_CLOSE, self.handle) self.sftp._request(_CMD_CLOSE, self.handle)
def _read(self, size): def _read(self, size):
size = min(size, self.MAX_REQUEST_SIZE) size = min(size, self.MAX_REQUEST_SIZE)
t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size)) t, msg = self.sftp._request(_CMD_READ, self.handle, long(self._realpos), int(size))
if t != CMD_DATA: if t != _CMD_DATA:
raise SFTPError('Expected data') raise SFTPError('Expected data')
return msg.get_string() return msg.get_string()
@ -158,11 +159,45 @@ class SFTPFile (BufferedFile):
offset = 0 offset = 0
while offset < len(data): while offset < len(data):
chunk = min(len(data) - offset, self.MAX_REQUEST_SIZE) chunk = min(len(data) - offset, self.MAX_REQUEST_SIZE)
t, msg = self.sftp._request(CMD_WRITE, self.handle, long(self._realpos + offset), t, msg = self.sftp._request(_CMD_WRITE, self.handle, long(self._realpos + offset),
str(data[offset : offset + chunk])) str(data[offset : offset + chunk]))
offset += chunk offset += chunk
return len(data) return len(data)
def settimeout(self, timeout):
"""
Set a timeout on read/write operations on the underlying socket or
ssh L{Channel}.
@see: L{Channel.settimeout}
@param timeout: seconds to wait for a pending read/write operation
before raising C{socket.timeout}, or C{None} for no timeout
@type timeout: float
"""
self.sock.settimeout(timeout)
def gettimeout(self):
"""
Returns the timeout in seconds (as a float) associated with the socket
or ssh L{Channel} used for this file.
@see: L{Channel.gettimeout}
@rtype: float
"""
return self.sock.gettimeout()
def setblocking(self, blocking):
"""
Set blocking or non-blocking mode on the underiying socket or ssh
L{Channel}.
@see: L{Channel.setblocking}
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
mode.
@type blocking: int
"""
self.sock.setblocking(blocking)
def seek(self, offset, whence=0): def seek(self, offset, whence=0):
self.flush() self.flush()
if whence == self.SEEK_SET: if whence == self.SEEK_SET:
@ -183,8 +218,8 @@ class SFTPFile (BufferedFile):
@return: an object containing attributes about this file. @return: an object containing attributes about this file.
@rtype: SFTPAttributes @rtype: SFTPAttributes
""" """
t, msg = self.sftp._request(CMD_FSTAT, self.handle) t, msg = self.sftp._request(_CMD_FSTAT, self.handle)
if t != CMD_ATTRS: if t != _CMD_ATTRS:
raise SFTPError('Expected attributes') raise SFTPError('Expected attributes')
attr = SFTPAttributes(msg) attr = SFTPAttributes(msg)
attr._pythonize() attr._pythonize()
@ -201,9 +236,9 @@ class SFTP (object):
else: else:
self.logger = logging.getLogger('paramiko.sftp') self.logger = logging.getLogger('paramiko.sftp')
# protocol: (maybe should move to a different method) # protocol: (maybe should move to a different method)
self._send_packet(CMD_INIT, struct.pack('>I', VERSION)) self._send_packet(_CMD_INIT, struct.pack('>I', VERSION))
t, data = self._read_packet() t, data = self._read_packet()
if t != CMD_VERSION: if t != _CMD_VERSION:
raise SFTPError('Incompatible sftp protocol') raise SFTPError('Incompatible sftp protocol')
version = struct.unpack('>I', data[:4])[0] version = struct.unpack('>I', data[:4])[0]
# if version != VERSION: # if version != VERSION:
@ -228,18 +263,18 @@ class SFTP (object):
@return: list of filenames. @return: list of filenames.
@rtype: list of string @rtype: list of string
""" """
t, msg = self._request(CMD_OPENDIR, path) t, msg = self._request(_CMD_OPENDIR, path)
if t != CMD_HANDLE: if t != _CMD_HANDLE:
raise SFTPError('Expected handle') raise SFTPError('Expected handle')
handle = msg.get_string() handle = msg.get_string()
filelist = [] filelist = []
while 1: while 1:
try: try:
t, msg = self._request(CMD_READDIR, handle) t, msg = self._request(_CMD_READDIR, handle)
except EOFError, e: except EOFError, e:
# done with handle # done with handle
break break
if t != CMD_NAME: if t != _CMD_NAME:
raise SFTPError('Expected name response') raise SFTPError('Expected name response')
count = msg.get_int() count = msg.get_int()
for i in range(count): for i in range(count):
@ -249,7 +284,7 @@ class SFTP (object):
if (filename != '.') and (filename != '..'): if (filename != '.') and (filename != '..'):
filelist.append(filename) filelist.append(filename)
# currently we ignore the rest # currently we ignore the rest
self._request(CMD_CLOSE, handle) self._request(_CMD_CLOSE, handle)
return filelist return filelist
def open(self, filename, mode='r', bufsize=-1): def open(self, filename, mode='r', bufsize=-1):
@ -288,8 +323,8 @@ class SFTP (object):
if ('a' in mode): if ('a' in mode):
imode |= self._FXF_APPEND imode |= self._FXF_APPEND
attrblock = SFTPAttributes() attrblock = SFTPAttributes()
t, msg = self._request(CMD_OPEN, filename, imode, attrblock) t, msg = self._request(_CMD_OPEN, filename, imode, attrblock)
if t != CMD_HANDLE: if t != _CMD_HANDLE:
raise SFTPError('Expected handle') raise SFTPError('Expected handle')
handle = msg.get_string() handle = msg.get_string()
return SFTPFile(self, handle, mode, bufsize) return SFTPFile(self, handle, mode, bufsize)
@ -304,7 +339,7 @@ class SFTP (object):
@raise IOError: if the path refers to a folder (directory). Use @raise IOError: if the path refers to a folder (directory). Use
L{rmdir} to remove a folder. L{rmdir} to remove a folder.
""" """
self._request(CMD_REMOVE, path) self._request(_CMD_REMOVE, path)
unlink = remove unlink = remove
@ -320,7 +355,7 @@ class SFTP (object):
@raise IOError: if C{newpath} is a folder, or something else goes @raise IOError: if C{newpath} is a folder, or something else goes
wrong. wrong.
""" """
self._request(CMD_RENAME, oldpath, newpath) self._request(_CMD_RENAME, oldpath, newpath)
def mkdir(self, path, mode=0777): def mkdir(self, path, mode=0777):
""" """
@ -335,7 +370,7 @@ class SFTP (object):
""" """
attr = SFTPAttributes() attr = SFTPAttributes()
attr.permissions = mode attr.permissions = mode
self._request(CMD_MKDIR, path, attr) self._request(_CMD_MKDIR, path, attr)
def rmdir(self, path): def rmdir(self, path):
""" """
@ -344,7 +379,7 @@ class SFTP (object):
@param path: name of the folder to remove. @param path: name of the folder to remove.
@type path: string @type path: string
""" """
self._request(CMD_RMDIR, path) self._request(_CMD_RMDIR, path)
def stat(self, path): def stat(self, path):
""" """
@ -365,8 +400,8 @@ class SFTP (object):
@return: an object containing attributes about the given file. @return: an object containing attributes about the given file.
@rtype: SFTPAttributes @rtype: SFTPAttributes
""" """
t, msg = self._request(CMD_STAT, path) t, msg = self._request(_CMD_STAT, path)
if t != CMD_ATTRS: if t != _CMD_ATTRS:
raise SFTPError('Expected attributes') raise SFTPError('Expected attributes')
attr = SFTPAttributes(msg) attr = SFTPAttributes(msg)
attr._pythonize() attr._pythonize()
@ -383,8 +418,8 @@ class SFTP (object):
@return: an object containing attributes about the given file. @return: an object containing attributes about the given file.
@rtype: SFTPAttributes @rtype: SFTPAttributes
""" """
t, msg = self._request(CMD_LSTAT, path) t, msg = self._request(_CMD_LSTAT, path)
if t != CMD_ATTRS: if t != _CMD_ATTRS:
raise SFTPError('Expected attributes') raise SFTPError('Expected attributes')
attr = SFTPAttributes(msg) attr = SFTPAttributes(msg)
attr._pythonize() attr._pythonize()
@ -400,7 +435,7 @@ class SFTP (object):
@param dest: path of the newly created symlink. @param dest: path of the newly created symlink.
@type dest: string @type dest: string
""" """
self._request(CMD_SYMLINK, source, dest) self._request(_CMD_SYMLINK, source, dest)
def chmod(self, path, mode): def chmod(self, path, mode):
""" """
@ -415,7 +450,7 @@ class SFTP (object):
""" """
attr = SFTPAttributes() attr = SFTPAttributes()
attr.permissions = mode attr.permissions = mode
self._request(CMD_SETSTAT, path, attr) self._request(_CMD_SETSTAT, path, attr)
def chown(self, path, uid, gid): def chown(self, path, uid, gid):
""" """
@ -433,7 +468,7 @@ class SFTP (object):
""" """
attr = SFTPAttributes() attr = SFTPAttributes()
attr.uid, attr.gid = uid, gid attr.uid, attr.gid = uid, gid
self._request(CMD_SETSTAT, path, attr) self._request(_CMD_SETSTAT, path, attr)
def utime(self, path, times): def utime(self, path, times):
""" """
@ -454,7 +489,7 @@ class SFTP (object):
times = (time.time(), time.time()) times = (time.time(), time.time())
attr = SFTPAttributes() attr = SFTPAttributes()
attr.atime, attr.mtime = times attr.atime, attr.mtime = times
self._request(CMD_SETSTAT, path, attr) self._request(_CMD_SETSTAT, path, attr)
def readlink(self, path): def readlink(self, path):
""" """
@ -467,8 +502,8 @@ class SFTP (object):
@return: target path. @return: target path.
@rtype: string @rtype: string
""" """
t, msg = self._request(CMD_READLINK, path) t, msg = self._request(_CMD_READLINK, path)
if t != CMD_NAME: if t != _CMD_NAME:
raise SFTPError('Expected name response') raise SFTPError('Expected name response')
count = msg.get_int() count = msg.get_int()
if count == 0: if count == 0:
@ -509,15 +544,11 @@ class SFTP (object):
def _read_all(self, n): def _read_all(self, n):
out = '' out = ''
while n > 0: while n > 0:
try: x = self.sock.recv(n)
x = self.sock.recv(n) if len(x) == 0:
if len(x) == 0: raise EOFError()
raise EOFError() out += x
out += x n -= len(x)
n -= len(x)
except socket.timeout:
if not self.active:
raise EOFError()
return out return out
def _send_packet(self, t, packet): def _send_packet(self, t, packet):
@ -556,7 +587,7 @@ class SFTP (object):
if num != self.request_number: if num != self.request_number:
raise SFTPError('Expected response #%d, got response #%d' % (self.request_number, num)) raise SFTPError('Expected response #%d, got response #%d' % (self.request_number, num))
self.request_number += 1 self.request_number += 1
if t == CMD_STATUS: if t == _CMD_STATUS:
self._convert_status(msg) self._convert_status(msg)
return t, msg return t, msg
@ -566,11 +597,10 @@ class SFTP (object):
""" """
code = msg.get_int() code = msg.get_int()
text = msg.get_string() text = msg.get_string()
if code == FX_OK: if code == _FX_OK:
return return
elif code == FX_EOF: elif code == _FX_EOF:
raise EOFError(text) raise EOFError(text)
else: else:
raise IOError(text) raise IOError(text)