2016-01-13 12 views
6

、私task.pyでなぜContextmanagerは実行時エラー 'throw()の後にジェネレータが停止しなかったのですか?私が持っている私のutility.pyで

@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 

私が呼ぶ:

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

与えられた日付範囲のタスクをruningて後、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

いいえいいえ.. for文を囲むforループがあります。 withステートメントは、forループの本文全体に対して1回実行されます。あなたのrate_limit_protection()関数の中にwhileループを持つことができないということを意味しますか? –

+0

なぜループステートメント内にあるなら、with文がループ全体に対して一度実行されると思いますか? – user2357112

答えて

13

withステートメントはループ構造ではありません。コードを繰り返し実行するために使用することはできません。 @contextmanagerで作成されたコンテキストマネージャは、yieldに1回だけ必要です。

コンテキストマネージャは、(基本的に)三つん:

  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. 

あなたはその後、呼び出すことができます。

1つのオプションは、引数としてコールバックを受け入れ、次にあなたが現在お使いのコンテキストマネージャを持っているようなループでコールバックを呼び出す関数を記述することですそれこのような:

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

コールバックがdate変数を必要としない場合は、繰り返し(資源の浪費である)と同じ機能を再現避けるために、ループの外に移動することができます。 datecallback()関数のパラメータにして、functools.partialを使用して渡すこともできます。

関連する問題