2016-05-06 14 views
9

私はPythonライブラリーurllibを使用してアプリケーションを開発していますが、URLにアクセスできないために例外が発生することがあります。ライブラリーコードではなく私の例外で停止

ただし、例外が標準ライブラリスタックにはほぼ6つのレベルを上げている:私はそれを調べることができ例外がある場合には、私は通常、%pdb魔法でipython3でコードを実行

/home/user/Workspace/application/main.py in call(path) 
    11         headers={'content-type': 'application/json'}, 
    12         data=b'') 
---> 13  resp = urllib.request.urlopen(req)   ####### THIS IS MY CODE 
    14  return json.loads(resp.read().decode('utf-8')) 

/usr/lib/python3.4/urllib/request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context) 
    159  else: 
    160   opener = _opener 
--> 161  return opener.open(url, data, timeout) 
    162 
    163 def install_opener(opener): 

/usr/lib/python3.4/urllib/request.py in open(self, fullurl, data, timeout) 
    461    req = meth(req) 
    462 
--> 463   response = self._open(req, data) 
    464 
    465   # post-process response 

/usr/lib/python3.4/urllib/request.py in _open(self, req, data) 
    479   protocol = req.type 
    480   result = self._call_chain(self.handle_open, protocol, protocol + 
--> 481         '_open', req) 
    482   if result: 
    483    return result 

/usr/lib/python3.4/urllib/request.py in _call_chain(self, chain, kind, meth_name, *args) 
    439   for handler in handlers: 
    440    func = getattr(handler, meth_name) 
--> 441    result = func(*args) 
    442    if result is not None: 
    443     return result 

/usr/lib/python3.4/urllib/request.py in http_open(self, req) 
    1208 
    1209  def http_open(self, req): 
-> 1210   return self.do_open(http.client.HTTPConnection, req) 
    1211 
    1212  http_request = AbstractHTTPHandler.do_request_ 

/usr/lib/python3.4/urllib/request.py in do_open(self, http_class, req, **http_conn_args) 
    1182     h.request(req.get_method(), req.selector, req.data, headers) 
    1183    except OSError as err: # timeout error 
-> 1184     raise URLError(err) 
    1185    r = h.getresponse() 
    1186   except: 

URLError: <urlopen error [Errno 111] Connection refused> 

はそうオンすぐに。しかし、このために私は自分のコードに到達するためにスタック6レベルに下がる必要があります。

私のアプリが自分のコードを直接指しているとクラッシュすることはありますか?

答えて

8

私はコードを変更することとなるだろう:

try: 
    resp = urllib.request.urlopen(req) 

except Exception as e: 
    raise RuntimeError(e) 

その方法:

  • % PDB移動しますあなたのコードには、
  • 元の例外は、「二次の引数として保存されます"例外。

またurllib.request.urlopen()機能をモンキーパッチがあります。

あなたはコンテキストマネージャの範囲内でurlibopen()呼び出しで発生した例外を持っている任意の時間を:

with MonkeyPatchUrllib(): 
    #your code here 

%のPDBはあなただけ1を移動しますあなたのコードから離れてレベル。

[EDIT]

sys.exc_info()と、(そのトレースバックのような)元の例外のより詳細なコンテキストを保存することが可能です。

-1

urllibは多くの例外を発生させる可能性があります。

あなたはurllibはへの呼び出しの周りにtryブロックを入れて、例えば例外を処理する方法を理解する必要があります。他の例外のpython2のurllibは多くがスローされ

try: 
    resp = urllib.request.urlopen(req) 

except URLError as e: 
    # analyse e to figure out the detail 
    ... 

確かに下。 python3のurllibについてわかりません。

0

私は答えはノーだと思います。

pdbは例外時に停止し、スタックを表示します。

実際の例外の発生源を隠すのはなぜ便利なのでしょうか?

6層のスタックをリクエストして隠しているように見える場合は、どうすれば修正するのですか?

