使用有用的消息来终止Python命令行脚本,pythonically
如果要处理的数据不完全正确,我正在编写的脚本应该退出到shell提示并带有有用的消息。用户应该修复被标记的问题,直到脚本开心并且不再以错误消息退出。我正在用TTD开发脚本,所以我在编写函数之前编写了一个pytest
测试。使用有用的消息来终止Python命令行脚本,pythonically
most heavily up-voted answer here建议通过调用sys.exit
或提高SystemExit
来编辑脚本。
功能:
通过测试(其中_non-text.png
是PNG文件,即,不以UTF-8编码):
def test_istext():
with pytest.raises(SystemExit):
istext('_non-text.png')
然而,脚本继续运行,并在try/except
块执行后发表声明。
我希望脚本每次都完全退出,以便用户可以调试数据,直到它正确为止,脚本将完成它应该做的事(即处理一个包含UTF-8文本的目录文件,而不是PNG,JPG,PPTX ...文件)。
也试过:
下也抛出一个异常是一个子类的SystemExit
通过上面的测试,但它也不会退出脚本:
def istext(file_to_test):
class NotUTF8Error(SystemExit): pass
try:
open(file_to_test).read(512)
except UnicodeDecodeError:
raise NotUTF8Error('File {} must be UTF-8.'.format(file_to_test))
您可以使用raise Exception from exception
语法:
class MyException(SystemExit):
pass
def istext(file_to_test):
try:
open(file_to_test).read(512)
except UnicodeDecodeError as exception:
raise MyException(f'File {file_to_test} must be encoded in UTF-8 (Unicode); try converting.') \
from exception
我这种情况下,你不改变原来的错误信息,并添加您自己的消息。
的尝试... except块是为了捕获一个错误并在内部处理它。你想要做的是重新提高错误。
def istext(file_to_test):
try:
open(file_to_test).read(512)
except UnicodeDecodeError:
print(('File {} must be encoded in UTF-8 (Unicode); try converting.'.format(file_to_test)))
raise
这将打印您的消息,然后自动重新提高您发现的错误。
而不是只重新提高旧的错误,您可能还想更改错误类型。对于这种情况,您可以指定进一步提高,例如:
raise NameError('I'm the shown error message')
我的第一本能已经创建沿线的自定义异常'类NotUTF8Error(SystemExit):'。所以你建议'除了UnicodeDecodeError之后:'我会'提出NotUTF8Error(“Some message”)'? –
没错。或者你在错误类的定义中已经指定了文本的内容,这样就不需要重新提升。 – Sudix
我尝试了第二个建议(请参阅上面的修改问题),但是像我最初的尝试,它通过pytest但不退出脚本。第一个建议(在打印消息之后重新提高'UnicodeDecodeError')会传递一个修改过的pytest,但不会退出脚本。 –
你问题不是如何退出程序(sys.exit()
工作正常)。你的问题是你的测试场景没有提高UnicodeDecodeError
。
下面是您的示例的简化版本。它按预期工作:
import pytest
import sys
def foo(n):
try:
1/n
except ZeroDivisionError as e:
sys.exit('blah')
def test_foo():
# Assertion passes.
with pytest.raises(SystemExit):
foo(0)
# Assertion fails: "DID NOT RAISE <type 'exceptions.SystemExit'>"
with pytest.raises(SystemExit):
foo(9)
将一些诊断打印添加到您的代码以了解更多信息。例如:
def istext(file_to_test):
try:
content = open(file_to_test).read(512)
# If you see this, no error occurred. Maybe your source
# file needs different content to trigger UnicodeDecodeError.
print('CONTENT len()', len(content))
except UnicodeDecodeError:
sys.exit('blah')
except Exception as e:
# Maybe some other type of error should also be handled?
...
这有助于澄清pytest没有测试'1/0'是否会引发'ZeroDivisionError',但是'sys.exit'是否会引发'SystemExit'。谢谢! –
到底,什么工作是类似于@ADR建议,有一点不同:我是不是能够得到如上图所示正确(f'File {file_to_test} must...'
)工作在格式化字符串语法,也不是我能查找字符串前缀f
的文档。
我略少优雅的解决方案,那么,对于(改名)功能:
def is_utf8(file):
class NotUTF8Error(SystemExit): pass
try:
open(file).read(512)
except UnicodeDecodeError as e:
raise NotUTF8Error('File {} not UTF-8: convert or delete, then retry.'.format(file)) from e
经过pytest:
def test_is_utf81():
with pytest.raises(SystemExit):
is_utf8('/Users/tbaker/github/tombaker/mklists/mklists/_non-text.png')
你认真吗?您不再喜欢我的解决方案了,因为我使用了Python 3.6的一个简单功能? https://docs.python.org/3/whatsnew/3.6.html https://www.python.org/dev/peps/pep-0498/ – ADR
@ADR感谢您的参考 - 我的道歉 - 我寻找该功能,但没有找到它;它不适合我,因为我有Python 3.5.2。我正在消除接受并接受你的答案! –
嗯,我无法得到这个例子工作,即使没有错误信息。 –
声明类'NotUTF8Error(SystemExit):pass'后,'除UnicodeDecodeError作为异常:'后,从异常处引发NotUTF8Error将得到'SyntaxError'。 –
没关系 - 我得到这个工作,只有pytest失败'NameError:name'NotUTF8Error'未定义。 –