diff --git a/src/calibre/utils/rapydscript.py b/src/calibre/utils/rapydscript.py index 3ae4ae7d1a..9958095f40 100644 --- a/src/calibre/utils/rapydscript.py +++ b/src/calibre/utils/rapydscript.py @@ -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 diff --git a/src/duktape/__init__.py b/src/duktape/__init__.py index a88e69eaf5..91d7a6aee3 100644 --- a/src/duktape/__init__.py +++ b/src/duktape/__init__.py @@ -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):