2013-05-07 227 views
3

我正在清理一個API庫,並試圖找出處理未處理的異常的最佳方法。如何處理未處理的異常?

現在,圖書館捕捉到所有可能出現錯誤的API - 憑證錯誤,服務器錯誤,urllib2錯誤,httplib錯誤等等。將會出現邊緣情況。

我目前的想法是,99%的圖書館用戶不關心異常本身,他們只關心API調用失敗。只有開發人員會關心這個異常。

這使我這個解決方案:

class ApiError(Exception): 
    pass 

class ApiUnhandledError(ApiError): 
    pass 

與API的已知問題引發ApiError中或特定的子類。

其他一切都會引發一個ApiUnhandledError,原始錯誤被隱藏,用戶可以捕獲或忽略該錯誤。

try: 
    stuff 
except urllib2.UrlError , e : 
    raise ApiError(raised=e) 
except Exception as e : 
    raise ApiUnhandledError(raised=e) 

這聽起來像是一種確保用戶只知道通過/失敗的好方法,而開發人員可以維護一種支持方法嗎?

更新

基於最佳實踐的共識,我不會捕捉這一點。

原來的目標是讓人們能夠做到這一點:

try: 
    stuff_a 
    other_stuff 
    even_more_stuff 

    api.proxy(url) 

    again_stuff 
    again_others 
except api.ApiUnhandledError , e : 
    handle error 
except api.ApiError , e : 
    handle error 
except Stuff , e : 
    other error 
except: 
    raise 

在這個例子中,用戶只趕上ApiError中(以及可選ApiUnhandledError或任何其他子類)

我想這將有自己的塊在很大程度上最好每個API互動:

try: 
    stuff_a 
    other_stuff 
    even_more_stuff 

    try: 
     api.proxy(url) 
    except api.ApiError , e : 
     handle error 
    except CatchSomething1 , e : 
     handle error 
    except CatchSomething2 , e : 
     handle error 
    except CatchSomething3 , e : 
     handle error 
    except CatchSomething4 , e : 
     handle error 
    except: 
     raise 

    again_stuff 
    again_others 
except Stuff , e : 
    other error 
except: 
    raise 

與urllib2的問題時,我已經似乎發現了新的除外離子每天。這些例外往往會變得很長,難以維持。

+1

您確定要使用此類機制嗎?你只會用你的「假」來掩蓋一個真正的例外。我只是留下未經處理的例外,因爲它們是最具信息性的方式。如果你想將它們記錄在某處或創建一個普通的動作,那就是另一回事了。 – Michal 2013-05-07 14:34:41

+0

我不確定我想要這樣做。我擔心的是有一些錯誤代碼可能會從底層庫中彈出。看看使用這個庫的我自己的「消費者」代碼,我不得不在自己的塊中嵌入對這個庫的調用,然後在它周圍鏈接大量的異常處理來捕獲這些錯誤。我試圖在上游端口處理這個異常處理,這似乎是一種可能的方式來最大限度地減少大多數消費者的工作,同時仍然保留例外情況。 – 2013-05-07 14:42:18

回答

2

如果你的圖書館提出了一個你沒有預見到但你沒有處理的例外,那就這樣吧。這可能是一個圖書館錯誤的跡象,否則將被忽略。如果可以明確錯誤的原因,那麼可以捕獲並重新啓動(例如,捕獲socket.error並在驗證器方法中重新啓動AuthenticationFailedError),但將圖書館用戶的故障點掩蓋是不公平的(程序員是誰他們自己)。

用戶不應該嘗試處理或消除直接來自庫內部的錯誤(即,不是您的API的一部分 - 大致上,不是您寫的或編寫的),因爲它們是內部的那段代碼。如果圖書館作者忘記處理它們,或者抓住並重新啓動一個更具體的作品,那麼這是一個錯誤,應該報告。如果函數對輸入做出假設(比如說,對於速度),應在API參考中明確聲明,並且任何違規都是用戶的錯誤。

在Python2中,只能繼承從BaseException繼承的經典類或新風格類,在Python3中,經典類不見了,因此您的庫可能引發的任何事情都必須從BaseException繼承。用戶通常要處理的任何異常(這不包括SystemExit,GeneratorExit,KeyboardInterrupt等,這些都是特殊情況)必須從Exception繼承。否則,將報告試圖提高它的時候:

Python3:

>>> class Test: 
...  pass 
... 
>>> raise Test() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: exceptions must derive from BaseException 

Python2:

>>> class Test(object): 
...  pass 
... 
>>> raise Test() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: exceptions must be old-style classes or derived from BaseException, not Test 
>>> 

這就是說,你不需要包裝一個UnhandledException容器內的異常,只需使代碼中的所有例外都從Exception繼承。屏蔽失敗是不好的做法(和你不應該鼓勵的話),但懶惰的用戶仍然可以從Exception基類採取繼承的優勢,趕上這一切未處理的異常:

try: 
    yourapi.call_me() 
except APIKnownError as error: 
    report(error) 
except Exception as error: 
    pass 
    # Or whatever 

這是值得注意的是,python提供一個warnings模塊用於報告「程序中的某些條件,其中該條件(通常)不保證引發異常並終止程序」。說到應用程序框架(不是庫),我非常喜歡Tornado方法:它會記錄所有未處理的異常,並在錯誤不重要時繼續。我認爲這個決定應該取決於最終用戶。

+0

在你的例子中,我在思考這個問題:我得到一個'socket.error'異常,然後用'socket.error'的實例作爲一個屬性引發'ApiUnhandledError'。我不想隱藏失敗點,我只是希望圖書館用戶有一個「更容易」的時間來處理錯誤。如果他們關心潛在問題是什麼,他們可以輕鬆檢查 - API提出了一個異常,該異常存儲原始的提升。由於這個庫使用了非常討厭的urllib2軟件包,我將所有可能發生的隨機邊緣案例(除了9個顯式捕獲)整合到一個ApiUnhandledError中。 – 2013-05-07 15:20:11

+0

@JonathanVanasco,我不會在另一個內部包裝一個異常,因爲這會向用戶公開一個內部依賴:如果你想使用自己的錯誤類型從'socket'切換到更高級的庫,該怎麼辦?用戶必須無需重寫他/她的處理代碼。我會提取相關信息並使用它們來構建您的異常。同時檢查編輯。 – 2013-05-07 16:49:01

+0

Python 2中的用戶異常應該從'Exception'派生,而不是'BaseException' http://docs.python.org/2/library/exceptions.html#exceptions.Exception – 2013-05-07 17:06:33