2010-05-16 26 views
11

對不起,對於我的問題來說,它似乎是最具描述性的。如何找到列表中給出的庫函數在Python中引發的所有異常?

基本上,我很難在官方的python文檔中找到異常信息。例如,在一個程序,我目前正在寫,我使用shutil libary的移動功能:

from shutil import move 
move('somefile.txt', '/tmp/somefile.txt') 

這工作得很好,只要我有到/ tmp /寫訪問,有足夠的磁盤空間,以及是否滿足所有其他要求。

然而,編寫通用的代碼時,往往很難保證這些因素,所以人們通常使用的例外:

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except: 
    print 'Move failed for some reason.' 

我想實際上捕獲拋出,而不是僅僅抓住一切適當的例外,但我根本無法找到大多數python模塊拋出的異常列表。有沒有辦法讓我看看給定函數可以拋出哪些異常,爲什麼?這樣我可以做出適當的情況下,爲每個例外,如:

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except PermissionDenied: 
    print 'No permission.' 
except DestinationDoesNotExist: 
    print "/tmp/ doesn't exist" 
except NoDiskSpace: 
    print 'No diskspace available.' 

答案要點去誰就可以聯繫我,我已經在官方文檔某種程度上忽視了一些相關的資料,或提供一個安全可靠找出究竟哪些異常是由哪些功能引發的,以及爲什麼。

謝謝!

UPDATE:從給出的答案看來,確實沒有一個100%直接的方法來確定哪些錯誤是由特定函數引發的。使用元編程,似乎我可以計算出簡單的案例並列出一些例外情況,但這不是一種特別有用或方便的方法。

我想最終會有一些標準來定義每個python函數引發哪些異常,並且這些信息將包含在官方文檔中。在此之前,我認爲我會允許這些例外通過,併爲我的用戶錯誤,因爲它似乎是最理智的事情。

+0

作爲一個Python初學者,我不沒有經驗可以肯定地說,但是相當於Java的'throws'在測試它們類型的函數參數的方式(當你的函數可以處理任何duck-typed迭代)的時候感覺不太平常。 – msw 2010-05-17 19:25:20

回答

11

爲了放大Messa,抓住你期望的是失敗模式,你知道如何恢復。 Ian Bicking寫了an article,它解決了Eli Bendersky的note的一些總體原則。

與示例代碼的問題是它是而不是處理錯誤,只是美化和丟棄它們。你的代碼不會「知道」如何處理NameError,除了傳遞它之外沒有太多的工作要做,如果你覺得必須添加細節,請看Bicking的重新提升。

對於shutil.move,IOError和OSError是合理的「可預期」,但不一定可處理。而且你的函數的調用者希望它移動一個文件,並且如果Eli寫的那個「契約」被破壞,它本身可能會中斷。

抓住你可以修復,修飾和重新提高你期望但不能修復的東西,並讓調用者處理你沒有想到的事情,即使「交易」的代碼是七層堆棧在main

+1

爲了更好地解決OP的問題,在這裏描述'IOError'和'OSError':http://docs.python.org/library/exceptions.html。但是你可以看到兩者都非常依賴主機系統和操作系統。如果你真的想吞下各種相關的錯誤,你應該做一些像'EnvironmentError as e:print e.strerror'的東西。否則,你必須檢查'errorno',處理一些特定的情況,不要忘記'提高'剩下的部分。 – 2010-05-16 14:26:32

+0

@ THC4k:它是'errno',而不是'errorno'。 – tzot 2010-06-13 11:01:56

+0

我同意!瞭解代碼可以處理什麼以及哪些不可以更重要。 – yaobin 2016-02-19 16:18:00

2

由於這些操作通常使用libc函數和操作系統調用,因此大多數情況下,您會收到IOError或OSError,其中包含errno數字;這些錯誤列在該libc/OS調用的手冊頁中。

我知道這可能是不完整的答案,這將是件好事,在文件中列出的所有異常...

