From afa111bb432a1abf1b811ccfc8c1c0c413f0bf0a Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Mon, 18 Nov 2024 17:09:49 +0100 Subject: [PATCH 1/7] Hand over control back to python for SIGTERM signals as well --- native/common/jp_context.cpp | 7 +++++++ native/java/org/jpype/JPypeSignal.java | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/native/common/jp_context.cpp b/native/common/jp_context.cpp index d6e25cb2f..18719b877 100644 --- a/native/common/jp_context.cpp +++ b/native/common/jp_context.cpp @@ -452,6 +452,13 @@ extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPy PyErr_SetInterrupt(); } +extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPyTerm +(JNIEnv *env, jclass cls) +{ + interruptState = 1; + PyErr_SetInterruptEx(15); +} + extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_acknowledgePy (JNIEnv *env, jclass cls) { diff --git a/native/java/org/jpype/JPypeSignal.java b/native/java/org/jpype/JPypeSignal.java index be8f1d476..12fc60140 100644 --- a/native/java/org/jpype/JPypeSignal.java +++ b/native/java/org/jpype/JPypeSignal.java @@ -53,8 +53,23 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return null; } }); + Object termHandler = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] + { + SignalHandler + }, new InvocationHandler() + { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + main.interrupt(); + interruptPyTerm(); + return null; + } + }); Object intr = Signal.getDeclaredConstructor(String.class).newInstance("INT"); method.invoke(null, intr, handler); + Object intrTerm = Signal.getDeclaredConstructor(String.class).newInstance("TERM"); + method.invoke(null, intrTerm, termHandler); } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | SecurityException ex) { // If we don't get the signal handler run without it. (ANDROID) @@ -62,5 +77,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } native static void interruptPy(); + native static void interruptPyTerm(); native static void acknowledgePy(); } From 46e9897fc2475d66b77ef8956b8b19f24db3d938 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 14:31:06 +0100 Subject: [PATCH 2/7] Add .venv to gitignore, add github actions wheel build --- .github/workflows/build.yml | 79 +++++++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 80 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..1101dc6c9 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,79 @@ +name: Build + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + + +jobs: + build_wheels: + runs-on: ubuntu-latest + strategy: + matrix: + # macos-13 is an intel runner, macos-14 is apple silicon + pyver: [ cp311, cp312 ] + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Used to host cibuildwheel + - name: Setup python + uses: actions/setup-python@v5 + + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Build wheels + uses: pypa/cibuildwheel@v2.21.3 + env: + # configure cibuildwheel to build native archs ('auto'), and some + # emulated ones + CIBW_BUILD: ${{matrix.pyver}}-* + CIBW_ARCHS_LINUX: auto aarch64 + CIBW_BEFORE_ALL_LINUX: > + yum install -y java-11-openjdk-devel + CIBW_SKIP: '*-musllinux_*' + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.pyver }} + path: ./wheelhouse/*.whl + + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build sdist + run: pipx run build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: cibw-sdist + path: dist/*.tar.gz + + upload_pypi: + needs: [build_wheels, build_sdist] + runs-on: ubuntu-latest + environment: pypi + # permissions: + # id-token: write + # if: github.event_name == 'release' && github.event.action == 'published' + # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this) + # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v4 + with: + # unpacks all CIBW artifacts into dist/ + pattern: cibw-* + path: dist + merge-multiple: true + - name: List artifacts + run: ls -lah dist/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 171de2920..9c07df97c 100755 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,4 @@ jacoco/ wheelhouse/ vc*.pdb *.class +.venv From 544b61e28757e537801759aff5f4726f1b76a965 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 14:46:43 +0100 Subject: [PATCH 3/7] rename and reversion package --- pyproject.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a4136135d..f718b4cc7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,15 +6,16 @@ build-backend = "setuptools.build_meta" [project] -name = "jpype1" -version = '1.5.2.dev0' +name = "jpype1-ext" +version = '0.0.1' authors = [ {name = "Steve Menard", email = "devilwolf@users.sourceforge.net"}, ] maintainers = [ {name = "Luis Nell", email = "cooperate@originell.org"}, + {name = "LocalStack Contributors", email = "info@localstack.cloud"}, ] -description = "A Python to Java bridge" +description = "A Python to Java bridge - ext" readme = "README.rst" requires-python = ">=3.8" license = {text = "License :: OSI Approved :: Apache Software License"} @@ -52,7 +53,7 @@ tests = [ [project.urls] -homepage = "https://github.com/jpype-project/jpype" +homepage = "https://github.com/localstack/jpype" [[tool.mypy.overrides]] From e919b98d68bb33aee963263746bb597352536752 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 14:56:49 +0100 Subject: [PATCH 4/7] Add fork information into README.rst --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 01618319f..d89685eef 100644 --- a/README.rst +++ b/README.rst @@ -7,6 +7,11 @@ JPype |implementation| |pyversions| |javaversions| |jvm| |platform| |license| +Why this fork for LocalStack? +-------------- +We forked the jpype project to add a SIGTERM signal handler to be used in `LocalStack ` +We plan on contributing the changes upstream, should they be accepted. + JPype is a Python module to provide full access to Java from within Python. It allows Python to make use of Java only libraries, exploring and visualization of Java structures, development and testing From eaa2a48ad0cf530901c5ae30891c464e8fe6a4c7 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 17:07:28 +0100 Subject: [PATCH 5/7] Make the signal handling code less duplicated --- native/common/jp_context.cpp | 11 ++---- native/java/org/jpype/JPypeSignal.java | 47 ++++++++------------------ pyproject.toml | 2 +- 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/native/common/jp_context.cpp b/native/common/jp_context.cpp index 18719b877..9d2f8a068 100644 --- a/native/common/jp_context.cpp +++ b/native/common/jp_context.cpp @@ -446,17 +446,10 @@ extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeContext_onShutdown static int interruptState = 0; extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPy -(JNIEnv *env, jclass cls) -{ - interruptState = 1; - PyErr_SetInterrupt(); -} - -extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPyTerm -(JNIEnv *env, jclass cls) +(JNIEnv *env, jclass cls, jint signal) { interruptState = 1; - PyErr_SetInterruptEx(15); + PyErr_SetInterruptEx((int) signal); } extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_acknowledgePy diff --git a/native/java/org/jpype/JPypeSignal.java b/native/java/org/jpype/JPypeSignal.java index 12fc60140..666336638 100644 --- a/native/java/org/jpype/JPypeSignal.java +++ b/native/java/org/jpype/JPypeSignal.java @@ -15,7 +15,6 @@ **************************************************************************** */ package org.jpype; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -31,6 +30,17 @@ public class JPypeSignal static Thread main; + static Object getSignalHandler(Class signalHandlerClazz, int signal) throws ClassNotFoundException { + return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] + { + signalHandlerClazz + }, (proxy, method, args) -> { + main.interrupt(); + interruptPy(signal); + return null; + }); + } + static void installHandlers() { try @@ -39,44 +49,17 @@ static void installHandlers() Class SignalHandler = Class.forName("sun.misc.SignalHandler"); main = Thread.currentThread(); Method method = Signal.getMethod("handle", Signal, SignalHandler); - - Object handler = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] - { - SignalHandler - }, new InvocationHandler() - { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - main.interrupt(); - interruptPy(); - return null; - } - }); - Object termHandler = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] - { - SignalHandler - }, new InvocationHandler() - { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - main.interrupt(); - interruptPyTerm(); - return null; - } - }); Object intr = Signal.getDeclaredConstructor(String.class).newInstance("INT"); - method.invoke(null, intr, handler); + method.invoke(null, intr, getSignalHandler(SignalHandler, 2)); Object intrTerm = Signal.getDeclaredConstructor(String.class).newInstance("TERM"); - method.invoke(null, intrTerm, termHandler); + method.invoke(null, intrTerm, getSignalHandler(SignalHandler, 15)); } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | SecurityException ex) { // If we don't get the signal handler run without it. (ANDROID) } } - native static void interruptPy(); - native static void interruptPyTerm(); + native static void interruptPy(int signal); + native static void acknowledgePy(); } diff --git a/pyproject.toml b/pyproject.toml index f718b4cc7..80397a78d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [project] name = "jpype1-ext" -version = '0.0.1' +version = '0.0.2' authors = [ {name = "Steve Menard", email = "devilwolf@users.sourceforge.net"}, ] From 5eb9202041184039a940d7867b169707e1242611 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 17:58:24 +0100 Subject: [PATCH 6/7] Add pypi upload on tagged releases --- .github/workflows/build.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1101dc6c9..dc3de9d06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,12 +62,14 @@ jobs: upload_pypi: needs: [build_wheels, build_sdist] runs-on: ubuntu-latest - environment: pypi - # permissions: - # id-token: write + environment: + name: pypi + url: https://pypi.org/p/jpype1-ext + permissions: + id-token: write # if: github.event_name == 'release' && github.event.action == 'published' # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this) - # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/download-artifact@v4 with: @@ -76,4 +78,6 @@ jobs: path: dist merge-multiple: true - name: List artifacts - run: ls -lah dist/ \ No newline at end of file + run: ls -lah dist/ + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file From 418118488cfe9b10a1ab0eaf5df2ba6acb062f33 Mon Sep 17 00:00:00 2001 From: Daniel Fangl Date: Tue, 19 Nov 2024 18:30:08 +0100 Subject: [PATCH 7/7] Add on push tags target --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc3de9d06..faaa3d2d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,8 @@ on: push: branches: - main + tags: + - v*.* pull_request: workflow_dispatch: