Improve error handling in the RS REPL

This commit is contained in:
Kovid Goyal 2015-06-26 23:25:01 +05:30
parent cea314f0d9
commit 8aaa1d0cdd
2 changed files with 53 additions and 10 deletions

View File

@ -10,7 +10,7 @@ import os, json, sys, re, atexit, errno
from threading import local
from functools import partial
from threading import Thread
from Queue import Queue
from Queue import Queue, Empty
from duktape import Context, JSError, to_python
from calibre.constants import cache_dir
@ -223,6 +223,14 @@ class Repl(Thread):
'lib_path': self.libdir or os.path.dirname(P(COMPILER_PATH)) # TODO: Change this to load pyj files from the src code
}
def get_from_repl(self):
while True:
try:
return self.from_repl.get(True, 1)
except Empty:
if not self.is_alive():
raise SystemExit(1)
def run(self):
self.init_ctx()
rl = None
@ -252,6 +260,8 @@ class Repl(Thread):
''')
rl = self.ctx.g.rl
self.ctx.eval('module.exports(repl_options)')
completer = to_python(rl.completer)
while True:
ev, line = self.to_repl.get()
try:
@ -261,12 +271,11 @@ class Repl(Thread):
elif ev == 'line':
rl.send_line(line)
else:
val = rl.completer(line)
val = completer(line)
val = to_python(val)
self.from_repl.put(val[0])
except Exception as e:
if 'JSError' in e.__class__.__name__:
e = JSError(e) # A bare JSError
if isinstance(e, JSError):
print (e.stack or e.message, file=sys.stderr)
else:
import traceback
@ -290,7 +299,7 @@ class Repl(Thread):
def completer(text, num):
if self.completions is None:
self.to_repl.put(('complete', text))
self.completions = self.from_repl.get()
self.completions = filter(None, self.get_from_repl())
if self.completions is None:
return None
try:
@ -302,7 +311,7 @@ class Repl(Thread):
self.readline.set_completer(completer)
while True:
lw = self.from_repl.get()
lw = self.get_from_repl()
if lw is None:
raise SystemExit(1)
q = self.prompt

View File

@ -24,7 +24,22 @@ exports.readFileSync = Duktape.readfile;
'''
vm = '''
exports.createContext = Duktape.create_context;
exports.runInContext = Duktape.run_in_context;
exports.runInContext = function(code, ctx) {
var result = Duktape.run_in_context(code, ctx);
if (result[0]) return result[1];
var cls = Error;
var e = result[1];
if (e.name) {
try {
cls = eval(e.name);
} catch(e) {}
}
var err = cls(e.message);
err.fileName = e.fileName;
err.lineNumber = e.lineNumber;
err.stack = e.stack;
throw err;
};
'''
path = '''
exports.join = function () { return arguments[0] + '/' + arguments[1]; }
@ -74,7 +89,13 @@ class Function(object):
return str('[Function: %s(...) from file: %s]' % (x.name, x.fileName))
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
try:
return self.func(*args, **kwargs)
except dukpy.JSError as e:
self.reraise(e)
def reraise(self, e):
raise JSError(e), None, sys.exc_info()[2]
def to_python(x):
try:
@ -113,6 +134,15 @@ class JSError(Exception):
Exception.__init__(self, type('')(e))
self.name = self.js_message = self.fileName = self.lineNumber = self.stack = None
def as_dict(self):
return {
'name':self.name or undefined,
'message': self.js_message or self.message,
'fileName': self.fileName or undefined,
'lineNumber': self.lineNumber or undefined,
'stack': self.stack or undefined
}
contexts = {}
def create_context(base_dirs, *args):
@ -125,8 +155,12 @@ def create_context(base_dirs, *args):
return key
def run_in_context(code, ctx, options=None):
ans = contexts[ctx].eval(code)
return to_python(ans)
c = contexts[ctx]
try:
ans = c.eval(code)
except JSError as e:
return [False, e.as_dict()]
return [True, to_python(ans)]
class Context(object):