GraalPy Documentation

repository·master·Indexed 23 days ago

https://github.com/oracle/graalpython

GraalPy is a Python 3.12 compliant runtime built on GraalVM, designed for high performance, native compilation, and seamless interoperability between Python and Java. It can be used as a standalone drop-in replacement for CPython or embedded directly into Java projects via Maven and Gradle. The documentation covers installation, native binary compilation, Java/Python interoperability, platform support for Oracle Linux, and specialized processes for PolyBench benchmarking and package patching via metadata.toml.

Tokens
48.4K
Snippets
112
Records
252
Agent score
82%

What's inside GraalPy

  1. Overview of idlelib implementation files

    master

    The idlelib package is composed of several modules categorized by their role in the environment:

    Startup

    • __main__.py: Entry point for -m idlelib.
    • idle.py / idle.pyw: Main script files.

    Core Implementation

    • editor.py: Core editor and utility functions.
    • pyshell.py: Manages the shell and editor window.
    • run.py: Manages the user code execution subprocess.
    • debugger.py: Handles code debugging and the debugger window.
    • config.py: Handles loading, fetching, and saving configuration.
    • autocomplete.py: Handles attribute and filename completion.
    • search.py / searchengine.py: Provides search, replace, and grep functionality.
    • windows.py: Manages the top-level window list.

    Configuration Files

    • config-extensions.def: Extension defaults.
    • config-highlight.def: Colorizing defaults.
    • config-keys.def: Keybinding defaults.
    • config-main.def: Font and general tab defaults.
  2. Standard PolyBench configuration functions for benchmarks

    master

    When porting a benchmark to the PolyBench harness, most benchmarks implement a consistent set of functions to control execution and reporting. A typical implementation includes:

    def run():
        # Execute the actual benchmark workload here
        benchmark_function(args)
    
    def warmupIterations():
        # Number of iterations to run before measurement starts
        return 0
    
    def iterations():
        # Number of measurement iterations
        return 10
    
    def summary():
        # Returns a dictionary defining outlier removal thresholds
        return {
            "name": "OutlierRemovalAverageSummary",
            "lower-threshold": 0.0,
            "upper-threshold": 1.0,
        }
  3. Use Java objects and type conversion in GraalPy

    master

    GraalPy provides seamless interoperability between Python and Java types.

    Working with Java Objects

    You can instantiate and interact with Java objects using standard Python syntax. Java methods can be retrieved as first-class objects (bound to the instance).

    from java.util import Random
    rg = Random(99)
    rg.nextInt()
    
    # Accessing a method as a bound object
    boundNextInt = rg.nextInt
    boundNextInt()

    Automatic Type Conversion

    GraalPy automatically converts Python types to Java types. It is more flexible than Jython, as it can convert any Python object with __int__ or __float__ methods to the corresponding Java type (e.g., using NumPy arrays as Java int[]).

    Java typePython type
    nullNone
    booleanbool
    byte, short, int, longint, or any object with __int__
    floatfloat, or any object with __float__
    charstr of length 1
    java.lang.Stringstr
    byte[]bytes, bytearray, wrapped Java array, or Python list with appropriate types
    Java arraysWrapped Java array or Python list with appropriate types
    Java objectsWrapped Java object
    java.lang.ObjectAny object
    from java.util import Random
    rg = Random(99)
    rg.nextInt()
    boundNextInt = rg.nextInt
    boundNextInt()
  4. How C Extensions and Memory Management work in GraalPy

    master

    GraalPy manages a dual memory model to bridge the gap between Python C extensions (which require reference counting) and the GraalVM managed environment (which uses tracing Garbage Collection).

    The Dual Model

    • Native Side: Uses standard reference counting via Py_IncRef and Py_DecRef.
    • Managed Side: Uses Java's tracing GC. Managed references are not individually ref-counted.

    Bridging the Gap

    To allow native code to interact with managed objects, GraalPy uses a hybrid approach:

    1. Approximating Managed References: All managed references to an object are treated as a single reference. When an object is referenced from managed code, its native refcount is incremented by a constant MANAGED_REFCNT.
    2. Cleanup via PhantomReferences: GraalPy uses a PhantomReference and a ReferenceQueue. When the Java GC determines there are no more managed references to an object, the reference is enqueued, and GraalPy decrements the native refcount by MANAGED_REFCNT.
    3. Lifecycle Completion: If the refcount reaches 0 after the managed decrement, the object is deallocated. If it is still > 0, it means native code still holds references, and the object stays alive until the native code eventually calls Py_DecRef to bring the count to 0.
  5. Handling Memory Pressure from Native Allocations

    master

    Because native (off-heap) allocations are invisible to the JVM, GraalPy implements mechanisms to prevent the JVM from being unaware of high memory usage caused by C extensions.

    Strategies

    • Tracking Allocations: GraalPy tracks off-heap allocations and counts the total bytes. It prepends a header to allocations in Python API memory management functions to account for freed memory.
    • Threshold-based GC: When off-heap usage exceeds a configurable threshold (MaxNativeMemory), GraalPy forces a full Java GC.
    • RSS Monitoring: A background thread monitors the process Resident Set Size (RSS). If RSS increases rapidly, GraalPy forces GCs more frequently.
    • GPU Memory: For extensions like PyTorch, GraalPy applies a patch that forces a GC whenever a CUDA allocation fails and retries.
    • Stub Polling: To prevent the nativeStubLookup handle table from growing too rapidly, GraalPy polls weak references during transitions.
  6. Interpret package compatibility status

    master

    When reviewing the GraalPy package compatibility tables, the following statuses are used to indicate the level of support:

    • Compatible: More than 90% of the package's tests run successfully on GraalPy.
    • Currently Untested: The package either does not install on GraalPy or is not currently tested.
    • Currently Incompatible: Fewer than 90% of the package's tests run successfully on GraalPy.
    • Not Supported: The maintainers have no plans to test the package.
  7. Work with Java Collections and Maps in Python

    master

    GraalPy allows you to interact with Java collections using Pythonic syntax.

    Java Collections (List, Set, etc.)

    Collections implementing java.util.Collection support:

    • Indexing: Use [] syntax.
    • Length: Use the built-in len() function.
    • Boolean Conversion: An empty collection evaluates to False.
    • Iteration: Use for loops or iter() (works with java.lang.Iterable).
    from java.util import ArrayList
    l = ArrayList()
    l.add("foo")
    l.add("baz")
    print(l[0])    # 'foo'
    print(len(l))  # 2
    for x in l:    # Iteration works
        print(x)

    Java Maps

    Maps implementing java.util.Map support:

    • Key/Value Access: Use [] notation.
    • Iteration: Iterating over a map yields its keys, similar to a Python dict.
    • Boolean Conversion: An empty map evaluates to False.
    from java.util import HashMap
    m = HashMap()
    m['foo'] = 5
    print(m['foo']) # 5
    for k in m:     # Iterates over keys
        print(k)
    from java.util import ArrayList
    l = ArrayList()
    l.add("foo")
    l.add("baz")
    l[0]
    l[1] = "bar"
    del l[1]
    len(l)
  8. How built-in modules and classes are implemented

    master

    GraalPy implements built-ins using two primary methods:

    1. Java Implementation: Most built-ins are in the com.oracle.graal.python.builtins package.

      • A Java class is annotated with @CoreFunctions.
      • Each function is implemented in a Node annotated with @Builtin.
      • New classes/modules must be added to com.oracle.graal.python.builtins.Python3Core.
    2. Pure Python Implementation: Some built-ins reside in graalpython/lib-graalpython/. These are also registered in Python3Core. If a file's name matches a built-in module, it is executed in that module's context during startup.

    Argument Clinic: GraalPy uses a variant of the Python Argument Clinic preprocessor. To use it, extend PythonXXXClinicBuiltinNode (e.g., PythonBinaryClinicBuiltinNode), use @ArgumentClinic annotations on the node class, and override getArgumentClinic to return the generated class (suffixed with ClinicProviderGen).

  9. Use modern inheritance (GraalPy >= 25.1)

    master

    From GraalPy version 25.1 onwards, new_style=True is the default. This mode provides a more seamless integration between Python and Java.

    Key features:

    • Transparent Dispatch: The generated class is a Java object, but attribute lookups are automatically dispatched to the Python object if they are not found on the Java class.
    • Standard Python Syntax: super() calls work correctly in both __new__ (for constructor overrides) and in standard Java method overrides.
    • Self Reference: In a method, self refers to the Java object, but access to non-Java fields/methods is transparently handled by Python.
    • Static Methods: Static methods can be called directly from both the instance and the class.
    • Generics: You can parameterize Java generic classes using Python type annotation syntax.
    from java.util.logging import Level
    
    class PythonLevel(Level, new_style=True):
        def __new__(cls, name="default name", level=2):
            return super().__new__(cls, name, level)
    
        def __init__(self, *args, **kwarg):
            self.misc_value = 42
    
        def getName(self):
            return super().getName() + " from Python"
    
        def callStaticFromPython(self, name):
            return self.parse(name)
    
    # Parameterizing Generics
    from java.util.function import Function
    class StringFunction(Function[str, str], new_style=True):
        def apply(self, value: str) -> str:
            return value.upper()
  10. Managed vs Native Object Memory Layouts

    master

    GraalPy distinguishes between managed objects (allocated on the Java heap) and native objects (allocated in off-heap memory).

    Managed Objects

    • Allocated in the interpreter.
    • If native code requires a built-in or pure Python object, GraalPy performs an upcall to create a managed object.
    • When passed to native code, a native stub is allocated in off-heap memory to represent the object. This stub includes:
      • A refcount initialized to MANAGED_REFCNT.
      • A PythonObjectReference (a weak reference to the PythonObjectNativeWrapper) used for lookup and lifecycle management.
      • A custom 32-bit integer index into the nativeStubLookup array.
      • The high bit of the pointer is set to allow native code to quickly identify stubs.

    Native Objects

    • Allocated via PyObject_GC_New in native code.
    • If a native object is passed to managed code, GraalPy:
      • Increments its refcount by MANAGED_REFCNT.
      • Creates a PythonAbstractNativeObject Java object to mirror it.
      • Creates a NativeObjectReference (weak reference) and maps the native address to it in CApiTransitions.nativeLookup.