CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyexecjs

Run JavaScript code from Python with automatic runtime selection

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

context-handling.mddocs/

Context Handling

Work with compiled JavaScript contexts for efficient repeated execution.

Overview

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.

Context Creation

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; }
    };
""")

Context Methods

eval()

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
    """

exec_()

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()

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
    """

is_available()

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
    """

Usage Examples

Function Calls

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)     # 4

Stateful Contexts

import 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") # []

Context Evaluation

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.5

Context Execution with Output

import 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 messages

Error Handling in Contexts

import 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}")

Context Lifecycle

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 6

Performance Considerations

Using contexts is more efficient than repeated eval() calls when:

  • Calling the same JavaScript functions multiple times
  • Maintaining state between JavaScript executions
  • Working with complex JavaScript objects or libraries
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

docs

context-handling.md

index.md

javascript-execution.md

runtime-management.md

tile.json