まだトピックに表示されていない場合は、質問に追加してください。

+0

'なぜ例外の本当の原因を隠すのが役に立つのでしょうか?'それはライブラリのコードなので。私は、ライブラリが私のコードでそれを呼び出す場所まで例外を伝播すると期待しています。 – TheMeaningfulEngineer

3

pdbには、インクリメンタルフレームの位置決め(フレームのリストの上または下の移動)のみがあります。

希望する機能を利用するには、trepangithub repository)を試してみてください。 IPython拡張子はhereです。例外が現れたら、あなたは、コマンドframe -1を使用します。

フレーム(絶対フレーム位置決め)

フレーム[スレッド名* | *スレッド数] [フレーム番号]

指定されている場合は、現在のフレームをフレームのフレーム番号に変更します。指定されていない場合は現在のフレームを0にします。

スレッド名またはスレッド番号が指定されている場合は、現在のフレームをそのスレッドのフレームに変更します。デバッガが停止している現在のフレームの名前を示すには、ドット(。)を使用できます。

負の数は、他のまたは最後に最後に入力された位置からの位置を示します。したがって、フレーム-1は最も古いフレームに移動し、フレーム0は最新のフレームに移動します。数値に評価される任意の変数または式は位置として使用できますが、解析の制限により、位置式は1つの空白で区切られたパラメータとして見なす必要があります。すなわち、(5 * 3)-1)はそうではないが、式(5 * 3)-1は大丈夫です。

希望のフレームに入ったら、editを使用してコードを変更できます。

コマンドbacktraceは、最近のコールが最下部にあるスタックトレースを提供するので便利です。

trepanは、uncompyle6が利用可能です。hereです。

pydbは同様の機能を提供しますが、残念ながらPython3には移植されていませんでした。

これ以外の場合は、辛抱強く、改善が必要な場合があります。 IPython/core/debugger.py:

""" 
Pdb debugger class. 

Modified from the standard pdb.Pdb class to avoid including readline, so that 
the command line completion of other programs which include this isn't damaged. 

In the future, this class will be expanded with improvements over the standard pdb. 
[...] 
""" 
2

これはいくつかのハッキングで行うことができます。

import sys 
from IPython.core import ultratb 
sys.excepthook = ultratb.FormattedTB(mode='Verbose', 
            color_scheme='Linux', call_pdb=1) 

例外は、私たちがdebugger方法でいじくり回す必要がある番組を提起された後にこのフックによってステッピング:These docsあなたは、エントリポイントで、次のコードで事後デバッグをオンにする方法を示しています。残念ながら、メソッド全体をコピーして必要に応じて修正する以外の方法はありません(self.tbを修正しようとしましたが、トレースバックオブジェクトは読み取り専用であり、copy.deepcopyでは使用できません)。ここにデモがあります:

import json 
import sys 
from IPython.core import debugger, ultratb 
from IPython.core.display_trap import DisplayTrap 

class CustomTB(ultratb.FormattedTB): 
    def debugger(self, force=False): 
     if force or self.call_pdb: 
      if self.pdb is None: 
       self.pdb = debugger.Pdb(
        self.color_scheme_table.active_scheme_name) 
      # the system displayhook may have changed, restore the original 
      # for pdb 
      display_trap = DisplayTrap(hook=sys.__displayhook__) 
      with display_trap: 
       self.pdb.reset() 
       # Find the right frame so we don't pop up inside ipython itself 
       if hasattr(self, 'tb') and self.tb is not None: 
        etb = self.tb 
       else: 
        etb = self.tb = sys.last_traceback 

       # only modification is here -----+ 
       #        | 
       #        V 
       while self.tb is not None and '/lib/python3' not in self.tb.tb_next.tb_frame.f_code.co_filename: 
        self.tb = self.tb.tb_next 
       if etb and etb.tb_next: 
        etb = etb.tb_next 
       self.pdb.botframe = etb.tb_frame 
       self.pdb.interaction(self.tb.tb_frame, self.tb) 

     if hasattr(self, 'tb'): 
      del self.tb 

