From d1f197ba1a26c40e61583b820d1388730291030b Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Tue, 1 Oct 2024 10:53:09 +0200 Subject: [PATCH] Add GraalPy JBang QRCode Demo. --- .github/workflows/graalpy-jbang-qrcode.yml | 29 ++++++++++++ graalpy/README.md | 1 + graalpy/graalpy-jbang-qrcode/README.md | 35 ++++++++++++++ graalpy/graalpy-jbang-qrcode/qrcode.java | 55 ++++++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 .github/workflows/graalpy-jbang-qrcode.yml create mode 100644 graalpy/graalpy-jbang-qrcode/README.md create mode 100644 graalpy/graalpy-jbang-qrcode/qrcode.java diff --git a/.github/workflows/graalpy-jbang-qrcode.yml b/.github/workflows/graalpy-jbang-qrcode.yml new file mode 100644 index 0000000..14bab11 --- /dev/null +++ b/.github/workflows/graalpy-jbang-qrcode.yml @@ -0,0 +1,29 @@ +name: Test GraalPy JBang QRCode Demo +on: + push: + paths: + - 'graalpy/graalpy-jbang-qrcode/**' + - '.github/workflows/graalpy-jbang-qrcode.yml' + pull_request: + paths: + - 'graalpy/graalpy-jbang-qrcode/**' + - '.github/workflows/graalpy-jbang-qrcode.yml' + workflow_dispatch: +permissions: + contents: read +jobs: + run: + name: 'graalpy-jbang-qrcode' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: graalvm/setup-graalvm@v1 + with: + java-version: '23.0.0' + distribution: 'graalvm' + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Run 'graalpy-jbang-qrcode' demo + run: | + cd graalpy/graalpy-jbang-qrcode + curl -Ls https://sh.jbang.dev | bash -s - run qrcode.java "Hello from GraalPy!" diff --git a/graalpy/README.md b/graalpy/README.md index ac3c726..9750ea1 100644 --- a/graalpy/README.md +++ b/graalpy/README.md @@ -8,6 +8,7 @@ This directory contains demo applications and guides for [GraalPy](https://www.g - [Minimal Java application that embeds GraalPy](graalpy-starter/README.md) - [Minimal Java application that embeds `openai` Python package with GraalPy](graalpy-openai-starter/README.md) +- [Embed `qrcode` Python package with GraalPy in JBang](graalpy-jbang-qrcode/README.md) ## Guides diff --git a/graalpy/graalpy-jbang-qrcode/README.md b/graalpy/graalpy-jbang-qrcode/README.md new file mode 100644 index 0000000..1e3b923 --- /dev/null +++ b/graalpy/graalpy-jbang-qrcode/README.md @@ -0,0 +1,35 @@ +# GraalPy JBang QRCode Demo + +This demo illustrates how GraalPy can be used to embed the [`qrcode` Python package](https://pypi.org/project/qrcode/) in a script that runs on [JBang](https://www.jbang.dev/). + +## Preparation + +Install GraalVM for JDK 23 and set the value of `JAVA_HOME` accordingly. +We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).) + +```bash +sdk install java 23-graal +``` + +Afterward, [install `jbang`](https://www.jbang.dev/download/), for with: + +```bash +sdk install jbang +``` + +## Run the Application + +To start the demo, run: + +```bash +jbang run QRCodeMaker.java "Hello from GraalPy!" +``` + +## Implementation Details + +[qrcode.java](qrcode.java) defines required dependencies on GraalPy, which also allows to depend on Python packages. +`//PIP qrcode==7.4.2` defines the dependency on the `qrcode` Python package. +The `main()` method checks the arguments and then creates a new `Context`. +It then fetches the `qrcode` Python module and maps it to a `QRCode` Java interface. +Afterward, it uses the `qrcode` module as illustrated in [this example](https://github.com/lincolnloop/python-qrcode/tree/v7.4.2?tab=readme-ov-file#examples) through Java interfaces. +Finally, it prints the result to `System.out`. diff --git a/graalpy/graalpy-jbang-qrcode/qrcode.java b/graalpy/graalpy-jbang-qrcode/qrcode.java new file mode 100644 index 0000000..603bf80 --- /dev/null +++ b/graalpy/graalpy-jbang-qrcode/qrcode.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/license/UPL. + */ + +//DEPS org.graalvm.python:python-language:24.1.0 +//DEPS org.graalvm.python:python-launcher:24.1.0 +//DEPS org.graalvm.python:python-resources:24.1.0 +//DEPS org.graalvm.python:python-embedding:24.1.0 +//DEPS org.graalvm.python:python-embedding-tools:24.1.0 +//PIP qrcode==7.4.2 + +import org.graalvm.python.embedding.utils.GraalPyResources; + +public class qrcode { + + public static void main(String[] args) { + if (args.length != 1 || args[0].equals("-h") || args[0].equals("--help")) { + System.out.println("This tool takes only a single argument, the QR code data."); + return; + } + try (var context = GraalPyResources.contextBuilder().option("python.PythonHome", "").build()) { + QRCodeModule qrcodeModule = context.eval("python", "import qrcode; qrcode").as(QRCodeModule.class); + IO io = context.eval("python", "import io; io").as(IO.class); + + QRCode qr = qrcodeModule.QRCode(); + qr.add_data(args[0]); + + StringIO f = io.StringIO(); + qr.print_ascii(f); + String qrCodeString = f.getvalue(); + + System.out.println(qrCodeString); + } + } + + public interface QRCodeModule { + QRCode QRCode(); + } + + public interface QRCode { + void add_data(String text); + + void print_ascii(StringIO out); + } + + public interface IO { + StringIO StringIO(); + } + + public interface StringIO { + String getvalue(); + } +} \ No newline at end of file