Blocking sockobj.makefile('w') objects
Stefan Schwarzer
s.schwarzer at ndh.net
Sun Jan 13 10:28:35 EST 2002
Hello all
Please excuse the long posting.
I'm writing a module to make FTP connections appear as file-like
objects (I will upload the code to the Vaults when it's ready).
Example code (code that is used behind the scenes follows below):
import ftputil
host = ftputil.FTPHost('ftp.domain.com', 'user', 'password')
text = 'Some example text\nSecond line'
fp = host.file('remote_name', 'w')
fp.write(text)
fp.close
host.close()
With read access (read, readline, readlines) all goes very well.
(I'm impressed. :-) )
However, if I use write (or writelines) like above, the interpreter
blocks after the statement "fp = host.file('remote_name', 'w')". I've
noted that the ftplib module itself does the "STOR" transfers by using
sockobj.send directly while it uses makefile for the "RETR" transfers.
Is there a way to use sockobj.makefile with "STOR" transfers? What's
the problem with the code below; why do 'w' modes not work?
I hope to have extracted the relevant code from ftputil.py ...
-----
import ftplib
class _FTPFile:
'''Represents a file-like object connected to an
FTP host. File and socket are closed appropriately if
the close operation is requested.'''
def __init__(self, host, conn, mode):
'''Construct the file(-like) object.'''
self._host = host
self._conn = conn
self._mode = mode
self._binary = 'b' in mode
self._fp = conn.makefile(mode)
#
# Read and write operations with support for
# line separator conversion for text modes.
#
# Note that we must convert line endings because
# the FTP server expects the native line separator
# format to be sent on ASCII transfers.
#
# read methods snipped
def write(self, data):
'''Write data to file. Do linesep conversion for
text mode.'''
if not self._binary:
data = _python_to_native_linesep(data)
#self._conn.send(data)
self._fp.write(data)
#
# other attributes
#
def __getattr__(self, attr_name):
'''Delegate unknown attribute requests to the file.'''
if attr_name in ( 'flush isatty fileno seek tell '
'truncate closed name softspace'.split() ):
return eval('self._fp.%s' % attr_name)
else:
raise AttributeError("'FTPFile' object has no "
"attribute '%s'" % attr_name)
# close and __del__ snipped
class FTPHost:
'''FTP host class'''
def __init__(self, *args, **kwargs):
'''Abstract initialization of FTPHost object. At this
stage I don't know if I need a new FTP connection for
each file transfer.'''
self._host = apply(ftplib.FTP, args, kwargs)
def file(self, path, mode='r'):
'''Return a file(-like) object that is connected to an
FTP host.'''
if '+' in mode:
raise FTPIOError("append modes not supported")
if mode not in ('r', 'rb', 'w', 'wb'):
raise FTPIOError("invalid mode")
# select ASCII or binary mode
transfer_type = ('A', 'I')['b' in mode]
command = 'TYPE %s' % transfer_type
# this logic taken from ftplib;
# why this strange distinction?
if mode == 'r':
self._host.sendcmd(command)
else: # rb, w, wb
self._host.voidcmd(command)
# make transfer command
command_type = ('STOR', 'RETR')['r' in mode]
command = '%s %s' % (command_type, path)
# get connection and file object
conn = self._host.transfercmd(command)
self._host.voidresp()
ftp_file = _FTPFile(self._host, conn, mode)
return ftp_file
-----
Many thanks for your advice in advance. :-)
Stefan
More information about the Python-list
mailing list