2015-11-04 24 views
9

私はSaltStackのためのいくつかのetcdモジュールを書いていて、この奇妙な問題に遭遇しました。何とか私が例外をキャッチできないようにしています。具体的にはurllib3を中心にしているようです。なぜこのPython例外を捕まえられないのですか?

小さなスクリプト(ない塩):

import etcd 
c = etcd.Client('127.0.0.1', 4001) 
print c.read('/test1', wait=True, timeout=2) 

そして、我々はそれを実行します。

[[email protected] utils]# /tmp/etcd_watch.py 
Traceback (most recent call last): 
    File "/tmp/etcd_watch.py", line 5, in <module> 
    print c.read('/test1', wait=True, timeout=2) 
    File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
    timeout=timeout) 
    File "/usr/lib/python2.6/site-packages/etcd/client.py", line 788, in api_execute 
    cause=e 
etcd.EtcdConnectionFailed: Connection to etcd failed due to ReadTimeoutError("HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out.",) 

[OK]を、のは、その時間を浪費をキャッチしてみましょう:

#!/usr/bin/python 

import etcd 
c = etcd.Client('127.0.0.1', 4001) 

try: 
    print c.read('/test1', wait=True, timeout=2) 
except etcd.EtcdConnectionFailed: 
    print 'connect failed' 

はそれを実行します。

[[email protected] _modules]# /tmp/etcd_watch.py 
connect failed 

いいですね。それはすべて動作しているPythonです。だから何が問題なの?私は塩etcdモジュールでこれを持っている:

[[email protected] _modules]# cat sjmh.py 
import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    return c.read('/test1', wait=True, timeout=2) 
    except etcd.EtcdConnectionFailed: 
    return False 

そして、私たちがいることを実行します。

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    The minion function caused an exception: Traceback (most recent call last): 
     File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return 
     return_data = func(*args, **kwargs) 
     File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 5, in test 
     c.read('/test1', wait=True, timeout=2) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
     timeout=timeout) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute 
     _ = response.data 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data 
     return self.read(cache_content=True) 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read 
     raise ReadTimeoutError(self._pool, None, 'Read timed out.') 
    ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out. 

HRM、それは奇妙だが。 etcdの読み込みがetcd.EtcdConnectionFailedを返しているはずです。だから、それをさらに見てみましょう。当社のモジュールはこれです:

import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    return c.read('/test1', wait=True, timeout=2) 
    except Exception as e: 
    return str(type(e)) 

そして、我々が得る:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    <class 'urllib3.exceptions.ReadTimeoutError'> 

[OK]を、ので、我々はこの事をつかまえることができることを知っています。そして、私たちはReadTimeoutErrorを投げたことを知っているので、それを捕まえましょう。当社のモジュールの最新バージョン:

import etcd 
import urllib3.exceptions 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError as e: 
    return 'caught ya!' 
    except Exception as e: 
    return str(type(e)) 

そして、我々のテスト...

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    <class 'urllib3.exceptions.ReadTimeoutError'> 

Erは、待って、何?なぜ私たちはそれを捕まえなかったのですか?例外は正しく働いていますか?

どう

[[email protected] _modules]# cat sjmh.py 
import etcd 
import urllib3.exceptions 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.HTTPError: 
    return 'got you this time!' 

..我々がしようとするとurllib3から基本クラスをキャッチした場合について

希望と..

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    The minion function caused an exception: Traceback (most recent call last): 
     File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return 
     return_data = func(*args, **kwargs) 
     File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 7, in test 
     c.read('/test1', wait=True, timeout=2) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
     timeout=timeout) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute 
     _ = response.data 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data 
     return self.read(cache_content=True) 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read 
     raise ReadTimeoutError(self._pool, None, 'Read timed out.') 
    ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out. 

BLAST YEを祈ります!さて、違うetcd例外を返す別のメソッドを試してみましょう。当社のモジュールは、次のようになります。

import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.delete('/') 
    except etcd.EtcdRootReadOnly: 
    return 'got you this time!' 

そして、我々の実行:最終テストとして

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    got you this time! 

を、私はどちらかのストレートのpythonから、または塩のモジュールとして実行することができ、このモジュールは、作られました。 。パイソンを通じて

