JavaScript <-> Python <-> Lua intergation¶
Lua and JavaScript are not connected directly; they communicate through Python.
Python <-> Lua is handled using lupa library.
splash.qtrender_lua.command()
decorator handles most of Python <-> Lua
integration.
Python <-> JavaScript is handled using custom serialization code. QT host objects are not used (with a few exceptions). Instead of this JavaScript results are sanitized and processed in Python; Python results are encoded to JSON and decoded/processed in JavaScript.
Python -> Lua¶
Data is converted from Python to Lua in two cases:
- method of an exposed Python object returns a result
(most common example is a method of
splash
Lua object); - Python code calls Lua function with arguments - it could be e.g. an on_request callback.
Conversion rules:
Basic Python types are converted to Lua: strings -> Lua strings, lists and dicts -> Lua tables, numbers -> Lua numbers, None -> nil(?).
This is handled using
splash.lua_runtime.SplashLuaRuntime.python2lua()
method. For attributes exposed to Lua this method is called manually; for return results of Python functions / methods it is handled bysplash.qtrender_lua.emits_lua_objects()
decorator. Methods decorated with@command
usesplash.qtrender_lua.emits_lua_objects
internally, so a Python method decorated with@command
decorator may return Python result in its body, and the final result would be a Lua object.If there is a need to expose a custom Python object to Lua then a subclass of
splash.qtrender_lua.BaseExposedObject
is used; it is wrapped to a Lua table using utilities from wraputils.lua. Lua table exposes whitelisted attributes and methods of the object using metatable, and disallows access to all other attributes.Other than that, there is no automatic conversion. If something is not converted then it is available for Lua as an opaque userdata object; access to methods and attributes is disabled by a sandbox.
To prevent wrapping method may return
splash.lua.PyResult
instance.
Lua -> Python¶
Lua -> Python conversion is needed in two cases:
- Lua code calls Python code, passing some arguments;
- Python code calls Lua code and wants a result back.
Basic Lua types are converted to Python using
splash.lua_runtime.SplashLuaRuntime.lua2python()
. For method arguments lua2python is called bysplash.qtrender_lua.decodes_lua_arguments()
decorator;@command
decorator usesdecodes_lua_arguments
internally.Python objects which were exposed to Lua (BaseExposedObject subclasses) are not converted back. By default they raise an error; with decode_arguments=False they are available as opaque Lua (lupa) table objects.
splash.qtrender_lua.is_wrapped_exposed_object()
can be used to check if a lupa object is a wrapped BaseExposedObject instance; obj.unwrapped() method can be used to access the underlying Python object.
JavaScript -> Python¶
To get results from JavaScript to Python they are converted to primitive JSON-serializable types first. QtWebKit host objects are not used. Objects of unknown JavaScript types are discared, max depth of result is limited.
JavaScript -> Python conversion utilities reside in
splash.jsutils
module - JavaScript side, i.e. sanitizing and encoding; two main functions areSANITIZE_FUNC_JS
andSTORE_DOM_ELEMENTS_JS
;splash.browser_tab.BrowserTab.evaljs()
method - Python side, i.e. decoding of the result.
For most types (objects, arrays, numbers, strings) conversion method is straightforward; the most tricky case is a reference to DOM nodes.
For top-level DOM nodes (i.e. a result is a DOM node or a NodeList)
a node is stored in a special window attribute, and generated id is returned
to Python instead. All other DOM nodes are discarded - returning a Node
or a NodeList as a part of data structure is not supported at the moment.
STORE_DOM_ELEMENTS_JS
processes Node and NodeList objects;
SANITIZE_FUNC_JS
sanitizes the result (handles all other data types,
drops unsupported data).
In Python HTMLElement objects are created for DOM nodes; they contain node_id
attribute with id returned by JavaScript; it allows to fetch the real Node
object in JavaScript. This is handled by
splash.browser_tab.BrowserTab.evaljs()
.
Python -> JavaScript¶
There are two cases Python objects are converted to JavaScript objects:
- functions created with splash:jsfunc() are called with arguments;
- methods of HtmlElement which wrap JS functions are called with arguments.
The conversion is handled either by splash.html_element.escape_js_args()
or by splash.jsutils.escape_js()
.
escape_js
just encodes Python data to JSON and removes quotes; the result can be used as literal representation of argument values, i.e. added to a JS function call using string formatting.escape_js_args
is similar toescape_js
, but it handlessplash.html_element.HTMLElement
instances by replacing them with JS code to access stored nodes.