sys.excepthook = CustomTB(mode='Verbose', 
          color_scheme='Linux', call_pdb=1) 

def foo(): 
    bar() 

def bar(): 
    json.dumps(json) 

foo() 

図からわかるように、ライブラリコードに近づくとトレースバックの検索が停止します。

TypeErrorTraceback (most recent call last) 
/Users/alexhall/Dropbox/python/sandbox3/sandbox.py in <module>() 
    40  json.dumps(json) 
    41 
---> 42 foo() 
     global foo = <function foo at 0x1031358c8> 

/Users/alexhall/Dropbox/python/sandbox3/sandbox.py in foo() 
    35 
    36 def foo(): 
---> 37  bar() 
     global bar = <function bar at 0x103135950> 
    38 
    39 def bar(): 

/Users/alexhall/Dropbox/python/sandbox3/sandbox.py in bar() 
    38 
    39 def bar(): 
---> 40  json.dumps(json) 
     global json.dumps = <function dumps at 0x10168b268> 
     global json = <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> 
    41 
    42 foo() 

/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py in dumps(obj=<module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'>, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw={}) 
    228   cls is None and indent is None and separators is None and 
    229   default is None and not sort_keys and not kw): 
--> 230   return _default_encoder.encode(obj) 
     global _default_encoder.encode = <bound method JSONEncoder.encode of <json.encoder.JSONEncoder object at 0x10166e8d0>> 
     obj = <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> 
    231  if cls is None: 
    232   cls = JSONEncoder 

/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/encoder.py in encode(self=<json.encoder.JSONEncoder object>, o=<module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'>) 
    197   # exceptions aren't as detailed. The list call should be roughly 
    198   # equivalent to the PySequence_Fast that ''.join() would do. 
--> 199   chunks = self.iterencode(o, _one_shot=True) 
     chunks = undefined 
     self.iterencode = <bound method JSONEncoder.iterencode of <json.encoder.JSONEncoder object at 0x10166e8d0>> 
     o = <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> 
     global _one_shot = undefined 
    200   if not isinstance(chunks, (list, tuple)): 
    201    chunks = list(chunks) 

/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/encoder.py in iterencode(self=<json.encoder.JSONEncoder object>, o=<module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'>, _one_shot=True) 
    255     self.key_separator, self.item_separator, self.sort_keys, 
    256     self.skipkeys, _one_shot) 
--> 257   return _iterencode(o, 0) 
     _iterencode = <_json.Encoder object at 0x1031296d8> 
     o = <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> 
    258 
    259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, 

/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/encoder.py in default(self=<json.encoder.JSONEncoder object>, o=<module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'>) 
    178 
    179   "" 
--> 180   raise TypeError(repr(o) + " is not JSON serializable") 
     global TypeError = undefined 
     global repr = undefined 
     o = <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> 
    181 
    182  def encode(self, o): 

TypeError: <module 'json' from '/Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py'> is not JSON serializable 
> /Users/alexhall/Dropbox/python/sandbox3/sandbox.py(40)bar() 
    38 
    39 def bar(): 
---> 40  json.dumps(json) 
    41 
    42 foo() 

ipdb> down 
> /Users/alexhall/.pyenv/versions/3.5.0/lib/python3.5/json/__init__.py(230)dumps() 
    228   cls is None and indent is None and separators is None and 
    229   default is None and not sort_keys and not kw): 
--> 230   return _default_encoder.encode(obj) 
    231  if cls is None: 
    232   cls = JSONEncoder 

ipdb> 

は基本的に完全なトレースバックがまだプリントアウトが、独自のコードでipdb開始されます。ここでの結果です。 downコマンドを入力すると、ライブラリフレームに自分自身が見つかります。

関連する問題