import etcd 
import urllib3 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError: 
    return 'got you this time!' 
    except etcd.EtcdConnectionFailed: 
    return 'cant get away from me!' 
    except etcd.EtcdException: 
    return 'oh no you dont' 
    except urllib3.exceptions.HTTPError: 
    return 'get back here!' 
    except Exception as e: 
    return 'HOW DID YOU GET HERE? {0}'.format(type(e)) 

if __name__ == "__main__": 
    print test() 

[[email protected] _modules]# python ./sjmh.py 
cant get away from me! 

塩を通して:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    HOW DID YOU GET HERE? <class 'urllib3.exceptions.ReadTimeoutError'> 

そこで、我々はそれをスローすることetcdから例外をキャッチすることができます。しかし、私たちは普通、python-etcdを孤独に走らせるときにurllib3 ReadTimeoutErrorを捕まえることができますが、塩を使って実行すると、ブランケット '例外'句を除いて、そのurllib3例外を捕まえることはできません。

私はそれを行うことができますが、私は本当に面白いのは、食べ物の塩が何をしているのか、例外が見つからないようにしています。私はPythonを使って作業するときこれまでこれを見たことがないので、どうやって起こっているのか、どうすれば回避できるのかが不思議です。

編集:

私は最後にそれをキャッチすることができました。

import etcd 
import urllib3.exceptions 
from urllib3.exceptions import ReadTimeoutError 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError: 
    return 'caught 1' 
    except urllib3.exceptions.HTTPError: 
    return 'caught 2' 
    except ReadTimeoutError: 
    return 'caught 3' 
    except etcd.EtcdConnectionFailed as ex: 
    return 'cant get away from me!' 
    except Exception as ex: 
    return 'HOW DID YOU GET HERE? {0}'.format(type(ex)) 

if __name__ == "__main__": 
    print test() 

そして実行すると:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    caught 3 

それはまだかかわらず、意味がありません。私が例外を知っていることから、リターンは「捕まえられる」べきである。完全なクラス名を使用するのではなく、例外の名前を直接インポートする必要があるのはなぜですか?

詳細編集!

したがって、2つのクラスの比較を追加すると、「False」が生成されます。これは、except節が機能していないため、同じではない可能性があるためです。

私はc.read()を呼び出す直前に次のスクリプトを追加しました。

log.debug(urllib3.exceptions.ReadTimeoutError.__module__) 
log.debug(ReadTimeoutError.__module__) 

そして今、私は、ログにこれを取得する:だから

[DEBUG ] requests.packages.urllib3.exceptions 
[DEBUG ] urllib3.exceptions 

、それは道を捕まるされる理由であると表示されます。これはちょうどetcdをダウンロードして、ライブラリを要求し、このような何かを行うことによっても再現可能です:

#!/usr/bin/python 

#import requests 
import etcd 

c = etcd.Client('127.0.0.1', 4001) 
c.read("/blah", wait=True, timeout=2) 

あなたが上げ「正しい」の例外を取得してしまいます - etcd.EtcdConnectionFailed。ただし、 '要求'のコメントを外して、urllib3.exceptions.ReadTimeoutErrorになります。これは、etcdが例外をキャッチしなくなったためです。

したがって、リクエストをインポートすると、urllib3例外が書き換えられ、それをキャッチしようとしている他のモジュールは失敗します。また、新しいバージョンのリクエストにはこの問題はないようです。

+0

'' Hrm、それは奇妙です。etcdの読み込みはetcd.EtcdConnectionFailed "を返しているはずです。奇妙なことではない。'ReadTimeoutError'は、データを受信する時間が下位プロトコルレベルで終了したという事実を記述します。 'ConnectionFailed'は、クライアントがリモートサービスに接続できなかった事実を記述しています(いくつかの理由で)。これらは、あなたが経験している2つの異なる条件です。特に、リモートサーバーのリソースが少なく、速度が遅い場合は、ネットワーキングでよく使用されます。 – Pynchia

+0

Pynchia、私はそれが返されているはずでした。なぜなら一般的にpython-etcd 0.4.2では、待ち時間が過ぎるとetcd.EtcdConnectionFailedが発生するからです。ローカルのetcdのインスタンスで、etcdがアップしていることを確認しました。また、最後のスクリプトで示されているように、ネットワーキング以外の何かが進行中です。 – sjmh

+0

