STPyV8 Documentation
repository·master·Indexed 20 days ago
https://github.com/cloudflare/stpyv8STPyV8 provides interoperability between Python 3 and the Google V8 JavaScript engine, allowing developers to embed JavaScript in Python or call Python code from within a JavaScript environment. It features JSContext for sandboxed execution, JSEngine and JSScript for efficient code compilation, and JSClass for exporting Python classes to V8. The library handles automatic type conversion between Python and JavaScript primitives and provides JSIsolate for thread-safe access to V8 isolates.
What's inside STPyV8
- STPyV8 is a Python wrapper for the Google V8 engine. It acts as a bridge between Python and JavaScript objects, allowing you to embed the V8 engine directly into a Python script. This enables you to evaluate JavaScript code, call JavaScript functions from Python, and call Python functions from JavaScript.
Use JSObject as a Python mapping
masterJavaScript objects can be accessed from Python as mapping types (similar to
dict).Capabilities:
- Access: Use bracket notation
obj['key']or attribute notationobj.key(via__getattr__). - Modification: Use
obj['key'] = valueto set properties. - Inspection: Use
JSObject.keys()to get keys or theinoperator to check for key existence.
Python Properties: If STPyV8 is built with
SUPPORT_PROPERTYenabled (default), Python properties (with getters, setters, and deleters) will be correctly triggered when accessing the object via JavaScript.>>> ctxt = JSContext() >>> ctxt.enter() >>> ctxt.eval("var obj = {a:1, b:2};") >>> ctxt.locals.obj['a'] # Access via mapping 1 >>> ctxt.locals.obj.a # Access via attribute 1 >>> 'a' in ctxt.locals.obj # Check existence True >>> ctxt.locals.obj['c'] = 3 # Set property # Example with Python properties class Global(JSClass): def __init__(self, name): self._name = name def getname(self): return self._name def setname(self, name): self._name = name def delname(self): self._name = 'deleted' name = property(getname, setname, delname) with JSContext(Global('test')) as ctxt: print(ctxt.eval("name")) # test print(ctxt.eval("this.name = 'flier';")) # flier print(ctxt.eval("delete name")) # True- Access: Use bracket notation
Understand V8 Isolate isolation and threading rules
masterIn STPyV8, V8 isolates maintain completely separate states. To ensure stability, follow these threading rules:
- No Cross-Isolate Object Sharing: Objects created within one isolate must never be used in another isolate.
- Single-Threaded Access: An isolate can be entered by at most one thread at any given time.
- Parallelism: While a single isolate is restricted to one thread, you can create multiple isolates and use them in parallel across multiple threads.
- Synchronization: Use the
JSIsolatecontext management (Locker/Unlocker API) to synchronize access to an isolate.
Use JSArray as a Python sequence
masterThe
JSArrayclass wraps a JavaScript Array and allows it to behave like a standard Python sequence (supporting__getitem__,__len__,__contains__, etc.).Key behaviors:
- Sparse Arrays: Since JavaScript arrays are associative, assigning an index larger than the current length will result in
Nonepadding for the intermediate indices. - Python to JS: You can pass a Python
listto theJSArrayconstructor to create a real JavaScript Array. - JS to Python: While Python sequences (like
list) can be accessed in JS as array-like objects, they do not support JS properties like.length. Use the Pythonlen()function in JS instead.
>>> ctxt = JSContext() >>> ctxt.enter() >>> array = ctxt.eval('[1, 2, 3]') >>> array[1] # Access via index 2 >>> len(array) # Get length 3 >>> 2 in array # Check existence True >>> array[5] = 3 # Sparse assignment >>> [i for i in array] # Result: [1, None, 3, None, None, 3] # Creating a real JS Array from a Python list >>> ctxt.locals.array = JSArray([1, 2, 3]) >>> ctxt.eval("array.length") 3- Sparse Arrays: Since JavaScript arrays are associative, assigning an index larger than the current length will result in
Understand JavaScript to Python type conversion
masterWhen retrieving values from JavaScript in Python, STPyV8 maps JavaScript types to specific Python classes:
JavaScript Type JavaScript Value Python Type Python Value NullnullNoneTypeNoneUndefinedundefinedNoneTypeNoneBooleantrue/falseboolTrue/FalseString'test'str'test'Number/Int32123int123Number3.14float3.14Datenew Date()datetime.datetimedatetime.datetimeArray[1, 2]JSArrayJSArrayobjectFunctionfunction(){}JSFunctionJSFunctionobjectObjectnew Object()JSObjectJSObjectobjectNote: STPyV8 utilizes V8's internal type system for optimized integer/float handling.
>>> ctxt = JSContext() >>> ctxt.enter() >>> type(ctxt.eval("null")) <type 'NoneType'> >>> type(ctxt.eval("[1, 2, 3]")) <class '_STPyV8.JSArray'> >>> type(ctxt.eval("new Object()")) <class '_STPyV8.JSObject'>Understand Python to JavaScript type conversion
masterSTPyV8 automatically converts Python primitives to their JavaScript equivalents when passing values into a
JSContext.Python Type Python Value JavaScript Type JavaScript Value NoneTypeNoneObject/Null nullboolTrue/FalseBoolean true/falseint/long123Number 123float3.14Number 3.14str/unicode'test'String 'test'datetime.datetimedatetime.now()Date Date object datetime.timetime()Date Date object built-in function / method / typeabs/intObject/Function Function Unknown Python types are converted to plain JavaScript objects.
>>> ctxt = JSContext() >>> ctxt.enter() >>> typeof = ctxt.eval("(function type(value) { return typeof value; })") >>> typeof(True) 'boolean' >>> typeof(123) 'number' >>> typeof('test') 'string'Interoperate with the Global Object via JSContext.locals
masterEvery
JSContexthas a global object that is accessible from both Python and JavaScript.- From Python: Use the
JSContext.localsattribute to access or modify global variables. - From JavaScript: Use the global namespace directly.
STPyV8 handles type conversion, function calls, and exception translation between the two languages automatically.
with JSContext() as ctxt: ctxt.eval("a = 1") print(ctxt.locals.a) # 1 ctxt.locals.a = 2 print(ctxt.eval("a")) # 2- From Python: Use the
Manage JavaScript execution with JSContext
masterA
JSContextis a sandboxed execution context that provides its own set of built-in objects and functions. To execute JavaScript code, you must enter a context.Best Practice: Use the Python
withstatement to ensure the context is entered and exited correctly, which automatically handles resource release.Manual Lifecycle:
- Create an instance:
ctxt = JSContext() - Enter the context:
ctxt.enter() - Execute code:
ctxt.eval("code") - Leave the context:
ctxt.leave()
# Recommended way using context manager with JSContext() as ctxt: print(ctxt.eval("1+2")) # 3- Create an instance:
Compile and execute JavaScript with JSEngine and JSScript
masterInstead of using
JSContext.evalfor immediate execution, you can useJSEngine.compileto parse JavaScript code into aJSScriptobject. This approach is more efficient for reusing the same code across different contexts and allows for code inspection via the Abstract Syntax Tree (AST).To use this pattern:
- Create a
JSEngineinstance. - Call
engine.compile(source)to get aJSScriptobject. - Use
JSScript.run()to execute the compiled code. - Access
JSScript.sourceto retrieve the original source string.
from STPyV8 import * with JSContext() as ctxt: with JSEngine() as engine: s = engine.compile("1+2") print(s.source) # "1+2" print(s.run()) # 3- Create a
Build STPyV8 from source
masterIf no pre-built wheels are available for your specific platform and Python version, you must build STPyV8 from source.
Requirements:
- Boost: STPyV8 requires
Boost.Python. While most Linux distributions provide Boost packages, you may need to download and build the latest version from the Boost website if packages are unavailable.
Build Commands: Use
setup.pyto build and install the package. You can also run tests usingpytestor create a distribution package usingbdist.# Build and install $ python setup.py build $ sudo python setup.py install # Run tests (requires pytest) $ pytest tests # Build a distribution package (Linux/Mac) $ python setup.py bdist- Boost: STPyV8 requires
Install STPyV8 via pip
masterFor most users, STPyV8 can be installed directly from PyPI. STPyV8 officially supports Python 3.9+.
Starting from version
v12.0.267.14, manual installation ofboost-pythonand other Boost dependencies is no longer required when using the PyPI package.$ pip install stpyv8Handle JavaScript exceptions in Python
masterSTPyV8 automatically translates JavaScript exceptions into Python exceptions. Well-known JavaScript exceptions are mapped to their Python equivalents, while other exceptions are wrapped in a
JSErrorinstance. You can use standardtry...exceptblocks in Python to catch these.from STPyV8 import JSContext, JSError with JSContext() as ctxt: try: ctxt.eval("throw Error('test');") except JSError as e: print(e) # JSError: Error: test ( @ 1 : 6 ) -> throw Error('test'); print(e.name) # Error print(e.message) # test