Changeset 258
- Timestamp:
- 2003-06-09 18:16:45 (6 years ago)
- Files:
-
- trunk/_test_ftp_stat.py (modified) (7 diffs)
- trunk/ftp_stat.py (modified) (4 diffs)
- trunk/ftputil.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/_test_ftp_stat.py
r253 r258 30 30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 31 32 # $Id: _test_ftp_stat.py,v 1. 2 2003/06/09 16:53:22schwa Exp $32 # $Id: _test_ftp_stat.py,v 1.3 2003/06/09 18:16:45 schwa Exp $ 33 33 34 34 import unittest … … 40 40 class TestStatParsers(unittest.TestCase): 41 41 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) 43 44 for line, expected_stat_result in zip(lines, expected_stat_results): 44 45 stat_result = parser.parse_line(line) … … 46 47 47 48 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) 49 51 for line in lines: 50 52 self.assertRaises(ftp_error.ParserError, parser.parse_line, line) … … 66 68 (41471, None, None, 2, '45854', '200', 512, None, 959551200.0, None) 67 69 ] 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) 70 71 71 72 def test_invalid_unix_lines(self): … … 77 78 "drwxr-sr-x 45854 200 512 May 4 2000 chemeng" 78 79 ] 79 self._test_invalid_lines(ftp_stat._UnixStat Parser, lines)80 self._test_invalid_lines(ftp_stat._UnixStat, lines) 80 81 81 82 def test_valid_ms_lines(self): … … 91 92 None) 92 93 ] 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) 95 95 96 96 def test_invalid_ms_lines(self): … … 100 100 "07-17-00 02:08AM 1226672x test.exe" 101 101 ] 102 self._test_invalid_lines(ftp_stat._MSStat Parser, lines)102 self._test_invalid_lines(ftp_stat._MSStat, lines) 103 103 104 104 trunk/ftp_stat.py
r257 r258 34 34 """ 35 35 36 # $Id: ftp_stat.py,v 1. 9 2003/06/09 18:00:13schwa Exp $36 # $Id: ftp_stat.py,v 1.10 2003/06/09 18:16:45 schwa Exp $ 37 37 38 38 import stat … … 69 69 70 70 71 class _StatParser: 72 """ 73 Provide parsing of directory lines and full directory listings. 74 """ 71 class _Stat: 72 """Methods for stat'ing directories, links and regular files.""" 73 def __init__(self, host): 74 self._host = host 75 75 76 def parse_line(self, line): 76 77 """ … … 89 90 return stat_results 90 91 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 161 class _UnixStat(_Stat): 93 162 # map month abbreviations to month numbers 94 163 _month_numbers = { … … 173 242 174 243 175 class _MSStat Parser(_StatParser):244 class _MSStat(_Stat): 176 245 def parse_line(self, line): 177 246 """ trunk/ftputil.py
r256 r258 30 30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 31 32 # $Id: ftputil.py,v 1.12 4 2003/06/09 17:42:43schwa Exp $32 # $Id: ftputil.py,v 1.125 2003/06/09 18:16:45 schwa Exp $ 33 33 34 34 """ … … 170 170 if response.find('ROBIN Microsoft') != -1 or \ 171 171 response.find('Bliss_Server Microsoft') != -1: 172 self._ parser = ftp_stat._MSStatParser()172 self._stat = ftp_stat._MSStat(self) 173 173 else: 174 self._ parser = ftp_stat._UnixStatParser()174 self._stat = ftp_stat._UnixStat(self) 175 175 176 176 # … … 533 533 for line in lines: 534 534 try: 535 stat_result = self._ parser.parse_line(line)535 stat_result = self._stat.parse_line(line) 536 536 except ftp_error.ParserError: 537 537 pass … … 540 540 return names 541 541 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 stat545 # (however, the string may be _anywhere_ on the line but not546 # necessarily the file's basename; e. g. the string could547 # occur as the name of the file's group)548 return [ line for line in lines549 if line.find(wanted_name) != -1 ]550 551 542 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) 582 544 583 545 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
