Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions docs/user/Embedding-Build-Tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ This approach involves the following steps:
- Smaller JAR/executable size since Python resources aren't embedded

To use an external directory, create your GraalPy context with:
- `GraalPyResources.createContextBuilder(Path)` - Creates a context builder pointing to your external directory path
- `GraalPyResources.contextBuilder(Path)` - Creates a context builder pointing to your external directory path

## Directory Structure

Expand Down Expand Up @@ -229,10 +229,9 @@ To customize the lock file path, configure _graalPyLockFile_ :
</configuration>
```

> **Note:** This only changes the path (defaults to _${basedir}/graalpy.lock_).To generate the lock file, run the `lock-packages` goal.
> **Note:** This only changes the path (defaults to _${basedir}/graalpy.lock_). To generate the lock file, run the `lock-packages` goal.

For more information of this feature, please see the
[Python Dependency Management for Reproducible Builds](#python-dependency-management-for-reproducible-builds) section.
For more information about dependency locking, see the [Dependency Management](#dependency-management) section.

## GraalPy Gradle Plugin

Expand Down Expand Up @@ -295,8 +294,7 @@ To customize the lock file path, configure _graalPyLockFile_:

> **Note:** This only changes the path (defaults to _$rootDir/graalpy.lock_). To generate the lock file, run the `graalPyLockPackages` task.

For more information of this feature, please see the
[Python Dependency Management for Reproducible Builds](#python-dependency-management-for-reproducible-builds) section.
For more information about dependency locking, see the [Dependency Management](#dependency-management) section.

### Related Documentation

Expand Down
117 changes: 81 additions & 36 deletions docs/user/Embedding-Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,45 +118,90 @@ If you prefer Gradle, here is how to set up a new project with GraalPy embedding

> **Note**: GraalPy's performance depends on the JDK you are using. For optimal performance, see the [Runtime Optimization Support](https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support) guide.

### Adding Python Dependencies
## Adding Python Dependencies

To use third-party Python packages like NumPy or Requests in your embedded application:

1. Add the GraalPy Gradle plugin and configure dependencies in _app/build.gradle_:

```gradle
plugins {
id "java"
id "application"
id "org.graalvm.python" version "25.0.3"
}

graalPy {
packages = ["termcolor==2.2"]
}
```

2. Update your Java code to use the Python package:

```java
package interop;

import org.graalvm.polyglot.*;
import org.graalvm.python.embedding.GraalPyResources;

class App {
public static void main(String[] args) {
try (Context context = GraalPyResources.contextBuilder().build()) {
String src = """
from termcolor import colored
colored_text = colored("hello java", "red", attrs=["reverse", "blink"])
print(colored_text)
""";
context.eval("python", src);
}
}
}
```
Configure the GraalPy Maven or Gradle plugin with the packages your application needs.
The plugin installs packages into a managed virtual environment, and `GraalPyResources` configures the Python context to import from it.

### Maven

Add the Python embedding dependency and GraalPy Maven plugin configuration to your _pom.xml_ file:

```xml
<dependencies>
<dependency>
<groupId>org.graalvm.python</groupId>
<artifactId>python-embedding</artifactId>
<version>25.0.3</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.graalvm.python</groupId>
<artifactId>graalpy-maven-plugin</artifactId>
<version>25.0.3</version>
<executions>
<execution>
<configuration>
<packages>
<package>termcolor==2.2</package>
</packages>
</configuration>
<goals>
<goal>process-graalpy-resources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```

### Gradle

Add the GraalPy Gradle plugin and configure dependencies in _app/build.gradle_:

```gradle
plugins {
id "java"
id "application"
id "org.graalvm.python" version "25.0.3"
}

dependencies {
implementation("org.graalvm.python:python-embedding:25.0.3")
}

graalPy {
packages = ["termcolor==2.2"]
}
```

Then use the Python package from Java:

```java
package interop;

import org.graalvm.polyglot.*;
import org.graalvm.python.embedding.GraalPyResources;

