For a project I wanted to try out something new - using Jupyter Notebook to document its XML-RPC API, so documentation and specification cannot drift apart.
This worked out great.
Except - as the API also has to handle invalid input, Jupyter Notebook shows - correctly - the traceback.
The traceback is super verbose.
request
server.get_licenses("not-existing-id")
current print out in Jupyter Notebook
---------------------------------------------------------------------------
Fault Traceback (most recent call last)
<ipython-input-5-366cceb6869e> in <module>
----> 1 server.get_licenses("not-existing-id")
/usr/lib/python3.9/xmlrpc/client.py in __call__(self, *args)
1114 return _Method(self.__send, "%s.%s" % (self.__name, name))
1115 def __call__(self, *args):
-> 1116 return self.__send(self.__name, args)
1117
1118 ##
/usr/lib/python3.9/xmlrpc/client.py in __request(self, methodname, params)
1456 allow_none=self.__allow_none).encode(self.__encoding, 'xmlcharrefreplace')
1457
-> 1458 response = self.__transport.request(
1459 self.__host,
1460 self.__handler,
/usr/lib/python3.9/xmlrpc/client.py in request(self, host, handler, request_body, verbose)
1158 for i in (0, 1):
1159 try:
-> 1160 return self.single_request(host, handler, request_body, verbose)
1161 except http.client.RemoteDisconnected:
1162 if i:
/usr/lib/python3.9/xmlrpc/client.py in single_request(self, host, handler, request_body, verbose)
1174 if resp.status == 200:
1175 self.verbose = verbose
-> 1176 return self.parse_response(resp)
1177
1178 except Fault:
/usr/lib/python3.9/xmlrpc/client.py in parse_response(self, response)
1346 p.close()
1347
-> 1348 return u.close()
1349
1350 ##
/usr/lib/python3.9/xmlrpc/client.py in close(self)
660 raise ResponseError()
661 if self._type == "fault":
--> 662 raise Fault(**self._stack[0])
663 return tuple(self._stack)
664
Fault: <Fault 1: 'company id is not valid'>
output should look like this
Fault: <Fault 1: 'company id is not valid'>
an almost perfect solution
The following solution, using sys.excepthook works in a REPL…
code
import sys
def my_exc_handler(type, value, traceback):
print(repr(value), file=sys.stderr)
sys.excepthook = my_exc_handler
1 / 0
bash
❯ python3.9 main.py
ZeroDivisionError('division by zero')
… but unfortunately not in Jupyter Notebook - I still get the full traceback.
When I have a look at Python’s documentation…
When an exception is raised and uncaught
… maybe the “uncaught” is the problem. When I have to guess, I think Jupyter Notebook catches all exceptions, and does the formatting and printing itself.
the actual solution
This weekend, during the wonderful PyOhio conference, I chatted with a couple of fellow participants in the channel for the “9 Jupyter Notebook Tricks for Your Next Advent of Code” talk, and boom… there was the solution, thanks to Kaspar (or David)*:
%xmode Minimal
This is a builtin of Jupyter Notebook. You just put it into a cell at the top of your notebook.
*) Yes - that is the actual user name :-)