Simple JavaScript interpreter for Python built on top of duktape engine without any external dependencies
—
Core JavaScript execution functionality supporting both one-off evaluation and persistent interpreter contexts with variable passing, module loading, and Python function exports.
Executes JavaScript code in a fresh interpreter context, automatically handling variable serialization and cleanup. Variables passed as keyword arguments are available in JavaScript as properties of the dukpy global object.
def evaljs(code, **kwargs):
"""
Evaluates the given code as JavaScript and returns the result.
Parameters:
- code: str or list of str - JavaScript code to execute
- **kwargs: Variables available in JavaScript as dukpy.varname
Returns:
JSON-serializable result of JavaScript execution
Raises:
JSRuntimeError: When JavaScript execution fails
"""Usage example:
import dukpy
# Simple expression evaluation
result = dukpy.evaljs("5 + 3") # Returns 8
# Using Python variables in JavaScript
result = dukpy.evaljs("dukpy.x * dukpy.y + 10", x=5, y=3) # Returns 25
# Multiple JavaScript statements
result = dukpy.evaljs([
"var obj = {count: 0}",
"obj.count += dukpy.increment",
"obj"
], increment=5) # Returns {'count': 5}
# Working with arrays and objects
data = [1, 2, 3, 4, 5]
result = dukpy.evaljs("dukpy.numbers.reduce((a, b) => a + b, 0)", numbers=data) # Returns 15Maintains JavaScript context between evaluations, enabling stateful execution, module loading, and bi-directional Python-JavaScript function calls. Ideal for applications requiring multiple JavaScript operations or module usage.
class JSInterpreter:
"""JavaScript Interpreter with persistent context"""
def __init__(self):
"""
Creates a new JavaScript interpreter instance.
Automatically initializes console logging and module system.
"""
def evaljs(self, code, **kwargs):
"""
Runs JavaScript code in the context of the interpreter.
Parameters:
- code: str or list of str - JavaScript code to execute
- **kwargs: Variables available in JavaScript as dukpy.varname
Returns:
JSON-serializable result of JavaScript execution
Raises:
JSRuntimeError: When JavaScript execution fails
"""
def export_function(self, name, func):
"""
Exports a Python function to the JavaScript layer.
Parameters:
- name: str - Function name in JavaScript
- func: callable - Python function to export
Note: Only JSON-serializable objects can be passed between JS and Python.
Objects are passed by copy, not by reference.
"""
@property
def loader(self):
"""
Access to JSModuleLoader instance for registering module search paths.
Returns:
JSModuleLoader: Module loader for require() support
"""Usage example:
import dukpy
# Create persistent interpreter
interpreter = dukpy.JSInterpreter()
# Maintain state between calls
interpreter.evaljs("var counter = 0")
count1 = interpreter.evaljs("++counter") # Returns 1
count2 = interpreter.evaljs("++counter") # Returns 2
# Export Python functions to JavaScript
def multiply(a, b):
return a * b
interpreter.export_function('multiply', multiply)
result = interpreter.evaljs("call_python('multiply', 6, 7)") # Returns 42
# Use require() to load modules
interpreter.loader.register_path('./js_modules')
result = interpreter.evaljs("var lodash = require('lodash'); lodash.isArray([1, 2, 3])")CommonJS-compatible module loading system that enables JavaScript modules to be loaded and used within the interpreter context using standard require() syntax.
class JSModuleLoader:
"""
Manages finding and loading JS modules in CommonJS format.
Supports standard Node.js module resolution patterns.
"""
def __init__(self):
"""
Creates module loader with default search paths:
- dukpy/jsmodules (built-in modules)
- current working directory
"""
def register_path(self, path):
"""
Registers a directory where to look for modules.
Parameters:
- path: str - Directory path to search for modules
Note: Paths are searched in reverse registration order (LIFO).
"""
def lookup(self, module_name):
"""
Searches for a file providing given module.
Parameters:
- module_name: str - Name of module to find
Returns:
tuple: (normalized_module_id, file_path) or (None, None) if not found
"""
def load(self, module_name):
"""
Returns source code and normalized module id of the given module.
Parameters:
- module_name: str - Name of module to load
Returns:
tuple: (module_id, source_code) or (None, None) if not found
Note: Only supports UTF-8 encoded source files.
"""Usage example:
import dukpy
# Create interpreter and register module paths
interpreter = dukpy.JSInterpreter()
interpreter.loader.register_path('./my_js_modules')
interpreter.loader.register_path('./node_modules')
# Use require() in JavaScript
result = interpreter.evaljs("""
var utils = require('utils');
var math = require('math-functions');
utils.processData(math.fibonacci(10))
""")Specialized interpreter providing Node.js-like environment with filesystem access and built-in core modules for enhanced JavaScript compatibility.
class NodeLikeInterpreter(JSInterpreter):
"""
A DukPy Interpreter that provides a minimal compatibility layer with NodeJS.
Inherits all JSInterpreter functionality with additional Node.js-like features.
"""
def __init__(self):
"""
Creates NodeLikeInterpreter with Node.js compatibility features:
- Registers jscore module path for built-in Node.js modules
- Exports file.exists and file.read functions to JavaScript
- Inherits all JSInterpreter capabilities
"""Usage example:
import dukpy
from dukpy.nodelike import NodeLikeInterpreter
# Create Node.js-like interpreter
interpreter = NodeLikeInterpreter()
# Access filesystem from JavaScript
result = interpreter.evaljs("""
var exists = call_python('file.exists', '/path/to/file.txt');
var content = '';
if (exists) {
content = call_python('file.read', '/path/to/file.txt', 'utf-8');
}
{exists: exists, content: content}
""")
# Use LESS compiler with Node.js compatibility
from dukpy import less_compile
css = less_compile("""
@primary-color: #333;
.header { color: @primary-color; }
""")Simplified filesystem operations available to JavaScript through the NodeLikeInterpreter.
class FS:
"""
Provides oversimplified fs.js native functions for JavaScript.
Available as exported functions in NodeLikeInterpreter.
"""
@classmethod
def exists(cls, filepath):
"""
Checks if a file or directory exists.
Parameters:
- filepath: str - Path to check
Returns:
bool: True if path exists, False otherwise
"""
@classmethod
def read(cls, path, encoding):
"""
Reads file contents with optional encoding.
Parameters:
- path: str - File path to read
- encoding: str or None - Text encoding (e.g., 'utf-8') or None for binary
Returns:
str or bytes: File contents as string (if encoding) or bytes (if None)
Raises:
IOError: When file cannot be read
"""class JSRuntimeError(Exception):
"""
Exception raised when JavaScript execution fails.
Provides details about JavaScript runtime errors including
syntax errors, reference errors, and other execution failures.
"""Common error scenarios:
import dukpy
try:
# Syntax error
result = dukpy.evaljs("var x = ;")
except dukpy.JSRuntimeError as e:
print(f"JavaScript error: {e}")
try:
# Reference error
result = dukpy.evaljs("undefinedVariable.property")
except dukpy.JSRuntimeError as e:
print(f"JavaScript error: {e}")
try:
# Type error
result = dukpy.evaljs("null.toString()")
except dukpy.JSRuntimeError as e:
print(f"JavaScript error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-dukpy