Skip to content

Commit 7dc0a58

Browse files
committed
Add docs about native extensions limitations
1 parent 2ba1f54 commit 7dc0a58

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

docs/user/Embedding-Permissions.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ The Java backend is the default when GraalPy is run via the `Context` API, that
5656
GraalPy can log information about known incompatibility of functions executed at runtime, which includes the OS interface-related functions.
5757
To turn on this logging, use the command-line option `--log.python.compatibility.level=FINE` (or other desired logging level).
5858

59-
Known limitations of the of the Java backend are:
59+
Known limitations of the Java backend are:
6060

6161
* Its state is disconnected from the actual OS state, which applies especially to:
6262
* *file descriptors*: Python-level file descriptors are not usable in native code.
@@ -73,4 +73,11 @@ Known limitations of the of the Java backend are:
7373

7474
## Python Native Extensions
7575

76-
Python native extensions run by default as native binaries, with full access to the underlying system.
76+
Python native extensions run by default as native binaries, with full access to the underlying system. See [Embedding limitations](Native-Extensions.md#embedding-limitations)
77+
78+
The context permissions needed to run native extensions are:
79+
```
80+
.allowIO(IOAccess.ALL)
81+
.allowCreateThread(true)
82+
.allowNativeAccess(true)
83+
```

docs/user/Native-Extensions.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
layout: docs-experimental
3+
toc_group: python
4+
link_title: Native Extensions Support
5+
permalink: /reference-manual/python/Native-Extensions/
6+
---
7+
8+
# Native Extensions Support
9+
10+
Python provides a native extensions API for writing Python extensions in C/C++. GraalPy's support for native extensions
11+
is currently considered experimental, although many packages like NumPy and PyTorch work well for many use cases.
12+
Native extensions built for CPython are not binary compatible with GraalPy, therefore it is not possible to use packages
13+
installed with CPython from GraalPy. Packages have to be installed with GraalPy. Likewise, prebuilt wheels for CPython
14+
from pypi.org cannot be used with GraalPy.
15+
The version of *pip* shipped with GraalPy applies additional patches to packages upon installation to make native
16+
extensions work, it is therefore crucial that you only use the *pip* preinstalled in GraalPy virtualenvs to install
17+
packages. Don't update *pip* or use alternative tools such as *uv*. GraalPy's *pip* is also preconfigured to use an
18+
extra repository from graalvm.org where we plan to publish prebuilt wheels for GraalPy for selected commonly used
19+
packages.
20+
21+
## Embedding limitations
22+
23+
Python native extensions run by default as native binaries, with full access to the underlying system.
24+
Native code is not sandboxed and can circumvent any protections Truffle or the JVM may provide, up to and including
25+
aborting the process.
26+
Native data structures are not subject to the Java GC and the combination of them with Java data structures may lead to
27+
memory leaks.
28+
Native libraries generally cannot be loaded multiple times into the same process, and they may contain global state that
29+
cannot be safely reset. Thus, it's not possible to create multiple GraalPy contexts that access native modules within
30+
the same JVM. This includes the case when you create a context, close it and then create another context. The second
31+
context will not be able to access native extensions.

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.io.IOException;
5555
import java.nio.file.LinkOption;
5656
import java.util.Set;
57+
import java.util.logging.Level;
5758

5859
import org.graalvm.collections.Pair;
5960
import org.graalvm.shadowed.com.ibm.icu.impl.Punycode;
@@ -304,15 +305,16 @@ private static String dlopenFlagsToString(int flags) {
304305
@TruffleBoundary
305306
public static Object loadCExtModule(Node location, PythonContext context, ModuleSpec spec, CheckFunctionResultNode checkFunctionResultNode)
306307
throws IOException, ApiInitException, ImportException {
307-
308-
if (context.getOption(PythonOptions.WarnExperimentalFeatures) && !C_EXT_SUPPORTED_LIST.contains(spec.name.toJavaStringUncached())) {
309-
getLogger().warning(() -> {
308+
if (getLogger().isLoggable(Level.WARNING) && context.getOption(PythonOptions.WarnExperimentalFeatures)) {
309+
boolean runViaLauncher = context.getOption(PythonOptions.RunViaLauncher);
310+
if (!runViaLauncher || !C_EXT_SUPPORTED_LIST.contains(spec.name.toJavaStringUncached())) {
310311
String message = "Loading C extension module %s from '%s'. Support for the Python C API is considered experimental.";
311-
if (!context.getOption(PythonOptions.RunViaLauncher)) {
312-
message += " You can suppress this warning by setting the context option 'python.WarnExperimentalFeatures' to 'false'";
312+
if (!runViaLauncher) {
313+
message += " See https://www.graalvm.org/latest/reference-manual/python/Native-Extensions/#embedding-limitations for the limitations. " +
314+
"You can suppress this warning by setting the context option 'python.WarnExperimentalFeatures' to 'false'";
313315
}
314-
return message.formatted(spec.name, spec.path);
315-
});
316+
getLogger().warning(message.formatted(spec.name, spec.path));
317+
}
316318
}
317319

318320
// we always need to load the CPython C API

0 commit comments

Comments
 (0)