+1

您寫道:「列出所有例外情況會很好」,我開始懷疑這個想法。如果語言不強制使用'throws'子句,那麼文檔解決方案就會失敗「顯式比隱式」更好。在缺乏快速合同和動態綁定(包括剛剛發生的「進口」)之間,你真的不知道可能會產生什麼。你不能處理你不期望的東西,當你注意到EnvironmentErrors是可以預期和可處理的時候,你不希望發生Assertion錯誤,而且你不希望你的代碼散佈MemoryError修復。 – msw 2010-05-16 12:56:43

4

Python沒有一個機制,現在來聲明其拋出異常,不像(例如)Java。 (在Java中,您必須準確定義哪些異常是由什麼引發的,並且如果您的某個實用方法需要拋出另一個異常,那麼您需要將其添加到所有調用它的方法中,快速枯燥!)

所以,如果你想發現究竟哪些異常是由任何給定的Python引發的,那麼你需要檢查文檔和源代碼。

然而,python有一個非常好的異常層次結構。

如果你研究下面的異常層次結構,你會發現你想要捕獲的錯誤超類叫做StandardError - 它應該捕獲在正常操作中可能產生的所有錯誤。談到錯誤進入到一個字符串會給出一個合理的想法給用戶,以什麼地方出了錯,所以我建議你上面的代碼看起來應該像

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except StandardError, e: 
    print 'Move failed: %s' % e 

異常層次結構

BaseException 
|---Exception 
|---|---StandardError 
|---|---|---ArithmeticError 
|---|---|---|---FloatingPointError 
|---|---|---|---OverflowError 
|---|---|---|---ZeroDivisionError 
|---|---|---AssertionError 
|---|---|---AttributeError 
|---|---|---BufferError 
|---|---|---EOFError 
|---|---|---EnvironmentError 
|---|---|---|---IOError 
|---|---|---|---OSError 
|---|---|---ImportError 
|---|---|---LookupError 
|---|---|---|---IndexError 
|---|---|---|---KeyError 
|---|---|---MemoryError 
|---|---|---NameError 
|---|---|---|---UnboundLocalError 
|---|---|---ReferenceError 
|---|---|---RuntimeError 
|---|---|---|---NotImplementedError 
|---|---|---SyntaxError 
|---|---|---|---IndentationError 
|---|---|---|---|---TabError 
|---|---|---SystemError 
|---|---|---TypeError 
|---|---|---ValueError 
|---|---|---|---UnicodeError 
|---|---|---|---|---UnicodeDecodeError 
|---|---|---|---|---UnicodeEncodeError 
|---|---|---|---|---UnicodeTranslateError 
|---|---StopIteration 
|---|---Warning 
|---|---|---BytesWarning 
|---|---|---DeprecationWarning 
|---|---|---FutureWarning 
|---|---|---ImportWarning 
|---|---|---PendingDeprecationWarning 
|---|---|---RuntimeWarning 
|---|---|---SyntaxWarning 
|---|---|---UnicodeWarning 
|---|---|---UserWarning 
|---GeneratorExit 
|---KeyboardInterrupt 
|---SystemExit 

這也意味着在定義自己的異常時,應該將它們置於StandardError而不是Exception之外。

Base class for all standard Python exceptions that do not represent 
interpreter exiting. 
+0

這是一個壞主意,因爲它會吞噬SyntaxError,AssertionError,NameError等,它們真正代表錯誤而不是「預期的」運行時錯誤。 – msw 2010-05-16 11:49:43

+0

比'except:'更好:'。 – fossilet 2012-08-13 08:43:05

3

是的,你可以(對於簡單的情況),但你需要一些元編程。和其他答案一樣,函數沒有聲明它拋出了一個特定的錯誤類型,所以你需要看看模塊,看看它定義了什麼異常類型,或者它引發了什麼異常類型。您可以嘗試使用文檔或利用Python API來執行此操作。

