# Copyright (C) 2003-2021, Stefan Schwarzer # and ftputil contributors (see `doc/contributors.txt`) # See the file LICENSE for licensing terms. """ ftputil.error - exception classes and wrappers """ # pylint: disable=too-many-ancestors import ftplib import ftputil.path_encoding import ftputil.tool import ftputil.version # You _can_ import these with `from ftputil.error import *`, - but it's _not_ # recommended. __all__ = [ "CommandNotImplementedError", "FTPIOError", "FTPOSError", "InaccessibleLoginDirError", "InternalError", "KeepAliveError", "NoEncodingError", "ParserError", "PermanentError", "RootDirError", "SyncError", "TemporaryError", "TimeShiftError", ] class FTPError(Exception): """ General ftputil error class. """ def __init__(self, *args, original_error=None): super().__init__(*args) # `strerror` self.strerror = "" if original_error is not None: try: self.strerror = str(original_error) except Exception: # Consume all errors. If the `str` call fails, it's more # appropriate to ignore `original_error` than to raise an # exception while instantiating `FTPError`. pass elif args: # Assume the first argument is a string. It may be a byte string # though. try: self.strerror = ftputil.tool.as_str( args[0], ftputil.path_encoding.DEFAULT_ENCODING ) except TypeError: # `args[0]` isn't `str` or `bytes`. pass # `errno` self.errno = None try: self.errno = int(self.strerror[:3]) except ValueError: # `int()` argument couldn't be converted to an integer. pass # `file_name` self.file_name = None def __str__(self): return "{}\nDebugging info: {}".format( self.strerror, ftputil.version.version_info ) # Internal errors are those that have more to do with the inner workings of # ftputil than with errors on the server side. class InternalError(FTPError): """Internal error.""" pass class RootDirError(InternalError): """Raised for generic stat calls on the remote root directory.""" pass class InaccessibleLoginDirError(InternalError): """May be raised if the login directory isn't accessible.""" pass class TimeShiftError(InternalError): """Raised for invalid time shift values.""" pass class ParserError(InternalError): """Raised if a line of a remote directory can't be parsed.""" pass class CacheMissError(InternalError): """Raised if a path isn't found in the cache.""" pass class NoEncodingError(InternalError): """Raised if session instances don't specify an encoding.""" pass # Currently not used class KeepAliveError(InternalError): """Raised if the keep-alive feature failed.""" pass class FTPOSError(FTPError, OSError): """Generic FTP error related to `OSError`.""" pass class TemporaryError(FTPOSError): """Raised for temporary FTP errors (4xx).""" pass class PermanentError(FTPOSError): """Raised for permanent FTP errors (5xx).""" pass class CommandNotImplementedError(PermanentError): """Raised if the server doesn't implement a certain feature (502).""" pass class RecursiveLinksError(PermanentError): """Raised if an infinite link structure is detected.""" pass # Currently not used class SyncError(PermanentError): """Raised for problems specific to syncing directories.""" pass class FtplibErrorToFTPOSError: """ Context manager to convert `ftplib` exceptions to exceptions derived from `FTPOSError`. """ def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: # No exception return if isinstance(exc_value, ftplib.error_temp): raise TemporaryError( *exc_value.args, original_error=exc_value ) from exc_value elif isinstance(exc_value, ftplib.error_perm): # If `exc_value.args[0]` is present, assume it's a byte or unicode # string. if exc_value.args and ftputil.tool.as_str( exc_value.args[0], ftputil.path_encoding.DEFAULT_ENCODING ).startswith("502"): raise CommandNotImplementedError( *exc_value.args, original_error=exc_value ) from exc_value else: raise PermanentError( *exc_value.args, original_error=exc_value ) from exc_value elif isinstance(exc_value, ftplib.all_errors): raise FTPOSError(*exc_value.args, original_error=exc_value) from exc_value else: raise ftplib_error_to_ftp_os_error = FtplibErrorToFTPOSError() class FTPIOError(FTPError, IOError): """Generic FTP error related to `IOError`.""" pass class FtplibErrorToFTPIOError: """ Context manager to convert `ftplib` exceptions to `FTPIOError` exceptions. """ def __enter__(self): pass def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: # No exception return if isinstance(exc_value, ftplib.all_errors): raise FTPIOError(*exc_value.args, original_error=exc_value) from exc_value else: raise ftplib_error_to_ftp_io_error = FtplibErrorToFTPIOError()