Feature suggestion: pass Python exceptions to Emacs in a more structured way
Closed this issue · 4 comments
Hi,
Thanks for python-epc, I find it a very interesting project. I've been experimenting with using it to create a smarter module-aware Emacs Python REPL (not ready to share on github yet, unfortunately).
I'm wondering if it's possible to change the way that Python exceptions are passed back to Emacs to make them more useful. Currently, if an EPC method throws an exception Emacs gets it only in the string form that repr
creates, which makes it hard to do things like dispatch on the type of exception. Since Emacs exceptions thrown with (signal ...)
can have an arbitrary list of data attached to them, it would be useful for Python exceptions to be sent back as a list (EXCEPTION-TYPE EXCEPTION-ARGS TRACEBACK)
. Then you could possibly do interesting things like (condition-case ...)
dispatch on the type of Python exception.
This patch to handler.py
gives the idea, but it only does half the job because the exception still gets formatted into a string somewhere within the Emacs side and I haven't been able to figure out where. WDYT?
diff --git a/epc/handler.py b/epc/handler.py
index b692799..8e70f93 100644
--- a/epc/handler.py
+++ b/epc/handler.py
@@ -23,6 +23,7 @@ from sexpdata import loads, dumps, Symbol, String
from .py3compat import SocketServer, Queue
from .utils import autolog, LockingDict, newthread, callwith
+from traceback import format_exc
class BaseRemoteError(Exception):
"""
@@ -249,7 +250,10 @@ class EPCHandler(SocketServer.StreamRequestHandler):
traceback = sys.exc_info()[2]
self.server.debugger.post_mortem(traceback)
name = 'epc-error' if uid is undefined else 'return-error'
- self._send(name, uid, repr(err))
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ self._send(name, uid,
+ [Symbol(exc_type.__name__),
+ exc_value.args, u''.join(format_exc())])
@autolog('debug')
def _handle_call(self, uid, meth, args):
If it is formatted into string in Emacs side, then that means EPC protocol does not support the usage you have in mind. I think it is possible to improve the protocol but you need to open an issue here:
https://github.com/kiwanami/emacs-epc
That said, I think it is better to handle Python error at Python side. There is no promise that Python error maps to a list structure. Also, you can always wrap the returned value using (success REAL-VALUE)
and (error ERROR-VALUE)
. I think it is better this way because you can format ERROR-VALUE
in the way it is easy to handle for you application.
BTW, if you are interested in a good REPL with server-client architecture, checkout IPython and my Emacs client (https://github.com/tkf/emacs-ipython-notebook). I guess what do you mean by "module-aware" is that a REPL that has multiple namespaces and each namespace maps to module namespace? Indeed this is something what I want for IPython. I think adding this feature (or probably another feature if my guess is wrong) to IPython is better because your work is going to benefit a lot more people than creating a REPL only for Emacs. Also, you don't need to re-implement all the stuff IPython already implemented.
OK, I will open an issue on Emacs-EPC. I see your point about exception handling, so I will keep on catching exceptions and returning as a tagged value.
That's basically what I mean about module-aware, I think. I want Emacs to automatically know what module is associated with a buffer and be able to automatically import it & quickly evaluate things in scope without fussing about with explicit import statements, as you can do in SLIME/Geiser/nREPL. It's so easy to code in Python by passing an environment to eval
that I'm surprised it's not already in IPython. The main problem (compared with Lisp REPLs) is that redefining things in a running Python isn't very useful, because any existing code and objects still use the old definition. Even so, I find it makes the workflow a lot smoother.
EIN looks like it has some great stuff. I will check it out, thanks!
IPython got "recursive import" magic. Probably you find it useful.
If you want server-client style REPL like SLIME/Geiser/nREPL, I think EIN has everything you need, except the multiple namespace support (it not, please point it out. I'd like to hear). Let's not reinvent the wheel and make IPython (and EIN) better!
(Closing since this needs upstream change)