簡體   English   中英

python終止帶有有用消息的Python命令行腳本

[英]Terminating a Python command-line script with helpful messages, pythonically

如果要處理的數據不正確,我正在編寫的腳本應返回到shell提示符並顯示一條有用的消息。 用戶應該解決標記的問題,直到腳本滿意並且不再退出並顯示錯誤消息為止。 我正在使用TTD開發腳本,因此在編寫函數之前先編寫pytest測試。

投票最多的答案是建議通過調用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的子類異常來通過上述測試,但它也不會退出腳本:

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語法中使用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')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM