I'm developing an app using a Python library urllib
and it is sometimes rising exceptions due to not being able to access an URL.
However, the exception is raised almost 6 levels into the standard library stack:
/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>
I usually run the code in ipython3
with the %pdb
magic turned on so in case there is an exception I can inspect it immediately. However for this I have to go down the stack 6 levels to get to my code.
Is it achievable that my app crashes pointing to my code directly?
urllib can raise a lot of exceptions.
You need to put a try block around the call into urllib and figure how to handle the exceptions for example:
Certainly under python2's urllib lots of other exceptions are thrown. I'm not sure about python3's urllib.
It can be done with some hacking. These docs show how you can turn on post-mortem debugging with the following code in the entry point:
Stepping through this hook after an exception is raised shows that we need to tinker with the
debugger
method. Unfortunately I can see no better way to do this other than to copy the entire method and modify it where needed (I tried modifyingself.tb
but traceback objects are read only and can't be used withcopy.deepcopy
). Here's a demo:As you can see it stops searching through the traceback when it's about to reach library code. Here's the result:
Basically the full traceback is still printed out but
ipdb
starts at your own code. If you enter thedown
command you find yourself in a library frame.I think the answer is no.
pdb stops at the exception and shows you the stack.
Why would it be useful to hide the real source of the exception?
If it worked as you seem to be requesting and hides the 6 layers of stack how would you work out what to fix?
If this is still not on topic please add to your question.
I would go with modifying the code:
That way:
You may also monkeypatch
urllib.request.urlopen()
function:Any time you have an exception raised in
urlibopen()
call within the context manager scope:%pdb will move you only 1 level away from your code.
[EDIT]
With
sys.exc_info()
it is possible to preserve a more verbose context of the original exception (like its traceback).pdb
has only incremental frame positioning (moving up or down the list of frames).To get the feature you want, you can try
trepan
(github repository). It has an IPython extension here. You then use the commandframe -1
once the exception shows up:Once you are in the desired frame, you can use
edit
to modify your code.You may find the command
backtrace
useful too as it gives a stack trace with the less recent call at the bottom.trepan
depends onuncompyle6
available here.pydb
provides a similar feature but was unfortunately not ported to Python3.Otherwise, you may decide to be patient and wait for improvements. In IPython/core/debugger.py: