Changeset 491:bbb12d16377f
- Timestamp:
- 2006-02-18 21:32:10 (4 years ago)
- Author:
- Stefan Schwarzer <sschwarzer@…>
- Branch:
- default
- convert_revision:
- svn:778c30c8-61e0-0310-89d4-fe2f97a467b2/trunk@510
- Message:
-
Reworked keep-alive functionality regarding writable files. If you read
the code, you will see that it's potentially brittle, and I may remove
the keep-alive code completely before the final 2.1 release.
In `ftp_file.py`, renamed `_binmode` and `_readmode` to `_bin_mode` and
`_read_mode`, respectively.
- Files:
-
Legend:
- Unmodified
- Added
- Removed
-
|
r489
|
r491
|
|
| 337 | 337 | print "Keeping host alive (%d of %d) " % (i+1, intervals) |
| 338 | 338 | host.keep_alive(ignore_errors=True) |
| | 339 | self.assertRaises(ftp_error.KeepAliveError, host.keep_alive) |
| | 340 | # open written files can't be "kept alive", |
| | 341 | # so cause some activity |
| | 342 | written_file.write("x") |
| | 343 | written_file.flush() |
| 339 | 344 | time.sleep(interval_length) |
| 340 | 345 | # do some tests; order of statements is important |
| … |
… |
|
| 342 | 347 | # files get reused) |
| 343 | 348 | self.assertEqual(read_file.read(2), "Te") |
| 344 | | written_file.write("xyz") |
| 345 | 349 | # - try to reuse the stale files |
| 346 | 350 | written_file2 = host.file("_test_file3_", "w") |
| … |
… |
|
| 354 | 358 | finally: |
| 355 | 359 | # clean up |
| 356 | | print 2, host.listdir(".") |
| 357 | | # host.remove("_test_file_") |
| 358 | | # host.remove("_test_file2_") |
| 359 | | # host.remove("_test_file3_") |
| | 360 | host.remove("_test_file_") |
| | 361 | host.remove("_test_file2_") |
| | 362 | host.remove("_test_file3_") |
| 360 | 363 | |
| 361 | 364 | |
-
|
r471
|
r491
|
|
| 61 | 61 | class TimeShiftError(InternalError): pass |
| 62 | 62 | class ParserError(InternalError): pass |
| | 63 | class KeepAliveError(InternalError): pass |
| 63 | 64 | |
| 64 | 65 | class FTPOSError(FTPError, OSError): pass |
-
|
r484
|
r491
|
|
| 99 | 99 | raise ftp_error.FTPIOError("invalid mode '%s'" % mode) |
| 100 | 100 | # remember convenience variables instead of mode |
| 101 | | self._binmode = 'b' in mode |
| 102 | | self._readmode = 'r' in mode |
| | 101 | self._bin_mode = 'b' in mode |
| | 102 | self._read_mode = 'r' in mode |
| 103 | 103 | # select ASCII or binary mode |
| 104 | | transfer_type = ('A', 'I')[self._binmode] |
| | 104 | transfer_type = ('A', 'I')[self._bin_mode] |
| 105 | 105 | command = 'TYPE %s' % transfer_type |
| 106 | 106 | ftp_error._try_with_ioerror(self._session.voidcmd, command) |
| 107 | 107 | # make transfer command |
| 108 | | command_type = ('STOR', 'RETR')[self._readmode] |
| | 108 | command_type = ('STOR', 'RETR')[self._read_mode] |
| 109 | 109 | command = '%s %s' % (command_type, path) |
| 110 | 110 | # ensure we can process the raw line separators; |
| … |
… |
|
| 131 | 131 | """Return read bytes, normalized if in text transfer mode.""" |
| 132 | 132 | data = self._fo.read(*args) |
| 133 | | if self._binmode: |
| | 133 | if self._bin_mode: |
| 134 | 134 | return data |
| 135 | 135 | data = _crlf_to_python_linesep(data) |
| … |
… |
|
| 164 | 164 | """Return one read line, normalized if in text transfer mode.""" |
| 165 | 165 | data = self._fo.readline(*args) |
| 166 | | if self._binmode: |
| | 166 | if self._bin_mode: |
| 167 | 167 | return data |
| 168 | 168 | # if necessary, complete begun newline |
| … |
… |
|
| 174 | 174 | """Return read lines, normalized if in text transfer mode.""" |
| 175 | 175 | lines = self._fo.readlines(*args) |
| 176 | | if self._binmode: |
| | 176 | if self._bin_mode: |
| 177 | 177 | return lines |
| 178 | 178 | # more memory-friendly than `return [... for line in lines]` |
| … |
… |
|
| 186 | 186 | separator conversion support. |
| 187 | 187 | """ |
| 188 | | if self._binmode: |
| | 188 | if self._bin_mode: |
| 189 | 189 | return self._fo.xreadlines() |
| 190 | 190 | return _XReadlines(self) |
| … |
… |
|
| 192 | 192 | def write(self, data): |
| 193 | 193 | """Write data to file. Do linesep conversion for text mode.""" |
| 194 | | if not self._binmode: |
| | 194 | if not self._bin_mode: |
| 195 | 195 | data = _python_to_crlf_linesep(data) |
| 196 | 196 | self._fo.write(data) |
| … |
… |
|
| 198 | 198 | def writelines(self, lines): |
| 199 | 199 | """Write lines to file. Do linesep conversion for text mode.""" |
| 200 | | if self._binmode: |
| | 200 | if self._bin_mode: |
| 201 | 201 | self._fo.writelines(lines) |
| 202 | 202 | return |
| … |
… |
|
| 221 | 221 | "'FTPFile' object has no attribute '%s'" % attr_name) |
| 222 | 222 | |
| 223 | | def keep_alive(self): |
| | 223 | def keep_alive(self, ignore_errors=False): |
| 224 | 224 | """ |
| 225 | 225 | Keep the connection busy to prevent lost connections |
| 226 | 226 | because of server timeouts. |
| 227 | | """ |
| 228 | | if self._readmode: |
| | 227 | |
| | 228 | Since this doesn't seem to work for writable files, |
| | 229 | `ignore_errors` must be set to a true value to avoid that a |
| | 230 | `KeepAliveError` is raised. By default, `ignore_errors` is a |
| | 231 | false value, so errors can't pass silently. |
| | 232 | """ |
| | 233 | if self.closed: |
| | 234 | self._host.getcwd() |
| | 235 | elif self._read_mode: |
| 229 | 236 | self.read(0) |
| 230 | 237 | else: |
| 231 | | self.write("") |
| | 238 | # I thought about putting `self.flush()` here; this will |
| | 239 | # work if the client wrote at least a byte in the file |
| | 240 | # since the last flush. However, using `flush()` here |
| | 241 | # could lead to subtle failures in ftputil client code. |
| | 242 | # Post on the ftputil mailing list if you think the |
| | 243 | # `flush` call is appropriate here. |
| | 244 | if not ignore_errors: |
| | 245 | raise ftp_error.KeepAliveError( |
| | 246 | "keep-alive doesn't work for writable files") |
| 232 | 247 | |
| 233 | 248 | def close(self): |
-
|
r487
|
r491
|
|
| 249 | 249 | return self.file(path, mode) |
| 250 | 250 | |
| 251 | | def keep_alive(self, also_files=True): |
| | 251 | def keep_alive(self, also_files=True, ignore_errors=False): |
| 252 | 252 | """ |
| 253 | 253 | Do something without side effects to keep the connection to |
| … |
… |
|
| 260 | 260 | may want to set `also_files` to `False` and call the |
| 261 | 261 | `keep_alive` method of the file-like objects individually. |
| 262 | | """ |
| 263 | | # just prevent loss of the connection, so discard the result |
| | 262 | |
| | 263 | If `ignore_errors` is set to a false value (the default), |
| | 264 | keep-alive attempts on writable files will raise a |
| | 265 | `KeepAliveError` (because there doesn't seem to be a way to |
| | 266 | implement this functionality). Set `ignore_errors` to a true |
| | 267 | value to mask such errors. |
| | 268 | """ |
| | 269 | # discard result |
| 264 | 270 | self.getcwd() |
| 265 | 271 | # refresh also connections of associated file-like objects |
| 266 | 272 | if also_files: |
| 267 | 273 | for host in self._children: |
| 268 | | host._file.keep_alive() |
| | 274 | host._file.keep_alive(ignore_errors=ignore_errors) |
| 269 | 275 | |
| 270 | 276 | def close(self): |