正如JimB 所指出的,当 python 尝试在 ssh 连接尝试期间首次使用时隐式导入解码器时,这是一个导入问题。str.decode('utf-8')
有关详细信息,请参阅分析部分。
一般来说,你不能强调你应该避免让一个模块在导入时自动产生新线程。如果可以的话,尽量避免使用魔法模块代码,因为它几乎总是会导致不必要的副作用。
如前所述,解决您的问题的简单且明智的方法是将您的代码放在一个if __name__ == '__main__':
主体中,该主体仅在您执行此特定模块时才会执行,并且不会在其他模块导入此 mmodule 时执行。
(不推荐)另一个解决方法是在您调用之前在您的代码中执行一个虚拟 str.decode('utf-8') SSHClient.connect()
- 请参阅下面的分析。
那么这个问题的根本原因是什么?
分析(简单密码验证)
提示:如果你想在 python 导入和设置中调试线程threading._VERBOSE = True
paramiko.SSHClient().connect(.., look_for_keys=False, ..)
隐含地为您的连接生成一个新线程。如果您打开paramiko.transport
.
[Thread-5 ] [paramiko.transport ] DEBUG : starting thread (client mode): 0x317f1d0L
这基本上是作为SSHClient.connect()
. 当client.py:324::start_client()
被调用时,会创建一个锁transport.py:399::event=threading.Event()
并启动线程transport.py:400::self.start()
。请注意,该start()
方法随后将执行该类的transport.py:1565::run()
方法。
transport.py:1580::self._log(..)
打印我们的日志消息“启动线程”,然后继续transport.py:1584::self._check_banner()
。
check_banner
做一件事。它检索 ssh 横幅(来自服务器的第一个响应)transport.py:1707::self.packetizer.readline(timeout)
(请注意,超时只是套接字读取超时),最后检查换行符,否则超时。
如果收到服务器横幅,它会尝试对响应字符串进行 utf-8 解码packet.py:287::return u(buf)
,这就是发生死锁的地方。执行u(s, encoding='utf-8')
str.decode('utf-i') 并隐式导入via最终导致导入死锁encodings.utf8
。encodings:99
encodings.search_function
因此,一个肮脏的解决方法是只导入一次 utf-8 解码器,以免由于模块导入副作用而阻塞该特定导入。( ''.decode('utf-8')
)
使固定
肮脏的修复-不推荐
import paramiko
hostname,username,password='fill','these','in'
''.decode('utf-8') # dirty fix
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=hostname, username=username, password=password)
i,o,e = c.exec_command('ls /')
print(o.read())
c.close()
很好的修复
import paramiko
if __name__ == '__main__':
hostname,username,password='fill','these','in'
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=hostname, username=username, password=password)
i,o,e = c.exec_command('ls /')
print(o.read())
c.close()
参考paramiko 问题跟踪器:问题 104