2016-05-06 2 views
9

Ich entwickle eine App mit einer Python-Bibliothek urllib und es gibt manchmal steigende Ausnahmen aufgrund der nicht auf eine URL zugreifen können.Stoppen Sie bei Ausnahme in meinem, nicht Bibliothekscode

ist jedoch die Ausnahme fast 6 Ebene in die Standardbibliothek Stapel angehoben:

/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> 

ich in der Regel den Code in ipython3 mit der %pdb Magie läuft eingeschaltet, so falls es eine Ausnahme ist, ich es kontrollieren kann sofort. Dafür muss ich aber den Stack um 6 Level runter gehen um zu meinem Code zu gelangen.

Ist es erreichbar, dass meine App direkt auf meinen Code verweist?

Antwort

8

Ich würde mit modifizierenden den Code:

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

except Exception as e: 
    raise RuntimeError(e) 

Auf diese Weise:

  • % pdb bewegt Sie Ihren Code,
  • die ursprüngliche Ausnahme als Argument der „secondary erhalten bleibt "Ausnahme.

Sie können auch monkeypatch urllib.request.urlopen() Funktion:

Alle Sie Zeit eine Ausnahme in urlibopen() Aufruf im Kontext Manager Umfang erhoben haben:

with MonkeyPatchUrllib(): 
    #your code here 

% pdb Sie nur 1 bewegt Level weg von Ihrem Code.

[EDIT]

Mit sys.exc_info() ist es möglich, einen ausführlicheren Kontext der ursprünglichen Ausnahme zu erhalten (wie seine Zurückverfolgungs).

-1

urllib kann viele Ausnahmen auslösen.

Sie benötigen einen try-Block um den Anruf in urllib zu setzen und herauszufinden, wie die Ausnahmen zum Beispiel zu handhaben:

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

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

Sicherlich urllib viele andere Ausnahmen unter python2 die geworfen werden. Ich bin mir nicht sicher über die Urllib von Python3.

0

Ich denke, die Antwort ist nein.

pdb stoppt bei der Ausnahme und zeigt Ihnen den Stapel.

Warum sollte es sinnvoll sein, die eigentliche Quelle der Ausnahme zu verstecken?

Wenn es funktioniert, wie Sie scheinen, fordert und versteckt die 6 Schichten des Stapels, wie würden Sie herausfinden, was zu beheben?

Wenn dies noch nicht zum Thema ist bitte fügen Sie Ihre Frage hinzu.

+0

'Warum sollte es sinnvoll sein, die echte Quelle der Ausnahme zu verstecken?'Weil es Bibliothekscode ist. Ich würde erwarten, dass die Bibliothek die Ausnahme so weit verbreitet, wie ich sie in meinem Code anrufe. – TheMeaningfulEngineer

3

pdb hat nur eine inkrementelle Bildpositionierung (die Bildliste wird nach oben oder nach unten verschoben).

Um die gewünschte Funktion zu erhalten, können Sie versuchen trepan (github repository). Es hat eine IPython-Erweiterung here. Dann verwenden Sie den Befehl frame -1 einmal die Ausnahme zeigt sich:

Frame (absolute Rahmenpositionierung)

frame [faden Name * | * Thread-Nummer] [Rahmennummer]

Ändern Sie den aktuellen Frame in die Frame-Frame-Nummer, falls angegeben, oder den aktuellen Frame, 0, wenn keine Frame-Nummer angegeben wurde.

Wenn ein Thread-Name oder eine Thread-Nummer angegeben wird, ändern Sie den aktuellen Frame in einen Frame in diesem Thread. Punkt (.) Kann verwendet werden, um den Namen des aktuellen Rahmens anzuzeigen, in dem der Debugger gestoppt ist.

Eine negative Zahl gibt die Position vom anderen oder vom zuletzt eingegebenen Ende an. Frame 1 bewegt sich also zum ältesten Frame und Frame 0 zum neuesten Frame. Jede Variable oder jeder Ausdruck, der zu einer Zahl ausgewertet wird, kann als Position verwendet werden. Aufgrund von Parsing-Einschränkungen muss der Positionsausdruck jedoch als einzelner leerer Parameter betrachtet werden. Das heißt, der Ausdruck (5 * 3) -1 ist in Ordnung, während (5 * 3) -1) nicht ist.

Sobald Sie im gewünschten Rahmen sind, können Sie edit verwenden, um Ihren Code zu ändern.

Sie können den Befehl backtrace auch nützlich finden, da es einen Stack-Trace mit dem letzten Aufruf am unteren Rand gibt.

trepan ist abhängig von uncompyle6 verfügbar here.

pydb bietet eine ähnliche Funktion, wurde aber leider nicht auf Python3 portiert.

Andernfalls können Sie sich entschließen, geduldig zu sein und auf Verbesserungen zu warten. In 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

Es kann mit etwas Hacking getan werden. These docs zeigen, wie Sie auf post-mortem-Debugging mit dem folgenden Code in dem Einstiegspunkt drehen können:

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

Stepping durch diesen Haken, nachdem eine Ausnahme ausgelöst wird zeigt, dass wir mit der debugger Methode basteln müssen. Leider kann ich keinen besseren Weg sehen, dies zu tun, als die gesamte Methode zu kopieren und wo nötig zu modifizieren (Ich habe versucht, self.tb zu modifizieren, aber Traceback-Objekte sind schreibgeschützt und können nicht mit copy.deepcopy verwendet werden). Hier ist eine Demo:

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() 

Wie Sie sehen können, hört es auf, das Traceback zu durchsuchen, wenn es den Bibliothekscode erreichen soll. Hier ist das Ergebnis:

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> 

Grundsätzlich ist die vollständige Rückverfolgung wird noch ausgedruckt aber ipdb beginnt in Ihrem eigenen Code. Wenn Sie den Befehl down eingeben, befinden Sie sich in einem Bibliotheksrahmen.

Verwandte Themen