Coverage for jseval/__init__ : 80%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
|
#! /usr/bin/env python3 # -*- coding: utf-8 -*-
# Copyright 2013-2014, 2017, Marten de Vries # # This file is part of OpenTeacher. # # OpenTeacher is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # OpenTeacher is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with OpenTeacher. If not, see <http://www.gnu.org/licenses/>.
// Sets up a system of function wrappers, because QtQml cannot directly insert // arbitrary functions into the runtime. Instead, a single callback function // is used, which is called with an id that is unique per function. Data, both // arguments and return type, are passed via a global variable // // buildJSWrapper creates a wrapper over all that. Note that for identical // ids, identical wrapper functions will be returned.
var cache = {}; var exports = this._callback = {};
function newWrapper(id) { return function() { exports.args = Array.prototype.slice.call(arguments);
exports.pyCallback(id);
var result = exports.result; delete exports.result; if (result.type === 'result') { return result.value; } else { var err = new Error(result.value.message); err.name = result.value.name; err.oldTraceback = result.value.oldTraceback; throw err; } }; }
exports.buildJSWrapper = function (id) { if (!cache[id]) { cache[id] = newWrapper(id); } return cache[id]; }; }()); """
# Used by the except hook to filter out jseval stuff from the traceback. # Change it to an empty list to temporarily disable filtering (e.g. for # debugging) os.path.join(os.path.dirname(__file__), '__init__.py'), os.path.join(os.path.dirname(__file__), 'pyproxies.py'), ]
# Used to remember exception classes, so exceptions can be recreated (name, exc) for name, exc in vars(builtins).items() if isinstance(exc, type) and issubclass(exc, BaseException) )
"""A QObject that can be registered in QtQml to provide a callback for init.js
"""
def callback(self, identifier):
# try to use the last argument as kwargs except BaseException as e: if not isinstance(e, pyproxies.JSError): #store the exception class so it can be reused #later. pythonExceptionStore[e.__class__.__name__] = e.__class__ newTb = filteredTb(sys.exc_info()[2]) oldTb = getattr(e, 'oldTraceback', []) self.callbackData.result = { 'type': 'exception', 'value': { 'message': str(e), 'name': getattr(e, 'name', e.__class__.__name__), # store the combined tracebacks on the JS object for # later reuse 'oldTraceback': newTb + oldTb }, } return 'type': 'result', 'value': result, }
newTb = traceback.extract_tb(tb) return [list(item) for item in newTb if item[0] not in JSEVAL_MASKED_FILES]
if not hasattr(value, "oldTraceback"): #the default excepthook sys.__excepthook__(type, value, tb) return
print("Traceback (most recent call last):", file=sys.stderr) #the last 2 traceback items are just JSEvaluator internals; they #shouldn't be frustrating the debugging process. print(''.join(traceback.format_list(filteredTb(tb))), end='', file=sys.stderr) print(''.join(traceback.format_list(value.oldTraceback)), end='', file=sys.stderr) print("%s: %s" % (type.__name__, value), file=sys.stderr)
"""JSEvaluator is an object that helps you interacting with JS code from Python. It allows you to modify the JS global scope in a dict-like fashion through its ``global_`` property, and to evaluate JavaScript code via its ``eval`` method, e.g.:
``evaluator.eval("3 + 2")``
You can also call JS functions by accessing them in the dict- like way, e.g.:
``evaluator.global_["Math"]["ceil"](4.56)``
Or use the ``.new()`` on a value to make an instance as with the JS ``new`` keyword:
``evaluator.global_["Date"].new()``
For more examples, see the examples file and the tests for this module.
Note that JS objects are proxied into Python objects, i.e. changes to the Python representation will change the JS value in the interpreter. The other way around (Python objects in JavaScript), this is not the case. Instead, copies are passed in.
""" # Error.prototype.stack parser # For quick access
# provides access to the global object
'log': logging.debug, 'error': logging.critical, }
"""Execute ``code`` from file ``path`` starting at line ``line`` in the interpreter. Note that if the last item evaluated is an error, that error will be raised as an exception, instead of returned as a value. This is a QtQml limitation. The same is true for function calls.
"""
"""Converts a QJSValue to a Python equivalent, often (but not always) by making a proxy object.
""" return self.buildRegExp(value) else:
"""Return a Python int if an integer number, otherwise a Python float"""
except (ValueError, OverflowError): return num
"""Converts a JS regexp to its Python equivalent compiled re-object.""" regexp = value.toVariant() caseInsensitive = regexp.caseSensitivity() == QtCore.Qt.CaseInsensitive flags = re.IGNORECASE if caseInsensitive else 0 return re.compile(regexp.pattern(), flags)
"""If the exception name was encountered earlier, this module will re-use the then-recorded exception class. Otherwise, JSError is used.
The JS traceback is stored on the exception object, for use by the exception handler hook.
""" # get the relevant part of the stacktrace, i.e. the part before we # dive into the init script, excluding sometimes the first item (which # is then the wrapper function inside the init script) stack = stack[1:] # convert JS stack line into its Python list equivalent func = '<global scope>' except KeyError: err = pyproxies.JSError(name, message)
"""Convert a Python value to its corresponding QJSValue. No proxy objects here, as QtQml's API does not provide enough flexibility to have those.
""" # proxy objects # null # numbers, strings, bools # functions # array # object # datetime raise ValueError("Cannot convert object of type '%s' to a JS value" % type(value))
# use the Python id, which is 1. unique and 2. the same if we get this # particular function another time. Both qualities are useful. |