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

13,082

Solution 1

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)

Solution 2

I think that's a bug in pysftp. You will always have that behavior when pysftp.Connection fails on a No hostkey for XXX found exception, because the failed Connection object (it fails so you can't access it, but it exists in the Python interpreter) gets cleaned up by the GC, which deletes it, and as you can see here, that tries to close the connection first.

We see that close() checks whether the connection is live by checking self._sftp_live. However, the Exception was thrown in the constructor of Connection before that attribute is defined (the exception happens line 132, while _sftp_live is defined line 134), so that leaves the failed Connection object in an inconsistent state and hence the uncaught exception you see.

This has no easy solution that I can think of except introducing a nice bug fix to the pysftp project ;)

Solution 3

I had the same problem. I solved this by disabling the hostkeys in the cnops:

import pysftp as sftp

FTP_HOST = "sftp.abcd.com"
FTP_USER = "root"
FTP_PASS = ""

cnopts = sftp.CnOpts()
cnopts.hostkeys = None

with sftp.Connection(host=FTP_HOST, username=FTP_USER, password=FTP_PASS, cnopts=cnopts) as sftp:
   print("Connection succesfully stablished ... ")
   sftp.cwd('/folder/')  # Switch to a remote directory
   directory_structure = sftp.listdir_attr() # Obtain structure of the remote directory

for attr in directory_structure:
   print(attr.filename, attr)
Share:
13,082
Basj
Author by

Basj

I work on R&D involving Python, maths, machine learning, deep learning, data science, product design, and MacGyver solutions to complex problems. I love prototyping, building proofs-of-concept. For consulting/freelancing inquiries : [email protected]

Updated on June 07, 2022

Comments

  • Basj
    Basj almost 2 years

    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:?

    • Martin Prikryl
      Martin Prikryl over 2 years
      As of 2022, the pysftp seems to be an abandoned project. I suggest you switch to Paramiko. The pysftp is just a wrapper around Paramiko. So if you have pysftp, you have Paramiko too. See pysftp vs. Paramiko.
  • reverse_engineer
    reverse_engineer over 3 years
    Nicely found, though these kind of fixes can be a bit risky since in the end you change the behavior of the constructor, but I suspects this works without side-effects
  • Karpi
    Karpi almost 3 years
    I tried the above code with my credentials but getting the below error >>> AttributeError: 'MyConnection' object has no attribute '_cnopts'
  • Booboo
    Booboo almost 3 years
    @Karpi If that doesn't resolve your issue, open a new question and add a comment directed to me so that I know you have done so. But be sure to post a full stacktrace.
  • Tommy
    Tommy over 2 years
    I believe this bug still exists for anyone coming to this answer in Sept 2021 - just got burned by this badly. When you hit this in durasftp, since on closure it tries to reconnect, you will see this log millions of times.
  • eckad158
    eckad158 over 2 years
    i still do have the same problem using pysftp 0.2.9
  • Martin Prikryl
    Martin Prikryl over 2 years
    Disabling host key checking is a security flaw. You lose a protection against MITM attacks. Do not do that!