【问题标题】:"'Connection' object has no attribute '_sftp_live'" when pysftp connection fails当 pysftp 连接失败时,“'Connection' 对象没有属性 '_sftp_live'”
【发布时间】:2021-03-08 04:26:31
【问题描述】:

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

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)

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

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'

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

【问题讨论】:

  • 截至 2022 年,pysftp 似乎是一个废弃的项目。我建议你切换到 Paramiko。 pysftp 只是 Paramiko 的一个包装器。所以如果你有 pysftp,你也有 Paramiko。见pysftp vs. Paramiko

标签: python exception sftp paramiko pysftp


【解决方案1】:

我认为这是 pysftp 中的一个错误。当pysftp.ConnectionNo hostkey for XXX found 异常而失败时,您将始终具有该行为,因为失败的Connection 对象(它失败,因此您无法访问它,但它存在于Python 解释器中)被GC 清理了,它会删除它,正如你所看到的here,它会首先尝试关闭连接。

我们通过检查self._sftp_live 看到close() checks whether the connection is live。但是,在定义该属性之前,Connection 的构造函数中引发了异常(异常发生在 line 132,而 _sftp_live 定义为 line 134),因此使失败的 Connection 对象处于不一致状态因此您会看到未捕获的异常。

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

【讨论】:

  • 我相信这个错误对于任何在 2021 年 9 月提出这个答案的人来说仍然存在 - 只是被这个严重烧毁了。当您在 durasftp 中点击它时,由于在关闭时它会尝试重新连接,您将看到此日志 数百万次
  • 我在使用 pysftp 0.2.9 时仍然遇到同样的问题
【解决方案2】:

@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)

更新

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

尝试以下更新的代码,如果它有效,请告诉我:

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)

【讨论】:

  • 很好地发现,虽然这些修复可能有点冒险,因为最终你改变了构造函数的行为,但我怀疑这没有副作用
  • 我用我的凭据尝试了上面的代码,但得到了以下错误 >>> AttributeError: 'MyConnection' object has no attribute '_cnopts'
  • @Karpi 如果这不能解决您的问题,请打开一个新问题并添加针对我的评论,以便我知道您已经这样做了。但请务必发布 full 堆栈跟踪。
【解决方案3】:

我遇到了同样的问题。我通过禁用 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)

【讨论】:

  • 禁用主机密钥检查是一个安全漏洞。您失去了对 MITM 攻击的保护。不要那样做!
猜你喜欢
  • 2021-09-25
  • 2022-11-27
  • 2023-01-24
  • 2020-01-28
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
相关资源
最近更新 更多