以 python 方式终止带有有用消息的 Python 命令行脚本
Terminating a Python command-line script with helpful messages, pythonically
如果要处理的数据不完全正确,我正在编写的脚本应该返回到 shell 提示符并显示一条有用的消息。用户应该修复标记的问题,直到脚本满意并且不再退出并显示错误消息。我正在使用 TTD 开发脚本,所以我在编写函数之前写了一个 pytest
测试。
most heavily up-voted answer here 建议通过调用 sys.exit
或提高 SystemExit
来编辑脚本。
函数:
def istext(file_to_test):
try:
open(file_to_test).read(512)
except UnicodeDecodeError:
sys.exit('File {} must be encoded in UTF-8 (Unicode); try converting.'.format(file_to_test))
通过此测试(其中 _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
的子 class 异常,但它也没有退出脚本:
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))
try...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')
您可以使用 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
在这种情况下,您不会更改原始错误消息并添加您自己的消息。
您的问题不是如何退出程序(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?
...
最后,工作与@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')
如果要处理的数据不完全正确,我正在编写的脚本应该返回到 shell 提示符并显示一条有用的消息。用户应该修复标记的问题,直到脚本满意并且不再退出并显示错误消息。我正在使用 TTD 开发脚本,所以我在编写函数之前写了一个 pytest
测试。
most heavily up-voted answer here 建议通过调用 sys.exit
或提高 SystemExit
来编辑脚本。
函数:
def istext(file_to_test):
try:
open(file_to_test).read(512)
except UnicodeDecodeError:
sys.exit('File {} must be encoded in UTF-8 (Unicode); try converting.'.format(file_to_test))
通过此测试(其中 _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
的子 class 异常,但它也没有退出脚本:
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))
try...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')
您可以使用 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
在这种情况下,您不会更改原始错误消息并添加您自己的消息。
您的问题不是如何退出程序(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?
...
最后,工作与@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')