Changeset 1:e93155993afc

Show
Ignore:
Timestamp:
2002-01-13 01:08:26 (9 years ago)
Author:
Stefan Schwarzer <sschwarzer@…>
Branch:
default
convert_revision:
svn:778c30c8-61e0-0310-89d4-fe2f97a467b2/trunk@2
Message:
Significant parts of _FTPFile and FTPHost implemented.
Files:
1 modified

Legend:

Unmodified
Added
Removed
  • ftputil.py

    r0 r1  
    3131 
    3232import ftplib 
    33 import cStringIO 
     33 
    3434 
    3535class FTPIOError(IOError): 
    36     pass 
     36    def __init__(self, msg, ftp_exception=None): 
     37        self.ftp_exception = ftp_exception 
     38        IOError(self, msg) 
     39 
    3740 
    3841class _FTPFile: 
    3942    '''Represents a file-like object connected to an 
    40     FTP host.''' 
     43    FTP host. File and socket are closed appropriately if 
     44    the close operation is requested.''' 
    4145 
    42     def __init__(self, host, path): 
     46    def __init__(self, conn, mode): 
    4347        '''Construct the file(-like) object.''' 
    44         self._host = host 
    45         self._path = path 
    46         self.closed = 0 
     48        self._conn = conn 
     49        # this should be returned if someone asks 
     50        self.mode = mode 
     51        self._binary = 'b' in mode 
     52        if mode == 'r': 
     53            # the FTP server ensures the correct mode via 
     54            #  the previous TYPE command 
     55            mode = 'rb' 
     56        self._fp = conn.makefile(mode) 
    4757         
    48     def read(self, bytes=None): 
    49         '''Return 'bytes' bytes from the file object, or up 
    50         to EOF if the argument is not provided.''' 
    51         pass 
     58    def _normalize_linefeeds(text): 
     59        r'''Return data with occurences of \r removed.''' 
     60        return data.replace('\r', '') 
    5261 
    53     def readline(self): 
    54         '''Return a single line from the file object.''' 
    55         pass 
     62    def read(self, *args, **kwargs): 
     63        '''Return read bytes, normalized if in ASCII 
     64        transfer mode.''' 
     65        text = apply(self._fp.read, args, kwargs) 
     66        if self._binary: 
     67            return text 
     68        else: 
     69            return self._normalize_linefeeds(text) 
    5670 
    57     def readlines(self): 
    58         '''Return a list of lines until EOF.''' 
    59         pass 
     71    def readlines(self, *args, **kwargs): 
     72        '''Return read lines, normalized if in ASCII 
     73        transfer mode.''' 
     74        lines = apply(self._fp.readlines, args, kwargs) 
     75        return [self._normalize_linefeeds(line) 
     76                for line in lines] 
     77         
     78    def __getattr__(self, attr_name): 
     79        '''Delegate unknown attribute requests to the file.''' 
     80        if attr_name in ( 'flush isatty fileno read readline ' 
     81          'readlines xreadlines seek tell truncate write ' 
     82          'writelines closed name softspace'.split() ): 
     83            return eval('self._fp.%s' % attr_name) 
     84        else: 
     85            raise AttributeError("'FTPFile' object has no " 
     86                  "attribute '%s'" % attr_name) 
     87         
     88    def close(self): 
     89        '''Close the FTPFile. We need no 'if'; the file and the 
     90        socket object can be closed multiply without harm.''' 
     91        self._fp.close() 
     92        self._conn.close() 
    6093 
    61     def write(self, data): 
    62         '''Write a data string to the file.''' 
    63         pass 
     94    def __del__(self): 
     95        # not strictly necessary; file and socket are 
     96        #  closed on garbage collection, anyway 
     97        self.close() 
    6498 
    65     def writelines(self, data): 
    66         '''Write a list of strings to the file object.''' 
    67         pass 
    6899 
    69     def close(self): 
    70         if not self.closed: 
    71             # what will we do? 
    72             pass 
    73  
    74      
    75100class FTPHost: 
    76101    '''FTP host class''' 
     
    80105        stage I don't know if I need a new FTP connection for 
    81106        each file transfer.''' 
    82         self._hostname = host 
     107        self._hostname = hostname 
    83108        self._user = user 
    84109        self._password = password 
    85         self._host = ftplib.FTP(hostnamme, user, password) 
     110        self._host = ftplib.FTP(hostname, user, password) 
    86111     
    87112    def file(self, path, mode='r'): 
    88         '''Return a file-like object that is connected to 
     113        '''Return a file(-like) object that is connected to an 
    89114        FTP host.''' 
    90         # we don't support append modes 
    91         assert '+' not in mode 
    92         if 'r' in mode: 
    93             # read modes 
    94             if 'b' in mode: 
    95                 # open for binary reading 
    96             else: 
    97                 # open for ASCII reading 
    98         elif 'w' in mode: 
    99             # write modes 
    100             if 'b' in mode: 
    101                 # open for binary writing 
    102             else: 
    103                 # open for ASCII writing 
     115        if '+' in mode: 
     116            raise FTPIOError("append modes not supported") 
     117        if mode not in ( 'r rb br w wb bw'.split() ): 
     118            raise FTPIOError("invalid mode") 
     119        # select ASCII or binary mode 
     120        transfer_type = ('A', 'I')['b' in mode] 
     121        command = 'TYPE %s' % transfer_type 
     122        # logic taken from ftplib; 
     123        #  why this strange distinction? 
     124        if mode == 'r': 
     125            self._host.sendcmd(command) 
     126        else: 
     127            self._host.voidcmd(command) 
     128        # make transfer command 
     129        command_type = ('STOR', 'RETR')['r' in mode] 
     130        command = '%s %s' % (command_type, path) 
     131        # get connection and file object 
     132        conn = self._host.transfercmd(command) 
     133        self._host.voidresp() 
     134        ftp_file = _FTPFile(conn, mode) 
     135        return ftp_file 
    104136