当pysftp连接失败时,“‘Connection’对象没有属性‘_sftp_live’”

Bas*_*asj 18 python sftp exception paramiko pysftp

我想很好地捕获“找不到主机 *** 的主机密钥”时的错误,并向最终用户提供适当的消息。我试过这个:

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)
Run Code Online (Sandbox Code Playgroud)

但不幸的是输出不是很好:

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'
Run Code Online (Sandbox Code Playgroud)

如何很好地避免最后几行/这个“忽略异常” try: except:

小智 18

我有同样的问题。我通过禁用 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)
Run Code Online (Sandbox Code Playgroud)


rev*_*eer 12

我认为这是 pysftp 中的一个错误。pysftp.Connection当异常失败时,您总是会遇到这种行为No hostkey for XXX found,因为失败的Connection对象(它失败了,所以您无法访问它,但它存在于 Python 解释器中)会被 GC 清理,GC 会删除它,并且您可以看这里,它首先尝试关闭连接。

我们看到通过检查来close() 检查连接是否有效self._sftp_live。但是,在定义该属性之前,在构造函数中抛出了异常Connection(异常发生在第 132 行,而_sftp_live定义在第 134 行),因此导致失败的Connection对象处于不一致的状态,从而导致您看到未捕获的异常。

除了向 pysftp 项目引入一个很好的错误修复之外,我没有想到简单的解决方案;)

  • 我相信对于 2021 年 9 月得出这个答案的人来说,这个错误仍然存​​在 - 只是被这个严重烧伤了。当您在 *durasftp* 中点击此命令时,由于关闭时它会尝试重新连接,因此您将看到此日志 *数百万次*。 (3认同)

Boo*_*boo 12

@reverse_engineer的分析是正确的。然而:

  1. 似乎附加属性self._transport也定义得太晚了。
  2. 可以暂时纠正该问题,直到通过对该类进行子类化来实现永久修复,pysftp.Connection如下所示:
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)
Run Code Online (Sandbox Code Playgroud)

更新

我无法在桌面上重复此错误。但是,我在代码的源代码中看到,pysftp它使用构造函数的关键字_cnopts参数self._cnopts = cnopts or CnOpts()where初始化其属性,并且如果未找到主机密钥,则构造函数有可能抛出异常,从而导致cnoptspysftp.ConnectionCnOptsHostKeysException_cnopts异常,从而导致未设置属性。

尝试以下更新的代码并让我知道它是否有效:

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)
Run Code Online (Sandbox Code Playgroud)