2016-01-13 11 views
6

在我utility.py我有,爲什麼Contextmanager拋出一個運行時錯誤'生成器在throw()'後沒有停止?

@contextmanager 
def rate_limit_protection(max_tries=3, wait=300): 
    tries = 0 
    while max_tries > tries: 
     try: 
      yield 
      break 
     except FacebookRequestError as e: 
      pprint.pprint(e) 
      if e._body['error']['message'] == '(#17) User request limit reached': 
       print("waiting...") 
       time.sleep(wait) 
       tries += 1 

在我task.py我打電話:

for date in interval: 
    with utility.rate_limit_protection(): 
     stats = account.get_insights(params=params) 

乳寧任務給定日期範圍後,一旦Facebook的限速踢的程序等待300秒,然後失敗並顯示錯誤。

File "/Users/kamal/.pyenv/versions/3.4.0/lib/python3.4/contextlib.py", line 78, in __exit__ 
    raise RuntimeError("generator didn't stop") 
RuntimeError: generator didn't stop 
+0

您是否期待這個'rate_limit_protection'上下文管理器重複運行'with'語句的主體? '與'不這樣做。身體運行一次。 – user2357112

+0

否否..圍繞with語句有for循環。 with語句爲for循環的整個主體運行一次。你的意思是我的rate_limit_protection()函數中沒有while循環 –

+0

爲什麼你會期望with語句在整個循環中運行一次,如果它在循環體內呢? – user2357112

回答

13

with聲明不是循環構造。它不能用於重複執行代碼。使用@contextmanager創建的上下文管理器應該只有yield一次。

上下文管理者做(基本)三件事情:

  1. 它運行一個代碼塊之前的一些代碼。
  2. 它在代碼塊後面運行一些代碼。
  3. (可選)它可以抑制代碼塊中引發的異常。

如果您想要做這樣的事情,您需要重寫它,以便循環移出上下文管理器,或者根本沒有上下文管理器。

一個辦法是寫一個接受一個回調作爲參數,然後就像你目前在你的上下文管理器的一個循環調用回調函數:

def do_rate_protection(callback, max_tries=3): 
    tries = 0 
    while max_tries > tries: 
     try: 
      callback() 
      break 
     except FacebookRequestError as e: 
      # etc. 

然後,您可以撥打它是這樣的:

for date in interval: 
    def callback(): 
     # code 
    do_rate_protection(callback) 

如果回調不需要date變量,你可以將它的外循環,以避免反覆重現相同的功能(這是資源的浪費)。您還可以使date作爲callback()函數的參數,並使用functools.partial傳遞它。

相關問題