Changeset 258

Show
Ignore:
Timestamp:
2003-06-09 18:16:45 (6 years ago)
Author:
schwa
Message:
Moved `lstat` and `stat` method bodies from class `ftputil.FTPHost` to
    `ftp_stat._Stat`.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/_test_ftp_stat.py

    r253 r258  
    3030# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    3131 
    32 # $Id: _test_ftp_stat.py,v 1.2 2003/06/09 16:53:22 schwa Exp $ 
     32# $Id: _test_ftp_stat.py,v 1.3 2003/06/09 18:16:45 schwa Exp $ 
    3333 
    3434import unittest 
     
    4040class TestStatParsers(unittest.TestCase): 
    4141    def _test_valid_lines(self, parser_class, lines, expected_stat_results): 
    42         parser = parser_class() 
     42        # no `FTPHost` is needed for these tests, so set it to `None` 
     43        parser = parser_class(None) 
    4344        for line, expected_stat_result in zip(lines, expected_stat_results): 
    4445            stat_result = parser.parse_line(line) 
     
    4647 
    4748    def _test_invalid_lines(self, parser_class, lines): 
    48         parser = parser_class() 
     49        # no `FTPHost` is needed for these tests, so set it to `None` 
     50        parser = parser_class(None) 
    4951        for line in lines: 
    5052            self.assertRaises(ftp_error.ParserError, parser.parse_line, line) 
     
    6668          (41471, None, None, 2, '45854', '200', 512, None, 959551200.0, None) 
    6769          ] 
    68         self._test_valid_lines(ftp_stat._UnixStatParser, lines, 
    69                                expected_stat_results) 
     70        self._test_valid_lines(ftp_stat._UnixStat, lines, expected_stat_results) 
    7071 
    7172    def test_invalid_unix_lines(self): 
     
    7778          "drwxr-sr-x     45854    200           512 May  4  2000 chemeng" 
    7879          ] 
    79         self._test_invalid_lines(ftp_stat._UnixStatParser, lines) 
     80        self._test_invalid_lines(ftp_stat._UnixStat, lines) 
    8081 
    8182    def test_valid_ms_lines(self): 
     
    9192           None) 
    9293          ] 
    93         self._test_valid_lines(ftp_stat._MSStatParser, lines, 
    94                                expected_stat_results) 
     94        self._test_valid_lines(ftp_stat._MSStat, lines, expected_stat_results) 
    9595 
    9696    def test_invalid_ms_lines(self): 
     
    100100          "07-17-00  02:08AM           1226672x test.exe" 
    101101          ] 
    102         self._test_invalid_lines(ftp_stat._MSStatParser, lines) 
     102        self._test_invalid_lines(ftp_stat._MSStat, lines) 
    103103 
    104104 
  • trunk/ftp_stat.py

    r257 r258  
    3434""" 
    3535 
    36 # $Id: ftp_stat.py,v 1.9 2003/06/09 18:00:13 schwa Exp $ 
     36# $Id: ftp_stat.py,v 1.10 2003/06/09 18:16:45 schwa Exp $ 
    3737 
    3838import stat 
     
    6969 
    7070 
    71 class _StatParser: 
    72     """ 
    73     Provide parsing of directory lines and full directory listings. 
    74     """ 
     71class _Stat: 
     72    """Methods for stat'ing directories, links and regular files.""" 
     73    def __init__(self, host): 
     74        self._host = host 
     75 
    7576    def parse_line(self, line): 
    7677        """ 
     
    8990        return stat_results 
    9091 
    91  
    92 class _UnixStatParser(_StatParser): 
     92    def _stat_candidates(self, lines, wanted_name): 
     93        """Return candidate lines for further analysis.""" 
     94        # return only lines that contain the name of the file to stat 
     95        #  (however, the string may be _anywhere_ on the line but not 
     96        #  necessarily the file's basename; e. g. the string could 
     97        #  occur as the name of the file's group) 
     98        return [ line  for line in lines 
     99                 if line.find(wanted_name) != -1 ] 
     100 
     101    def lstat(self, path): 
     102        """Return an object similar to that returned by `os.lstat`.""" 
     103        host_path = self._host.path 
     104        # get output from FTP's `DIR` command 
     105        lines = [] 
     106        path = host_path.abspath(path) 
     107        # Note: (l)stat works by going one directory up and parsing 
     108        #  the output of an FTP `DIR` command. Unfortunately, it is 
     109        #  not possible to to this for the root directory `/`. 
     110        if path == '/': 
     111            raise ftp_error.RootDirError( 
     112                  "can't invoke stat for remote root directory") 
     113        dirname, basename = host_path.split(path) 
     114        lines = self._host._dir(dirname) 
     115        # search for name to be stat'ed without parsing the whole 
     116        #  directory listing 
     117        candidates = self._stat_candidates(lines, basename) 
     118        # parse candidates; return the first stat result where the 
     119        #  calculated name matches the previously determined 
     120        #  basename 
     121        for line in candidates: 
     122            try: 
     123                stat_result = self.parse_line(line) 
     124            except ftp_error.ParserError: 
     125                pass 
     126            else: 
     127                if stat_result._st_name == basename: 
     128                    return stat_result 
     129        # if the basename wasn't found in any line, raise an 
     130        #  exception 
     131        raise ftp_error.PermanentError( 
     132              "550 %s: no such file or directory" % path) 
     133 
     134    def stat(self, path): 
     135        """Return info from a `stat` call.""" 
     136        host_path = self._host.path 
     137        # most code in this method is used to detect recursive 
     138        #  link structures 
     139        visited_paths = {} 
     140        while True: 
     141            # stat the link if it is one, else the file/directory 
     142            stat_result = self.lstat(path) 
     143            # if the file is not a link, the `stat` result is the 
     144            #  same as the `lstat` result 
     145            if not stat.S_ISLNK(stat_result.st_mode): 
     146                return stat_result 
     147            # if we stat'ed a link, calculate a normalized path for 
     148            #  the file the link points to 
     149            dirname, basename = host_path.split(path) 
     150            path = host_path.join(dirname, stat_result._st_target) 
     151            path = host_path.normpath(path) 
     152            # check for cyclic structure 
     153            if visited_paths.has_key(path): 
     154                # we had this path already 
     155                raise ftp_error.PermanentError( 
     156                      "recursive link structure detected") 
     157            # remember the path we have encountered 
     158            visited_paths[path] = True 
     159 
     160 
     161class _UnixStat(_Stat): 
    93162    # map month abbreviations to month numbers 
    94163    _month_numbers = { 
     
    173242 
    174243 
    175 class _MSStatParser(_StatParser): 
     244class _MSStat(_Stat): 
    176245    def parse_line(self, line): 
    177246        """ 
  • trunk/ftputil.py

    r256 r258  
    3030# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    3131 
    32 # $Id: ftputil.py,v 1.124 2003/06/09 17:42:43 schwa Exp $ 
     32# $Id: ftputil.py,v 1.125 2003/06/09 18:16:45 schwa Exp $ 
    3333 
    3434""" 
     
    170170        if response.find('ROBIN Microsoft') != -1 or \ 
    171171           response.find('Bliss_Server Microsoft') != -1: 
    172             self._parser = ftp_stat._MSStatParser(
     172            self._stat = ftp_stat._MSStat(self
    173173        else: 
    174             self._parser = ftp_stat._UnixStatParser(
     174            self._stat = ftp_stat._UnixStat(self
    175175 
    176176    # 
     
    533533        for line in lines: 
    534534            try: 
    535                 stat_result = self._parser.parse_line(line) 
     535                stat_result = self._stat.parse_line(line) 
    536536            except ftp_error.ParserError: 
    537537                pass 
     
    540540        return names 
    541541 
    542     def _stat_candidates(self, lines, wanted_name): 
    543         """Return candidate lines for further analysis.""" 
    544         # return only lines that contain the name of the file to stat 
    545         #  (however, the string may be _anywhere_ on the line but not 
    546         #  necessarily the file's basename; e. g. the string could 
    547         #  occur as the name of the file's group) 
    548         return [ line  for line in lines 
    549                  if line.find(wanted_name) != -1 ] 
    550  
    551542    def lstat(self, path): 
    552         """Return an object similar to that returned by `os.lstat`.""" 
    553         # get output from FTP's `DIR` command 
    554         lines = [] 
    555         path = self.path.abspath(path) 
    556         # Note: (l)stat works by going one directory up and parsing 
    557         #  the output of an FTP `DIR` command. Unfortunately, it is 
    558         #  not possible to to this for the root directory `/`. 
    559         if path == '/': 
    560             raise ftp_error.RootDirError( 
    561                   "can't invoke stat for remote root directory") 
    562         dirname, basename = self.path.split(path) 
    563         lines = self._dir(dirname) 
    564         # search for name to be stat'ed without parsing the whole 
    565         #  directory listing 
    566         candidates = self._stat_candidates(lines, basename) 
    567         # parse candidates; return the first stat result where the 
    568         #  calculated name matches the previously determined 
    569         #  basename 
    570         for line in candidates: 
    571             try: 
    572                 stat_result = self._parser.parse_line(line) 
    573             except ftp_error.ParserError: 
    574                 pass 
    575             else: 
    576                 if stat_result._st_name == basename: 
    577                     return stat_result 
    578         # if the basename wasn't found in any line, raise an 
    579         #  exception 
    580         raise ftp_error.PermanentError( 
    581               "550 %s: no such file or directory" % path) 
     543        return self._stat.lstat(path) 
    582544 
    583545    def stat(self, path): 
    584         """Return info from a `stat` call.""" 
    585         # most code in this method is used to detect recursive 
    586         #  link structures 
    587         visited_paths = {} 
    588         while True: 
    589             # stat the link if it is one, else the file/directory 
    590             stat_result = self.lstat(path) 
    591             # if the file is not a link, the `stat` result is the 
    592             #  same as the `lstat` result 
    593             if not stat.S_ISLNK(stat_result.st_mode): 
    594                 return stat_result 
    595             # if we stat'ed a link, calculate a normalized path for 
    596             #  the file the link points to 
    597             dirname, basename = self.path.split(path) 
    598             path = self.path.join(dirname, stat_result._st_target) 
    599             path = self.path.normpath(path) 
    600             # check for cyclic structure 
    601             if visited_paths.has_key(path): 
    602                 # we had this path already 
    603                 raise ftp_error.PermanentError( 
    604                       "recursive link structure detected") 
    605             # remember the path we have encountered 
    606             visited_paths[path] = True 
    607  
     546        return self._stat.stat(path) 
     547