diff --git a/docs/user/Embedding-Build-Tools.md b/docs/user/Embedding-Build-Tools.md
index 30e6f5c01a..0526fb473b 100644
--- a/docs/user/Embedding-Build-Tools.md
+++ b/docs/user/Embedding-Build-Tools.md
@@ -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
@@ -229,10 +229,9 @@ To customize the lock file path, configure _graalPyLockFile_ :
```
-> **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
@@ -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
diff --git a/docs/user/Embedding-Getting-Started.md b/docs/user/Embedding-Getting-Started.md
index 7ce3c82c37..e97c96ec07 100644
--- a/docs/user/Embedding-Getting-Started.md
+++ b/docs/user/Embedding-Getting-Started.md
@@ -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
+
+
+ org.graalvm.python
+ python-embedding
+ 25.0.3
+
+
+
+
+
+
+ org.graalvm.python
+ graalpy-maven-plugin
+ 25.0.3
+
+
+
+
+ termcolor==2.2
+
+
+
+ process-graalpy-resources
+
+
+
+
+
+
+```
+
+### 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).
diff --git a/docs/user/Native-Images-with-Python.md b/docs/user/Native-Images-with-Python.md
index d897c6dfbe..2abd76988c 100644
--- a/docs/user/Native-Images-with-Python.md
+++ b/docs/user/Native-Images-with-Python.md
@@ -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:
diff --git a/docs/user/Standalone-Getting-Started.md b/docs/user/Standalone-Getting-Started.md
index 20a5f320f1..a448fd9c19 100644
--- a/docs/user/Standalone-Getting-Started.md
+++ b/docs/user/Standalone-Getting-Started.md
@@ -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 `
- - Only pure Python binary wheels supported
- PowerShell works better than CMD
## Using Virtual Environments
diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java
index a78d825b0f..7958946fe6 100644
--- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java
+++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java
@@ -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
@@ -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;
@@ -485,7 +484,6 @@ public Object execute(Value... arguments) {
}
}
- @Ignore // blocked by GR-46281
@Test
public void runAForeignExecutable() throws IOException {
Source suitePy = Source.newBuilder("python",
@@ -493,7 +491,7 @@ public void runAForeignExecutable() throws IOException {
def foo(obj):
try:
obj()
- except TypeError as e:
+ except:
pass
else:
assert False
@@ -504,7 +502,6 @@ def foo(obj):
foo.execute(new AForeignExecutable());
}
- @Ignore // blocked by GR-46281
@Test
public void invokeAForeignMember() throws IOException {
Source suitePy = Source.newBuilder("python",
@@ -512,7 +509,7 @@ public void invokeAForeignMember() throws IOException {
def foo(obj):
try:
obj.fun()
- except TypeError as e:
+ except:
pass
else:
assert False
diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java
index b81ccd74d7..fefe2f9209 100644
--- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java
+++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java
@@ -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
@@ -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;
@@ -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
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py b/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py
index 0b9b89f51e..80d7f523a6 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py
@@ -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()
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt
index d6598dac7b..475953d465 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt
+++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt
@@ -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
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java
index 5a0585e80f..3092560d74 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java
@@ -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);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java
index 5a04dbef56..ee391fe12e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -40,7 +40,6 @@
*/
package com.oracle.graal.python.builtins.objects;
-import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
@@ -48,7 +47,6 @@
import static com.oracle.graal.python.nodes.ErrorMessages.S_MUST_BE_A_S_TUPLE;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T_KEYS;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DELETE__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTRIBUTE__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GET__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SET__;
import static com.oracle.graal.python.nodes.StringLiterals.T_LBRACKET;
@@ -125,7 +123,6 @@
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
import com.oracle.graal.python.nodes.call.CallNode;
-import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
import com.oracle.graal.python.nodes.expression.CastToListExpressionNode.CastToListInteropNode;
import com.oracle.graal.python.nodes.interop.GetInteropBehaviorNode;
import com.oracle.graal.python.nodes.interop.GetInteropBehaviorValueNode;
@@ -606,29 +603,15 @@ public boolean hasMemberWriteSideEffects(String member,
public Object invokeMember(String member, Object[] arguments,
@Bind Node inliningTarget,
@Exclusive @Cached TruffleString.FromJavaStringNode fromJavaStringNode,
- @Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupGetattributeNode,
- @Exclusive @Cached CallBinaryMethodNode callGetattributeNode,
+ @Exclusive @Cached PyObjectLookupAttr lookup,
@Exclusive @Cached PExecuteNode executeNode,
- @Exclusive @Cached InlinedConditionProfile profileGetattribute,
@Exclusive @Cached InlinedConditionProfile profileMember,
- // GR-44020: make shared:
- @Exclusive @Cached IsBuiltinObjectProfile attributeErrorProfile,
@Exclusive @Cached GilNode gil)
throws UnknownIdentifierException, UnsupportedMessageException {
boolean mustRelease = gil.acquire();
try {
- Object memberObj;
- try {
- Object attrGetattribute = lookupGetattributeNode.execute(inliningTarget, this, T___GETATTRIBUTE__);
- if (profileGetattribute.profile(inliningTarget, attrGetattribute == PNone.NO_VALUE)) {
- throw UnknownIdentifierException.create(member);
- }
- memberObj = callGetattributeNode.executeObject(attrGetattribute, this, fromJavaStringNode.execute(member, TS_ENCODING));
- if (profileMember.profile(inliningTarget, memberObj == PNone.NO_VALUE)) {
- throw UnknownIdentifierException.create(member);
- }
- } catch (PException e) {
- e.expect(inliningTarget, AttributeError, attributeErrorProfile);
+ Object memberObj = lookup.execute(null, inliningTarget, this, fromJavaStringNode.execute(member, TS_ENCODING));
+ if (profileMember.profile(inliningTarget, memberObj == PNone.NO_VALUE)) {
throw UnknownIdentifierException.create(member);
}
return executeNode.execute(memberObj, arguments);
@@ -1166,6 +1149,7 @@ static boolean access(Object object, TruffleString attrKeyName, int type,
@Cached GetClassNode getClassNode,
@Cached IsImmutable isImmutable,
@Cached GetMroNode getMroNode,
+ @Cached PyObjectLookupAttr lookupAttr,
@Cached GilNode gil) {
boolean mustRelease = gil.acquire();
try {
@@ -1185,10 +1169,14 @@ static boolean access(Object object, TruffleString attrKeyName, int type,
if (attr == PNone.NO_VALUE) {
attr = readObjectAttrNode.execute(owner, attrKeyName);
}
+ Object dynamicAttr = PNone.NO_VALUE;
+ if (attr == PNone.NO_VALUE && (type == READABLE || type == INVOCABLE)) {
+ dynamicAttr = lookupAttr.execute(null, inliningTarget, object, attrKeyName);
+ }
switch (type) {
case READABLE:
- return attr != PNone.NO_VALUE;
+ return attr != PNone.NO_VALUE || dynamicAttr != PNone.NO_VALUE;
case INSERTABLE:
return attr == PNone.NO_VALUE && !isImmutable.execute(inliningTarget, object);
case REMOVABLE:
@@ -1222,6 +1210,9 @@ static boolean access(Object object, TruffleString attrKeyName, int type,
}
return callableCheck.execute(inliningTarget, attr);
}
+ if (dynamicAttr != PNone.NO_VALUE) {
+ return callableCheck.execute(inliningTarget, dynamicAttr);
+ }
return false;
case READ_SIDE_EFFECTS:
if (attr != PNone.NO_VALUE && owner != object && !(attr instanceof PFunction || attr instanceof PBuiltinFunction)) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java
index a4df98074a..1e1b09297d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java
@@ -2445,11 +2445,14 @@ public void unsetenv(Object name) {
@ExportMessage
@TruffleBoundary
public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
- int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
+ int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork,
@Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) throws PosixException {
if (PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS) {
throw new UnsupportedPosixFeatureException("forkExec was excluded");
}
+ if (pgidToSet >= 0) {
+ throw createUnsupportedFeature("process_group in fork_exec");
+ }
// TODO there are a few arguments we ignore, we should throw an exception or report a
// compatibility warning
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java
index 5b469bf5d3..396f647f60 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java
@@ -1114,13 +1114,14 @@ public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length,
@ExportMessage
final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
- int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
+ int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork,
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
- logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %s, %b", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd,
- stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
+ logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %d, %s, %b", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd,
+ stderrReadFd,
+ stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork);
try {
return logExit("forkExec", "%d", lib.forkExec(delegate, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd,
- errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork));
+ errPipeWriteFd, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork));
} catch (PosixException e) {
throw logException("forkExec", e);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java
index 28dba24183..3c87508303 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java
@@ -442,10 +442,10 @@ abstract static class PosixNativeFunctionInvoker {
@DowncallSignature(returnType = SINT32, argumentTypes = {
POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32,
- SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64
+ SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64
})
abstract int fork_exec(long data, long offsets, int offsetsLen, int argsPos, int envPos, int cwdPos, int stdinRdFd, int stdinWrFd, int stdoutRdFd, int stdoutWrFd, int stderrRdFd,
- int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, int allowVFork, long fdsToKeep, long fdsToKeepLen);
+ int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, int pgidToSet, int allowVFork, long fdsToKeep, long fdsToKeepLen);
@DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER, SINT32})
abstract void call_execv(long data, long offsets, int offsetsLen);
@@ -1845,7 +1845,7 @@ public void unsetenv(Object name) throws PosixException {
@ExportMessage
public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
- int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork) throws PosixException {
+ int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork) throws PosixException {
// The following strings and string arrays need to be present in the native function:
// - char** of executable names ('\0'-terminated strings with an extra NULL at the end)
@@ -1943,6 +1943,7 @@ public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] en
closeFds ? 1 : 0,
restoreSignals ? 1 : 0,
callSetsid ? 1 : 0,
+ pgidToSet,
allowVFork ? 1 : 0,
nativeFdsToKeep, fdsToKeep.length);
if (res == -1) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java
index 9c671d0757..728dbf8770 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java
@@ -355,7 +355,7 @@ public record OpenPtyResult(int masterFd, int slaveFd) {
public abstract void unsetenv(Object receiver, Object name) throws PosixException;
public abstract int forkExec(Object receiver, Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd,
- int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep,
+ int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep,
boolean allowVFork) throws PosixException;
// args.length must be > 0
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java
index ad34385ab3..8df18530a9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java
@@ -807,11 +807,11 @@ final void unsetenv(Object name,
@ExportMessage
final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
- int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
+ int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork,
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
checkNotInPreInitialization();
return nativeLib.forkExec(nativePosixSupport, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd, errPipeWriteFd,
- closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
+ closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork);
}
@ExportMessage
diff --git a/graalpython/lib-graalpython/modules/standalone/__main__.py b/graalpython/lib-graalpython/modules/standalone/__main__.py
index ea91502e34..b5951d505d 100644
--- a/graalpython/lib-graalpython/modules/standalone/__main__.py
+++ b/graalpython/lib-graalpython/modules/standalone/__main__.py
@@ -79,7 +79,7 @@
NATIVE_EXEC_LAUNCHER_FILE = f"{NATIVE_EXEC_LAUNCHER}.java"
NATIVE_EXEC_LAUNCHER_TEMPLATE_PATH = f"resources/{NATIVE_EXEC_LAUNCHER_FILE}"
-NATIVE_IMAGE_RESOURCES_FILE = "native-image-resources.json"
+NATIVE_IMAGE_RESOURCES_FILE = "resource-config.json"
NATIVE_IMAGE_RESOURCES_PATH = f"resources/{NATIVE_IMAGE_RESOURCES_FILE}"
GRAALVM_URL_BASE = "https://download.oracle.com/graalvm/"
@@ -436,9 +436,8 @@ def build_binary(target_dir, ni, jc, modules_path, legacy_embedding_path, launch
]
cmd += [
"--no-fallback",
- "-H:+UnlockExperimentalVMOptions",
"-H:-CopyLanguageResources",
- f"-H:ResourceConfigurationFiles={target_dir}/{NATIVE_IMAGE_RESOURCES_FILE}",
+ f"-H:ConfigurationFileDirectories={target_dir}",
"-o",
output,
f"{NATIVE_EXEC_LAUNCHER}",
diff --git a/graalpython/lib-graalpython/modules/standalone/resources/native-image-resources.json b/graalpython/lib-graalpython/modules/standalone/resources/resource-config.json
similarity index 100%
rename from graalpython/lib-graalpython/modules/standalone/resources/native-image-resources.json
rename to graalpython/lib-graalpython/modules/standalone/resources/resource-config.json
diff --git a/graalpython/python-libposix/src/fork_exec.c b/graalpython/python-libposix/src/fork_exec.c
index 51cde1464a..476f72c987 100644
--- a/graalpython/python-libposix/src/fork_exec.c
+++ b/graalpython/python-libposix/src/fork_exec.c
@@ -357,6 +357,7 @@ child_exec(char *const exec_array[],
int errpipe_read, int errpipe_write,
int close_fds, int restore_signals,
int call_setsid,
+ int pgid_to_set,
const void *child_sigmask,
int *fds_to_keep,
ssize_t fds_to_keep_len)
@@ -446,6 +447,9 @@ child_exec(char *const exec_array[],
POSIX_CALL(setsid());
#endif
+ if (pgid_to_set >= 0)
+ POSIX_CALL(setpgid(0, pgid_to_set));
+
err_msg = "";
/* close FDs after executing preexec_fn, which might open FDs */
@@ -508,6 +512,7 @@ do_fork_exec(char *const exec_list[],
int errPipeRdFd, int errPipeWrFd,
int closeFds, int restoreSignals,
int callSetsid,
+ int pgidToSet,
const void *childSigmask,
int *fdsToKeep, int64_t fdsToKeepLen)
{
@@ -534,6 +539,7 @@ do_fork_exec(char *const exec_list[],
closeFds,
restoreSignals,
callSetsid,
+ pgidToSet,
childSigmask,
fdsToKeep, fdsToKeepLen
);
@@ -558,6 +564,7 @@ do_fork_exec(char *const exec_list[],
* (if nonzero, then errPipeWrFd must be in fdsToKeep)
* restoreSignals - currently not used
* callSetsid - if nonzero, the child calls setsid before exec()
+ * pgidToSet - if non-negative, the child calls setpgid(0, pgidToSet) before exec()
* allowVFork - if nonzero, use vfork() instead of fork() where it is safe and supported
* fdsToKeep, fdsToKeepLen - a sorted list of fds to keep open (the child clears their O_CLOEXEC)
*/
@@ -570,6 +577,7 @@ int32_t fork_exec(
int32_t closeFds,
int32_t restoreSignals,
int32_t callSetsid,
+ int32_t pgidToSet,
int32_t allowVFork,
int32_t *fdsToKeep, int64_t fdsToKeepLen
) {
@@ -588,7 +596,8 @@ int32_t fork_exec(
#ifdef VFORK_USABLE
const void *oldSigmask = NULL;
sigset_t oldSigs;
- if (allowVFork) {
+ /* Keep process-group setup on the normal fork path. */
+ if (allowVFork && pgidToSet < 0) {
sigset_t allSigs;
sigfillset(&allSigs);
int err = pthread_sigmask(SIG_BLOCK, &allSigs, &oldSigs);
@@ -611,6 +620,7 @@ int32_t fork_exec(
closeFds,
restoreSignals,
callSetsid,
+ pgidToSet,
oldSigmask,
fdsToKeep, fdsToKeepLen
);