Run JavaScript code from Python with automatic runtime selection
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Work with compiled JavaScript contexts for efficient repeated execution.
JavaScript contexts allow you to compile JavaScript code once and execute it multiple times efficiently. This is particularly useful when you need to call JavaScript functions repeatedly or maintain JavaScript state between calls.
Contexts are created using the compile() function:
import execjs
# Compile JavaScript code into a context
ctx = execjs.compile("""
var counter = 0;
var utils = {
increment: function() { return ++counter; },
reset: function() { counter = 0; return counter; },
getValue: function() { return counter; }
};
""")Evaluate JavaScript code within the compiled context.
def eval(self, source):
"""
Evaluate JavaScript code in the compiled context.
Args:
source (str): JavaScript source code to evaluate
Returns:
any: Result of JavaScript evaluation
Raises:
ProgramError: When JavaScript code contains syntax or runtime errors
RuntimeError: When JavaScript runtime engine encounters errors
"""Execute JavaScript code in the context and return stdout output.
def exec_(self, source):
"""
Execute JavaScript code in context and return stdout output.
Args:
source (str): JavaScript source code to execute
Returns:
str: Standard output from JavaScript execution
Raises:
ProgramError: When JavaScript code contains syntax or runtime errors
RuntimeError: When JavaScript runtime engine encounters errors
"""Call a JavaScript function by name with arguments.
def call(self, name, *args):
"""
Call a JavaScript function by name with arguments.
Args:
name (str): Name of JavaScript function to call
*args: Arguments to pass to the function
Returns:
any: Result of function call
Raises:
ProgramError: When function doesn't exist or JavaScript errors occur
RuntimeError: When JavaScript runtime engine encounters errors
"""Check if the context is available for execution.
def is_available(self):
"""
Check if context is available.
Returns:
bool: True if context is available for execution, False otherwise
"""import execjs
# Compile JavaScript with functions
ctx = execjs.compile("""
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
var math = {
power: function(base, exponent) {
return Math.pow(base, exponent);
},
sqrt: function(n) {
return Math.sqrt(n);
}
};
""")
# Call top-level functions
result1 = ctx.call("add", 5, 3) # 8
result2 = ctx.call("multiply", 4, 7) # 28
# Call object methods
result3 = ctx.call("math.power", 2, 8) # 256
result4 = ctx.call("math.sqrt", 16) # 4import execjs
# Create stateful JavaScript context
ctx = execjs.compile("""
var state = {
items: [],
counter: 0
};
function addItem(item) {
state.items.push(item);
state.counter++;
return state.counter;
}
function getItems() {
return state.items;
}
function getCount() {
return state.counter;
}
function reset() {
state.items = [];
state.counter = 0;
return 'reset';
}
""")
# Use stateful functions
count1 = ctx.call("addItem", "apple") # 1
count2 = ctx.call("addItem", "banana") # 2
count3 = ctx.call("addItem", "cherry") # 3
items = ctx.call("getItems") # ['apple', 'banana', 'cherry']
total = ctx.call("getCount") # 3
ctx.call("reset") # 'reset'
items_after_reset = ctx.call("getItems") # []import execjs
# Compile base context
ctx = execjs.compile("""
var data = {
users: [
{name: 'Alice', age: 30},
{name: 'Bob', age: 25}
]
};
""")
# Evaluate expressions in context
oldest = ctx.eval("data.users.reduce((a, b) => a.age > b.age ? a : b)")
# Returns: {'name': 'Alice', 'age': 30}
names = ctx.eval("data.users.map(u => u.name)")
# Returns: ['Alice', 'Bob']
average_age = ctx.eval("data.users.reduce((sum, u) => sum + u.age, 0) / data.users.length")
# Returns: 27.5import execjs
# Compile context with logging
ctx = execjs.compile("""
var log = [];
function debug(message) {
log.push(new Date().toISOString() + ': ' + message);
console.log(message);
}
function processData(items) {
debug('Starting data processing');
var result = items.map(item => item * 2);
debug('Processing complete, ' + result.length + ' items processed');
return result;
}
function getLogs() {
return log;
}
""")
# Execute with console output
output = ctx.exec_("debug('System initialized')")
print(output) # "System initialized\n"
# Call function that produces output
result = ctx.call("processData", [1, 2, 3, 4, 5])
# Console output: "Starting data processing" and "Processing complete, 5 items processed"
# Get internal logs
logs = ctx.call("getLogs")
print(logs) # Array of timestamped log messagesimport execjs
ctx = execjs.compile("""
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
function callNonExistentFunction() {
return nonExistentFunction();
}
""")
# Handle JavaScript errors
try:
result = ctx.call("divide", 10, 0)
except execjs.ProgramError as e:
print(f"JavaScript error: {e}") # "JavaScript error: Division by zero"
try:
result = ctx.call("callNonExistentFunction")
except execjs.ProgramError as e:
print(f"JavaScript error: {e}") # "JavaScript error: nonExistentFunction is not defined"
# Handle non-existent function calls
try:
result = ctx.call("nonExistentFunction", 1, 2, 3)
except execjs.ProgramError as e:
print(f"Function not found: {e}")Contexts maintain their state throughout their lifetime:
import execjs
# Create persistent context
ctx = execjs.compile("var x = 1;")
# State persists between calls
ctx.eval("x += 1") # x is now 2
ctx.eval("x *= 3") # x is now 6
result = ctx.eval("x") # Returns 6
# Context state is isolated from other contexts
ctx2 = execjs.compile("var x = 100;")
ctx2_result = ctx2.eval("x") # Returns 100
ctx_result = ctx.eval("x") # Still returns 6Using contexts is more efficient than repeated eval() calls when:
import execjs
import time
# Inefficient: recompiling each time
start = time.time()
for i in range(100):
result = execjs.eval(f"Math.pow(2, {i})")
slow_time = time.time() - start
# Efficient: using compiled context
ctx = execjs.compile("function power(base, exp) { return Math.pow(base, exp); }")
start = time.time()
for i in range(100):
result = ctx.call("power", 2, i)
fast_time = time.time() - start
print(f"Without context: {slow_time:.3f}s")
print(f"With context: {fast_time:.3f}s")Install with Tessl CLI
npx tessl i tessl/pypi-pyexecjs