Search code examples
pythonexceptionsftpparamikopysftp

"'Connection' object has no attribute '_sftp_live'" when pysftp connection fails


I'd like to catch nicely the error when "No hostkey for host *** is found" and give an appropriate message to the end user. I tried this:

import pysftp, paramiko
try: 
    with pysftp.Connection('1.2.3.4', username='root', password='') as sftp:
        sftp.listdir()
except paramiko.ssh_exception.SSHException as e:
    print('SSH error, you need to add the public key of your remote in your local known_hosts file first.', e)

but unfortunately the output is not very nice:

SSH error, you need to add the public key of your remote in your local known_hosts file first. No hostkey for host 1.2.3.4 found.
Exception ignored in: <function Connection.__del__ at 0x00000000036B6D38>
Traceback (most recent call last):
  File "C:\Python37\lib\site-packages\pysftp\__init__.py", line 1013, in __del__
    self.close()
  File "C:\Python37\lib\site-packages\pysftp\__init__.py", line 784, in close
    if self._sftp_live:
AttributeError: 'Connection' object has no attribute '_sftp_live'

How to nicely avoid these last lines / this "exception ignored" with a try: except:?


Solution

  • The analysis by @reverse_engineer is correct. However:

    1. It seems that an additional attribute, self._transport, also is defined too late.
    2. The problem can be temporarily corrected until a permanent fix comes by subclassing the pysftp.Connection class as follows:
    import pysftp
    import paramiko
    
    
    class My_Connection(pysftp.Connection):
        def __init__(self, *args, **kwargs):
            self._sftp_live = False
            self._transport = None
            super().__init__(*args, **kwargs)
    
    try: 
        with My_Connection('1.2.3.4', username='root', password='') as sftp:
            l = sftp.listdir()
            print(l)
    except paramiko.ssh_exception.SSHException as e:
        print('SSH error, you need to add the public key of your remote in your local known_hosts file first.', e)
    

    Update

    I could not duplicate this error on my desktop. However, I see in the source for pysftp in the code where it initializes its _cnopts attribute with self._cnopts = cnopts or CnOpts() where cnopts is a keyword parameter to the pysftp.Connection constructor and there is a possibilty of the CnOpts constructor throwing a HostKeysException exception if no host keys are found resulting in the _cnopts attribute not being set.

    Try the following updated code and let me know if it works:

    import pysftp
    import paramiko
    
    class My_Connection(pysftp.Connection):
        def __init__(self, *args, **kwargs):
            try:
                if kwargs.get('cnopts') is None:
                    kwargs['cnopts'] = pysftp.CnOpts()
            except pysftp.HostKeysException as e:
                self._init_error = True
                raise paramiko.ssh_exception.SSHException(str(e))
            else:
                self._init_error = False
    
            self._sftp_live = False
            self._transport = None
            super().__init__(*args, **kwargs)
    
        def __del__(self):
            if not self._init_error:
                self.close()
    
    try:
        with My_Connection('1.2.3.4', username='root', password='') as sftp:
            l = sftp.listdir()
            print(l)
    except paramiko.ssh_exception.SSHException as e:
        print('SSH error, you need to add the public key of your remote in your local known_hosts file first.', e)