2016-01-24 12 views
1

新十歲上下到Python,但我試圖理解這個代碼片段:pytest.raises(錯誤)如何工作?

with pytest.raises(ValueError): 
    group_adjust(vals, [grps_1, grps_2], weights) 

閱讀this tutorial on with後,我明白了pytest.raises()返回一個上下文管理器,設置和之前清理東西,group_adjust()後調用。我也明白group_adjust()應該提出一個ValueError如果出現問題。

當一個ValueError被引發時pytest如何「反應」? AFAIK,只有設置和清理,所以我不知道它如何捕捉異常。最終目標是瞭解pytest作爲上下文管理器的好處。

回答

1

__exit__神奇的功能接受exception_typeexception_valuetraceback參數:

In [5]: class RaisesContext: 
    ...:  def __enter__(self): 
    ...:   return self 
    ...:  def __exit__(self, exception_type, exception_value, traceback): 
    ...:   print('Exception type:', exception_type) 
    ...:   print('Exception value:', exception_value) 
    ...:   print('Traceback:', traceback) 
    ...:   return True 
    ...:  

In [6]: with RaisesContext(): 
    ...:  raise ValueError('Something went wrong') 
    ...: 
Exception type: <class 'ValueError'> 
Exception value: Something went wrong 
Traceback: <traceback object at 0x7fd92f4a2c48> 

他們None,如果with塊正常結束:

In [7]: with RaisesContext(): 
    ...:  pass 
    ...: 
Exception type: None 
Exception value: None 
Traceback: None 
+0

這是否意味着'raise'語句調用__exit __()魔術函數? – joshualan

+0

@joshualan,不完全如此。其實'raise'不叫任何東西。當解釋器評估「RAISE_VARARGS」操作碼時,它解開堆棧以找到「except」或「finally」塊。 'with'語句通過調用'__exit__'方法在幕後創建'finally'塊。所以,'raise' * calls *'__exit__',但間接。 – soon

1

我不能完全肯定,但pytest任何上下文管理器在其退出時傳遞3個參數,exc_type,Excepton和Traceback,如果沒有異常被提出,則全部三個都不是,如果退出返回true,則異常被抑制,以及,

更好地解釋在這裏:https://docs.python.org/2/reference/datamodel.html#object.exit

,所以如果我想打一個簡單的處理程序,將顯示回溯無需停止程序,我可以做到這一點:

import traceback 
class VerboseTry: 
    def __enter__(self): 
     pass 
    def __exit__(self,exc_type,error,trace): 
     if exc_type: 
      traceback.print_exception(exc_type,error,trace) 
      return True 

def f(depth=4): 
    """this will (needlessly) raise an error with several iterations to the traceback""" 
    if depth==0: 
     int("this is going to fail") 
    else: 
     return f(depth-1) 

print("starting") 
with VerboseTry(): 
    f() 
print("got to end") 

地看到,在不暫停程序引發的錯誤。

1

with構造分別在代碼塊的開始和結尾調用兩個「魔術」方法,__enter____exit__。因此,

with foo: 
    x = 1 

可以理解爲:

foo.__enter__() 
x = 1 
foo.__exit__() 

除此之外,一旦在HIR答覆中提到,__exit__被稱爲與細節上爲什麼代碼塊退出:如果有異常,什麼樣的,否則None

因此,通過pytest.raises(TYPE)返回的對象已經被配置爲期待類型的異常。 __exit__方法比較它接收的參數,聲明實際的(如果有的話)異常與它存儲預期的異常類型的內部數據成員,然後決定通過或不通過測試。