class App {
public static void main(String[] args) {
try (Context context = GraalPyResources.contextBuilder().build()) {
String src = """
from termcolor import colored
colored_text = colored("hello java", "red", attrs=["reverse", "blink"])
print(colored_text)
""";
context.eval("python", src);
}
}
}
```

For complete plugin configuration options, deployment strategies, and dependency management, see [Embedding Build Tools](Embedding-Build-Tools.md).

Expand Down
2 changes: 1 addition & 1 deletion docs/user/Native-Images-with-Python.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ GraalPy supports GraalVM Native Image to generate native binaries of Java applic

## Building Executables with Python

If you started with the [Maven archetype](Embedding-Getting-Started.md#maven), the generated _pom.xml_ file already includes the necessary configuration for creating a native executable using the [Maven plugin for Native Image building](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html).
If you started with the [Maven archetype](Embedding-Getting-Started.md#maven-quick-start), the generated _pom.xml_ file already includes the necessary configuration for creating a native executable using the [Maven plugin for Native Image building](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html).

To build the application, run:

Expand Down
7 changes: 0 additions & 7 deletions docs/user/Standalone-Getting-Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,6 @@ pyenv shell graalpy-25.0.3

#### Known Windows Limitations

- JLine treats Windows as a dumb terminal (no autocomplete, limited REPL editing)
- Interactive `help()` in REPL doesn't work
- Virtual environment issues:
- `graalpy.cmd` and `graalpy.exe` are broken inside `venv`
- `pip.exe` cannot be used directly
- Use `myvenv/Scripts/python.exe -m pip --no-cache-dir install <pkg>`
- Only pure Python binary wheels supported
- PowerShell works better than CMD

## Using Virtual Environments
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -70,7 +70,6 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -485,15 +484,14 @@ public Object execute(Value... arguments) {
}
}

@Ignore // blocked by GR-46281
@Test
public void runAForeignExecutable() throws IOException {
Source suitePy = Source.newBuilder("python",
"""
def foo(obj):
try:
obj()
except TypeError as e:
except:
pass
else:
assert False
Expand All @@ -504,15 +502,14 @@ def foo(obj):
foo.execute(new AForeignExecutable());
}

@Ignore // blocked by GR-46281
@Test
public void invokeAForeignMember() throws IOException {
Source suitePy = Source.newBuilder("python",
"""
def foo(obj):
try:
obj.fun()
except TypeError as e:
except:
pass
else:
assert False
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -41,6 +41,7 @@
package com.oracle.graal.python.test.interop;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

Expand Down Expand Up @@ -77,6 +78,35 @@ private Value run(String evalString) {
return context.eval("python", evalString);
}

@Test
public void testLazyModuleMemberInvoke() {
Value module = run("""
import types

class LazyModule(types.ModuleType):
def __getattr__(self, name):
if name == "set_seed":
def set_seed(seed):
self.seed = seed
return seed + 1
self.__dict__[name] = set_seed
return set_seed
raise AttributeError(name)

direct = LazyModule("direct")
probe = LazyModule("probe")
""");

Value direct = module.getMember("direct");
assertEquals(44, direct.invokeMember("set_seed", 43).asInt());
assertEquals(43, direct.getMember("seed").asInt());

Value probe = module.getMember("probe");
assertTrue(probe.hasMember("set_seed"));
assertFalse(probe.hasMember("missing"));
assertEquals(12, probe.invokeMember("set_seed", 11).asInt());
}

@Test
public void testPositionalArgs01() {
// @formatter:off
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ def test_waitpid(self):
p.kill()
p.wait()

@unittest.skipIf(sys.platform == 'win32' or POSIX_BACKEND_IS_JAVA or not hasattr(os, 'getpgid'), "Posix-specific")
def test_process_group_0(self):
p = subprocess.Popen([sys.executable, "-c", "import os; print(os.getpgid(0))"],
stdout=subprocess.PIPE, process_group=0)
stdout, _ = p.communicate(timeout=60)
self.assertEqual(p.returncode, 0)
self.assertEqual(int(stdout), p.pid)

def _run_in_new_group(self, code):
def safe_decode(x):
return '' if not x else x.decode().strip()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ test.test_subprocess.POSIXProcessTestCase.test_group_error @ darwin-arm64,linux-
test.test_subprocess.POSIXProcessTestCase.test_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_kill @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_kill_dead @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_process_group_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_remapping_std_fds @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_select_unbuffered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
test.test_subprocess.POSIXProcessTestCase.test_send_signal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo
gil.release(true);
try {
return posixLib.forkExec(context.getPosixSupport(), executables, processArgs, cwd, env == null ? null : (Object[]) env, stdinRead, stdinWrite, stdoutRead, stdoutWrite, stderrRead,
stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork);
} catch (PosixException e) {
gil.acquire();
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);
Expand Down
Loading
Loading