0
# Context Handling
1
2
Work with compiled JavaScript contexts for efficient repeated execution.
3
4
## Overview
5
6
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.
7
8
## Context Creation
9
10
Contexts are created using the `compile()` function:
11
12
```python
13
import execjs
14
15
# Compile JavaScript code into a context
16
ctx = execjs.compile("""
17
var counter = 0;
18
var utils = {
19
increment: function() { return ++counter; },
20
reset: function() { counter = 0; return counter; },
21
getValue: function() { return counter; }
22
};
23
""")
24
```
25
26
## Context Methods
27
28
### eval()
29
30
Evaluate JavaScript code within the compiled context.
31
32
```python { .api }
33
def eval(self, source):
34
"""
35
Evaluate JavaScript code in the compiled context.
36
37
Args:
38
source (str): JavaScript source code to evaluate
39
40
Returns:
41
any: Result of JavaScript evaluation
42
43
Raises:
44
ProgramError: When JavaScript code contains syntax or runtime errors
45
RuntimeError: When JavaScript runtime engine encounters errors
46
"""
47
```
48
49
### exec_()
50
51
Execute JavaScript code in the context and return stdout output.
52
53
```python { .api }
54
def exec_(self, source):
55
"""
56
Execute JavaScript code in context and return stdout output.
57
58
Args:
59
source (str): JavaScript source code to execute
60
61
Returns:
62
str: Standard output from JavaScript execution
63
64
Raises:
65
ProgramError: When JavaScript code contains syntax or runtime errors
66
RuntimeError: When JavaScript runtime engine encounters errors
67
"""
68
```
69
70
### call()
71
72
Call a JavaScript function by name with arguments.
73
74
```python { .api }
75
def call(self, name, *args):
76
"""
77
Call a JavaScript function by name with arguments.
78
79
Args:
80
name (str): Name of JavaScript function to call
81
*args: Arguments to pass to the function
82
83
Returns:
84
any: Result of function call
85
86
Raises:
87
ProgramError: When function doesn't exist or JavaScript errors occur
88
RuntimeError: When JavaScript runtime engine encounters errors
89
"""
90
```
91
92
### is_available()
93
94
Check if the context is available for execution.
95
96
```python { .api }
97
def is_available(self):
98
"""
99
Check if context is available.
100
101
Returns:
102
bool: True if context is available for execution, False otherwise
103
"""
104
```
105
106
## Usage Examples
107
108
### Function Calls
109
110
```python
111
import execjs
112
113
# Compile JavaScript with functions
114
ctx = execjs.compile("""
115
function add(a, b) {
116
return a + b;
117
}
118
119
function multiply(a, b) {
120
return a * b;
121
}
122
123
var math = {
124
power: function(base, exponent) {
125
return Math.pow(base, exponent);
126
},
127
sqrt: function(n) {
128
return Math.sqrt(n);
129
}
130
};
131
""")
132
133
# Call top-level functions
134
result1 = ctx.call("add", 5, 3) # 8
135
result2 = ctx.call("multiply", 4, 7) # 28
136
137
# Call object methods
138
result3 = ctx.call("math.power", 2, 8) # 256
139
result4 = ctx.call("math.sqrt", 16) # 4
140
```
141
142
### Stateful Contexts
143
144
```python
145
import execjs
146
147
# Create stateful JavaScript context
148
ctx = execjs.compile("""
149
var state = {
150
items: [],
151
counter: 0
152
};
153
154
function addItem(item) {
155
state.items.push(item);
156
state.counter++;
157
return state.counter;
158
}
159
160
function getItems() {
161
return state.items;
162
}
163
164
function getCount() {
165
return state.counter;
166
}
167
168
function reset() {
169
state.items = [];
170
state.counter = 0;
171
return 'reset';
172
}
173
""")
174
175
# Use stateful functions
176
count1 = ctx.call("addItem", "apple") # 1
177
count2 = ctx.call("addItem", "banana") # 2
178
count3 = ctx.call("addItem", "cherry") # 3
179
180
items = ctx.call("getItems") # ['apple', 'banana', 'cherry']
181
total = ctx.call("getCount") # 3
182
183
ctx.call("reset") # 'reset'
184
items_after_reset = ctx.call("getItems") # []
185
```
186
187
### Context Evaluation
188
189
```python
190
import execjs
191
192
# Compile base context
193
ctx = execjs.compile("""
194
var data = {
195
users: [
196
{name: 'Alice', age: 30},
197
{name: 'Bob', age: 25}
198
]
199
};
200
""")
201
202
# Evaluate expressions in context
203
oldest = ctx.eval("data.users.reduce((a, b) => a.age > b.age ? a : b)")
204
# Returns: {'name': 'Alice', 'age': 30}
205
206
names = ctx.eval("data.users.map(u => u.name)")
207
# Returns: ['Alice', 'Bob']
208
209
average_age = ctx.eval("data.users.reduce((sum, u) => sum + u.age, 0) / data.users.length")
210
# Returns: 27.5
211
```
212
213
### Context Execution with Output
214
215
```python
216
import execjs
217
218
# Compile context with logging
219
ctx = execjs.compile("""
220
var log = [];
221
222
function debug(message) {
223
log.push(new Date().toISOString() + ': ' + message);
224
console.log(message);
225
}
226
227
function processData(items) {
228
debug('Starting data processing');
229
var result = items.map(item => item * 2);
230
debug('Processing complete, ' + result.length + ' items processed');
231
return result;
232
}
233
234
function getLogs() {
235
return log;
236
}
237
""")
238
239
# Execute with console output
240
output = ctx.exec_("debug('System initialized')")
241
print(output) # "System initialized\n"
242
243
# Call function that produces output
244
result = ctx.call("processData", [1, 2, 3, 4, 5])
245
# Console output: "Starting data processing" and "Processing complete, 5 items processed"
246
247
# Get internal logs
248
logs = ctx.call("getLogs")
249
print(logs) # Array of timestamped log messages
250
```
251
252
### Error Handling in Contexts
253
254
```python
255
import execjs
256
257
ctx = execjs.compile("""
258
function divide(a, b) {
259
if (b === 0) {
260
throw new Error('Division by zero');
261
}
262
return a / b;
263
}
264
265
function callNonExistentFunction() {
266
return nonExistentFunction();
267
}
268
""")
269
270
# Handle JavaScript errors
271
try:
272
result = ctx.call("divide", 10, 0)
273
except execjs.ProgramError as e:
274
print(f"JavaScript error: {e}") # "JavaScript error: Division by zero"
275
276
try:
277
result = ctx.call("callNonExistentFunction")
278
except execjs.ProgramError as e:
279
print(f"JavaScript error: {e}") # "JavaScript error: nonExistentFunction is not defined"
280
281
# Handle non-existent function calls
282
try:
283
result = ctx.call("nonExistentFunction", 1, 2, 3)
284
except execjs.ProgramError as e:
285
print(f"Function not found: {e}")
286
```
287
288
## Context Lifecycle
289
290
Contexts maintain their state throughout their lifetime:
291
292
```python
293
import execjs
294
295
# Create persistent context
296
ctx = execjs.compile("var x = 1;")
297
298
# State persists between calls
299
ctx.eval("x += 1") # x is now 2
300
ctx.eval("x *= 3") # x is now 6
301
result = ctx.eval("x") # Returns 6
302
303
# Context state is isolated from other contexts
304
ctx2 = execjs.compile("var x = 100;")
305
ctx2_result = ctx2.eval("x") # Returns 100
306
ctx_result = ctx.eval("x") # Still returns 6
307
```
308
309
## Performance Considerations
310
311
Using contexts is more efficient than repeated `eval()` calls when:
312
313
- Calling the same JavaScript functions multiple times
314
- Maintaining state between JavaScript executions
315
- Working with complex JavaScript objects or libraries
316
317
```python
318
import execjs
319
import time
320
321
# Inefficient: recompiling each time
322
start = time.time()
323
for i in range(100):
324
result = execjs.eval(f"Math.pow(2, {i})")
325
slow_time = time.time() - start
326
327
# Efficient: using compiled context
328
ctx = execjs.compile("function power(base, exp) { return Math.pow(base, exp); }")
329
start = time.time()
330
for i in range(100):
331
result = ctx.call("power", 2, i)
332
fast_time = time.time() - start
333
334
print(f"Without context: {slow_time:.3f}s")
335
print(f"With context: {fast_time:.3f}s")
336
```