我正在尝试捕获paramiko
异常,但它们仍被写入stderr。
有没有办法在那里停止写作?
编辑:它甚至发生在paramiko参与之前:
import pysftp
try:
pysftp.Connection(host="localhost")
except Exception as e:
print(e)
结果是:
具有正确的SFTP参数的示例:
更新:
$ pipenv graph
...
pysftp==0.2.9
- paramiko [required: >=1.17, installed: 2.6.0]
...
$ pipenv run python
Python 3.7.3 (default, Jul 19 2019, 11:21:39)
[Clang 11.0.0 (clang-1100.0.28.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pysftp
>>> try:
... pysftp.Connection(host="localhost")
... except Exception as e:
... print(e)
...
No hostkey for host localhost found.
Exception ignored in: <function Connection.__del__ at 0x10f7e8268>
Traceback (most recent call last):
File "/Users/andrei/Work/try/.venv/lib/python3.7/site-packages/pysftp/__init__.py", line 1013, in __del__
self.close()
File "/Users/andrei/Work/try/.venv/lib/python3.7/site-packages/pysftp/__init__.py", line 784, in close
if self._sftp_live:
AttributeError: 'Connection' object has no attribute '_sftp_live'
>>>
最佳答案
首先,我要指出PySFTP([PyPI]: PySFTP)已有3年以上未维护(或已移至其他位置-到目前为止是秘密的:))。
我重现了问题。以下是您的代码的详细版本。
code00.py:
#!/usr/bin/env python3
import sys
import pysftp
import traceback
def main(argv):
hostname = argv[0] if argv else "localhost"
print("Attempting to connect to {0:s} ...".format(hostname))
try:
print("----------Before conn----------")
conn = pysftp.Connection(host=hostname)
print("----------After conn----------")
except:
print("----------Before exc print----------")
traceback.print_exc()
print("----------After exc print----------")
finally:
print("----------Finally----------")
print("----------After try / except / finally----------")
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
print("pysftp version: {0:s}\n".format(pysftp.__version__))
main(sys.argv[1:])
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058110732]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
pysftp version: 0.2.9
Attempting to connect to localhost ...
----------Before conn----------
e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py:61: UserWarning: Failed to load HostKeys from C:\Users\cfati\.ssh\known_hosts. You will need to explicitly load HostKeys (cnopts.hostkeys.load(filename)) or disableHostKey checking (cnopts.hostkeys = None).
warnings.warn(wmsg, UserWarning)
----------Before exc print----------
Traceback (most recent call last):
File "code00.py", line 13, in main
conn = pysftp.Connection(host=hostname)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 132, in __init__
self._tconnect['hostkey'] = self._cnopts.get_hostkey(host)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 71, in get_hostkey
raise SSHException("No hostkey for host %s found." % host)
paramiko.ssh_exception.SSHException: No hostkey for host localhost found.
----------After exc print----------
Exception ignored in: <function Connection.__del__ at 0x000001CC720C80D0>
Traceback (most recent call last):
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 1013, in __del__
self.close()
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 784, in close
if self._sftp_live:
AttributeError: 'Connection' object has no attribute '_sftp_live'
----------Finally----------
----------After try / except / finally----------
Done.
这是一个PySFTP错误:
连接对象被构造(__new__)
初始化程序(__init__)被称为
在初始化器中的某处发生异常
异常后的行不执行
当对象(自动)被垃圾收集时(当它超出范围时,在except块的末尾),在其close方法(由析构函数(__del__)调用)中,引用了某些属性
但是由于这些属性初始化发生在#2.2。的行(引发异常)之后,因此它们从未被初始化,因此它们的引用引发AttributeError
解决方法很简单:在初始化程序开始时,将属性初始化为一些默认值,因此,如果发生上述情况,则对它们的引用并不代表问题。
我注意到您已经在BitBucket上提交了问题。
考虑到:
我不是BitBucket专家
我没有对[BitBucket]: dundeemt/pysftp - pysftp is an easy to use sftp module that utilizes paramiko and pycrypto.的权限(我既不能推送也不能提交拉取请求)
我创建了自己的存储库(在上面的存储库中),并将更改推送到:[BitBucket]: CristiFati0/pysftp - [Issue #144]: Exceptions leaking into stderr(到目前为止,只有一次提交)。
输出(将修补程序手动应用到pip安装的文件后):
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058110732]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
pysftp version: 0.2.9
Attempting to connect to localhost ...
----------Before conn----------
e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py:61: UserWarning: Failed to load HostKeys from C:\Users\cfati\.ssh\known_hosts. You will need to explicitly load HostKeys (cnopts.hostkeys.load(filename)) or disableHostKey checking (cnopts.hostkeys = None).
warnings.warn(wmsg, UserWarning)
----------Before exc print----------
Traceback (most recent call last):
File "code00.py", line 13, in main
conn = pysftp.Connection(host=hostname)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 135, in __init__
self._tconnect['hostkey'] = self._cnopts.get_hostkey(host)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 71, in get_hostkey
raise SSHException("No hostkey for host %s found." % host)
paramiko.ssh_exception.SSHException: No hostkey for host localhost found.
----------After exc print----------
----------Finally----------
----------After try / except / finally----------
Done.
不用说,对于不同的情况,可能会引发其他未处理的异常。
@ EDIT0
显然,这不仅是吸引眼球。除了上述的PySFTP错误外,还有2件事会污染stderr。
1.警告
就我而言(因为我在Win上并且没有安装任何本机SSH工具)每次都会弹出(除非我在主目录中创建/复制了一些有效的known_hosts文件),但是在Nix系统上,它很可能会弹出。 t。
无论如何,此问题的修复很容易(如果需要),只需简单地抑制UserWarning即可,例如,将%PYTHONWARNINGS%env var设置为ignore :: UserWarning(也可以通过代码来实现-类似于下一种情况)。
2. Paramiko例外
我能够通过手动修改transport.py(并提高
socket.timeout
)来重现此内容。由
paramiko.Transport
初始化的pysftp.Connection._start_transport
(由初始化程序调用)在线程中工作(通过对threading.Thread
进行子类化)。该线程中引发的任何异常都不能被调用线程(我们的线程)捕获。这是计划在v3.8([Python.Bugs]: threading.Thread should have way to catch an exception thrown within)中解决的Python限制。为此,有一个(()解决方法(gainarie):重定向stderr。当然,还有其他解决方法,但是它们暗示着要修改Paramiko,因此我建议不要这样做。
下面是将stderr重定向到stdout的示例(但您可以选择任何其他文件-包括/ dev / null(或Win上的nul))。它是通过代码完成的(但也可以通过解释器命令行完成),因此它仅影响所需的(热)区域。
code01.py:
#!/usr/bin/env python3
import sys
import pysftp
import paramiko
import traceback
import threading
_sys_stderr = sys.stderr # For restoring purposes
def main(argv):
hostname = argv[0] if argv else "localhost"
print("Attempting to connect to {0:s} ...".format(hostname))
try:
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
print("---------- STATS: {0:s} {1:d} ----------".format(__file__, threading.get_ident()))
print("---------- Before conn ----------")
sys.stderr.write("DUMMY TEXT before sent to stderr\n")
sys.stderr = sys.stdout # @TODO - cfati: decomment so that everything from stderr is redirected to stdout
conn = pysftp.Connection(host=hostname, port=22001, username="usr", password="pwd", cnopts=cnopts,)
print("---------- After conn ----------")
except:
sys.stderr = _sys_stderr
print("---------- Before exc tb ----------")
traceback.print_exc(file=sys.stdout)
print("---------- After exc tb ----------")
finally:
sys.stderr = _sys_stderr
print("---------- Finally ----------")
sys.stderr.write("DUMMY TEXT after sent to stderr\n")
print("---------- After try / except / finally ----------")
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
print("pysftp version: {0:s}\nparamiko version: {1:s}".format(pysftp.__version__, paramiko.__version__))
main(sys.argv[1:])
print("\nDone.")
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058110732]> sopr.bat
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> dir /b
code00.py
code01.py
[prompt]> :: Suppress warning
[prompt]> set PYTHONWARNINGS=ignore::UserWarning
[prompt]> :: Redirect stdout and stderr to different files, so it is obvious which is which
[prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code01.py 1>1.out 2>1.err
[prompt]> type 1.err
DUMMY TEXT before sent to stderr
DUMMY TEXT after sent to stderr
[prompt]> type 1.out
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
pysftp version: 0.2.9
paramiko version: 2.6.0
Attempting to connect to localhost ...
---------- STATS: code01.py 23016 ----------
---------- Before conn ----------
---------- STATS: e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py 45616 ----------
Exception: Error reading SSH protocol banner
Traceback (most recent call last):
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2212, in _check_banner
raise socket.timeout()
socket.timeout
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2039, in run
self._check_banner()
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2218, in _check_banner
"Error reading SSH protocol banner" + str(e)
paramiko.ssh_exception.SSHException: Error reading SSH protocol banner
---------- Before exc tb ----------
Traceback (most recent call last):
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2212, in _check_banner
raise socket.timeout()
socket.timeout
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "code01.py", line 23, in main
conn = pysftp.Connection(host=hostname, port=22001, username="usr", password="pwd", cnopts=cnopts,)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pysftp\__init__.py", line 144, in __init__
self._transport.connect(**self._tconnect)
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 1291, in connect
self.start_client()
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 660, in start_client
raise e
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2039, in run
self._check_banner()
File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\paramiko\transport.py", line 2218, in _check_banner
"Error reading SSH protocol banner" + str(e)
paramiko.ssh_exception.SSHException: Error reading SSH protocol banner
---------- After exc tb ----------
---------- Finally ----------
---------- After try / except / finally ----------
Done.
以及PyCharm的外观(必须将其扩展到最大以适合整个事物):