mirror of https://github.com/amol-/dukpy.git
Experimental support for 'require' in js code
This commit is contained in:
parent
3856a486a6
commit
5b06de69b8
|
@ -1,4 +1,8 @@
|
||||||
|
from __future__ import print_function
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from dukpy.module_loader import JSModuleLoader
|
||||||
from . import _dukpy
|
from . import _dukpy
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -16,19 +20,17 @@ except NameError: # pragma: no cover
|
||||||
class JSInterpreter(object):
|
class JSInterpreter(object):
|
||||||
"""JavaScript Interpreter"""
|
"""JavaScript Interpreter"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self._loader = JSModuleLoader()
|
||||||
self._ctx = _dukpy.create_context()
|
self._ctx = _dukpy.create_context()
|
||||||
self._funcs = {}
|
self._funcs = {}
|
||||||
|
|
||||||
def _adapt_code(self, code):
|
self._init_process()
|
||||||
def _read_files(f):
|
self._init_console()
|
||||||
if hasattr(f, 'read'):
|
self._init_require()
|
||||||
return f.read()
|
|
||||||
else:
|
@property
|
||||||
return f
|
def loader(self):
|
||||||
code = _read_files(code)
|
return self._loader
|
||||||
if not isinstance(code, string_types) and hasattr(code, '__iter__'):
|
|
||||||
code = ';\n'.join(map(_read_files, code))
|
|
||||||
return code
|
|
||||||
|
|
||||||
def evaljs(self, code, **kwargs):
|
def evaljs(self, code, **kwargs):
|
||||||
"""Runs JavaScript code in the context of the interpreter.
|
"""Runs JavaScript code in the context of the interpreter.
|
||||||
|
@ -73,6 +75,42 @@ class JSInterpreter(object):
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
return json.dumps(ret).encode('utf-8')
|
return json.dumps(ret).encode('utf-8')
|
||||||
|
|
||||||
|
def _init_process(self):
|
||||||
|
self.evaljs("process = {}; process.env = dukpy.environ", environ=dict(os.environ))
|
||||||
|
|
||||||
|
def _init_console(self):
|
||||||
|
self.export_function('dukpy.print', print)
|
||||||
|
self.evaljs("""
|
||||||
|
;console = {
|
||||||
|
log: function() {
|
||||||
|
call_python('dukpy.print', Array.prototype.join.call(arguments, ' '));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _init_require(self):
|
||||||
|
self.export_function('dukpy.lookup_module', self._loader.load)
|
||||||
|
self.evaljs("""
|
||||||
|
;Duktape.modSearch = function (id, require, exports, module) {
|
||||||
|
var m = call_python('dukpy.lookup_module', id);
|
||||||
|
if (!m)
|
||||||
|
throw new Error('cannot find module: ' + id);
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
def _adapt_code(self, code):
|
||||||
|
def _read_files(f):
|
||||||
|
if hasattr(f, 'read'):
|
||||||
|
return f.read()
|
||||||
|
else:
|
||||||
|
return f
|
||||||
|
|
||||||
|
code = _read_files(code)
|
||||||
|
if not isinstance(code, string_types) and hasattr(code, '__iter__'):
|
||||||
|
code = ';\n'.join(map(_read_files, code))
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
def evaljs(code, **kwargs):
|
def evaljs(code, **kwargs):
|
||||||
"""Evaluates the given ``code`` as JavaScript and returns the result"""
|
"""Evaluates the given ``code`` as JavaScript and returns the result"""
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class JSModuleLoader(object):
|
||||||
|
"""Manages finding and loading JS modules in CommonJS format.
|
||||||
|
|
||||||
|
This allows to import a module from JSInterpreter using the
|
||||||
|
`require('modulename')` command.
|
||||||
|
|
||||||
|
To register additional paths where to look for modules use
|
||||||
|
the `JSModuleLoader.register_path` method.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self._paths = []
|
||||||
|
self.register_path(os.path.join(os.path.dirname(__file__), 'jsmodules'))
|
||||||
|
self.register_path(os.getcwd())
|
||||||
|
|
||||||
|
def register_path(self, path):
|
||||||
|
"""Registers a directory where to look for modules.
|
||||||
|
|
||||||
|
By default only modules relative to current path are found.
|
||||||
|
"""
|
||||||
|
self._paths.insert(0, os.path.abspath(path))
|
||||||
|
|
||||||
|
def lookup(self, module_name):
|
||||||
|
"""Searches for a file providing given module."""
|
||||||
|
for search_path in self._paths:
|
||||||
|
module_path = os.path.join(search_path, module_name)
|
||||||
|
module_file = self._lookup(module_path)
|
||||||
|
if module_file:
|
||||||
|
return module_file
|
||||||
|
|
||||||
|
def load(self, module_name):
|
||||||
|
"""Returns source code of the given module."""
|
||||||
|
path = self.lookup(module_name)
|
||||||
|
if path:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def _lookup(self, module_path):
|
||||||
|
# Module is a plain .js file
|
||||||
|
for path in (module_path, os.path.extsep.join((module_path, 'js'))):
|
||||||
|
if os.path.exists(path) and os.path.isfile(path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
# Module is a package
|
||||||
|
package = os.path.join(module_path, os.path.extsep.join(('package', 'json')))
|
||||||
|
try:
|
||||||
|
with open(package) as f:
|
||||||
|
package = json.load(f)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
package_main = package.get('main')
|
||||||
|
if package_main:
|
||||||
|
path = self._lookup(os.path.join(module_path, package_main))
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
|
||||||
|
# Module is directory with index.js inside
|
||||||
|
indexjs = os.path.join(module_path, os.path.extsep.join(('index', 'js')))
|
||||||
|
if os.path.exists(indexjs):
|
||||||
|
return indexjs
|
4
setup.py
4
setup.py
|
@ -8,7 +8,9 @@ except IOError:
|
||||||
README = ''
|
README = ''
|
||||||
|
|
||||||
duktape = Extension('dukpy._dukpy',
|
duktape = Extension('dukpy._dukpy',
|
||||||
define_macros=[('DUK_OPT_DEEP_C_STACK', '1')],
|
define_macros=[('DUK_OPT_DEEP_C_STACK', '1'),
|
||||||
|
('DUK_OPT_NONSTD_REGEXP_DOLLAR_ESCAPE', '1'),
|
||||||
|
('DUK_OPT_OCTAL_SUPPORT', '1')],
|
||||||
sources=[os.path.join('src', 'duktape', 'duktape.c'),
|
sources=[os.path.join('src', 'duktape', 'duktape.c'),
|
||||||
os.path.join('src','_support.c'),
|
os.path.join('src','_support.c'),
|
||||||
os.path.join('src','pyduktape.c')],
|
os.path.join('src','pyduktape.c')],
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import dukpy
|
import dukpy
|
||||||
|
from diffreport import report_diff
|
||||||
|
|
||||||
|
|
||||||
class TestJSInterpreter(object):
|
class TestJSInterpreter(object):
|
||||||
|
@ -16,4 +17,16 @@ class TestJSInterpreter(object):
|
||||||
interpreter = dukpy.JSInterpreter()
|
interpreter = dukpy.JSInterpreter()
|
||||||
interpreter.export_function('say_hello', _say_hello)
|
interpreter.export_function('say_hello', _say_hello)
|
||||||
res = interpreter.evaljs("call_python('say_hello', 3, 'world')")
|
res = interpreter.evaljs("call_python('say_hello', 3, 'world')")
|
||||||
assert res == 'Hello world world world', res
|
assert res == 'Hello world world world', res
|
||||||
|
|
||||||
|
def test_module_loader(self):
|
||||||
|
interpreter = dukpy.JSInterpreter()
|
||||||
|
res = interpreter.evaljs('''
|
||||||
|
babel = require('babel-6.4.4.min');
|
||||||
|
babel.transform(dukpy.es6code, {presets: ["es2015"]}).code;
|
||||||
|
''', es6code='let i=5;')
|
||||||
|
|
||||||
|
expected = '''"use strict";
|
||||||
|
|
||||||
|
var i = 5;'''
|
||||||
|
assert res == expected, report_diff(expected, res)
|
Loading…
Reference in New Issue