STPyV8 Documentation

repository·master·Indexed 20 days ago

https://github.com/cloudflare/stpyv8

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

Tokens
11.1K
Snippets
43
Records
55
Agent score
68%

What's inside STPyV8

  1. What is STPyV8

    master
    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.
  2. Use JSObject as a Python mapping

    master

    JavaScript objects can be accessed from Python as mapping types (similar to dict).

    Capabilities:

    • Access: Use bracket notation obj['key'] or attribute notation obj.key (via __getattr__).
    • Modification: Use obj['key'] = value to set properties.
    • Inspection: Use JSObject.keys() to get keys or the in operator to check for key existence.

    Python Properties: If STPyV8 is built with SUPPORT_PROPERTY enabled (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
  3. Understand V8 Isolate isolation and threading rules

    master

    In STPyV8, V8 isolates maintain completely separate states. To ensure stability, follow these threading rules:

    1. No Cross-Isolate Object Sharing: Objects created within one isolate must never be used in another isolate.
    2. Single-Threaded Access: An isolate can be entered by at most one thread at any given time.
    3. Parallelism: While a single isolate is restricted to one thread, you can create multiple isolates and use them in parallel across multiple threads.
    4. Synchronization: Use the JSIsolate context management (Locker/Unlocker API) to synchronize access to an isolate.
  4. Use JSArray as a Python sequence

    master

    The JSArray class 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 None padding for the intermediate indices.
    • Python to JS: You can pass a Python list to the JSArray constructor 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 Python len() 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
  5. Understand JavaScript to Python type conversion

    master

    When retrieving values from JavaScript in Python, STPyV8 maps JavaScript types to specific Python classes:

    JavaScript TypeJavaScript ValuePython TypePython Value
    NullnullNoneTypeNone
    UndefinedundefinedNoneTypeNone
    Booleantrue/falseboolTrue/False
    String'test'str'test'
    Number/Int32123int123
    Number3.14float3.14
    Datenew Date()datetime.datetimedatetime.datetime
    Array[1, 2]JSArrayJSArray object
    Functionfunction(){}JSFunctionJSFunction object
    Objectnew Object()JSObjectJSObject object

    Note: 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'>
  6. Understand Python to JavaScript type conversion

    master

    STPyV8 automatically converts Python primitives to their JavaScript equivalents when passing values into a JSContext.

    Python TypePython ValueJavaScript TypeJavaScript Value
    NoneTypeNoneObject/Nullnull
    boolTrue/FalseBooleantrue/false
    int / long123Number123
    float3.14Number3.14
    str / unicode'test'String'test'
    datetime.datetimedatetime.now()DateDate object
    datetime.timetime()DateDate object
    built-in function / method / typeabs / intObject/FunctionFunction

    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'
  7. Interoperate with the Global Object via JSContext.locals

    master

    Every JSContext has a global object that is accessible from both Python and JavaScript.

    • From Python: Use the JSContext.locals attribute 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
  8. Manage JavaScript execution with JSContext

    master

    A JSContext is 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 with statement to ensure the context is entered and exited correctly, which automatically handles resource release.

    Manual Lifecycle:

    1. Create an instance: ctxt = JSContext()
    2. Enter the context: ctxt.enter()
    3. Execute code: ctxt.eval("code")
    4. Leave the context: ctxt.leave()
    # Recommended way using context manager
    with JSContext() as ctxt:
        print(ctxt.eval("1+2")) # 3
  9. Compile and execute JavaScript with JSEngine and JSScript

    master

    Instead of using JSContext.eval for immediate execution, you can use JSEngine.compile to parse JavaScript code into a JSScript object. 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:

    1. Create a JSEngine instance.
    2. Call engine.compile(source) to get a JSScript object.
    3. Use JSScript.run() to execute the compiled code.
    4. Access JSScript.source to 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
  10. Build STPyV8 from source

    master

    If 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.py to build and install the package. You can also run tests using pytest or create a distribution package using bdist.

    # 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
  11. Install STPyV8 via pip

    master

    For most users, STPyV8 can be installed directly from PyPI. STPyV8 officially supports Python 3.9+.

    Starting from version v12.0.267.14, manual installation of boost-python and other Boost dependencies is no longer required when using the PyPI package.

    $ pip install stpyv8
  12. Handle JavaScript exceptions in Python

    master

    STPyV8 automatically translates JavaScript exceptions into Python exceptions. Well-known JavaScript exceptions are mapped to their Python equivalents, while other exceptions are wrapped in a JSError instance. You can use standard try...except blocks 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