Quarkus extension that embeds GraalPy in Java apps. Exposes a managed Context, a fluent API, and declarative Java-to-Python bindings generated at build time.
Based on quarkusio/quarkus#56029.
- Managed
GraalPyCDI bean (@ApplicationScoped), lifecycle tied to the app. src/main/python/is auto-packaged into the runtime classpath and added tosys.path.@GraalPyModuleBinding("mymodule")on a Java interface: a CDI implementation is generated at build time (Jandex + Gizmo) that routes calls to Python functions.- Fluent API:
graalPy.module("x").function("y").invoke(args).as(Type.class). - Build-time validation (
quarkus.graalpy.validate-bindings=true): fails the build if a@GraalPyModuleBindingpoints at a missing module or function. - Dev Mode: edits under
src/main/python/trigger a reload. - Test Python sources under
src/test/python/, loaded only intestmode.
runtime/: public API (@GraalPyModuleBinding,@GraalPyFunction,GraalPybean,GraalPyConfig).deployment/: build-time processor (discovery, Gizmo codegen, source packaging, dev-mode watching, validation).integration-tests/: in-process tests plus a REST endpoint sample.docs/: Antora docs (index.adoc).
- JDK 17+
- Maven 3.9+
- Quarkus 3.27.0
- GraalPy 25.0.0
mvn clean installAdd the extension to your pom.xml:
<dependency>
<groupId>io.quarkiverse.graalpy</groupId>
<artifactId>quarkus-graalpy</artifactId>
<version>999-SNAPSHOT</version>
</dependency>Drop a Python module under src/main/python/:
# src/main/python/classifier.py
from types import SimpleNamespace
def predict(features):
total = sum(features)
label = "positive" if total >= 0 else "negative"
return SimpleNamespace(label=label, confidence=min(1.0, abs(total)))Declare a typed binding:
@GraalPyModuleBinding("classifier")
public interface Classifier {
Prediction predict(float[] features);
}Inject it like any CDI bean:
@Inject
Classifier classifier;
Prediction p = classifier.predict(new float[]{0.2f, -0.1f, 0.9f});Or use the programmatic API:
@Inject
GraalPy graalPy;
Prediction p = graalPy.module("classifier")
.function("predict")
.invoke(features)
.as(Prediction.class);A full runnable sample lives in integration-tests/.
Recommended for dev:
%dev.quarkus.graalpy.validate-bindings=trueTo load Python sources from a directory outside the project (on top of packaged ones):
%dev.quarkus.graalpy.source-path=/home/me/python-scriptsFull docs live under docs/ as an Antora module. Start from docs/modules/ROOT/pages/index.adoc.
Topics:
- Getting started
- Programmatic API
- Declarative bindings (
@GraalPyModuleBinding,@GraalPyFunction) - Build-time validation
- Dev Mode
- Test Python sources
- Configuration reference
- Testing with
@QuarkusTest - Current limitations
Items from the upstream proposal not yet implemented:
- Native image support (plus docs on C extension / wheel limitations).
- Full integration with
python-embeddingfor PyPI deps (requirements.txt, venv viagraalpy-maven-plugin). - Typed
GraalPyExceptionhierarchy for import/execution failures (currentlyPolyglotException). - Extended type conversions beyond GraalPy's default
Value.as(Class)(e.g.BigDecimal,LocalDate, custom collection generics).
PRs welcome. See the upstream proposal for the full roadmap.