ああはい。それがConnectionが失敗するもう一つの理由です。しかし、あなたのケースでは、タイムアウトによりReadが失敗しています。つまり、ライブラリは奇妙な動作をするかもしれません。 – Pynchia

答えて

3

以下の私の答えは、これらの正確なライブラリを使って実際には証明できないためです(ライブラリのバージョンやインストール方法にも依存するため、エラーを再現できません)。

最後の例は、実際にはプログラムの実行時に別の瞬間に、urllib3.exceptions.ReadTimeoutErrorが別のクラスを参照する可能性があることを示す良いヒントを与えます。 ReadTimeoutErrorは、Pythonの他のすべてのモジュールと同様に、urllib3.exceptions名前空間の単純な名前で、であり、を再割り当てすることができます(ただし、そうすることをお勧めしません)。

完全に修飾された "パス"でこの名前を参照するとき、私たちはそれを参照する時までに実際の状態を参照することが保証されています。ただし、最初にfrom urllib3.exceptions import ReadTimeoutErrorのようにインポートすると、インポートする名前空間に名前ReadTimeoutErrorが入り、の時点でurllib3.exceptions.ReadTimeoutErrorの値にバインドされています。さて、他のコードがurllib3.exceptions.ReadTimeoutErrorの値を後で再割り当てすると、その2つの値(実際には "最新"の値と以前にインポートされた値)が実際に異なっている可能性があるので、技術的には2つの異なるクラスになる可能性があります。実際に発生する例外クラスは、エラーを発生させるコードがどのように使用するかによって異なります。以前にReadTimeoutErrorを名前空間にインポートした場合、この1つ(「オリジナル」)が生成されます。

これはあなたがexcept ReadTimeoutErrorブロックに以下を追加する可能性がある場合であるかどうかを確認するには:

print(urllib3.exceptions.ReadTimeoutError == ReadTimeoutError) 

これはFalseを印刷した場合 - 時間によって例外が発生したことを証明し、2「参照」を参照してください。確かに別のクラスに。


同様の結果を得ることができる貧しい実装の簡略化した例:

ファイルapi.py(適切に設計され、自身が喜んで存在する):

class MyApiException(Exception): 
    pass 

def foo(): 
    raise MyApiException('BOOM!') 

ファイルapibreaker.py(非難する一つの):

import api 

class MyVeryOwnException(Exception): 
    # note, this doesn't extend MyApiException, 
    # but creates a new "branch" in the hierarhcy 
    pass 

# DON'T DO THIS AT HOME! 
api.MyApiException = MyVeryOwnException 

ファイルapiuser.py

import api 
from api import MyApiException, foo 
import apibreaker 

if __name__ == '__main__': 
    try: 
     foo() 
    except MyApiException: 
     print("Caught exception of an original class") 
    except api.MyApiException: 
     print("Caught exception of a reassigned class") 

を実行する場合:

$ python apiuser.py 
Caught exception of a reassigned class 

あなたが行import apibreakerを削除する場合 - それがあるべきよう明確に、すべてが自分の場所に戻ります。

これは非常に単純化された例ですが、あるモジュールでクラスが定義されている場合、新しく作成された型(新しいクラス自体を表すオブジェクト)は、宣言されたクラス名の下でモジュールの名前空間に "追加"他の変数と同様に、その値は技術的に変更することができます。同じことが機能に起こります。

+0

私は実際に数日前に非常によく似たテストを提案しましたが、それは説明なしで投票されたので、私は答えを取り除きました。私の提案は、実際に受け取った例外と捕らえようとしている例外を比較することでした。同じものを印刷しているにもかかわらず、実際に同じであるかどうかを確認することでした。誰がそれを落札したのか分かりません。 –

+0

これはありがとうございます - それは私が解決策を提供し、SaltStackを通じてこれを実行すると、salt-stackがurllib3例外を再割当てしている要求をインポートしているということを私に導きます。 urllib3.exceptions.ReadTimeoutErrorはrequests.packages.urllib3.exceptionsに属しています。ReadTimeoutErrorは再割り当てされず、urllib3.exceptionsモジュールからのものです。 Etcdはリクエストではなくurllib3バージョンをキャッチしようとしています。また、@ TomKarzes、誰がdownvotedがわからないが、私ではなかった! – sjmh

関連する問題