首先找到的異常類型的模塊定義,只寫一個簡單的腳本要經過在模塊字典module.__dict__每個對象,看看它是否在詞「錯誤」結尾,或者如果它是異常的子類:

def listexns(mod): 
    """Saved as: http://gist.github.com/402861 
    """ 
    module = __import__(mod) 
    exns = [] 
    for name in module.__dict__: 
     if (issubclass(module.__dict__[name], Exception) or 
      name.endswith('Error')): 
      exns.append(name) 
    for name in exns: 
     print '%s.%s is an exception type' % (str(mod), name) 
    return 

如果我對你的shutils例如運行此我得到這個:

$ python listexn.py shutil 
Looking for exception types in module: shutil 
shutil.Error is an exception type 
shutil.WindowsError is an exception type 
$ 

,告訴你錯誤類型定義了,但沒有哪些是拋出。爲了實現後者,我們需要遍歷Python解釋器解析模塊時生成的抽象語法樹,並查找每個raise語句,然後保存所引發的名稱列表。該代碼,這是有點長,所以首先我要說明的輸出:

$ python listexn-raised.py /usr/lib/python2.6/shutil.py 
Looking for exception types in: /usr/lib/python2.6/shutil.py 
/usr/lib/python2.6/shutil.py:OSError is an exception type 
/usr/lib/python2.6/shutil.py:Error is an exception type 
$ 

所以,現在我們知道,shutil.py定義錯誤類型ErrorWindowsError並引發異常類型OSErrorError。如果我們想要更完整一些,我們可以編寫另一種方法來檢查每個except子句以查看shutil處理哪些異常。

這裏走過去的AST的代碼,它只是使用了compiler.visitor接口創建一個學步車它實現了從四本書剛的「訪客模式」:

class ExceptionFinder(visitor.ASTVisitor): 
    """List all exceptions raised by a module. 
    Saved as: http://gist.github.com/402869 
    """ 

    def __init__(self, filename): 
     visitor.ASTVisitor.__init__(self) 
     self.filename = filename 
     self.exns = set() 
     return 

    def __visitName(self, node): 
     """Should not be called by generic visit, otherwise every name 
     will be reported as an exception type. 
     """ 
     self.exns.add(node.name) 
     return 

    def __visitCallFunc(self, node): 
     """Should not be called by generic visit, otherwise every name 
     will be reported as an exception type. 
     """ 
     self.__visitName(node.node) 
     return 

    def visitRaise(self, node): 
     """Visit a raise statement. 
     Cheat the default dispatcher. 
     """ 
     if issubclass(node.expr1, compiler.ast.Name): 
      self.__visitName(node.expr1) 
     elif isinstance(node.expr1, compiler.ast.CallFunc): 
      self.__visitCallFunc(node.expr1) 
     return 
+3

不,你無法在理論和實踐中列出所有異常:實際上,任何事情都可能引發很多異常,從Python的C部分,從AssertionError到ZeroDivisionError。從理論上講,你無法找出這引發了什麼:'raise type(raw_input(「Raise What?」),(Exception,),{})'(它爲每個輸入引發一種新的異常*) – 2010-05-16 13:37:05

+1

好的,那是真的,我只在這裏列出Python異常,靜態分析不會捕捉你的特定例子。你的例子表明這個問題沒有爭議,但在實踐中,合理的答案可能是使用一段第三方代碼所需要的,而這正是OP所要求的。 – snim2 2010-05-16 13:44:08

+0

我真的很感激這個答案。我發現它非常有用和有見地。然而,我沒有給你答案,因爲在閱讀完答案之後,似乎沒有什麼可靠的方法來找到我要找的東西。你的解決方案似乎在簡單的情況下工作,但對於常見的用法並不方便或優雅(對於每個功能來說運行它會很痛苦)。不過謝謝你。它很有意思。 – rdegges 2010-05-17 00:10:21

相關問題