├── .github ├── requirements.txt ├── env │ ├── Windows │ │ ├── requirements.txt │ │ └── bdist-wheel.ps1 │ ├── macOS │ │ ├── requirements.txt │ │ └── bdist-wheel.sh │ └── Linux │ │ ├── requirements.txt │ │ └── bdist-wheel.sh ├── dependabot.yml ├── scripts │ └── collect-artifacts.sh ├── docker │ ├── README.md │ ├── Dockerfile │ └── docker-bake.hcl └── workflows │ └── check.yml ├── .dockerignore ├── doc ├── .gitignore ├── _static │ ├── java-apidocs │ │ ├── package-list │ │ ├── script.js │ │ ├── allclasses-noframe.html │ │ ├── allclasses-frame.html │ │ ├── org │ │ │ └── jpy │ │ │ │ ├── package-frame.html │ │ │ │ └── class-use │ │ │ │ ├── DL.html │ │ │ │ ├── PyLib.html │ │ │ │ └── PyLib.Diag.html │ │ └── index.html │ └── figures │ │ ├── deployment.png │ │ └── deployment.puml ├── index.rst ├── tutorial.rst └── modify.rst ├── src ├── test │ ├── resources │ │ ├── pymodules │ │ │ └── mod_1.py │ │ └── META-INF │ │ │ └── services │ │ │ └── javax.script.ScriptEngineFactory │ ├── python │ │ ├── fixtures │ │ │ ├── raise_errors.py │ │ │ ├── proc_module.py │ │ │ ├── proc_class.py │ │ │ ├── hasheqstr.py │ │ │ ├── special_methods.py │ │ │ └── proxy_test_classes.py │ │ ├── jpy_obj_test.py │ │ ├── jpy_java_embeddable_test.py │ │ ├── jpy_reentrant_test.py │ │ ├── jpy_mt_test.py │ │ ├── jpy_translation_test.py │ │ ├── jpy_diag_test.py │ │ ├── jpy_eval_exec_test.py │ │ ├── jpy_perf_test.py │ │ ├── jpy_typeconv_test_pyobj.py │ │ ├── jpy_typeres_test.py │ │ ├── imp │ │ │ ├── import_ex2.py │ │ │ └── import_ex1.py │ │ ├── jpy_mt_eval_exec_test.py │ │ ├── jpy_gettype_test.py │ │ └── jpy_retval_test.py │ └── java │ │ └── org │ │ └── jpy │ │ ├── fixtures │ │ ├── JavaArrayTestFixture.java │ │ ├── ConstructionTestFixture.java │ │ ├── CyclicReferenceChild2.java │ │ ├── GetTypeFailureParent.java │ │ ├── GetTypeFailureChild.java │ │ ├── EvalTestFixture.java │ │ ├── CyclicReferenceGrandParent.java │ │ ├── CyclicReferenceParent.java │ │ ├── CyclicReferenceChild1.java │ │ ├── Processor.java │ │ ├── DefaultInterfaceImplTestFixture.java │ │ ├── DefaultInterfaceTestFixture.java │ │ ├── TypeTranslationTestFixture.java │ │ ├── TypeResolutionTestFixture.java │ │ ├── MultiThreadedEvalTestFixture.java │ │ ├── CovariantOverloadExtendTestFixture.java │ │ ├── CovariantOverloadTestFixture.java │ │ ├── TypeConversionTestFixture.java │ │ ├── Thing.java │ │ ├── ConstructorOverloadTestFixture.java │ │ ├── FieldTestFixture.java │ │ ├── ExceptionTestFixture.java │ │ ├── ModifyAndReturnParametersTestFixture.java │ │ ├── MethodReturnValueTestFixture.java │ │ ├── VarArgsTestFixture.java │ │ └── MethodOverloadTestFixture.java │ │ ├── EmbeddableTestJunit.java │ │ ├── TestStatePrinter.java │ │ ├── LifeCycleTest.java │ │ ├── jsr223 │ │ └── Jsr223Test.java │ │ ├── PyLibWithSysPathTest.java │ │ ├── EmbeddableTest.java │ │ ├── PyModuleTest.java │ │ └── UseCases.java └── main │ ├── java │ └── org │ │ └── jpy │ │ ├── PyObjectCleanup.java │ │ ├── IdentityModule.java │ │ ├── KeyError.java │ │ ├── StopIteration.java │ │ ├── annotations │ │ ├── Return.java │ │ ├── Mutable.java │ │ └── Output.java │ │ ├── Assignment.java │ │ ├── PyInputMode.java │ │ ├── CreateModule.java │ │ ├── package-info.java │ │ ├── PyObjectState.java │ │ ├── DL.java │ │ └── PyLibInitializer.java │ └── c │ ├── jni │ ├── org_jpy_PyLib_CallableKind.h │ ├── org_jpy_DL.h │ ├── org_jpy_PyLib_Diag.h │ └── org_jpy_DL.c │ ├── jpy_compat.c │ ├── jpy_jbyte_buffer.c │ ├── jpy_verboseexcept.h │ ├── jpy_jbyte_buffer.h │ ├── jpy_jobj.h │ ├── jpy_diag.h │ ├── jpy_jfield.h │ ├── jpy_compat.h │ ├── jpy_jarray.h │ ├── jpy_jmethod.h │ ├── jpy_verboseexcept.c │ └── jpy_conv.h ├── pysobug ├── .gitignore ├── setup.py ├── mypydl.c ├── make.sh ├── mypy.c ├── mypymod.c └── README.txt ├── ci ├── appveyor │ └── dump-dlls.py └── travis │ ├── upload-to-ftp.sh │ ├── run.sh │ └── before_script.sh ├── setup.cfg ├── .gitignore ├── MANIFEST.in ├── RELEASE.md └── CONTRIBUTING.md /.github/requirements.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .git 3 | __pycache__ -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _templates 3 | -------------------------------------------------------------------------------- /src/test/resources/pymodules/mod_1.py: -------------------------------------------------------------------------------- 1 | answer = 42 -------------------------------------------------------------------------------- /doc/_static/java-apidocs/package-list: -------------------------------------------------------------------------------- 1 | org.jpy 2 | -------------------------------------------------------------------------------- /.github/env/Windows/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | wheel 3 | -------------------------------------------------------------------------------- /.github/env/macOS/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | wheel 3 | -------------------------------------------------------------------------------- /pysobug/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.o 3 | *.so 4 | mypy 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/javax.script.ScriptEngineFactory: -------------------------------------------------------------------------------- 1 | org.jpy.jsr223.ScriptEngineFactoryImpl -------------------------------------------------------------------------------- /doc/_static/figures/deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpy-consortium/jpy/HEAD/doc/_static/figures/deployment.png -------------------------------------------------------------------------------- /.github/env/Windows/bdist-wheel.ps1: -------------------------------------------------------------------------------- 1 | python -m pip install -r .github/env/Windows/requirements.txt 2 | python setup.py bdist_wheel 3 | -------------------------------------------------------------------------------- /src/test/python/fixtures/raise_errors.py: -------------------------------------------------------------------------------- 1 | def raise_if_zero(arg): 2 | if not arg: 3 | raise IndexError("arg wasn't there") -------------------------------------------------------------------------------- /src/main/java/org/jpy/PyObjectCleanup.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | interface PyObjectCleanup { 4 | int threadSafeCleanup(); 5 | } 6 | -------------------------------------------------------------------------------- /ci/appveyor/dump-dlls.py: -------------------------------------------------------------------------------- 1 | import psutil, os 2 | 3 | p = psutil.Process(os.getpid()) 4 | for dll in p.memory_maps(): 5 | print(dll.path) 6 | 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | verbose = 1 3 | 4 | [build] 5 | force = 1 6 | 7 | [install] 8 | record = setup.out 9 | 10 | [metadata] 11 | description-file = README.md -------------------------------------------------------------------------------- /.github/env/Linux/requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | wheel 3 | auditwheel>=6.4.2; python_version >= '3.14' 4 | auditwheel>=6.1.0; python_version >= '3.13' 5 | auditwheel>=6.0.0; python_version >= '3.9' 6 | patchelf 7 | -------------------------------------------------------------------------------- /.github/env/macOS/bdist-wheel.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | python -m pip install -r .github/env/macOS/requirements.txt 8 | python setup.py bdist_wheel 9 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/JavaArrayTestFixture.java: -------------------------------------------------------------------------------- 1 | package org.jpy.fixtures; 2 | 3 | public class JavaArrayTestFixture { 4 | public static byte[] createByteArray(int len) { 5 | return new byte[len]; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | target 3 | build 4 | dist 5 | MANIFEST 6 | .idea 7 | *.iml 8 | *.jar 9 | *.war 10 | *.ear 11 | __pycache__/ 12 | hs_err_pid* 13 | winbuild.log 14 | local.dat 15 | setup.out 16 | *.pyc 17 | jpy.egg-info 18 | lib/ 19 | .vagrant 20 | Vagrantfile 21 | *.so 22 | *.dll 23 | 24 | .vscode/ -------------------------------------------------------------------------------- /.github/scripts/collect-artifacts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | mkdir collect-artifacts 8 | find download-artifacts -type f 9 | 10 | mv download-artifacts/*/*.whl collect-artifacts/ 11 | mv download-artifacts/*/*.tar.gz collect-artifacts/ 12 | -------------------------------------------------------------------------------- /ci/travis/upload-to-ftp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function upload_ftp { 4 | echo Uploading ${1}: ${2} 5 | curl --ftp-create-dirs -T $2 -u "$FTP_USER:$FTP_PASSWORD" "ftp://$FTP_HOST/software/$TRAVIS_OS_NAME/" 6 | } 7 | 8 | echo "success pushing artifacts to FTP..." 9 | upload_ftp "wheel" "dist/*.whl" 10 | -------------------------------------------------------------------------------- /pysobug/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from distutils.core import setup 4 | from distutils.extension import Extension 5 | 6 | # This one works 7 | #extension = Extension('mypymod', sources=['mypymod.c'], libraries=['python3.4m']) 8 | # This one not 9 | extension = Extension('mypymod', sources=['mypymod.c']) 10 | setup(name='mypymod', ext_modules=[extension]) 11 | -------------------------------------------------------------------------------- /src/main/c/jni/org_jpy_PyLib_CallableKind.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_jpy_PyLib_CallableKind */ 4 | 5 | #ifndef _Included_org_jpy_PyLib_CallableKind 6 | #define _Included_org_jpy_PyLib_CallableKind 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | #endif 14 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/ConstructionTestFixture.java: -------------------------------------------------------------------------------- 1 | package org.jpy.fixtures; 2 | 3 | public class ConstructionTestFixture { 4 | public static ConstructionTestFixture viaStatic(int len) { 5 | return new ConstructionTestFixture(len); 6 | } 7 | 8 | private final byte[] array; 9 | 10 | public ConstructionTestFixture(int len) { 11 | array = new byte[len]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | include *.xml 3 | include *.md 4 | include doc/make.bat 5 | include doc/Makefile 6 | include lib/*.jar 7 | recursive-include doc *.py 8 | recursive-include doc *.rst 9 | recursive-include src/main/c *.h 10 | recursive-include src/main/c *.c 11 | recursive-include src/main/java *.java 12 | recursive-include src/test/java *.java 13 | recursive-include src/test/python *.py 14 | 15 | -------------------------------------------------------------------------------- /pysobug/mypydl.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int mypy_run(int argc, const char** argv) 4 | { 5 | const char* script; 6 | int i, status; 7 | 8 | Py_Initialize(); 9 | 10 | for (i = 0; i < argc; i++) { 11 | script = argv[i]; 12 | printf("mypy: executing [%s]\n", script); 13 | status = PyRun_SimpleString(script); 14 | printf("mypy: status %d\n", status); 15 | } 16 | 17 | Py_Finalize(); 18 | 19 | return 0; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /doc/_static/figures/deployment.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | component JVM 4 | component Python 5 | 6 | artifact jdl.so 7 | artifact jpy.so 8 | artifact jpy.jar 9 | artifact jpyutil.py 10 | file jpyconfig.properties 11 | file jpyconfig.py 12 | 13 | jpy.jar ..> jpy.so 14 | jpy.jar ..> jdl.so 15 | jpy.jar ..> jpyconfig.properties 16 | 17 | jpyutil.py ..> jpyconfig.py 18 | 19 | Python ..> jpy.so 20 | Python ..> jpyutil.py 21 | JVM ..> jpy.jar 22 | 23 | @enduml -------------------------------------------------------------------------------- /pysobug/make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -x #echo on 3 | 4 | # These are more or less the compiler and linker flags used by Python's distutils.core.setup() 5 | 6 | CC_FLAGS="-pthread -g -fwrapv -O2 -Wall -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2" 7 | LD_FLAGS="-shared -fPIC -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-O1" 8 | 9 | gcc $CC_FLAGS mypy.c -ldl -o mypy 10 | gcc $CC_FLAGS mypydl.c -I/usr/include/python3.4m -lpython3.4m $LD_FLAGS -o mypydl.so 11 | 12 | python3 setup.py install --user 13 | 14 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public class CyclicReferenceChild2 extends CyclicReferenceParent { 17 | public String getName() { 18 | return "Child2: " + this.toString(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/env/Linux/bdist-wheel.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 8 | 9 | python -m pip install -r "${__dir}/requirements.txt" 10 | 11 | # Can add --build-number if necessary 12 | python setup.py bdist_wheel --dist-dir dist.linux 13 | 14 | # Note: auditwheel only works with a single file argument - we are relying on finding exactly one wheel 15 | auditwheel \ 16 | repair \ 17 | --exclude libjvm.so \ 18 | --wheel-dir dist/ \ 19 | dist.linux/* 20 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/GetTypeFailureParent.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public abstract class GetTypeFailureParent { 17 | static { 18 | toFail(); 19 | } 20 | 21 | static void toFail() { 22 | throw new RuntimeException("Can't be loaded!"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/IdentityModule.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | public interface IdentityModule extends AutoCloseable { 4 | 5 | String IDENTITY_CODE = "def identity(x):\n return x"; 6 | 7 | static IdentityModule create(CreateModule createModule) { 8 | return create(createModule, IdentityModule.class); 9 | } 10 | 11 | static T create(CreateModule createModule, Class clazz) { 12 | return createModule.callAsFunctionModule("identity_module", IDENTITY_CODE, clazz); 13 | } 14 | 15 | PyObject identity(Object object); 16 | 17 | @Override 18 | void close(); 19 | } 20 | -------------------------------------------------------------------------------- /ci/travis/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | if [[ $TRAVIS_OS_NAME != 'osx' ]]; then 5 | export PYENV_ROOT="$HOME/.pyenv" 6 | export PATH="$PYENV_ROOT/bin:$PATH" 7 | fi 8 | 9 | eval "$(pyenv init -)" 10 | eval "$(pyenv virtualenv-init -)" 11 | pyenv activate jpy-venv 12 | python --version 13 | 14 | # oracle-java8-set-default seems to modify PATH but not JAVA_HOME :( 15 | java -version 16 | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 17 | export JAVA_HOME=$(/usr/libexec/java_home -v 1.8); 18 | else 19 | export JAVA_HOME=/usr/lib/jvm/java-8-oracle 20 | fi 21 | echo $JAVA_HOME 22 | 23 | python setup.py --maven bdist_wheel 24 | -------------------------------------------------------------------------------- /src/test/python/fixtures/proc_module.py: -------------------------------------------------------------------------------- 1 | 2 | def initialize(): 3 | return 'initialize' 4 | 5 | def computeTile(x, y, array): 6 | spend_some_time() 7 | return 'computeTile-' + str(x) + ',' + str(y) 8 | 9 | def dispose(): 10 | return 'dispose' 11 | 12 | 13 | def spend_some_time(): 14 | l = list(range(10000)) 15 | for i in range(10000): 16 | l.reverse() 17 | 18 | _module_val = None 19 | def setVal(val): 20 | global _module_val 21 | _module_val = val 22 | 23 | def getVal(): 24 | global _module_val 25 | return _module_val 26 | 27 | def check1234(): 28 | global _module_val 29 | return _module_val == 1234 30 | -------------------------------------------------------------------------------- /.github/docker/README.md: -------------------------------------------------------------------------------- 1 | # docker buildx bake 2 | 3 | This is an alternative way to build the jpy Linux wheels. 4 | It is mainly used as a means to produce arm64 wheels via QEMU cross-compiling since GHA does not have a native Linux arm64 runner. 5 | 6 | That said, it can be useful locally too; either to produce native or arm64 Linux wheels. 7 | 8 | ```bash 9 | docker buildx bake \ 10 | --file .github/docker/docker-bake.hcl \ 11 | --set "*.output=type=local,dest=/tmp/dist" 12 | ``` 13 | 14 | ```bash 15 | docker buildx bake \ 16 | --file .github/docker/docker-bake.hcl \ 17 | --set "*.output=type=local,dest=/tmp/dist" \ 18 | --set "*.platform=linux/arm64/v8" 19 | ``` 20 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/GetTypeFailureChild.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public class GetTypeFailureChild extends GetTypeFailureParent { 17 | private int x; 18 | 19 | private GetTypeFailureChild(int x) { 20 | this.x = x; 21 | } 22 | 23 | public static GetTypeFailureChild of(int x) { 24 | return new GetTypeFailureChild(x); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. jpy documentation master file, created by 2 | sphinx-quickstart on Mon Jan 20 21:26:19 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to jpy's documentation! 7 | =============================== 8 | 9 | jpy is a *bi-directional* Java-Python bridge allowing you to call Java from Python and Python from Java. 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | intro 17 | install 18 | tutorial 19 | reference 20 | modify 21 | 22 | 23 | Indices and tables 24 | ================== 25 | 26 | * :ref:`genindex` 27 | * :ref:`modindex` 28 | * :ref:`search` 29 | 30 | -------------------------------------------------------------------------------- /src/main/c/jpy_compat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "jpy_compat.h" 18 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/EvalTestFixture.java: -------------------------------------------------------------------------------- 1 | package org.jpy.fixtures; 2 | 3 | import org.jpy.PyInputMode; 4 | import org.jpy.PyLib; 5 | import org.jpy.PyObject; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class EvalTestFixture { 13 | 14 | public static PyObject expression(String expression) { 15 | return PyObject.executeCode(expression,PyInputMode.EXPRESSION, PyLib.getCurrentGlobals(),PyLib.getCurrentLocals()); 16 | } 17 | 18 | public static void script(String expression) { 19 | PyObject.executeCode(expression,PyInputMode.SCRIPT, PyLib.getCurrentGlobals(),PyLib.getCurrentLocals()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/test/python/fixtures/proc_class.py: -------------------------------------------------------------------------------- 1 | class Processor: 2 | def __init__(self): 3 | pass 4 | 5 | 6 | def initialize(self): 7 | return 'initialize' 8 | 9 | def computeTile(self, x, y, array): 10 | self.spend_some_time() 11 | return 'computeTile-' + str(x) + ',' + str(y) 12 | 13 | def dispose(self): 14 | return 'dispose' 15 | 16 | 17 | def spend_some_time(self): 18 | l = list(range(10000)) 19 | for i in range(10000): 20 | l.reverse() 21 | 22 | def setVal(self, val): 23 | self._val = val 24 | 25 | def getVal(self): 26 | return self._val 27 | 28 | def check1234(self): 29 | return self._val == 1234 30 | -------------------------------------------------------------------------------- /pysobug/mypy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef int (*mypy_run_fn)(int argc, const char** argv); 6 | 7 | int main(int argc, const char** argv) 8 | { 9 | void* handle; 10 | mypy_run_fn mypy_run; 11 | 12 | // This one works 13 | handle = dlopen("./mypydl.so", RTLD_LAZY | RTLD_GLOBAL); 14 | // This one not 15 | //handle = dlopen("./mypydl.so", RTLD_LAZY); 16 | if (handle == NULL) { 17 | fprintf(stderr, "mypy: error: %s\n", dlerror()); 18 | return 1; 19 | } 20 | 21 | mypy_run = (mypy_run_fn) dlsym(handle, "mypy_run"); 22 | if (mypy_run == NULL) { 23 | fprintf(stderr, "mypy: error: %s\n", dlerror()); 24 | return 2; 25 | } 26 | 27 | mypy_run(argc - 1, argv + 1); 28 | 29 | dlclose(handle); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public class CyclicReferenceGrandParent { 17 | private int x; 18 | public int z = 100; 19 | 20 | public CyclicReferenceGrandParent() { 21 | } 22 | 23 | public void refChild2(CyclicReferenceChild2 child2) { 24 | } 25 | 26 | public int grandParentMethod() { 27 | return 888; 28 | } 29 | 30 | public int get_x() { 31 | return this.x; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CyclicReferenceParent.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public abstract class CyclicReferenceParent extends CyclicReferenceGrandParent { 17 | private int x; 18 | public int y = 10; 19 | 20 | public static CyclicReferenceChild1 of(int x) { 21 | return CyclicReferenceChild1.of(x); 22 | } 23 | 24 | public int parentMethod() { 25 | return 88; 26 | } 27 | 28 | public int get_x() { 29 | return this.x; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/python/jpy_obj_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | 6 | jpyutil.init_jvm(jvm_maxmem='32M', jvm_classpath=['target/test-classes']) 7 | import jpy 8 | 9 | 10 | class TestJavaArrays(unittest.TestCase): 11 | def setUp(self): 12 | self.Fixture = jpy.get_type('org.jpy.fixtures.ConstructionTestFixture') 13 | self.assertIsNotNone(self.Fixture) 14 | 15 | def test_large_obj_by_constructor_alloc(self): 16 | # 100 * 1MB 17 | for _ in range(100): 18 | fixture = self.Fixture(1000000) # 1MB 19 | 20 | def test_large_obj_by_static_alloc(self): 21 | # 100 * 1MB 22 | for _ in range(100): 23 | fixture = self.Fixture.viaStatic(1000000) # 1MB 24 | 25 | if __name__ == '__main__': 26 | print('\nRunning ' + __file__) 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /src/test/python/fixtures/hasheqstr.py: -------------------------------------------------------------------------------- 1 | class Simple(object): 2 | def __init__(self, v): 3 | self._v = v 4 | 5 | def __str__(self): 6 | return "Simple: " + str(self._v) 7 | 8 | def getValue(self): 9 | return self._v 10 | 11 | class HashSimple(object): 12 | def __init__(self, v): 13 | self._v = v 14 | 15 | def __str__(self): 16 | return "HashSimple: " + str(self._v) 17 | 18 | def getValue(self): 19 | return self._v 20 | 21 | def __hash__(self): 22 | return hash(self._v) 23 | 24 | def __eq__(self, other): 25 | if isinstance(other, self.__class__): 26 | return self._v == other._v 27 | else: 28 | return False # Java can't support NotImplemented 29 | 30 | -------------------------------------------------------------------------------- /src/main/c/jpy_jbyte_buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 JPY-CONSORTIUM Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "jpy_module.h" 18 | #include "jpy_diag.h" 19 | #include "jpy_jarray.h" 20 | #include "jpy_jbyte_buffer.h" 21 | 22 | /* 23 | * This file for now is just a place-holder for future JPy_JByteBufferObj specific functions. 24 | */ -------------------------------------------------------------------------------- /src/test/python/jpy_java_embeddable_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes', 'target/classes']) 6 | import jpy 7 | 8 | class TestJavaTests(unittest.TestCase): 9 | def testStartingAndStoppingIfAvailable(self): 10 | PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') 11 | PyLibTest.testStartingAndStoppingIfAvailable() 12 | 13 | def testPassStatement(self): 14 | PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') 15 | PyLibTest.testPassStatement() 16 | 17 | def testPrintStatement(self): 18 | PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') 19 | PyLibTest.testPrintStatement() 20 | 21 | def testIncrementByOne(self): 22 | PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') 23 | PyLibTest.testIncrementByOne() 24 | 25 | if __name__ == '__main__': 26 | print('\nRunning ' + __file__) 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/EmbeddableTestJunit.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.TestRule; 6 | 7 | /** 8 | * Wraps up {@link EmbeddableTest} with JUnit so {@link EmbeddableTest} doesn't have to have the 9 | * JUnit dependency. 10 | */ 11 | public class EmbeddableTestJunit { 12 | 13 | @Rule 14 | public TestRule testStatePrinter = new TestStatePrinter(); 15 | 16 | @Test 17 | public void testStartingAndStoppingIfAvailable() { 18 | EmbeddableTest.testStartingAndStoppingIfAvailable(); 19 | } 20 | 21 | @Test 22 | public void testPassStatement() { 23 | EmbeddableTest.testPassStatement(); 24 | } 25 | 26 | @Test 27 | public void testPrintStatement() { 28 | EmbeddableTest.testPrintStatement(); 29 | } 30 | 31 | @Test 32 | public void testIncrementByOne() { 33 | EmbeddableTest.testIncrementByOne(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/python/jpy_reentrant_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes']) 6 | import jpy 7 | 8 | class TestReentrant(unittest.TestCase): 9 | def test_reentrant_function(self): 10 | PyObject = jpy.get_type('org.jpy.PyObject') 11 | PyInputMode = jpy.get_type('org.jpy.PyInputMode') 12 | PyModule = jpy.get_type('org.jpy.PyModule') 13 | 14 | PyObject.executeCode('def incByOne(x): return x + 1', PyInputMode.SCRIPT) 15 | 16 | mainModule = PyModule.getMain() 17 | res = mainModule.incByOne(41) 18 | self.assertEqual(res, 42) 19 | 20 | def test_reentrant_statement(self): 21 | PyObject = jpy.get_type('org.jpy.PyObject') 22 | PyInputMode = jpy.get_type('org.jpy.PyInputMode') 23 | PyObject.executeCode('print("hi from test_reentrant_statement")', PyInputMode.STATEMENT) 24 | 25 | if __name__ == '__main__': 26 | print('\nRunning ' + __file__) 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/script.js: -------------------------------------------------------------------------------- 1 | function show(type) 2 | { 3 | count = 0; 4 | for (var key in methods) { 5 | var row = document.getElementById(key); 6 | if ((methods[key] & type) != 0) { 7 | row.style.display = ''; 8 | row.className = (count++ % 2) ? rowColor : altColor; 9 | } 10 | else 11 | row.style.display = 'none'; 12 | } 13 | updateTabs(type); 14 | } 15 | 16 | function updateTabs(type) 17 | { 18 | for (var value in tabs) { 19 | var sNode = document.getElementById(tabs[value][0]); 20 | var spanNode = sNode.firstChild; 21 | if (value == type) { 22 | sNode.className = activeTableTab; 23 | spanNode.innerHTML = tabs[value][1]; 24 | } 25 | else { 26 | sNode.className = tableTab; 27 | spanNode.innerHTML = "" + tabs[value][1] + ""; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/KeyError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | package org.jpy; 20 | 21 | /** 22 | * Translation of Python KeyErrors so that they can be programmatically detected from Java. 23 | */ 24 | public class KeyError extends RuntimeException { 25 | KeyError(String message) { 26 | super(message); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/org/jpy/StopIteration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | package org.jpy; 20 | 21 | /** 22 | * Translation of Python StopIteration so that they can be programmatically detected from Java. 23 | */ 24 | public class StopIteration extends RuntimeException { 25 | StopIteration(String message) { 26 | super(message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2024 jpy-consortium 3 | // 4 | 5 | package org.jpy.fixtures; 6 | 7 | import java.lang.reflect.Array; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Used as a test class for the test cases in jpy_gettype_test.py 12 | * 13 | * @author Jianfeng Mao 14 | */ 15 | @SuppressWarnings("UnusedDeclaration") 16 | public class CyclicReferenceChild1 extends CyclicReferenceParent { 17 | private int x; 18 | 19 | private CyclicReferenceChild1(int x) { 20 | this.x = x; 21 | } 22 | 23 | public static CyclicReferenceChild1 of(int x) { 24 | return new CyclicReferenceChild1(x); 25 | } 26 | 27 | public int get_x() { 28 | return this.x; 29 | } 30 | 31 | public CyclicReferenceChild2[] getChild2s() { 32 | CyclicReferenceChild2[] child2s = new CyclicReferenceChild2[2]; 33 | child2s[0] = new CyclicReferenceChild2(); 34 | child2s[1] = new CyclicReferenceChild2(); 35 | return child2s; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/Processor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * Stands for an image data processor. 21 | * 22 | * Created by Norman on 19.12.13. 23 | */ 24 | public interface Processor { 25 | String initialize(); 26 | 27 | String computeTile(int w, int h, float[] data); 28 | 29 | String dispose(); 30 | 31 | void setVal(int n); 32 | 33 | int getVal(); 34 | 35 | boolean check1234(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/c/jpy_verboseexcept.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | #ifndef JPY_VERBOSEEXCEPT_H 21 | #define JPY_VERBOSEEXCEPT_H 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #include "jpy_compat.h" 28 | 29 | extern PyTypeObject VerboseExceptions_Type; 30 | extern int JPy_VerboseExceptions; 31 | 32 | PyObject* VerboseExceptions_New(void); 33 | 34 | #ifdef __cplusplus 35 | } /* extern "C" */ 36 | #endif 37 | #endif /* !JPY_DIAG_H */ -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/DefaultInterfaceImplTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; 20 | 21 | /** 22 | * Used as a test class for the test cases in jpy_overload_test.py 23 | * 24 | * @author Charles Wright 25 | */ 26 | @SuppressWarnings("UnusedDeclaration") 27 | public class DefaultInterfaceImplTestFixture implements DefaultInterfaceTestFixture { 28 | public int doIt() { 29 | return 2; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pysobug/mypymod.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | PyObject* id(PyObject* self, PyObject* args); 5 | 6 | 7 | static PyMethodDef functions[] = 8 | { 9 | {"id", id, METH_VARARGS, "id()"}, 10 | {NULL, NULL, 0, NULL} /*Sentinel*/ 11 | }; 12 | 13 | 14 | static struct PyModuleDef module_def = 15 | { 16 | PyModuleDef_HEAD_INIT, 17 | "mypymod", /* Name */ 18 | "My Python Module", /* Module documentation */ 19 | -1, /* Size of per-interpreter state */ 20 | functions, 21 | NULL, // m_reload 22 | NULL, // m_traverse 23 | NULL, // m_clear 24 | NULL // m_free 25 | }; 26 | 27 | 28 | PyMODINIT_FUNC PyInit_mypymod(void) 29 | { 30 | PyObject* module = NULL; 31 | 32 | printf("PyInit_mypymod: enter\n"); 33 | 34 | module = PyModule_Create(&module_def); 35 | if (module == NULL) { 36 | return NULL; 37 | } 38 | 39 | printf("PyInit_mypymod: exit: module=%p\n", module); 40 | 41 | return module; 42 | } 43 | 44 | 45 | PyObject* id(PyObject* self, PyObject* args) 46 | { 47 | static int idval = 0; 48 | idval++; 49 | return Py_BuildValue("i", idval); 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/DefaultInterfaceTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; 20 | 21 | /** 22 | * Used as a test class for the test cases in jpy_overload_test.py 23 | * 24 | * @author Charles Wright 25 | */ 26 | @SuppressWarnings("UnusedDeclaration") 27 | public interface DefaultInterfaceTestFixture { 28 | int doIt(); 29 | 30 | default public int doItPlusOne() { 31 | return 1 + doIt(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/c/jni/org_jpy_DL.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_jpy_DL */ 4 | 5 | #ifndef _Included_org_jpy_DL 6 | #define _Included_org_jpy_DL 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #undef org_jpy_DL_RTLD_LAZY 11 | #define org_jpy_DL_RTLD_LAZY 1L 12 | #undef org_jpy_DL_RTLD_NOW 13 | #define org_jpy_DL_RTLD_NOW 2L 14 | #undef org_jpy_DL_RTLD_LOCAL 15 | #define org_jpy_DL_RTLD_LOCAL 4L 16 | #undef org_jpy_DL_RTLD_GLOBAL 17 | #define org_jpy_DL_RTLD_GLOBAL 8L 18 | /* 19 | * Class: org_jpy_DL 20 | * Method: dlopen 21 | * Signature: (Ljava/lang/String;I)J 22 | */ 23 | JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen 24 | (JNIEnv *, jclass, jstring, jint); 25 | 26 | /* 27 | * Class: org_jpy_DL 28 | * Method: dlclose 29 | * Signature: (J)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose 32 | (JNIEnv *, jclass, jlong); 33 | 34 | /* 35 | * Class: org_jpy_DL 36 | * Method: dlerror 37 | * Signature: ()Ljava/lang/String; 38 | */ 39 | JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror 40 | (JNIEnv *, jclass); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | #endif 46 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/TypeTranslationTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | package org.jpy.fixtures; 21 | 22 | /** 23 | * Used as a test class for the test cases in jpy_retval_test.py 24 | * Note: Please make sure to not add any method overloads to this class. 25 | * This is done in {@link MethodOverloadTestFixture}. 26 | * 27 | * @author Norman Fomferra 28 | */ 29 | @SuppressWarnings("UnusedDeclaration") 30 | public class TypeTranslationTestFixture { 31 | public Thing makeThing(int value) { 32 | return new Thing(value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/python/jpy_mt_test.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import unittest 3 | import jpyutil 4 | 5 | jpyutil.init_jvm(jvm_maxmem='512M') 6 | import jpy 7 | 8 | 9 | class MyThread(threading.Thread): 10 | 11 | def __init__(self, value): 12 | threading.Thread.__init__(self) 13 | Integer = jpy.get_type('java.lang.Integer') 14 | self.intObj = Integer(value) 15 | 16 | def run(self): 17 | # perform some operation on the local object using a new thread 18 | self.intValue = self.intObj.intValue() 19 | 20 | 21 | class TestMultipleThreads(unittest.TestCase): 22 | 23 | 24 | def test_multi_thread_access(self): 25 | 26 | t1 = MyThread(123) 27 | t2 = MyThread(234) 28 | t3 = MyThread(345) 29 | t4 = MyThread(456) 30 | 31 | t1.start() 32 | t2.start() 33 | t3.start() 34 | t4.start() 35 | 36 | t1.join() 37 | t2.join() 38 | t3.join() 39 | t4.join() 40 | 41 | self.assertEqual(123, t1.intValue) 42 | self.assertEqual(234, t2.intValue) 43 | self.assertEqual(345, t3.intValue) 44 | self.assertEqual(456, t4.intValue) 45 | 46 | 47 | if __name__ == '__main__': 48 | print('\nRunning ' + __file__) 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/annotations/Return.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Used to mark method parameters as return values, that is, an argument may be returned as-is by the method. 26 | *

27 | * Note: this class is not used yet. 28 | * 29 | * @author Norman Fomferra 30 | */ 31 | @Target(value = ElementType.PARAMETER) 32 | @Retention(value = RetentionPolicy.RUNTIME) 33 | public @interface Return { 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/annotations/Mutable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Used to mark method parameters as mutable, that is, an argument's state is expected to be modified by the method. 26 | *

27 | * Note: this class is not used yet. 28 | * 29 | * @author Norman Fomferra 30 | */ 31 | @Target(value = ElementType.PARAMETER) 32 | @Retention(value = RetentionPolicy.RUNTIME) 33 | public @interface Mutable { 34 | } 35 | -------------------------------------------------------------------------------- /src/test/python/fixtures/special_methods.py: -------------------------------------------------------------------------------- 1 | class Simple: 2 | def __init__(self, x): 3 | self.x = x 4 | 5 | def __hash__(self): 6 | return hash(self.x) 7 | 8 | def __str__(self): 9 | return str(self.x) 10 | 11 | def __eq__(self, other): 12 | return self.x == other 13 | 14 | class ThrowsException: 15 | def __hash__(self): 16 | raise Exception('this __hash__ always raises Exception') 17 | 18 | def __str__(self): 19 | raise Exception('this __str__ always raises Exception') 20 | 21 | def __eq__(self, other): 22 | raise Exception('this __eq__ always raises Exception') 23 | 24 | # __eq__ *can* return non boolean values 25 | class NonBooleanEq: 26 | def __eq__(self, other): 27 | if self is other: 28 | return 1 29 | return 0 30 | 31 | # __str__ should *not* return non string values 32 | class NonStringStr: 33 | def __str__(self): 34 | return 1 35 | 36 | class NoMethodsDefined: 37 | def __init__(self): 38 | pass 39 | 40 | class EqAlwaysFalse: 41 | def __eq__(self, other): 42 | return False 43 | 44 | class EqAlwaysTrue: 45 | def __eq__(self, other): 46 | return True 47 | 48 | class HashNegativeOne: 49 | def __hash__(self): 50 | return -1 -------------------------------------------------------------------------------- /doc/tutorial.rst: -------------------------------------------------------------------------------- 1 | ######## 2 | Tutorial 3 | ######## 4 | 5 | 6 | Sorry, the jpy tutorial is not yet written. Meanwhile please refer to jpy's Python and Java unit-level tests 7 | in order to learn how to use jpy. They are located at 8 | 9 | * `src/test/python `_ 10 | * `src/test/java `_ 11 | 12 | 13 | ********************* 14 | Using jpy with Python 15 | ********************* 16 | 17 | Using the Java Standard Library 18 | =============================== 19 | 20 | 21 | Calling your Java Classes from Python 22 | ===================================== 23 | 24 | 25 | Primitive array parameters that are mutable 26 | ------------------------------------------- 27 | 28 | 29 | Primitive array parameters that are return value 30 | ------------------------------------------------ 31 | 32 | 33 | 34 | ******************* 35 | Using jpy with Java 36 | ******************* 37 | 38 | 39 | Getting Started 40 | =============== 41 | 42 | 43 | Using the Python Standard Library 44 | ================================= 45 | 46 | 47 | Calling your Python functions from Java 48 | ======================================= 49 | 50 | 51 | Extending Java with Python 52 | ========================== 53 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/annotations/Output.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Used to mark method parameters as mere output, that is, an argument is expected to be written to by the method but not read from. 26 | *

27 | * Note: this class is not used yet. 28 | * 29 | * @author Norman Fomferra 30 | */ 31 | @Target(value = ElementType.PARAMETER) 32 | @Retention(value = RetentionPolicy.RUNTIME) 33 | public @interface Output { 34 | } 35 | -------------------------------------------------------------------------------- /ci/travis/before_script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 4 | 5 | # Install some custom requirements on OS X 6 | # e.g. brew install pyenv-virtualenv 7 | # See https://gist.github.com/Bouke/11261620 8 | # and https://github.com/bincrafters/conan-bazel_installer 9 | 10 | brew update || brew update 11 | brew outdated pyenv || brew upgrade pyenv 12 | brew install pyenv-virtualenv 13 | eval "$(pyenv init -)" 14 | eval "$(pyenv virtualenv-init -)" 15 | 16 | pyenv install --list 17 | pyenv install --skip-existing $PYTHON_VERSION 18 | pyenv virtualenv $PYTHON_VERSION jpy-venv 19 | 20 | else 21 | # Install pyenv 22 | # See https://github.com/pyenv/pyenv 23 | git clone https://github.com/pyenv/pyenv.git $HOME/.pyenv 24 | export PYENV_ROOT="$HOME/.pyenv" 25 | export PATH="$PYENV_ROOT/bin:$PATH" 26 | eval "$(pyenv init -)" 27 | 28 | # Install pyenv virtualenv plugin 29 | # See https://github.com/pyenv/pyenv-virtualenv 30 | git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv 31 | eval "$(pyenv virtualenv-init -)" 32 | 33 | # Create virtualenv from current Python 34 | pyenv virtualenv jpy-venv 35 | fi 36 | 37 | pyenv rehash 38 | pyenv activate jpy-venv 39 | pip install wheel 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/TestStatePrinter.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import org.junit.rules.TestWatcher; 4 | import org.junit.runner.Description; 5 | 6 | import java.time.Instant; 7 | 8 | public class TestStatePrinter extends TestWatcher { 9 | @Override 10 | protected void starting(Description desc) { 11 | System.out.println(Instant.now().toString() + " Starting test: " + desc.getClassName() + ": " + desc.getMethodName()); 12 | } 13 | 14 | @Override 15 | protected void succeeded(Description desc) { 16 | System.out.println(Instant.now().toString() + " Passed test: " + desc.getClassName() + ": " + desc.getMethodName()); 17 | } 18 | 19 | @Override 20 | protected void failed(Throwable e, Description desc) { 21 | System.err.println(Instant.now().toString() + " Failed test: " + desc.getClassName() + ": " + desc.getMethodName()); 22 | } 23 | 24 | @Override 25 | protected void finished(Description desc) { 26 | // TODO: Seems like this makes the tests fail (w/ JVM crash) more reliably. need to figure out why. 27 | // (Without the GC, the tests usually fail but sometimes pass. With it, they always fail.) 28 | System.out.println(Instant.now().toString() + " Running GC after test: " + desc.getClassName() + ": " + desc.getMethodName()); 29 | System.gc(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/TypeResolutionTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * Used as a test class for the test cases in jpy_typeres_test.py 21 | * 22 | * @author Norman Fomferra 23 | */ 24 | @SuppressWarnings("UnusedDeclaration") 25 | public class TypeResolutionTestFixture { 26 | 27 | public SuperThing createSuperThing(int value) { 28 | return new SuperThing(value); 29 | } 30 | 31 | 32 | public static class SuperThing extends Thing { 33 | public SuperThing(int value) { 34 | super(value); 35 | } 36 | 37 | public void add(int val) { 38 | setValue(getValue() + val); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/c/jni/org_jpy_PyLib_Diag.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_jpy_PyLib_Diag */ 4 | 5 | #ifndef _Included_org_jpy_PyLib_Diag 6 | #define _Included_org_jpy_PyLib_Diag 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #undef org_jpy_PyLib_Diag_F_OFF 11 | #define org_jpy_PyLib_Diag_F_OFF 0L 12 | #undef org_jpy_PyLib_Diag_F_TYPE 13 | #define org_jpy_PyLib_Diag_F_TYPE 1L 14 | #undef org_jpy_PyLib_Diag_F_METH 15 | #define org_jpy_PyLib_Diag_F_METH 2L 16 | #undef org_jpy_PyLib_Diag_F_EXEC 17 | #define org_jpy_PyLib_Diag_F_EXEC 4L 18 | #undef org_jpy_PyLib_Diag_F_MEM 19 | #define org_jpy_PyLib_Diag_F_MEM 8L 20 | #undef org_jpy_PyLib_Diag_F_JVM 21 | #define org_jpy_PyLib_Diag_F_JVM 16L 22 | #undef org_jpy_PyLib_Diag_F_ERR 23 | #define org_jpy_PyLib_Diag_F_ERR 32L 24 | #undef org_jpy_PyLib_Diag_F_ALL 25 | #define org_jpy_PyLib_Diag_F_ALL 255L 26 | /* 27 | * Class: org_jpy_PyLib_Diag 28 | * Method: getFlags 29 | * Signature: ()I 30 | */ 31 | JNIEXPORT jint JNICALL Java_org_jpy_PyLib_00024Diag_getFlags 32 | (JNIEnv *, jclass); 33 | 34 | /* 35 | * Class: org_jpy_PyLib_Diag 36 | * Method: setFlags 37 | * Signature: (I)V 38 | */ 39 | JNIEXPORT void JNICALL Java_org_jpy_PyLib_00024Diag_setFlags 40 | (JNIEnv *, jclass, jint); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | #endif 46 | -------------------------------------------------------------------------------- /src/main/c/jpy_jbyte_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 JPY-CONSORTIUM Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_JBYTE_BUFFER_H 18 | #define JPY_JBYTE_BUFFER_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "jpy_compat.h" 25 | 26 | /** 27 | * The Java ByteBuffer representation in Python. 28 | * 29 | * IMPORTANT: JPy_JByteBufferObj must only differ from the JPy_JObj structure by the 'pyBuffer' member 30 | * since we use the same basic type, name JPy_JType for it. DON'T ever change member positions! 31 | * @see JPy_JObj 32 | */ 33 | typedef struct JPy_JByteBufferObj 34 | { 35 | PyObject_HEAD 36 | jobject objectRef; 37 | Py_buffer *pyBuffer; 38 | } 39 | JPy_JByteBufferObj; 40 | 41 | #ifdef __cplusplus 42 | } /* extern "C" */ 43 | #endif 44 | #endif /* !JPY_JBYTE_BUFFER_H */ 45 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/MultiThreadedEvalTestFixture.java: -------------------------------------------------------------------------------- 1 | package org.jpy.fixtures; 2 | 3 | import org.jpy.PyInputMode; 4 | import org.jpy.PyLib; 5 | import org.jpy.PyObject; 6 | 7 | import java.util.List; 8 | 9 | public class MultiThreadedEvalTestFixture { 10 | 11 | public static void expression(String expression, int numThreads) { 12 | execute(PyInputMode.EXPRESSION, expression, numThreads); 13 | } 14 | 15 | public static void script(String expression, int numThreads) { 16 | execute(PyInputMode.SCRIPT, expression, numThreads); 17 | } 18 | 19 | private static void execute(PyInputMode mode, String expression, int numThreads) { 20 | List threads = new java.util.ArrayList<>(); 21 | PyObject globals = PyLib.getCurrentGlobals(); 22 | PyObject locals = PyLib.getCurrentLocals(); 23 | for (int i = 0; i < numThreads; i++) { 24 | threads.add(new Thread(() -> { 25 | PyObject.executeCode(expression, mode, globals, locals); 26 | })); 27 | } 28 | for (Thread thread : threads) { 29 | thread.start(); 30 | } 31 | for (Thread thread : threads) { 32 | try { 33 | thread.join(); 34 | } catch (InterruptedException e) { 35 | Thread.currentThread().interrupt(); 36 | } 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/LifeCycleTest.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.junit.rules.TestRule; 7 | 8 | public class LifeCycleTest { 9 | 10 | @Rule 11 | public TestRule testStatePrinter = new TestStatePrinter(); 12 | 13 | private static final boolean ON_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); 14 | 15 | @Test 16 | public void testCanStartAndStopWithoutException() { 17 | PyLib.startPython(); 18 | Assert.assertTrue(PyLib.isPythonRunning()); 19 | PyModule sys1 = PyModule.importModule("sys"); 20 | Assert.assertNotNull(sys1); 21 | final long sys1Pointer = sys1.getPointer(); 22 | 23 | PyLib.stopPython(); 24 | if (!ON_WINDOWS) { 25 | Assert.assertFalse(PyLib.isPythonRunning()); 26 | } 27 | 28 | PyLib.startPython(); 29 | Assert.assertTrue(PyLib.isPythonRunning()); 30 | 31 | PyModule sys2 = PyModule.importModule("sys"); 32 | Assert.assertNotNull(sys2); 33 | final long sys2Pointer = sys2.getPointer(); 34 | 35 | if (!ON_WINDOWS) { 36 | Assert.assertNotEquals(sys1Pointer, sys2Pointer); 37 | } 38 | 39 | PyLib.stopPython(); 40 | if (!ON_WINDOWS) { 41 | Assert.assertFalse(PyLib.isPythonRunning()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CovariantOverloadExtendTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | */ 18 | 19 | package org.jpy.fixtures; 20 | 21 | import java.lang.reflect.Array; 22 | import java.lang.reflect.Method; 23 | 24 | /** 25 | * Used as a test class for the test cases in jpy_overload_test.py 26 | * 27 | * @author Charles P. Wright 28 | */ 29 | @SuppressWarnings("UnusedDeclaration") 30 | public class CovariantOverloadExtendTestFixture extends CovariantOverloadTestFixture { 31 | public CovariantOverloadExtendTestFixture(int x) { 32 | super(x * 2); 33 | } 34 | 35 | public CovariantOverloadExtendTestFixture foo(Number a, int b) { 36 | return new CovariantOverloadExtendTestFixture(a.intValue() - b); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/CovariantOverloadTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | */ 18 | 19 | package org.jpy.fixtures; 20 | 21 | import java.lang.reflect.Array; 22 | import java.lang.reflect.Method; 23 | 24 | /** 25 | * Used as a test class for the test cases in jpy_overload_test.py 26 | * 27 | * @author Charles P. Wright 28 | */ 29 | @SuppressWarnings("UnusedDeclaration") 30 | public class CovariantOverloadTestFixture { 31 | int x; 32 | 33 | public CovariantOverloadTestFixture(int x) { 34 | this.x = x; 35 | } 36 | 37 | public CovariantOverloadTestFixture foo(Number a, int b) { 38 | return new CovariantOverloadTestFixture(a.intValue() + b); 39 | } 40 | 41 | public int getX() { 42 | return x; 43 | } 44 | } -------------------------------------------------------------------------------- /src/test/python/fixtures/proxy_test_classes.py: -------------------------------------------------------------------------------- 1 | class HasStr: 2 | def __str__(self): 3 | return 'This is my __str__ method' 4 | 5 | class DoesNotHaveStr: 6 | def __init__(self): 7 | pass 8 | 9 | class HasToStringWithArg: 10 | def toString(self, arg): 11 | return 'weird' 12 | 13 | class HasHashCodeWithArg: 14 | def hashCode(self, arg): 15 | return 0 16 | 17 | def __hash__(self): 18 | return 1 19 | 20 | class HasHashNegativeOne: 21 | def __hash__(self): 22 | return -1 23 | 24 | class HasEqualsWithoutArg: 25 | def equals(self): 26 | return True 27 | 28 | class EqAlwaysFalse: 29 | def __eq__(self, other): 30 | return False 31 | 32 | class EqAlwaysTrue: 33 | def __eq__(self, other): 34 | return True 35 | 36 | class NoEqDefined: 37 | def __init__(self): 38 | pass 39 | 40 | class ProxyAsArgument: 41 | def self_is_other(self, other): 42 | return self is other 43 | 44 | class ThrowsException: 45 | def __hash__(self): 46 | raise Exception('this __hash__ always raises Exception') 47 | 48 | def __str__(self): 49 | raise Exception('this __str__ always raises Exception') 50 | 51 | def __eq__(self, other): 52 | raise Exception('this __eq__ always raises Exception') 53 | 54 | class NonBooleanEq: 55 | def __eq__(self, other): 56 | if self is other: 57 | return 1 58 | return 0 -------------------------------------------------------------------------------- /src/test/python/jpy_translation_test.py: -------------------------------------------------------------------------------- 1 | # This file was modified by Deephaven Data Labs. 2 | import unittest 3 | 4 | import jpyutil 5 | 6 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) 7 | import jpy 8 | 9 | 10 | class DummyWrapper: 11 | def __init__(self, theThing): 12 | self.theThing = theThing 13 | 14 | def getValue(self): 15 | return 2 * self.theThing.getValue() 16 | 17 | 18 | def make_wrapper(type, thing): 19 | return DummyWrapper(thing) 20 | 21 | 22 | class TestTypeTranslation(unittest.TestCase): 23 | def setUp(self): 24 | self.Fixture = jpy.get_type('org.jpy.fixtures.TypeTranslationTestFixture') 25 | self.assertIsNotNone(self.Fixture) 26 | 27 | def test_Translation(self): 28 | fixture = self.Fixture() 29 | thing = fixture.makeThing(7) 30 | self.assertEqual(thing.getValue(), 7) 31 | self.assertEqual(repr(type(thing)), "") 32 | 33 | jpy.type_translations['org.jpy.fixtures.Thing'] = make_wrapper 34 | thing = fixture.makeThing(8) 35 | self.assertEqual(thing.getValue(), 16) 36 | self.assertEqual(type(thing), type(DummyWrapper(None))) 37 | 38 | jpy.type_translations['org.jpy.fixtures.Thing'] = None 39 | self.assertEqual(fixture.makeThing(9).getValue(), 9) 40 | 41 | 42 | if __name__ == '__main__': 43 | print('\nRunning ' + __file__) 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /src/test/python/jpy_diag_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | 6 | jpyutil.init_jvm(jvm_maxmem='512M') 7 | import jpy 8 | 9 | 10 | class TestJavaArrays(unittest.TestCase): 11 | def test_diag_flags_constants(self): 12 | self.assertIsNotNone(jpy.diag) 13 | self.assertIsNotNone(jpy.diag.flags) 14 | self.assertEqual(jpy.diag.F_OFF, 0x00) 15 | self.assertEqual(jpy.diag.F_TYPE, 0x01) 16 | self.assertEqual(jpy.diag.F_METH, 0x02) 17 | self.assertEqual(jpy.diag.F_EXEC, 0x04) 18 | self.assertEqual(jpy.diag.F_MEM, 0x08) 19 | self.assertEqual(jpy.diag.F_JVM, 0x10) 20 | self.assertEqual(jpy.diag.F_ERR, 0x20) 21 | self.assertEqual(jpy.diag.F_ALL, 0xff) 22 | 23 | 24 | def test_diag_flags_value(self): 25 | self.assertIsNotNone(jpy.diag) 26 | self.assertEqual(jpy.diag.flags, 0) 27 | jpy.diag.flags = 1 28 | self.assertEqual(jpy.diag.flags, 1) 29 | jpy.diag.flags = 0 30 | self.assertEqual(jpy.diag.flags, 0) 31 | jpy.diag.flags = jpy.diag.F_EXEC + jpy.diag.F_MEM 32 | self.assertEqual(jpy.diag.flags, 12) 33 | jpy.diag.flags = 0 34 | self.assertEqual(jpy.diag.flags, 0) 35 | jpy.diag.flags += jpy.diag.F_EXEC 36 | jpy.diag.flags += jpy.diag.F_MEM 37 | self.assertEqual(jpy.diag.flags, 12) 38 | 39 | 40 | if __name__ == '__main__': 41 | print('\nRunning ' + __file__) 42 | unittest.main() 43 | -------------------------------------------------------------------------------- /src/test/python/jpy_eval_exec_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes', 'target/test-classes']) 6 | import jpy 7 | 8 | 9 | class TestEvalExec(unittest.TestCase): 10 | def setUp(self): 11 | self.fixture = jpy.get_type("org.jpy.fixtures.EvalTestFixture") 12 | self.assertIsNotNone(self.fixture) 13 | 14 | def test_foo_42(self): 15 | foo = 42 16 | self.assertEqual(self.fixture.expression("foo"), 42) 17 | 18 | def test_foo_bar(self): 19 | foo = "bar" 20 | self.assertEqual(self.fixture.expression("foo"), "bar") 21 | 22 | def test_x_add_y(self): 23 | x = 123 24 | y = 456 25 | self.assertEqual(self.fixture.expression("x + y"), 579) 26 | 27 | def test_inc_baz(self): 28 | baz = 15 29 | self.fixture.script("baz = baz + 1; self.assertEqual(baz, 16)") 30 | # note: this *is* correct wrt python semantics w/ exec(code, globals(), locals()) 31 | # https://bugs.python.org/issue4831 (Note: it's *not* a bug, is working as intended) 32 | self.assertEqual(baz, 15) 33 | 34 | def test_exec_import(self): 35 | import sys 36 | self.assertTrue("json" not in sys.modules) 37 | self.fixture.script("import json") 38 | self.assertTrue("json" in sys.modules) 39 | 40 | 41 | if __name__ == '__main__': 42 | print('\nRunning ' + __file__) 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /src/main/c/jpy_jobj.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | #ifndef JPY_JOBJ_H 21 | #define JPY_JOBJ_H 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #include "jpy_compat.h" 28 | 29 | /** 30 | * The Java Object representation in Python. 31 | * @see JPy_JArray 32 | */ 33 | typedef struct JPy_JObj 34 | { 35 | PyObject_HEAD 36 | jobject objectRef; 37 | } 38 | JPy_JObj; 39 | 40 | 41 | int JObj_Check(PyObject* arg); 42 | 43 | int JByteBuffer_Check(JPy_JType* type); 44 | 45 | PyObject* JObj_New(JNIEnv* jenv, jobject objectRef); 46 | PyObject* JObj_FromType(JNIEnv* jenv, JPy_JType* type, jobject objectRef); 47 | 48 | int JObj_InitTypeSlots(PyTypeObject* type, const char* typeName, PyTypeObject* superType); 49 | 50 | 51 | #ifdef __cplusplus 52 | } /* extern "C" */ 53 | #endif 54 | #endif /* !JPY_JOBJ_H */ -------------------------------------------------------------------------------- /src/test/python/jpy_perf_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import time 3 | import random 4 | import jpyutil 5 | jpyutil.init_jvm(jvm_maxmem='512M') 6 | import jpy 7 | 8 | 9 | class TestPerformance(unittest.TestCase): 10 | 11 | def test_general_rt_perf(self): 12 | 13 | Integer = jpy.get_type('java.lang.Integer') 14 | String = jpy.get_type('java.lang.String') 15 | File = jpy.get_type('java.io.File') 16 | HashMap = jpy.get_type('java.util.HashMap') 17 | 18 | # 1 million 19 | N = 1000000 20 | 21 | indexes = list(range(N)) 22 | random.shuffle(indexes) 23 | 24 | t0 = time.time() 25 | pairs = [(Integer(index), File('path')) for index in indexes] 26 | t1 = time.time() 27 | print('Integer + File object instantiation took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') 28 | 29 | map = HashMap() 30 | 31 | t0 = time.time() 32 | for pair in pairs: 33 | i, f = pair 34 | map.put(i, f) 35 | t1 = time.time() 36 | print('HashMap.put() took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') 37 | 38 | t0 = time.time() 39 | for pair in pairs: 40 | i, f = pair 41 | f = map.get(i) 42 | t1 = time.time() 43 | print('HashMap.get() took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') 44 | 45 | 46 | 47 | if __name__ == '__main__': 48 | print('\nRunning ' + __file__) 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/Assignment.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | class Assignment { 8 | 9 | private static final Map, Class> boxedToPrimitive; 10 | 11 | static { 12 | boxedToPrimitive = new HashMap<>(); 13 | boxedToPrimitive.put(Boolean.class, boolean.class); 14 | boxedToPrimitive.put(Byte.class, byte.class); 15 | boxedToPrimitive.put(Character.class, char.class); 16 | boxedToPrimitive.put(Short.class, short.class); 17 | boxedToPrimitive.put(Integer.class, int.class); 18 | boxedToPrimitive.put(Long.class, long.class); 19 | boxedToPrimitive.put(Float.class, float.class); 20 | boxedToPrimitive.put(Double.class, double.class); 21 | } 22 | 23 | static Optional> getUnboxedType(Class clazz) { 24 | return Optional.ofNullable(boxedToPrimitive.get(clazz)); 25 | } 26 | 27 | static boolean isAssignableFrom(Class signatureType, Object instance) { 28 | return isAssignableFrom(signatureType, instance.getClass()); 29 | } 30 | 31 | static boolean isAssignableFrom(Class signatureType, Class instanceType) { 32 | if (signatureType.isAssignableFrom(instanceType)) { 33 | return true; 34 | } 35 | if (signatureType.isPrimitive()) { 36 | return getUnboxedType(instanceType) 37 | .filter(signatureType::isAssignableFrom) 38 | .isPresent(); 39 | } 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/TypeConversionTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; 20 | 21 | /** 22 | * Used as a test class for the test cases in jpy_typeconv_test.py 23 | * 24 | * @author Norman Fomferra 25 | */ 26 | @SuppressWarnings("UnusedDeclaration") 27 | public class TypeConversionTestFixture { 28 | 29 | public String stringifyObjectArg(Object arg) { 30 | return stringifyArgs(arg); 31 | } 32 | 33 | public String stringifyIntArrayArg(int[] arg) { 34 | return stringifyArgs((Object) arg); 35 | } 36 | 37 | public String stringifyObjectArrayArg(Object[] arg) { 38 | return stringifyArgs((Object) arg); 39 | } 40 | 41 | public String stringifyStringArrayArg(String[] arg) { 42 | return stringifyArgs((Object) arg); 43 | } 44 | 45 | public boolean isSameObject(Object o1, Object o2) { 46 | return o1 == o2; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.4 2 | 3 | ARG PYTHON_TAG 4 | FROM python:${PYTHON_TAG} as build 5 | 6 | ARG DEBIAN_FRONTEND="noninteractive" 7 | ARG TARGETARCH 8 | ARG PYTHON_TAG 9 | RUN \ 10 | --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-${TARGETARCH}-${PYTHON_TAG} \ 11 | --mount=type=cache,target=/var/lib/apt,sharing=locked,id=apt-${TARGETARCH}-${PYTHON_TAG} \ 12 | set -eux; \ 13 | rm -f /etc/apt/apt.conf.d/docker-clean; \ 14 | echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache; \ 15 | wget --no-hsts -O /usr/share/keyrings/adoptium.asc https://packages.adoptium.net/artifactory/api/gpg/key/public; \ 16 | echo "deb [signed-by=/usr/share/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list; \ 17 | apt-get -qq update; \ 18 | apt-get -qq -y --no-install-recommends install temurin-8-jdk maven 19 | RUN \ 20 | --mount=type=cache,target=/root/.cache/pip,sharing=locked \ 21 | set -eux; \ 22 | python -m venv /jpy-build-venv; \ 23 | /jpy-build-venv/bin/pip install --upgrade pip setuptools; \ 24 | /jpy-build-venv/bin/pip install --only-binary=:all: wheel patchelf 25 | ENV JAVA_HOME=/usr/lib/jvm/temurin-8-jdk-${TARGETARCH} \ 26 | VIRTUAL_ENV='/jpy-build-venv' \ 27 | PATH="/jpy-build-venv/bin:${PATH}" \ 28 | CI=true 29 | COPY --link . /jpy 30 | RUN \ 31 | --mount=type=cache,target=/root/.m2,sharing=locked \ 32 | cd /jpy; \ 33 | .github/env/Linux/bdist-wheel.sh 34 | 35 | FROM scratch 36 | COPY --link --from=build /jpy/dist/* . 37 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/Thing.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * Stands for any Java object. 21 | * 22 | * @author Norman Fomferra 23 | */ 24 | @SuppressWarnings("UnusedDeclaration") 25 | public class Thing { 26 | private int value; 27 | 28 | public Thing() { 29 | } 30 | 31 | public Thing(int value) { 32 | this.value = value; 33 | } 34 | 35 | public int getValue() { 36 | return value; 37 | } 38 | 39 | public void setValue(int value) { 40 | this.value = value; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (o == null || getClass() != o.getClass()) return false; 47 | Thing thing = (Thing) o; 48 | return value == thing.value; 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return value; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "Thing[value=" + value + "]"; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/c/jpy_diag.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_DIAG_H 18 | #define JPY_DIAG_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "jpy_compat.h" 25 | 26 | typedef struct JPy_Diag 27 | { 28 | PyObject_HEAD 29 | int flags; 30 | int F_OFF; 31 | int F_TYPE; 32 | int F_METH; 33 | int F_EXEC; 34 | int F_MEM; 35 | int F_JVM; 36 | int F_ERR; 37 | int F_ALL; 38 | } 39 | JPy_Diag; 40 | 41 | 42 | #define JPy_DIAG_F_OFF 0x00 43 | #define JPy_DIAG_F_TYPE 0x01 44 | #define JPy_DIAG_F_METH 0x02 45 | #define JPy_DIAG_F_EXEC 0x04 46 | #define JPy_DIAG_F_MEM 0x08 47 | #define JPy_DIAG_F_JVM 0x10 48 | #define JPy_DIAG_F_ERR 0x20 49 | #define JPy_DIAG_F_ALL 0xff 50 | 51 | extern PyTypeObject Diag_Type; 52 | extern int JPy_DiagFlags; 53 | 54 | PyObject* Diag_New(void); 55 | 56 | void JPy_DiagPrint(int diagFlags, const char * format, ...); 57 | 58 | #define JPy_DIAG_PRINT if (JPy_DiagFlags != 0) JPy_DiagPrint 59 | 60 | 61 | #ifdef __cplusplus 62 | } /* extern "C" */ 63 | #endif 64 | #endif /* !JPY_DIAG_H */ -------------------------------------------------------------------------------- /src/main/c/jpy_jfield.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_JFIELD_H 18 | #define JPY_JFIELD_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "jpy_compat.h" 25 | 26 | /** 27 | * Python object representing a Java method. It's type is 'JMethod'. 28 | */ 29 | typedef struct 30 | { 31 | PyObject_HEAD 32 | 33 | // The declaring class. 34 | JPy_JType* declaringClass; 35 | // Field name. 36 | PyObject* name; 37 | // Field type. 38 | JPy_JType* type; 39 | // Method is static? 40 | char isStatic; 41 | // Method is final? 42 | char isFinal; 43 | // Field ID retrieved from JNI. 44 | jfieldID fid; 45 | } 46 | JPy_JField; 47 | 48 | /** 49 | * The Python 'JMethod' type singleton. 50 | */ 51 | extern PyTypeObject JField_Type; 52 | 53 | JPy_JField* JField_New(JPy_JType* declaringType, PyObject* fieldKey, JPy_JType* fieldType, jboolean isStatic, jboolean isFinal, jfieldID fid); 54 | void JField_Del(JPy_JField* field); 55 | 56 | #ifdef __cplusplus 57 | } /* extern "C" */ 58 | #endif 59 | 60 | #endif /* !JPY_JFIELD_H */ -------------------------------------------------------------------------------- /src/main/c/jpy_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_COMPAT_H 18 | #define JPY_COMPAT_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include "frameobject.h" 26 | 27 | #define JPY_VERSION_ERROR "jpy requires Python 3.9+" 28 | 29 | #if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 9 30 | #error JPY_VERSION_ERROR 31 | #endif 32 | 33 | #define JPy_IS_CLONG(pyArg) PyLong_Check(pyArg) 34 | #define JPy_AS_CLONG(pyArg) PyLong_AsLong(pyArg) 35 | #define JPy_AS_CLONGLONG(pyArg) PyLong_AsLongLong(pyArg) 36 | #define JPy_FROM_CLONG(cl) PyLong_FromLong(cl) 37 | 38 | #define JPy_IS_STR(pyArg) PyUnicode_Check(pyArg) 39 | #define JPy_FROM_CSTR(cstr) PyUnicode_FromString(cstr) 40 | #define JPy_FROM_FORMAT PyUnicode_FromFormat 41 | 42 | #define JPy_AS_UTF8(unicode) PyUnicode_AsUTF8(unicode) 43 | #define JPy_AS_WIDE_CHAR_STR(unicode, size) PyUnicode_AsWideCharString(unicode, size) 44 | #define JPy_FROM_WIDE_CHAR_STR(wc, size) PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, wc, size) 45 | 46 | #ifdef __cplusplus 47 | } /* extern "C" */ 48 | #endif 49 | #endif /* !JPY_COMPAT_H */ 50 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/ConstructorOverloadTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * @author Norman Fomferra 21 | */ 22 | @SuppressWarnings("UnusedDeclaration") 23 | public class ConstructorOverloadTestFixture { 24 | String state; 25 | 26 | public ConstructorOverloadTestFixture() { 27 | initState(); 28 | } 29 | 30 | public ConstructorOverloadTestFixture(int a) { 31 | initState(a); 32 | } 33 | 34 | public ConstructorOverloadTestFixture(int a, int b) { 35 | initState(a, b); 36 | } 37 | 38 | public ConstructorOverloadTestFixture(float a) { 39 | initState(a); 40 | } 41 | 42 | public ConstructorOverloadTestFixture(float a, float b) { 43 | initState(a, b); 44 | } 45 | 46 | public ConstructorOverloadTestFixture(int a, float b) { 47 | initState(a, b); 48 | } 49 | 50 | public ConstructorOverloadTestFixture(float a, int b) { 51 | initState(a, b); 52 | } 53 | 54 | public String getState() { 55 | return state; 56 | } 57 | 58 | private void initState(Object... args) { 59 | state = MethodOverloadTestFixture.stringifyArgs(args); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.github/docker/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | group "default" { 2 | targets = [ 3 | "python-39-linux", 4 | "python-310-linux", 5 | "python-311-linux", 6 | "python-312-linux", 7 | "python-313-linux", 8 | "python-314-linux" 9 | ] 10 | } 11 | 12 | variable "DEBIAN_BASE" { 13 | # bullseye: manylinux2014 / manylinux_2_17 14 | # bookworm: manylinux_2_34 15 | # trixie: Unable to build on Debian trixie, https://github.com/jpy-consortium/jpy/issues/202 16 | default = "bullseye" 17 | } 18 | 19 | variable "GITHUB_ACTIONS" { 20 | default = false 21 | } 22 | 23 | target "shared" { 24 | dockerfile = ".github/docker/Dockerfile" 25 | cache-from = [ 26 | GITHUB_ACTIONS ? "type=gha,scope=jpy-build" : "" 27 | ] 28 | cache-to = [ 29 | GITHUB_ACTIONS ? "type=gha,mode=max,scope=jpy-build" : "" 30 | ] 31 | } 32 | 33 | target "python-39-linux" { 34 | inherits = [ "shared" ] 35 | args = { 36 | PYTHON_TAG = "3.9-${DEBIAN_BASE}" 37 | } 38 | } 39 | 40 | target "python-310-linux" { 41 | inherits = [ "shared" ] 42 | args = { 43 | PYTHON_TAG = "3.10-${DEBIAN_BASE}" 44 | } 45 | } 46 | 47 | target "python-311-linux" { 48 | inherits = [ "shared" ] 49 | args = { 50 | PYTHON_TAG = "3.11-${DEBIAN_BASE}" 51 | } 52 | } 53 | 54 | target "python-312-linux" { 55 | inherits = [ "shared" ] 56 | args = { 57 | PYTHON_TAG = "3.12-${DEBIAN_BASE}" 58 | } 59 | } 60 | 61 | target "python-313-linux" { 62 | inherits = [ "shared" ] 63 | args = { 64 | PYTHON_TAG = "3.13-${DEBIAN_BASE}" 65 | } 66 | } 67 | 68 | target "python-314-linux" { 69 | inherits = [ "shared" ] 70 | args = { 71 | DEBIAN_BASE = "bookworm" 72 | # For Python 3.14, only Debian Bookworm and above is supported 73 | PYTHON_TAG = "3.14-bookworm" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/c/jpy_jarray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_JARRAY_H 18 | #define JPY_JARRAY_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "jpy_compat.h" 25 | 26 | /** 27 | * The Java primitive array representation in Python. 28 | * 29 | * IMPORTANT: JPy_JArray must only differ from the JPy_JObj structure by the 'bufferExportCount' member 30 | * since we use the same basic type, name JPy_JType for it. DON'T ever change member positions! 31 | * @see JPy_JObj 32 | */ 33 | typedef struct JPy_JArray 34 | { 35 | PyObject_HEAD 36 | jobject objectRef; 37 | jint bufferExportCount; 38 | void *buf; 39 | char javaType; 40 | jint bufReadonly; 41 | jint isCopy; 42 | } 43 | JPy_JArray; 44 | 45 | extern PyBufferProcs JArray_as_buffer_boolean; 46 | extern PyBufferProcs JArray_as_buffer_char; 47 | extern PyBufferProcs JArray_as_buffer_byte; 48 | extern PyBufferProcs JArray_as_buffer_short; 49 | extern PyBufferProcs JArray_as_buffer_int; 50 | extern PyBufferProcs JArray_as_buffer_long; 51 | extern PyBufferProcs JArray_as_buffer_float; 52 | extern PyBufferProcs JArray_as_buffer_double; 53 | 54 | extern void JArray_ReleaseJavaArrayElements(JPy_JArray* self, char javaType); 55 | 56 | #ifdef __cplusplus 57 | } /* extern "C" */ 58 | #endif 59 | #endif /* !JPY_JARRAY_H */ 60 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/allclasses-noframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | All Classes (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 |

All Classes

14 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/PyInputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy; 18 | 19 | /** 20 | * Source code input modes for compiling/executing Python source code. 21 | * 22 | * @author Norman Fomferra 23 | * @since 0.8 24 | * @see PyObject#executeCode(String, PyInputMode) 25 | * @see PyObject#executeCode(String, PyInputMode, Object, Object) 26 | */ 27 | public enum PyInputMode { 28 | /** 29 | * Compile/execute single statement code. 30 | *

31 | * The start symbol from the Python grammar for a 32 | * single statement ({@code Py_single_input} in Python PyRun API). 33 | * This is the symbol used for the interactive interpreter loop. 34 | */ 35 | STATEMENT(256), 36 | /** 37 | * Compile/execute multi-statement script code. 38 | *

39 | * The start symbol from the Python grammar for sequences of 40 | * statements as read from a file or other source ({@code Py_file_input} in Python PyRun API). 41 | * This is the symbol to use when compiling arbitrarily long Python source code. 42 | */ 43 | SCRIPT(257), 44 | /** 45 | * Compile/execute single expression. 46 | *

47 | * The start symbol from the Python grammar for sequences of 48 | * statements as read from a file or other source ({@code Py_eval_input} in Python PyRun API). 49 | */ 50 | EXPRESSION(258); 51 | 52 | public int value() { 53 | return value; 54 | } 55 | 56 | private final int value; 57 | 58 | PyInputMode(int value) { 59 | this.value = value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/CreateModule.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import java.util.Objects; 4 | import org.jpy.PyLib.CallableKind; 5 | 6 | public class CreateModule implements AutoCloseable { 7 | 8 | // note: we are keeping the imports in the function to limit the scope needed 9 | 10 | private static final String PREFERRED = "def create_module(module_name, script):\n" 11 | + " from importlib import util\n" 12 | + " spec = util.spec_from_loader(module_name, loader=None)\n" 13 | + " module = util.module_from_spec(spec)\n" 14 | + " exec(script, module.__dict__)\n" 15 | + " return module"; 16 | 17 | private static final String DEPRECATED = "def create_module(module_name, script):\n" 18 | + " import imp\n" 19 | + " module = imp.new_module(module_name)\n" 20 | + " exec(script, module.__dict__)\n" 21 | + " return module"; 22 | 23 | public static CreateModule create() { 24 | final String code = PyLib.getPythonVersion().startsWith("3") ? PREFERRED : DEPRECATED; 25 | try ( 26 | final PyObject dict = PyObject.executeCode("dict()", PyInputMode.EXPRESSION); 27 | final PyObject exec = PyObject.executeCode(code, PyInputMode.SCRIPT, null, dict)) { 28 | return new CreateModule(dict.asDict().get("create_module")); 29 | } 30 | } 31 | 32 | private final PyObject function; 33 | 34 | private CreateModule(PyObject function) { 35 | this.function = Objects.requireNonNull(function, "function"); 36 | } 37 | 38 | public PyObject call(String moduleName, String moduleScript) { 39 | return function.call("__call__", moduleName, moduleScript); 40 | } 41 | 42 | public T callAsFunctionModule(String moduleName, String moduleScript, Class clazz) { 43 | //noinspection unchecked 44 | return (T)call(moduleName, moduleScript).createProxy(CallableKind.FUNCTION, clazz); 45 | } 46 | 47 | public T callAsMethodModule(String moduleName, String moduleScript, Class clazz) { 48 | return call(moduleName, moduleScript).createProxy(clazz); 49 | } 50 | 51 | @Override 52 | public void close() { 53 | function.close(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/FieldTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * Used as a test class for the test cases in jpy_field_test.py 21 | * 22 | * @author Norman Fomferra 23 | */ 24 | @SuppressWarnings("UnusedDeclaration") 25 | public class FieldTestFixture { 26 | 27 | public static final boolean z_STATIC_FIELD = true; 28 | public static final char c_STATIC_FIELD = 'A'; 29 | public static final byte b_STATIC_FIELD = (byte) 123; 30 | public static final short s_STATIC_FIELD = (short) 12345; 31 | public static final int i_STATIC_FIELD = 123456789; 32 | public static final long j_STATIC_FIELD = 1234567890123456789L; 33 | public static final float f_STATIC_FIELD = 0.12345F; 34 | public static final double d_STATIC_FIELD = 0.123456789; 35 | 36 | public static final String S_OBJ_STATIC_FIELD = "ABC"; 37 | public static final Thing l_OBJ_STATIC_FIELD = new Thing(123); 38 | 39 | public boolean zInstField; 40 | public char cInstField; 41 | public byte bInstField; 42 | public short sInstField; 43 | public int iInstField; 44 | public long jInstField; 45 | public float fInstField; 46 | public double dInstField; 47 | 48 | public Boolean zObjInstField; 49 | public Character cObjInstField; 50 | public Byte bObjInstField; 51 | public Short sObjInstField; 52 | public Integer iObjInstField; 53 | public Long jObjInstField; 54 | public Float fObjInstField; 55 | public Double dObjInstField; 56 | 57 | public String SObjInstField; 58 | public Thing lObjInstField; 59 | } 60 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/allclasses-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | All Classes (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 |

All Classes

14 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /pysobug/README.txt: -------------------------------------------------------------------------------- 1 | 2 | ############################# 3 | Python shared library problem 4 | ############################# 5 | 6 | Problem: Native Python C-extensions can't find Python symbols if Python interpreter is embedded into a shared library. 7 | This problem does not occur on Windows systems. 8 | 9 | * mypydl.c - Source for a shared libary (mypydl.so) that embeds a Python interpreter 10 | * mypy.c - Source for a main program (mypy) that dynamically loads the shared library mypydl.so 11 | * mypymod.c - Native Python extension module (mypymod) that uses the Python C-API 12 | 13 | make.sh - builds mypydl.so, mypy and the mypymod Python extension module. 14 | 15 | The main program mypy interprets all given arguments as Python code and executes it:: 16 | 17 | $ bash ./make.sh 18 | $ ./mypy "print('Hello')" 19 | mypy: executing [print('Hello')] 20 | Hello 21 | mypy: status 0 22 | 23 | Here is how to reproduce the problem:: 24 | 25 | $ ./mypy "import mypymod" 26 | mypy: executing [import mypymod] 27 | Traceback (most recent call last): 28 | File "", line 1, in 29 | ImportError: /home/norman/.local/lib/python3.4/site-packages/mypymod.cpython-34m.so: undefined symbol: Py_BuildValue 30 | ... 31 | mypy: status -1 32 | 33 | Or with numpy:: 34 | 35 | $ ./mypy "import numpy" 36 | mypy: executing [import numpy] 37 | ... 38 | ImportError: /home/norman/.local/lib/python3.4/site-packages/numpy/core/multiarray.cpython-34m.so: undefined symbol: PyExc_SystemError 39 | mypy: status -1 40 | 41 | 42 | If the Python shared library is explicitly pre-loaded, then these errors don't occur:: 43 | 44 | $ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.4m.so ./mypy "import mypymod" 45 | mypy: executing [import mypymod] 46 | PyInit_mypymod: enter 47 | PyInit_mypymod: exit: module=0x7f72d34a4188 48 | mypy: status 0 49 | 50 | The error also doesn't occur, if I link my Python extension module explicitly against the Python shared library:: 51 | 52 | extension = Extension('mypymod', sources=['mypymod.c'], libraries=['python3.4m']) 53 | setup(name='mypymod', ext_modules=[extension]) 54 | 55 | Understandably but unfortunately, most Python extensions don't declare the dependency to a specific Python version explicitly. -------------------------------------------------------------------------------- /src/main/java/org/jpy/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Provides the classes necessary to 19 | *
    20 | *
  1. create an embedded Python VM,
  2. 21 | *
  3. to execute Python code,
  4. 22 | *
  5. to import Python modules,
  6. 23 | *
  7. to access Python variables, and finally
  8. 24 | *
  9. to call Python functions and class methods.
  10. 25 | *
26 | * 27 | *

28 | * The entry point to the jpy Java API is the {@link org.jpy.PyLib} class which is used to either 29 | * detect an already running Python interpreter or to start a new one: 30 | * 31 | *

32 |  *     if (!PyLib.isPythonRunning()) {
33 |  *         PyLib.startPython(opt1, opt2, ...);
34 |  *     }
35 |  * 
36 | * 37 | * jpy API clients should first call {@link org.jpy.PyLib#isPythonRunning()} in order to check if a Python interpreter is already available. 38 | * If not, {@link org.jpy.PyLib#startPython(String...)} must be called before any other jpy API is used. 39 | *

40 | * Once the Python interpreter in running clients can either execute Python code directly using the {@link org.jpy.PyLib#execScript(String)} method or 41 | * load a Python module using {@link org.jpy.PyModule#importModule(String)}. The returned {@link org.jpy.PyModule} object 42 | * then is the entry point to access Python variables and invoke functions. 43 | *

44 | * Some {@link org.jpy.PyModule} methods return {@link org.jpy.PyObject} instances. These can be used to 45 | * create instances of Python classes, and to access Python class members, attributes and to invoke Python object methods.. 46 | * 47 | * @since 0.7 48 | */ 49 | package org.jpy; -------------------------------------------------------------------------------- /src/main/java/org/jpy/PyObjectState.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import java.util.concurrent.atomic.AtomicLongFieldUpdater; 4 | 5 | /** 6 | * An auxiliary structure that allows for proper cleanup of {@link PyObject} after they have been 7 | * GC'd. 8 | */ 9 | final class PyObjectState implements AutoCloseable { 10 | private static final AtomicLongFieldUpdater updater = 11 | AtomicLongFieldUpdater.newUpdater(PyObjectState.class, "pointer"); 12 | 13 | /** 14 | * The value of the Python/C API {@code PyObject*} which this class represents. 15 | */ 16 | private volatile long pointer; 17 | 18 | PyObjectState(long pointer) { 19 | if (pointer == 0) { 20 | throw new IllegalArgumentException("pointer == 0"); 21 | } 22 | this.pointer = pointer; 23 | } 24 | 25 | public final long borrowPointer() { 26 | final long localPointer = updater.get(this); // TODO: how does this differ from just a volatile read against pointer? 27 | if (localPointer == 0) { 28 | throw new IllegalStateException("PyObjectState has already been taken"); 29 | } 30 | return localPointer; 31 | } 32 | 33 | /** 34 | * Only a single caller can ever take the pointer. Once taken, they are responsible for closing 35 | * the pointer. 36 | * 37 | * @return the pointer, or 0 if it's already been taken 38 | */ 39 | final long takePointer() { 40 | final long localPointer = updater.get(this); // TODO: how does this differ from just a volatile read against pointer? 41 | if (localPointer == 0) { 42 | // It's already been taken 43 | return 0; 44 | } 45 | if (!updater.compareAndSet(this, localPointer, 0)) { 46 | // Another thread concurrently took the pointer 47 | return 0; 48 | } 49 | // the caller is responsible for closing it now! 50 | return localPointer; 51 | } 52 | 53 | @Override 54 | public void close() { 55 | final long pointerForClosure = takePointer(); 56 | if (pointerForClosure == 0) { 57 | // It's already been taken 58 | return; 59 | } 60 | // We are closing it 61 | PyLib.decRef(pointerForClosure); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | pull_request: 5 | branches: [ 'master', 'release/v*' ] 6 | push: 7 | branches: [ 'master', 'release/v*' ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-22.04 12 | strategy: 13 | matrix: 14 | python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] 15 | java: ['8', '11', '17', '21', '23'] 16 | steps: 17 | - uses: actions/checkout@v5 18 | 19 | - uses: actions/setup-python@v6 20 | with: 21 | python-version: ${{ matrix.python }} 22 | 23 | - uses: actions/setup-java@v5 24 | with: 25 | distribution: 'temurin' 26 | java-version: ${{ matrix.java }} 27 | 28 | - run: pip install "setuptools < 72" numpy 29 | 30 | - name: Run Test 31 | run: python setup.py test 32 | 33 | - name: Upload JVM Error Logs 34 | uses: actions/upload-artifact@v4 35 | if: failure() 36 | with: 37 | name: check-ci-jvm-err 38 | path: | 39 | **/*_pid*.log 40 | **/core.* 41 | if-no-files-found: ignore 42 | 43 | test-free-threaded: 44 | runs-on: ubuntu-22.04 45 | strategy: 46 | matrix: 47 | python: ['3.13t', '3.14t'] 48 | java: ['8', '11', '17', '21', '23'] 49 | steps: 50 | - uses: actions/checkout@v5 51 | 52 | - uses: actions/setup-java@v5 53 | with: 54 | distribution: 'temurin' 55 | java-version: ${{ matrix.java }} 56 | 57 | - uses: astral-sh/setup-uv@v7 58 | - run: | 59 | uv python install ${{ matrix.python }} 60 | uv venv --python ${{ matrix.python }} 61 | source .venv/bin/activate 62 | uv pip install pip 63 | echo $JAVA_HOME 64 | echo PATH=$PATH >> $GITHUB_PATH 65 | 66 | - run: pip install "setuptools < 72" numpy 67 | 68 | - name: Run Free-threaded Test 69 | run: python setup.py test 70 | 71 | - name: Upload JVM Error Logs 72 | uses: actions/upload-artifact@v4 73 | if: failure() 74 | with: 75 | name: check-ci-jvm-err 76 | path: | 77 | **/*_pid*.log 78 | **/core.* 79 | if-no-files-found: ignore 80 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/ExceptionTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | package org.jpy.fixtures; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Used as a test class for the test cases in jpy_exception_test.py 26 | * 27 | * @author Norman Fomferra 28 | */ 29 | @SuppressWarnings("UnusedDeclaration") 30 | public class ExceptionTestFixture { 31 | public int throwNpeIfArgIsNull(String arg) { 32 | return arg.length(); 33 | } 34 | 35 | public int throwNpeIfArgIsNull2(String arg) { 36 | return throwNpeIfArgIsNull(arg); 37 | } 38 | 39 | public int throwNpeIfArgIsNullNested(String arg) { 40 | try { 41 | return throwNpeIfArgIsNull(arg); 42 | } catch (Exception e) { 43 | throw new RuntimeException("Nested exception", e); 44 | } 45 | } 46 | 47 | public int throwNpeIfArgIsNullNested2(String arg) { 48 | return throwNpeIfArgIsNullNested(arg); 49 | } 50 | 51 | public int throwNpeIfArgIsNullNested3(String arg) { 52 | try { 53 | return throwNpeIfArgIsNullNested2(arg); 54 | } catch (Exception e) { 55 | throw new RuntimeException("Nested exception 3", e); 56 | } 57 | } 58 | 59 | public int throwAioobeIfIndexIsNotZero(int index) { 60 | int[] ints = new int[]{101}; 61 | return ints[index]; 62 | } 63 | 64 | 65 | public void throwRteIfMessageIsNotNull(String message) { 66 | if (message != null) { 67 | throw new RuntimeException(message); 68 | } 69 | } 70 | 71 | public void throwIoeIfMessageIsNotNull(String message) throws IOException { 72 | if (message != null) { 73 | throw new IOException(message); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/org/jpy/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | org.jpy (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 |

org.jpy

14 |
15 |

Interfaces

16 | 19 |

Classes

20 | 31 |

Enums

32 | 36 |

Exceptions

37 | 41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /src/test/python/jpy_typeconv_test_pyobj.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes']) 6 | import jpy 7 | 8 | 9 | class TestTypeConversionsPyObj(unittest.TestCase): 10 | """ 11 | This test covers explicitly converting Python objects to Java PyObject instances. It is separate from 12 | jpy_typeconv_test.py because it requires a different classpath. 13 | """ 14 | 15 | def test_convert_toPyObject(self): 16 | # Note that this test requires jvm_classpath=['target/classes'] (not jvm_classpath=['target/***test-***classes'] 17 | 18 | print('Starting test_convert_toPyObject') 19 | PyObject_type = jpy.get_type('org.jpy.PyObject') 20 | print('test_convert_toPyObject: Got type for PyObject') 21 | 22 | print('test_convert_toPyObject: Testing value: \'A\'') 23 | print('test_convert_toPyObject: Doing first conversion') 24 | val = 'A' 25 | conv = jpy.convert(val, PyObject_type) 26 | print('test_convert_toPyObject: Getting first pointer') 27 | ptr = conv.getPointer() 28 | print('test_convert_toPyObject: Got first pointer') 29 | self.assertEqual(ptr, id(val)) 30 | print('test_convert_toPyObject: Passed first assertion') 31 | 32 | print('test_convert_toPyObject: Testing value: string') 33 | val = 'ABCDE' 34 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 35 | 36 | print('test_convert_toPyObject: Testing value: True') 37 | val = True 38 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 39 | 40 | print('test_convert_toPyObject: Testing value: False') 41 | val = False 42 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 43 | 44 | print('test_convert_toPyObject: Testing value: 12') 45 | val = 12 46 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 47 | 48 | print('test_convert_toPyObject: Testing value: 12.2') 49 | val = 12.2 50 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 51 | 52 | print('test_convert_toPyObject: Testing value: [1, 2.0, "ABCDE"]') 53 | val = [1, 2.0, "ABCDE"] 54 | self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) 55 | 56 | print('Finished test_convert_toPyObject') 57 | 58 | 59 | if __name__ == '__main__': 60 | print('\nRunning ' + __file__) 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/jsr223/Jsr223Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.jsr223; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | import javax.script.ScriptEngine; 23 | import javax.script.ScriptEngineFactory; 24 | import javax.script.ScriptEngineManager; 25 | import java.util.List; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertNotNull; 29 | 30 | public class Jsr223Test { 31 | 32 | @Test 33 | public void testThatScriptEngineFactoryIsRegistered() throws Exception { 34 | assertNotNull(getScriptEngineFactory()); 35 | } 36 | 37 | @Test 38 | public void testThatScriptEngineFactorySupportsMinimumParameterKeys() throws Exception { 39 | ScriptEngineFactoryImpl scriptEngineFactory = getScriptEngineFactory(); 40 | assertEquals("cpython", scriptEngineFactory.getParameter(ScriptEngine.NAME)); 41 | assertEquals("jpy Python Engine", scriptEngineFactory.getParameter(ScriptEngine.ENGINE)); 42 | assertEquals("0.1-alpha", scriptEngineFactory.getParameter(ScriptEngine.ENGINE_VERSION)); 43 | assertEquals("python", scriptEngineFactory.getParameter(ScriptEngine.LANGUAGE)); 44 | assertEquals("3.x", scriptEngineFactory.getParameter(ScriptEngine.LANGUAGE_VERSION)); 45 | } 46 | 47 | private ScriptEngineFactoryImpl getScriptEngineFactory() { 48 | ScriptEngineManager engineManager = new ScriptEngineManager(); 49 | List engineFactories = engineManager.getEngineFactories(); 50 | ScriptEngineFactoryImpl engineFactoryImpl = null; 51 | for (ScriptEngineFactory engineFactory : engineFactories) { 52 | if (engineFactory instanceof ScriptEngineFactoryImpl) { 53 | engineFactoryImpl = (ScriptEngineFactoryImpl) engineFactory; 54 | } 55 | } 56 | return engineFactoryImpl; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/python/jpy_typeres_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | 6 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) 7 | import jpy 8 | 9 | 10 | class TestTypeResolution(unittest.TestCase): 11 | def setUp(self): 12 | self.Fixture = jpy.get_type('org.jpy.fixtures.TypeResolutionTestFixture') 13 | self.assertIsNotNone(self.Fixture) 14 | 15 | 16 | def test_ThatTypeIsResolvedLate(self): 17 | self.assertTrue('org.jpy.fixtures.TypeResolutionTestFixture' in jpy.types) 18 | self.assertTrue('org.jpy.fixtures.TypeResolutionTestFixture$SuperThing' in jpy.types) 19 | self.assertTrue('org.jpy.fixtures.Thing' in jpy.types) 20 | 21 | fixture = self.Fixture() 22 | 23 | # Create a thing instance, the type 'org.jpy.fixtures.Thing' is not resolved yet 24 | thing = fixture.createSuperThing(2001) 25 | 26 | # Assert that 'org.jpy.fixtures.Thing' is not resolved yet 27 | SuperThing = jpy.types['org.jpy.fixtures.TypeResolutionTestFixture$SuperThing'] 28 | self.assertFalse('add' in SuperThing.__dict__) 29 | 30 | # Assert that 'org.jpy.fixtures.Thing' is not resolved yet 31 | Thing = jpy.types['org.jpy.fixtures.Thing'] 32 | self.assertFalse('getValue' in Thing.__dict__) 33 | 34 | # Calling 'add()' on SuperThing will resolve 'org.jpy.fixtures.Thing' 35 | thing.add(10) 36 | self.assertTrue('add' in SuperThing.__dict__) 37 | self.assertTrue('getValue' in Thing.__dict__) 38 | 39 | value = thing.getValue() 40 | self.assertEqual(value, 2011) 41 | 42 | # see https://github.com/bcdev/jpy/issues/63 43 | def test_ThatJavaTypesHaveAValidClassAttribute(self): 44 | Long = jpy.get_type('java.lang.Long') 45 | self.assertIsNotNone(Long.jclass) 46 | Class = jpy.get_type('java.lang.Class') 47 | self.assertIsNotNone(Class.jclass) 48 | Object = jpy.get_type('java.lang.Object') 49 | self.assertIsNotNone(Object.jclass) 50 | 51 | # see https://github.com/bcdev/jpy/issues/64 52 | def test_ThatInterfaceTypesIncludeMethodsOfExtendedTypes(self): 53 | ObjectInput = jpy.get_type('java.io.ObjectInput', resolve=True) 54 | # assert that a method declared of java.io.ObjectInput is in __dict__ 55 | self.assertTrue('readObject' in ObjectInput.__dict__) 56 | # assert that a method declared of java.io.DataInput is in __dict__ 57 | self.assertTrue('readLine' in ObjectInput.__dict__) 58 | 59 | 60 | 61 | if __name__ == '__main__': 62 | print('\nRunning ' + __file__) 63 | unittest.main() 64 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/ModifyAndReturnParametersTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | import org.jpy.annotations.Mutable; 20 | import org.jpy.annotations.Output; 21 | import org.jpy.annotations.Return; 22 | 23 | /** 24 | * Used as a test class for the test cases in jpy_modretparam_test.py 25 | * 26 | * @author Norman Fomferra 27 | */ 28 | @SuppressWarnings("UnusedDeclaration") 29 | public class ModifyAndReturnParametersTestFixture { 30 | 31 | public void modifyThing(@Mutable Thing thing, int value) { 32 | thing.setValue(value); 33 | } 34 | 35 | public Thing returnThing(@Return Thing thing) { 36 | if (thing == null) { 37 | thing = new Thing(); 38 | } 39 | return thing; 40 | } 41 | 42 | public Thing modifyAndReturnThing(@Mutable @Return Thing thing, int value) { 43 | if (thing == null) { 44 | thing = new Thing(); 45 | } 46 | thing.setValue(value); 47 | return thing; 48 | } 49 | 50 | public void modifyIntArray(@Mutable int[] array, int item0, int item1, int item2) { 51 | array[0] = item0; 52 | array[1] = item1; 53 | array[2] = item2; 54 | } 55 | 56 | public int[] returnIntArray(@Return int[] array) { 57 | if (array == null) { 58 | array = new int[3]; 59 | } 60 | return array; 61 | } 62 | 63 | public int[] modifyAndReturnIntArray(@Mutable @Return int[] array, int item0, int item1, int item2) { 64 | if (array == null) { 65 | array = new int[3]; 66 | } 67 | array[0] = item0; 68 | array[1] = item1; 69 | array[2] = item2; 70 | return array; 71 | } 72 | 73 | public void modifyAndOutputIntArray(@Mutable @Output int[] array, int item0, int item1, int item2) { 74 | if (array == null) { 75 | return; 76 | } 77 | array[0] = item0; 78 | array[1] = item1; 79 | array[2] = item2; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | Jpy is built and released across a matrix of operating systems, architectures, and Python versions. 4 | 5 | The jar artifacts are Java 8+ compatible and released to [org.jpyconsortium:jpy](https://repo1.maven.org/maven2/org/jpyconsortium/jpy) on Maven Central. 6 | 7 | The wheel artifacts are compatible with the table below, and released to the PyPi package [jpy](https://pypi.org/project/jpy/). 8 | 9 | | Version | OS | Arch | 10 | |---------|---------|--------| 11 | | 3.6 | Linux | x86_64 | 12 | | 3.7 | Linux | x86_64 | 13 | | 3.8 | Linux | x86_64 | 14 | | 3.9 | Linux | x86_64 | 15 | | 3.10 | Linux | x86_64 | 16 | | 3.11 | Linux | x86_64 | 17 | | 3.12 | Linux | x86_64 | 18 | | 3.13 | Linux | x86_64 | 19 | | 3.13t | Linux | x86_64 | 20 | | 3.14 | Linux | x86_64 | 21 | | 3.14t | Linux | x86_64 | 22 | | 3.6 | Linux | arm64 | 23 | | 3.7 | Linux | arm64 | 24 | | 3.8 | Linux | arm64 | 25 | | 3.9 | Linux | arm64 | 26 | | 3.10 | Linux | arm64 | 27 | | 3.11 | Linux | arm64 | 28 | | 3.12 | Linux | arm64 | 29 | | 3.13 | Linux | arm64 | 30 | | 3.13t | Linux | arm64 | 31 | | 3.14 | Linux | arm64 | 32 | | 3.14t | Linux | arm64 | 33 | | 3.6 | MacOS | x86_64 | 34 | | 3.7 | MacOS | x86_64 | 35 | | 3.8 | MacOS | x86_64 | 36 | | 3.9 | MacOS | x86_64 | 37 | | 3.10 | MacOS | x86_64 | 38 | | 3.11 | MacOS | x86_64 | 39 | | 3.12 | MacOS | x86_64 | 40 | | 3.13 | MacOS | x86_64 | 41 | | 3.13t | MacOS | x86_64 | 42 | | 3.14 | MacOS | x86_64 | 43 | | 3.14t | MacOS | x86_64 | 44 | | 3.6 | MacOS | arm64 | 45 | | 3.7 | MacOS | arm64 | 46 | | 3.8 | MacOS | arm64 | 47 | | 3.9 | MacOS | arm64 | 48 | | 3.10 | MacOS | arm64 | 49 | | 3.11 | MacOS | arm64 | 50 | | 3.12 | MacOS | arm64 | 51 | | 3.13 | MacOS | arm64 | 52 | | 3.13t | MacOS | arm64 | 53 | | 3.14 | MacOS | arm64 | 54 | | 3.14t | MacOS | arm64 | 55 | | 3.6 | Windows | x86_64 | 56 | | 3.7 | Windows | x86_64 | 57 | | 3.8 | Windows | x86_64 | 58 | | 3.9 | Windows | x86_64 | 59 | | 3.10 | Windows | x86_64 | 60 | | 3.11 | Windows | x86_64 | 61 | | 3.12 | Windows | x86_64 | 62 | | 3.13 | Windows | x86_64 | 63 | | 3.13t | Windows | x86_64 | 64 | | 3.14 | Windows | x86_64 | 65 | | 3.14t | Windows | x86_64 | 66 | 67 | ## Process 68 | 69 | The [build.yml](.github/workflows/build.yml) workflow is the main process by which PRs and releases are built. 70 | The release process is kicked off whenever a branch name matches `release/v*` is pushed to [jpy-consortium/jpy](https://github.com/jpy-consortium/jpy). 71 | -------------------------------------------------------------------------------- /src/test/python/imp/import_ex2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing Python import machinery with the goal to let users import Java class like this: 3 | from jpy.java.io import File 4 | or 5 | from jpy import java.io.File as File 6 | """ 7 | 8 | __author__ = 'Norman' 9 | 10 | import sys 11 | import importlib.abc 12 | 13 | ModuleType = type(sys) 14 | yyyy = ModuleType('yyyy') 15 | 16 | 17 | class JavaClass: 18 | def __init__(self, fullname): 19 | self.fullname = fullname 20 | 21 | def __repr__(self): 22 | return "JavaClass('" + self.fullname + "')" 23 | 24 | def __str__(self): 25 | return self.fullname 26 | 27 | 28 | class JavaPackageImporter(importlib.abc.MetaPathFinder): 29 | 30 | def get_package(self, fullname): 31 | dot_pos = fullname.rfind('.') 32 | if dot_pos > 0: 33 | package = fullname[:dot_pos] 34 | name = fullname[dot_pos + 1:] 35 | else: 36 | package = '' 37 | name = fullname 38 | return (package, name) 39 | 40 | def load_java_class(self, module, fullname): 41 | print('try loading java class "' + fullname + '"...') 42 | package, name = self.get_package(fullname) 43 | #module.__dict__[name] = JavaClass(fullname) 44 | #return JavaClass(fullname) 45 | return None 46 | 47 | def new_module(self, fullname): 48 | #package, name = self.get_package(fullname) 49 | 50 | print('creating module "' + fullname + '"') 51 | module = type(sys)(fullname) 52 | module.__loader__ = self 53 | module.__path__ = None 54 | #module.__package__ = package 55 | return module 56 | 57 | 58 | def find_module(self, fullname, path): 59 | print('find_module(fullname="' + str(fullname) + '", path="' + str(path) + '")') 60 | 61 | if fullname == 'yyyy' or fullname.startswith('yyyy.'): 62 | return self 63 | 64 | return None 65 | 66 | 67 | def load_module(self, fullname): 68 | print('load_module(fullname="' + str(fullname) + '")') 69 | is_reload = fullname in sys.modules 70 | if is_reload: 71 | module = sys.modules[fullname] 72 | print('module found!') 73 | # Now, check what to do next...? 74 | else: 75 | print('new module: ' + fullname) 76 | module = self.new_module(fullname) 77 | sys.modules[fullname] = module 78 | return module 79 | 80 | 81 | jpi = JavaPackageImporter() 82 | sys.meta_path += [jpi] 83 | 84 | import yyyy.bibo 85 | import yyyy.riser.bibo 86 | import yyyy.bibo.riser 87 | from yyyy.bibo import Riser as Riser 88 | 89 | 90 | import numpy 91 | from numpy import array 92 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/PyLibWithSysPathTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy; 18 | 19 | import org.junit.*; 20 | import org.junit.rules.TestRule; 21 | 22 | import java.io.File; 23 | import java.net.URI; 24 | import java.security.CodeSource; 25 | 26 | import static org.junit.Assert.*; 27 | 28 | 29 | public class PyLibWithSysPathTest { 30 | 31 | @Rule 32 | public TestRule testStatePrinter = new TestStatePrinter(); 33 | 34 | @Before 35 | public void setUp() throws Exception { 36 | 37 | CodeSource codeSource = PyLibWithSysPathTest.class.getProtectionDomain().getCodeSource(); 38 | if (codeSource == null) { 39 | System.out.println(PyLibWithSysPathTest.class + " not run: no code source found"); 40 | return; 41 | } 42 | URI codeSourceLocation = codeSource.getLocation().toURI(); 43 | System.out.println(PyLibWithSysPathTest.class + ": code source: " + codeSourceLocation); 44 | File codeSourceDir = new File(codeSourceLocation); 45 | if (!codeSourceDir.isDirectory()) { 46 | System.out.println(PyLibWithSysPathTest.class + " not run: code source is not a directory: " + codeSourceLocation); 47 | return; 48 | } 49 | 50 | File pymodulesDir = new File(codeSourceDir, "pymodules"); 51 | //assertFalse(PyLib.isPythonRunning()); 52 | System.out.println("PyLibWithSysPathTest: starting Python with 'sys.path' extension: " + pymodulesDir); 53 | PyLib.startPython(pymodulesDir.getPath()); 54 | //PyLib.startPython("x"); 55 | assertTrue(PyLib.isPythonRunning()); 56 | } 57 | 58 | @After 59 | public void tearDown() throws Exception { 60 | PyLib.stopPython(); 61 | } 62 | 63 | @Test 64 | public void testLoadModule() throws Exception { 65 | try (final PyModule pyModule = PyModule.importModule("mod_1")) { 66 | assertNotNull(pyModule); 67 | try (final PyObject pyAnswer = pyModule.getAttribute("answer")) { 68 | assertNotNull(pyAnswer); 69 | assertEquals(42, pyAnswer.getIntValue()); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This guide will serve as a reference for contributing to Jpy. 4 | 5 | ## Getting the source 6 | 7 | Jpy uses the [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow). In this workflow, the [jpy-consortium/jpy](https://github.com/jpy-consortium/jpy) repository contains a minimum number of branches, and development work happens in user-forked repositories. 8 | 9 | To learn more see: 10 | * [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow) 11 | * [Forking Projects](https://guides.github.com/activities/forking/) 12 | * [Fork A Repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) 13 | * [Working With Forks](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks) 14 | 15 | To get started quickly: 16 | 1) Navigate to [https://github.com/jpy-consortium/jpy](https://github.com/jpy-consortium/jpy). 17 | 2) Click `Fork` in the top right corner. 18 | 3) `git clone git@github.com:/jpy.git` 19 | 4) Commit changes to your own branches in your forked repository. 20 | 21 | Forked repositories do not have access to the same tokens/secrets as the jpy-consortium/jpy depository, so GitHub actions that depend upon secrets will fail. 22 | If you experience this problem, disable the offending GitHub action. To disable GitHub actions in your forked repository, go to "Actions" -> "Disable Actions" in your forked repository settings (`https://github.com//jpy/settings/actions`). 23 | 24 | Over time, forks will get out of sync with the upstream repository. To stay up to date, either: 25 | * Navigate to `https://github.com//jpy` and click on `Fetch upstream`, or 26 | * Follow these directions on [Syncing A Fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork). 27 | 28 | ## Creating a Pull Request 29 | Pull requests can be created through the GitHub website or through the GitHub CLI. 30 | 31 | ### GitHub Web 32 | 33 | Follow the directions in [Creating A Pull Request From A Fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). 34 | 35 | ### GitHub CLI 36 | 37 | 1) [Install GitHub command line tool](https://github.com/cli/cli). 38 | 2) On the command line, cd into your checked-out fork/branch. 39 | 3) `gh pr create -f -w` 40 | * Use `jpy-consortium/jpy` as the base repository. 41 | * Use `/jpy` as the repository to push to. 42 | 4) Your changes should automatically get pushed, and then a new pull request with your changes should open up in your browser. 43 | 5) Complete the information in the pull request and click `Create pull request`. 44 | 45 | For more information, see: 46 | * [gh pr create](https://cli.github.com/manual/gh_pr_create) 47 | * [CLI In Use](https://cli.github.com/manual/examples.html) 48 | 49 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Java-Python Bridge 0.10.0-SNAPSHOT Java API 8 | 61 | 62 | 63 | 64 | 65 | 66 | <noscript> 67 | <div>JavaScript is disabled on your browser.</div> 68 | </noscript> 69 | <h2>Frame Alert</h2> 70 | <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="org/jpy/package-summary.html">Non-frame version</a>.</p> 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/DL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy; 18 | 19 | /** 20 | * A replacement for {@link System#load(String)} with support for POSIX {@code dlopen} flags. 21 | *

22 | * Important note: This class is useful on POSIX (Unix/Linux) systems only. On Windows OSes, all methods 23 | * are no-ops. 24 | * 25 | * @author Norman Fomferra 26 | * @see dlopen(3) - Linux manual page 27 | * @since 0.7 28 | */ 29 | public class DL { 30 | /** 31 | * Resolve undefined symbols as code from the dynamic library is executed. 32 | */ 33 | public static final int RTLD_LAZY = 0x0001; 34 | /** 35 | * Resolve all undefined symbols before {@link #dlopen} returns and fail if this cannot be done. 36 | */ 37 | public static final int RTLD_NOW = 0x0002; 38 | /** 39 | * This is the converse of RTLD_GLOBAL, and the default if neither flag is specified. 40 | */ 41 | public static final int RTLD_LOCAL = 0x0004; 42 | /** 43 | * External symbols defined in the library will be made available to subsequently loaded libraries. 44 | */ 45 | public static final int RTLD_GLOBAL = 0x0008; 46 | 47 | /** 48 | * loads the dynamic library file named by the null-terminated string filename and returns 49 | * an opaque "handle" for the dynamic library. If filename is {@code null}, then the returned handle 50 | * is for the main program. If filename contains a slash ("/"), then it is interpreted as a 51 | * (relative or absolute) pathname. 52 | * 53 | * @param filename dynamic library filename or {@code null} 54 | * @param flag combination of {@link #RTLD_GLOBAL} or {@link #RTLD_LOCAL} with {@link #RTLD_LAZY}, 55 | * {@link #RTLD_NOW}. 56 | * @return opaque "handle" for the dynamic library. 57 | */ 58 | public static native long dlopen(String filename, int flag); 59 | 60 | public static native int dlclose(long handle); 61 | 62 | public static native String dlerror(); 63 | 64 | static { 65 | // see documentation in PyLibInitializer for explanation 66 | PyLibInitializer.dlInitialized = true; 67 | try { 68 | System.loadLibrary("jdl"); 69 | } catch (Throwable t) { 70 | String jdlLibPath = System.getProperty(PyLibConfig.JDL_LIB_KEY); 71 | if (jdlLibPath != null) { 72 | System.load(jdlLibPath); 73 | } else { 74 | throw new RuntimeException("Failed to load 'jdl' shared library. You can use system property 'jpy.jdlLib' to specify it.", t); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/jpy/PyLibInitializer.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * This helper class is to safely and programmatically configure jpy. 7 | * 8 | * Configuration of {@link PyLib} is done at class initialization time via system properties. 9 | * This is a potentially delicate time in the lifecycle of a program - and errors around order of 10 | * initialization can be hard to track down. To safely and programmatically configure jpy, we must 11 | * ensure that we are the ones causing the initialization of {@link PyLib} (and {@link DL}). 12 | */ 13 | public class PyLibInitializer { 14 | 15 | /** 16 | * Should be set to true during {@link PyLib}'s static initialization 17 | */ 18 | static boolean pyLibInitialized = false; 19 | 20 | /** 21 | * Should be set to true during {@link DL}'s static initialization 22 | */ 23 | static boolean dlInitialized = false; 24 | 25 | /** 26 | * Returns true iff the {@link PyLib} class has been initialized 27 | */ 28 | public static boolean isPyLibInitialized() { 29 | return pyLibInitialized; 30 | } 31 | 32 | /** 33 | * Returns true iff the {@link DL} class has been initialized 34 | */ 35 | public static boolean isDlInitialized() { 36 | return dlInitialized; 37 | } 38 | 39 | /** 40 | * This method should only be called once - it is dependent on {@link PyLib} and {@link DL} being 41 | * uninitialized. Any consumers who want to programmatically configure jpy should call this method 42 | * first. 43 | * 44 | * @param pyLib the python library 45 | * @param jpyLib the jpy library 46 | * @param jdlLib the jdl library 47 | */ 48 | public static void initPyLib(String pyLib, String jpyLib, String jdlLib) { 49 | synchronized (PyLibInitializer.class) { 50 | System.setProperty(PyLibConfig.PYTHON_LIB_KEY, pyLib); 51 | System.setProperty(PyLibConfig.JPY_LIB_KEY, jpyLib); 52 | System.setProperty(PyLibConfig.JDL_LIB_KEY, jdlLib); 53 | 54 | ensurePyLibInit(); 55 | 56 | // safety check to make sure that the PyLib initialization process didn't change any values 57 | ensurePropertySame(PyLibConfig.PYTHON_LIB_KEY, pyLib); 58 | ensurePropertySame(PyLibConfig.JPY_LIB_KEY, jpyLib); 59 | ensurePropertySame(PyLibConfig.JDL_LIB_KEY, jdlLib); 60 | } 61 | } 62 | 63 | private static void ensurePyLibInit() { 64 | if (pyLibInitialized) { 65 | throw new IllegalStateException("PyLib is already initialized"); 66 | } 67 | if (dlInitialized) { 68 | throw new IllegalStateException("DL is already initialized"); 69 | } 70 | 71 | PyLib.dummyMethodForInitialization(); 72 | 73 | if (!pyLibInitialized) { 74 | throw new IllegalStateException( 75 | "PyLib should have been initialized. This should not happen."); 76 | } 77 | // DL is not always initialized (platform dependent), so don't need to check it 78 | } 79 | 80 | private static void ensurePropertySame(String propertyName, String propertyValue) { 81 | final String currentValue = System.getProperty(propertyName); 82 | if (!Objects.equals(propertyValue, currentValue)) { 83 | throw new IllegalStateException(String.format( 84 | "PyLib initialization has changed the value of system property '%s': was '%s', is now '%s'", 85 | propertyName, propertyValue, currentValue)); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/EmbeddableTest.java: -------------------------------------------------------------------------------- 1 | package org.jpy; 2 | 3 | import java.io.Closeable; 4 | import java.util.Objects; 5 | 6 | /** 7 | * This class is meant to be invoked both from junit via {@link EmbeddableTestJunit}, and embedded 8 | * in python (see jpy_java_embeddable_test.py). 9 | * 10 | * For simplicity purposes, we don't want to have any external dependencies (ie, junit), so we can 11 | * keep the embedded classpath simple. 12 | */ 13 | public class EmbeddableTest { 14 | private static final boolean ON_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); 15 | 16 | public static void testStartingAndStoppingIfAvailable() { 17 | try (PyLibControl controller = new PyLibControl()) { 18 | 19 | } 20 | } 21 | 22 | public static void testPassStatement() { 23 | try (PyLibControl controller = new PyLibControl()) { 24 | PyObject object = PyObject.executeCode("pass", PyInputMode.STATEMENT); 25 | assertNotNull(object); 26 | assertNull(object.getObjectValue()); 27 | } 28 | } 29 | 30 | public static void testPrintStatement() { 31 | try (PyLibControl controller = new PyLibControl()) { 32 | PyObject.executeCode("print('hi')", PyInputMode.STATEMENT); 33 | } 34 | } 35 | 36 | public static void testIncrementByOne() { 37 | try (PyLibControl controller = new PyLibControl()) { 38 | PyObject.executeCode("def incByOne(x): return x + 1", PyInputMode.SCRIPT); 39 | PyObject results = PyModule.getMain().call("incByOne", 41); 40 | assertNotNull(results); 41 | assertTrue(results.isInt() || results.isLong()); 42 | assertEquals(results.getIntValue(), 42); 43 | } 44 | } 45 | 46 | private static void assertTrue(boolean b) { 47 | if (!b) { 48 | throw new AssertionError(); 49 | } 50 | } 51 | 52 | private static void assertFalse(boolean b) { 53 | if (!b) { 54 | throw new AssertionError(); 55 | } 56 | } 57 | 58 | private static void assertNotNull(Object o) { 59 | if (o == null) { 60 | throw new AssertionError(); 61 | } 62 | } 63 | 64 | private static void assertNull(Object o) { 65 | if (o != null) { 66 | throw new AssertionError(); 67 | } 68 | } 69 | 70 | private static void assertNotEquals(Object a, Object b) { 71 | if (Objects.equals(a, b)) { 72 | throw new AssertionError(); 73 | } 74 | } 75 | 76 | private static void assertEquals(int a, int b) { 77 | if (a != b) { 78 | throw new AssertionError(); 79 | } 80 | } 81 | 82 | private static class PyLibControl implements Closeable { 83 | private final boolean weAreEmbeddingPython; 84 | 85 | PyLibControl() { 86 | weAreEmbeddingPython = !PyLib.isPythonRunning(); 87 | if (weAreEmbeddingPython) { 88 | PyLib.startPython(); 89 | } 90 | assertTrue(PyLib.isPythonRunning()); 91 | } 92 | 93 | @Override 94 | public void close() { 95 | if (weAreEmbeddingPython) { 96 | PyLib.stopPython(); 97 | if (!ON_WINDOWS) { 98 | assertFalse(PyLib.isPythonRunning()); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /doc/modify.rst: -------------------------------------------------------------------------------- 1 | ############# 2 | How to Modify 3 | ############# 4 | 5 | =============== 6 | Rebuild Process 7 | =============== 8 | 9 | jpy's source distribution directory layout uses the `Maven common directory structure `_. 10 | 11 | * ``setup.py`` - Python build/installation script, will compile Python and Java sources, install the libraries and run all unit-level tests. 12 | * ``pom.xml`` - Maven project file to build the Java sources. Called by ``setup.py``. 13 | * ``src/main/c`` - C source files for the jpy Python API 14 | * ``src/test/python`` - Python API test cases 15 | * ``src/main/java`` - Java source files for the jpy Java API 16 | * ``src/test/java`` - Java API test cases 17 | 18 | 19 | After changing any source code just run setup again as indicated in the :ref:`build` process. 20 | 21 | 22 | After changing signatures of native methods in ``src/main/java/org/jpy/PyLib.java``, you need to compile the Java classes 23 | and regenerate the C headers for the ``PyLib`` class using Maven:: 24 | 25 | mvn compile 26 | javah -d src/main/c/jni -v -classpath target/classes org.jpy.PyLib 27 | 28 | Then always adapt changes ``org_jpy_PyLib.c`` according to newly generated ``org_jpy_PyLib.h`` and ``org_jpy_PyLib_Diag.h``. 29 | Files are found in ``src/main/c/jni/``. Then run setup again as indicated above. 30 | 31 | ======================= 32 | C Programming Guideline 33 | ======================= 34 | 35 | * Follow style used in Python itself 36 | * Python type global variable names: `J_Type` 37 | * Python type instance structs: `JPy_J` 38 | * Python function decl for a type: `J_(JNIEnv* jenv, JPy_J* , ...)` 39 | * The pointer is always the first parameter, only type slots obtain their `jenv` from `JPy_GetJEnv()` 40 | * Python slots function for a type: `J_(JNIEnv* jenv, JPy_J* self, ...)` 41 | * Usually functions shall indicate errors by returning NULL or -1 on error. 42 | Callers can expect that the PyErr_SetError has been set correctly and thus simply 43 | return NULL or -1 again. 44 | Exception: very simple functions, e.g. `JObj_Check()`, can go without error status indication. 45 | * Naming conventions: 46 | 47 | * jpy_jtype.h/c - The Java Meta-Type 48 | * JPy_JType type 49 | * JType_xxx() functions 50 | * jpy_jobj.h/c - The Java Object Wrapper 51 | * JPy_JObj type 52 | * JObj_xxx() functions 53 | * jpy_jmethod.h/c - The Java Method Wrapper 54 | * JPy_JMethod type 55 | * JPy_JOverloadedMethod type 56 | * JMethod_xxx() functions 57 | * JOverloadedMethod_xxx() functions 58 | * jpy_jfield.h/c - The Java Field Wrapper 59 | * JPy_JField type 60 | * JField_xxx() functions 61 | * jpy_conv.h/c - Conversion of Python objects from/to Java values 62 | * JPy_From functions / JPy_FROM_ macros create Python objects (new references!) from Java types 63 | * JPy_As functions / JPy_AS_ macros convert from Python objects to Java types 64 | * jpy_diag.h/c - Control of outputting diagnostic info 65 | * JPy_Diag type 66 | * JPy_DIAG_F_ macros 67 | * JPy_DIAG_PRINT(flags, format, ...) macros 68 | * jpy_module.h/c - The 'jpy' module definition 69 | * JPy_xxx() functions 70 | * jni/org_jpy_PyLib.h - generated by javah from PyLib.java 71 | * jni/org_jpy_PyLib_Diag.h - generated by javah from PyLib.java 72 | * jni/org_jpy_PyLib.c - native implementations from PyLib.java 73 | 74 | -------------------------------------------------------------------------------- /src/main/c/jni/org_jpy_DL.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "org_jpy_DL.h" 18 | 19 | #if !defined(_WIN32) && !defined(__CYGWIN__) 20 | 21 | #include 22 | 23 | /* 24 | * Class: org_jpy_DL 25 | * Method: dlopen 26 | * Signature: (Ljava/lang/String;I)J 27 | */ 28 | JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen 29 | (JNIEnv *jenv, jclass dlClass, jstring jPath, jint mode) 30 | { 31 | const char* path; 32 | int flags; 33 | 34 | if (jPath == NULL) { 35 | return 0; 36 | } 37 | 38 | path = (*jenv)->GetStringUTFChars(jenv, jPath, NULL); 39 | if (path == NULL) { 40 | return 0; 41 | } 42 | 43 | flags = 0; 44 | if ((mode & org_jpy_DL_RTLD_LAZY) != 0) { 45 | flags |= RTLD_LAZY; 46 | } 47 | if ((mode & org_jpy_DL_RTLD_NOW) != 0) { 48 | flags |= RTLD_NOW; 49 | } 50 | if ((mode & org_jpy_DL_RTLD_LOCAL) != 0) { 51 | flags |= RTLD_LOCAL; 52 | } 53 | if ((mode & org_jpy_DL_RTLD_GLOBAL) != 0) { 54 | flags |= RTLD_GLOBAL; 55 | } 56 | 57 | return (jlong) dlopen(path, flags); 58 | } 59 | 60 | /* 61 | * Class: org_jpy_DL 62 | * Method: dlclose 63 | * Signature: (J)I 64 | */ 65 | JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose 66 | (JNIEnv *jenv, jclass dlClass, jlong handle) 67 | { 68 | return dlclose((void*) handle); 69 | } 70 | 71 | /* 72 | * Class: org_jpy_DL 73 | * Method: dlerror 74 | * Signature: ()Ljava/lang/String; 75 | */ 76 | JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror 77 | (JNIEnv *jenv, jclass dlClass) 78 | { 79 | const char* message; 80 | 81 | message = dlerror(); 82 | if (message != NULL) { 83 | return (*jenv)->NewStringUTF(jenv, message); 84 | } else { 85 | return NULL; 86 | } 87 | } 88 | 89 | #else /* !defined(_WIN32) && !defined(__CYGWIN__) */ 90 | 91 | // Dummy DLL entry point for Python 2.7 (Windows requires it) 92 | __declspec(dllexport) void initjdl(void) { } 93 | 94 | // Dummy DLL entry point for Python 3.3+ (Windows requires it) 95 | __declspec(dllexport) void* PyInit_jdl(void) { return NULL; } 96 | 97 | /* 98 | * Class: org_jpy_DL 99 | * Method: dlopen 100 | * Signature: (Ljava/lang/String;I)J 101 | */ 102 | JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen 103 | (JNIEnv *jenv, jclass dlClass, jstring jPath, jint mode) 104 | { 105 | return 0; 106 | } 107 | 108 | /* 109 | * Class: org_jpy_DL 110 | * Method: dlclose 111 | * Signature: (J)I 112 | */ 113 | JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose 114 | (JNIEnv *jenv, jclass dlClass, jlong handle) 115 | { 116 | return 0; 117 | } 118 | 119 | /* 120 | * Class: org_jpy_DL 121 | * Method: dlerror 122 | * Signature: ()Ljava/lang/String; 123 | */ 124 | JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror 125 | (JNIEnv *jenv, jclass dlClass) 126 | { 127 | return NULL; 128 | } 129 | 130 | #endif /* !defined(_WIN32) && !defined(__CYGWIN__) */ 131 | 132 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/PyModuleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy; 18 | 19 | import org.junit.*; 20 | import org.junit.rules.TestRule; 21 | 22 | import java.io.File; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertNotNull; 26 | import static org.junit.Assert.assertTrue; 27 | 28 | /** 29 | * @author Norman Fomferra 30 | */ 31 | public class PyModuleTest { 32 | 33 | @Rule 34 | public TestRule testStatePrinter = new TestStatePrinter(); 35 | @Before 36 | public void setUp() throws Exception { 37 | //System.out.println("PyModuleTest: Current thread: " + Thread.currentThread()); 38 | 39 | String importPath = new File("src/test/python/fixtures").getCanonicalPath(); 40 | PyLib.startPython(importPath); 41 | assertEquals(true, PyLib.isPythonRunning()); 42 | 43 | //PyLib.Diag.setFlags(PyLib.Diag.F_METH); 44 | } 45 | 46 | @After 47 | public void tearDown() throws Exception { 48 | PyLib.Diag.setFlags(PyLib.Diag.F_OFF); 49 | PyLib.stopPython(); 50 | } 51 | 52 | @Test 53 | public void testCreateAndCallProxySingleThreaded() throws Exception { 54 | //PyObjectTest.addTestDirToPythonSysPath(); 55 | PyModule procModule = PyModule.importModule("proc_module"); 56 | PyObjectTest.testCallProxySingleThreaded(procModule); 57 | } 58 | 59 | // see https://github.com/bcdev/jpy/issues/26 60 | @Test 61 | public void testCreateAndCallProxyMultiThreaded() throws Exception { 62 | //PyObjectTest.addTestDirToPythonSysPath(); 63 | PyModule procModule = PyModule.importModule("proc_module"); 64 | PyObjectTest.testCallProxyMultiThreaded(procModule); 65 | } 66 | 67 | // see: https://github.com/bcdev/jpy/issues/39: Improve Java exception messages on Python errors #39 68 | @Test 69 | public void testPythonErrorMessages() throws Exception { 70 | //PyObjectTest.addTestDirToPythonSysPath(); 71 | PyModule raiserModule = PyModule.importModule("raise_errors"); 72 | for (int i=0;i < 10;i++) { 73 | try { 74 | raiserModule.call("raise_if_zero", 0); 75 | Assert.fail(); 76 | } catch (RuntimeException e) { 77 | //e.printStackTrace(); 78 | String message = e.getMessage(); 79 | //System.out.println("message = " + message); 80 | assertNotNull(message); 81 | assertTrue(message.startsWith("Error in Python interpreter")); 82 | assertTrue(message.contains("Type: <")); 83 | assertTrue(message.contains("IndexError'>\n")); 84 | assertTrue(message.contains("Value: arg wasn't there\n")); 85 | assertTrue(message.contains("Line: 3\n")); 86 | assertTrue(message.contains("Namespace: raise_if_zero\n")); 87 | assertTrue(message.contains("File: ")); 88 | } 89 | // ok 90 | raiserModule.call("raise_if_zero", 1); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/c/jpy_jmethod.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | */ 18 | 19 | #ifndef JPY_JMETHOD_H 20 | #define JPY_JMETHOD_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #include "jpy_compat.h" 27 | 28 | /** 29 | * Python object representing a Java method. It's type is 'JMethod'. 30 | */ 31 | typedef struct 32 | { 33 | PyObject_HEAD 34 | 35 | // The declaring class. 36 | JPy_JType* declaringClass; 37 | // Method name. 38 | PyObject* name; 39 | // Method parameter count. 40 | int paramCount; 41 | // Method is static? 42 | char isStatic; 43 | // Method is varargs? 44 | char isVarArgs; 45 | // Method parameter types. Will be NULL, if parameter_count == 0. 46 | JPy_ParamDescriptor* paramDescriptors; 47 | // Method return type. Will be NULL for constructors. 48 | JPy_ReturnDescriptor* returnDescriptor; 49 | // The JNI method ID obtained from the declaring class. 50 | jmethodID mid; 51 | } 52 | JPy_JMethod; 53 | 54 | /** 55 | * The Python 'JMethod' type singleton. 56 | */ 57 | extern PyTypeObject JMethod_Type; 58 | 59 | /** 60 | * Python object representing an overloaded Java method. It's type is 'JOverloadedMethod'. 61 | */ 62 | typedef struct 63 | { 64 | PyObject_HEAD 65 | 66 | // The declaring class. 67 | JPy_JType* declaringClass; 68 | // Method name. 69 | PyObject* name; 70 | // List of method overloads (a PyList with items of type JPy_JMethod). 71 | PyObject* methodList; 72 | } 73 | JPy_JOverloadedMethod; 74 | 75 | /** 76 | * The Python 'JOverloadedMethod' type singleton. 77 | */ 78 | extern PyTypeObject JOverloadedMethod_Type; 79 | 80 | JPy_JMethod* JOverloadedMethod_FindMethod(JNIEnv* jenv, JPy_JOverloadedMethod* overloadedMethod, PyObject* argTuple, jboolean visitSuperClass, int *isVarArgsArray); 81 | JPy_JMethod* JOverloadedMethod_FindStaticMethod(JPy_JOverloadedMethod* overloadedMethod, PyObject* argTuple); 82 | JPy_JOverloadedMethod* JOverloadedMethod_New(JPy_JType* declaringClass, PyObject* name, JPy_JMethod* method); 83 | int JOverloadedMethod_AddMethod(JPy_JOverloadedMethod* overloadedMethod, JPy_JMethod* method); 84 | 85 | JPy_JMethod* JMethod_New(JPy_JType* declaringClass, 86 | PyObject* name, 87 | int paramCount, 88 | JPy_ParamDescriptor* paramDescriptors, 89 | JPy_ReturnDescriptor* returnDescriptor, 90 | jboolean isStatic, 91 | jboolean isVarArgs, 92 | jmethodID mid); 93 | 94 | void JMethod_Del(JPy_JMethod* method); 95 | 96 | int JMethod_ConvertToJavaValues(JNIEnv* jenv, JPy_JMethod* jMethod, int argCount, PyObject* argTuple, jvalue* jArgs); 97 | 98 | int JMethod_CreateJArgs(JNIEnv* jenv, JPy_JMethod* jMethod, PyObject* argTuple, jvalue** jValues, JPy_ArgDisposer** jDisposers, int isVarArgsArray); 99 | void JMethod_DisposeJArgs(JNIEnv* jenv, int paramCount, jvalue* jValues, JPy_ArgDisposer* jDisposers); 100 | 101 | #ifdef __cplusplus 102 | } /* extern "C" */ 103 | #endif 104 | 105 | #endif /* !JPY_JMETHOD_H */ -------------------------------------------------------------------------------- /src/main/c/jpy_verboseexcept.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | #include 21 | #include "jpy_verboseexcept.h" 22 | 23 | int JPy_VerboseExceptions = 0; 24 | 25 | PyObject* VerboseExceptions_New(void) 26 | { 27 | return PyObject_New(PyObject, &VerboseExceptions_Type); 28 | } 29 | 30 | 31 | PyObject* VerboseExceptions_getattro(PyObject* self, PyObject *attr_name) 32 | { 33 | if (strcmp(JPy_AS_UTF8(attr_name), "enabled") == 0) { 34 | return PyBool_FromLong(JPy_VerboseExceptions); 35 | } else { 36 | return PyObject_GenericGetAttr(self, attr_name); 37 | } 38 | } 39 | 40 | 41 | int VerboseExceptions_setattro(PyObject* self, PyObject *attr_name, PyObject *v) 42 | { 43 | if (strcmp(JPy_AS_UTF8(attr_name), "enabled") == 0) { 44 | if (PyBool_Check(v)) { 45 | JPy_VerboseExceptions = v == Py_True; 46 | } else { 47 | PyErr_SetString(PyExc_ValueError, "value for 'flags' must be a boolean"); 48 | return -1; 49 | } 50 | return 0; 51 | } else { 52 | return PyObject_GenericSetAttr(self, attr_name, v); 53 | } 54 | } 55 | 56 | 57 | PyTypeObject VerboseExceptions_Type = 58 | { 59 | PyVarObject_HEAD_INIT(NULL, 0) 60 | "jpy.VerboseExceptions", /* tp_name */ 61 | sizeof (VerboseExceptions_Type), /* tp_basicsize */ 62 | 0, /* tp_itemsize */ 63 | NULL, /* tp_dealloc */ 64 | 0, /* tp_print */ 65 | NULL, /* tp_getattr */ 66 | NULL, /* tp_setattr */ 67 | NULL, /* tp_reserved */ 68 | NULL, /* tp_repr */ 69 | NULL, /* tp_as_number */ 70 | NULL, /* tp_as_sequence */ 71 | NULL, /* tp_as_mapping */ 72 | NULL, /* tp_hash */ 73 | NULL, /* tp_call */ 74 | NULL, /* tp_str */ 75 | (getattrofunc) VerboseExceptions_getattro, /* tp_getattro */ 76 | (setattrofunc) VerboseExceptions_setattro, /* tp_setattro */ 77 | NULL, /* tp_as_buffer */ 78 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 79 | "Controls python exception verbosity", /* tp_doc */ 80 | NULL, /* tp_traverse */ 81 | NULL, /* tp_clear */ 82 | NULL, /* tp_richcompare */ 83 | 0, /* tp_weaklistoffset */ 84 | NULL, /* tp_iter */ 85 | NULL, /* tp_iternext */ 86 | NULL, /* tp_methods */ 87 | NULL, /* tp_members */ 88 | NULL, /* tp_getset */ 89 | NULL, /* tp_base */ 90 | NULL, /* tp_dict */ 91 | NULL, /* tp_descr_get */ 92 | NULL, /* tp_descr_set */ 93 | 0, /* tp_dictoffset */ 94 | (initproc) NULL, /* tp_init */ 95 | NULL, /* tp_alloc */ 96 | NULL, /* tp_new */ 97 | }; 98 | -------------------------------------------------------------------------------- /src/main/c/jpy_conv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JPY_CONV_H 18 | #define JPY_CONV_H 19 | 20 | #include "jpy_compat.h" 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define JPy_AS_JBOOLEAN(pyArg) (jboolean) (pyArg == Py_True ? 1 : (pyArg == Py_False || pyArg == Py_None) ? 0 : PyObject_IsTrue(pyArg)) 27 | #define JPy_AS_JCHAR(pyArg) (jchar) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) 28 | #define JPy_AS_JBYTE(pyArg) (jbyte) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) 29 | #define JPy_AS_JSHORT(pyArg) (jshort) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) 30 | #define JPy_AS_JINT(pyArg) (jint) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) 31 | #define JPy_AS_JLONG(pyArg) (jlong) (pyArg == Py_None ? 0 : JPy_AS_CLONGLONG(pyArg)) 32 | #define JPy_AS_JFLOAT(pyArg) (jfloat) (pyArg == Py_None ? 0 : PyFloat_AsDouble(pyArg)) 33 | #define JPy_AS_JDOUBLE(pyArg) (jdouble) (pyArg == Py_None ? 0 : PyFloat_AsDouble(pyArg)) 34 | #define JPy_FROM_JBOOLEAN(jArg) PyBool_FromLong(jArg) 35 | #define JPy_FROM_JCHAR(jArg) PyLong_FromLong(jArg) 36 | #define JPy_FROM_JBYTE(jArg) PyLong_FromLong(jArg) 37 | #define JPy_FROM_JSHORT(jArg) PyLong_FromLong(jArg) 38 | #define JPy_FROM_JINT(jArg) PyLong_FromLong(jArg) 39 | #define JPy_FROM_JLONG(jArg) PyLong_FromLongLong(jArg) 40 | #define JPy_FROM_JFLOAT(jArg) PyFloat_FromDouble(jArg) 41 | #define JPy_FROM_JDOUBLE(jArg) PyFloat_FromDouble(jArg) 42 | 43 | #define JPy_PY_NONE() Py_BuildValue("") // Builds a Python 'None' object 44 | #define JPy_FROM_JVOID() JPy_PY_NONE() 45 | #define JPy_FROM_JNULL() JPy_PY_NONE() 46 | 47 | 48 | /** 49 | * Convert Java string to Python string/unicode object. 50 | */ 51 | PyObject* JPy_FromJString(JNIEnv* jenv, jstring stringRef); 52 | 53 | /** 54 | * Convert any Java Object to Python Object. 55 | */ 56 | PyObject* JPy_FromJObject(JNIEnv* jenv, jobject objectRef); 57 | 58 | /** 59 | * Convert any Java Object of known type to Python Object. 60 | */ 61 | PyObject* JPy_FromJObjectWithType(JNIEnv* jenv, jobject objectRef, JPy_JType* type); 62 | 63 | /** 64 | * Convert Python unicode object to Java String. 65 | */ 66 | int JPy_AsJString(JNIEnv* jenv, PyObject* pyObj, jstring* stringRef); 67 | 68 | /** 69 | * Convert any Python objects to Java object. 70 | * 71 | * @param allowObjectWrapping if true, may return a PyObject for unrecognized object types 72 | */ 73 | int JPy_AsJObject(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jboolean allowObjectWrapping); 74 | 75 | /** 76 | * Convert Python objects to Java object with known type. 77 | */ 78 | int JPy_AsJObjectWithType(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, JPy_JType* type); 79 | 80 | /** 81 | * Convert Python objects to Java object with known type. 82 | */ 83 | int JPy_AsJObjectWithClass(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jclass classRef); 84 | 85 | 86 | /** 87 | * Creates a Python unicode object representing the name of the given class. 88 | * Returns a new reference. 89 | */ 90 | PyObject* JPy_FromTypeName(JNIEnv* jenv, jclass classRef); 91 | 92 | /** 93 | * Gets the UTF8-encoded name of the given Java type. 94 | * Caller is responsible for freeing the returned string using PyMem_Del(). 95 | */ 96 | char* JPy_GetTypeName(JNIEnv* jenv, jclass classRef); 97 | 98 | 99 | #ifdef __cplusplus 100 | } /* extern "C" */ 101 | #endif 102 | 103 | #endif /* !JPY_CONV_H */ -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/MethodReturnValueTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | /** 20 | * Used as a test class for the test cases in jpy_retval_test.py 21 | * Note: Please make sure to not add any method overloads to this class. 22 | * This is done in {@link MethodOverloadTestFixture}. 23 | * 24 | * @author Norman Fomferra 25 | */ 26 | @SuppressWarnings("UnusedDeclaration") 27 | public class MethodReturnValueTestFixture { 28 | 29 | public void getVoid() { 30 | } 31 | 32 | public boolean getValue_boolean(boolean value) { 33 | return value; 34 | } 35 | 36 | public byte getValue_byte(byte value) { 37 | return value; 38 | } 39 | 40 | public short getValue_short(short value) { 41 | return value; 42 | } 43 | 44 | public int getValue_int(int value) { 45 | return value; 46 | } 47 | 48 | public long getValue_long(long value) { 49 | return value; 50 | } 51 | 52 | public float getValue_float(float value) { 53 | return value; 54 | } 55 | 56 | public double getValue_double(double value) { 57 | return value; 58 | } 59 | 60 | public String getString(String string) { 61 | return string; 62 | } 63 | 64 | public Thing getObject(Thing object) { 65 | return object; 66 | } 67 | 68 | /////////////////////////////////////////////////////////////////////////////////// 69 | // 1D-Array Return Values 70 | 71 | public boolean[] getArray1D_boolean(boolean item0, boolean item1, boolean item2) { 72 | return new boolean[]{item0, item1, item2}; 73 | } 74 | 75 | public byte[] getArray1D_byte(byte item0, byte item1, byte item2) { 76 | return new byte[]{item0, item1, item2}; 77 | } 78 | 79 | public short[] getArray1D_short(short item0, short item1, short item2) { 80 | return new short[]{item0, item1, item2}; 81 | } 82 | 83 | public int[] getArray1D_int(int item0, int item1, int item2) { 84 | return new int[]{item0, item1, item2}; 85 | } 86 | 87 | public long[] getArray1D_long(long item0, long item1, long item2) { 88 | return new long[]{item0, item1, item2}; 89 | } 90 | 91 | public float[] getArray1D_float(float item0, float item1, float item2) { 92 | return new float[]{item0, item1, item2}; 93 | } 94 | 95 | public double[] getArray1D_double(double item0, double item1, double item2) { 96 | return new double[]{item0, item1, item2}; 97 | } 98 | 99 | public String[] getArray1D_String(String item0, String item1, String item2) { 100 | return new String[]{item0, item1, item2}; 101 | } 102 | 103 | public Thing[] getArray1D_Object(Thing item0, Thing item1, Thing item2) { 104 | return new Thing[]{item0, item1, item2}; 105 | } 106 | 107 | // add other variants 108 | 109 | /////////////////////////////////////////////////////////////////////////////////// 110 | // 2D-Array Return Values 111 | 112 | public boolean[][] getArray2D_boolean(boolean item00, boolean item01, boolean item10, boolean item11) { 113 | return new boolean[][]{{item00, item01}, {item10, item11}}; 114 | } 115 | 116 | public byte[][] getArray2D_byte(byte item00, byte item01, byte item10, byte item11) { 117 | return new byte[][]{{item00, item01}, {item10, item11}}; 118 | } 119 | 120 | public int[][] getArray2D_byte(int item00, int item01, int item10, int item11) { 121 | return new int[][]{{item00, item01}, {item10, item11}}; 122 | } 123 | 124 | // add other variants 125 | } 126 | -------------------------------------------------------------------------------- /src/test/python/jpy_mt_eval_exec_test.py: -------------------------------------------------------------------------------- 1 | import math 2 | import unittest 3 | 4 | import jpyutil 5 | 6 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes', 'target/test-classes']) 7 | import jpy 8 | 9 | NUM_THREADS = 20 10 | 11 | 12 | # A CPU-bound task: computing a large number of prime numbers 13 | def is_prime(n: int) -> bool: 14 | if n <= 1: 15 | return False 16 | for i in range(2, int(math.sqrt(n)) + 1): 17 | if n % i == 0: 18 | return False 19 | return True 20 | 21 | 22 | def count_primes(start: int, end: int) -> int: 23 | count = 0 24 | for i in range(start, end): 25 | if is_prime(i): 26 | count += 1 27 | return count 28 | 29 | 30 | def use_circular_java_classes(): 31 | j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") 32 | j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") 33 | j_child2 = j_child2_class() 34 | j_child1 = j_child1_class.of(8) 35 | result = j_child1.parentMethod() 36 | assert result == 88 37 | assert 888 == j_child1.grandParentMethod() 38 | j_child1.refChild2(j_child2) 39 | assert 8 == j_child1.get_x() 40 | assert 10 == j_child1.y 41 | assert 100 == j_child1.z 42 | 43 | 44 | class MultiThreadedTestEvalExec(unittest.TestCase): 45 | def setUp(self): 46 | self.fixture = jpy.get_type("org.jpy.fixtures.MultiThreadedEvalTestFixture") 47 | self.assertIsNotNone(self.fixture) 48 | 49 | def test_inc_baz(self): 50 | baz = 15 51 | self.fixture.script("baz = baz + 1; self.assertGreater(baz, 15)", NUM_THREADS) 52 | # note: this *is* correct wrt python semantics w/ exec(code, globals(), locals()) 53 | # https://bugs.python.org/issue4831 (Note: it's *not* a bug, is working as intended) 54 | self.assertEqual(baz, 15) 55 | 56 | def test_exec_import(self): 57 | import sys 58 | self.assertTrue("json" not in sys.modules) 59 | self.fixture.script("import json", NUM_THREADS) 60 | self.assertTrue("json" in sys.modules) 61 | 62 | def test_exec_function_call(self): 63 | self.fixture.expression("use_circular_java_classes()", NUM_THREADS) 64 | 65 | def test_count_primes(self): 66 | self.fixture.expression("count_primes(1, 10000)", NUM_THREADS) 67 | 68 | def test_java_threading_jpy_get_type(self): 69 | 70 | py_script = """ 71 | j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") 72 | j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") 73 | j_child2 = j_child2_class() 74 | j_child1 = j_child1_class.of(8) 75 | result = j_child1.parentMethod() 76 | assert result == 88 77 | assert 888 == j_child1.grandParentMethod() 78 | j_child1.refChild2(j_child2) 79 | assert 8 == j_child1.get_x() 80 | assert 10 == j_child1.y 81 | assert 100 == j_child1.z 82 | """ 83 | self.fixture.script(py_script, NUM_THREADS) 84 | 85 | def test_py_threading_jpy_get_type(self): 86 | import threading 87 | 88 | test_self = self 89 | 90 | class MyThread(threading.Thread): 91 | def __init__(self): 92 | threading.Thread.__init__(self) 93 | 94 | def run(self): 95 | barrier.wait() 96 | j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") 97 | j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") 98 | j_child2 = j_child2_class() 99 | j_child1 = j_child1_class.of(8) 100 | test_self.assertEqual(88, j_child1.parentMethod()) 101 | test_self.assertEqual(888, j_child1.grandParentMethod()) 102 | test_self.assertIsNone(j_child1.refChild2(j_child2)) 103 | test_self.assertEqual(8, j_child1.get_x()) 104 | test_self.assertEqual(10, j_child1.y) 105 | test_self.assertEqual(100, j_child1.z) 106 | 107 | barrier = threading.Barrier(NUM_THREADS) 108 | threads = [] 109 | for i in range(NUM_THREADS): 110 | t = MyThread() 111 | t.start() 112 | threads.append(t) 113 | 114 | for t in threads: 115 | t.join() 116 | 117 | 118 | if __name__ == '__main__': 119 | print('\nRunning ' + __file__) 120 | unittest.main() 121 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/VarArgsTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * This file was modified by Deephaven Data Labs. 17 | * 18 | */ 19 | 20 | package org.jpy.fixtures; 21 | 22 | import java.lang.reflect.Array; 23 | 24 | /** 25 | * Used as a test class for the test cases in jpy_overload_test.py 26 | * 27 | * @author Norman Fomferra 28 | */ 29 | @SuppressWarnings("UnusedDeclaration") 30 | public class VarArgsTestFixture { 31 | 32 | public String join(String prefix, int ... a) { 33 | return stringifyArgs(prefix, a); 34 | } 35 | 36 | public String join(String prefix, double ... a) { 37 | return stringifyArgs(prefix, a); 38 | } 39 | public String join(String prefix, float ... a) { 40 | return stringifyArgs(prefix, a); 41 | } 42 | public String join(String prefix, String ... a) { 43 | return stringifyArgs(prefix, a); 44 | } 45 | 46 | public String joinFloat(String prefix, float ... a) { 47 | return stringifyArgs(prefix, a); 48 | } 49 | 50 | public String joinLong(String prefix, long ... a) { 51 | return stringifyArgs(prefix, a); 52 | } 53 | public String joinShort(String prefix, short ... a) { 54 | return stringifyArgs(prefix, a); 55 | } 56 | public String joinByte(String prefix, byte ... a) { 57 | return stringifyArgs(prefix, a); 58 | } 59 | public String joinChar(String prefix, char ... a) { 60 | return stringifyArgs(prefix, a); 61 | } 62 | public String joinBoolean(String prefix, boolean ... a) { 63 | return stringifyArgs(prefix, a); 64 | } 65 | public String joinObjects(String prefix, Object ... a) { 66 | return stringifyArgs(prefix, a); 67 | } 68 | 69 | public int chooseFixedArity(int... a) { 70 | return 2; 71 | } 72 | 73 | public int chooseFixedArity() { 74 | return 1; 75 | } 76 | 77 | public int stringOrObjectVarArgs(String ... a) { 78 | return 1 + a.length; 79 | } 80 | public int stringOrObjectVarArgs(Object ... a) { 81 | return 2 + a.length; 82 | } 83 | 84 | static String stringifyArgs(Object... args) { 85 | StringBuilder argString = new StringBuilder(); 86 | for (int i = 0; i < args.length; i++) { 87 | if (i > 0) { 88 | argString.append(","); 89 | } 90 | Object arg = args[i]; 91 | if (arg != null) { 92 | Class argClass = arg.getClass(); 93 | argString.append(argClass.getSimpleName()); 94 | argString.append('('); 95 | if (argClass.isArray()) { 96 | stringifyArray(arg, argString); 97 | } else { 98 | stringifyObject(arg, argString); 99 | } 100 | argString.append(')'); 101 | } else { 102 | argString.append("null"); 103 | } 104 | } 105 | return argString.toString(); 106 | } 107 | 108 | private static void stringifyObject(Object arg, StringBuilder argString) { 109 | argString.append(String.valueOf(arg)); 110 | } 111 | 112 | private static void stringifyArray(Object arg, StringBuilder argString) { 113 | boolean primitive = arg.getClass().getComponentType().isPrimitive(); 114 | int length = Array.getLength(arg); 115 | for (int i = 0; i < length; i++) { 116 | Object item = Array.get(arg, i); 117 | if (i > 0) { 118 | argString.append(","); 119 | } 120 | if (primitive) { 121 | argString.append(String.valueOf(item)); 122 | } else { 123 | argString.append(stringifyArgs(item)); 124 | } 125 | } 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/test/python/jpy_gettype_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | 4 | import jpyutil 5 | 6 | 7 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) 8 | import jpy 9 | 10 | 11 | if sys.version_info >= (3, 0, 0): 12 | TYPE_STR_PREFIX = '") 21 | 22 | IntArray2D = jpy.get_type('[[I') 23 | self.assertEqual(str(IntArray2D), TYPE_STR_PREFIX + "'[[I'>") 24 | 25 | IntArray3D = jpy.get_type('[[[I') 26 | self.assertEqual(str(IntArray3D), TYPE_STR_PREFIX + "'[[[I'>") 27 | 28 | with self.assertRaises(RuntimeError) as e: 29 | IntArray1D() 30 | self.assertEqual(str(e.exception), "no constructor found (missing JType attribute '__jinit__')") 31 | 32 | 33 | def test_get_class_of_object_array(self): 34 | StringArray1D = jpy.get_type('[Ljava.lang.String;') 35 | self.assertEqual(str(StringArray1D), TYPE_STR_PREFIX + "'[Ljava.lang.String;'>") 36 | 37 | StringArray2D = jpy.get_type('[[Ljava.lang.String;') 38 | self.assertEqual(str(StringArray2D), TYPE_STR_PREFIX + "'[[Ljava.lang.String;'>") 39 | 40 | StringArray3D = jpy.get_type('[[[Ljava.lang.String;') 41 | self.assertEqual(str(StringArray3D), TYPE_STR_PREFIX + "'[[[Ljava.lang.String;'>") 42 | 43 | with self.assertRaises(RuntimeError) as e: 44 | StringArray1D() 45 | self.assertEqual(str(e.exception), "no constructor found (missing JType attribute '__jinit__')") 46 | 47 | def test_get_class_fromm_inner_class(self): 48 | Point2D = jpy.get_type('java.awt.geom.Point2D') 49 | self.assertEqual(str(Point2D), TYPE_STR_PREFIX + "'java.awt.geom.Point2D'>") 50 | DoublePoint = jpy.get_type('java.awt.geom.Point2D$Double') 51 | self.assertEqual(str(DoublePoint), TYPE_STR_PREFIX + "'java.awt.geom.Point2D$Double'>") 52 | 53 | 54 | def test_get_class_of_unknown_type(self): 55 | with self.assertRaises(ValueError) as e: 56 | String = jpy.get_type('java.lang.Spring') 57 | self.assertEqual(str(e.exception), "Java class 'java.lang.Spring' not found") 58 | 59 | with self.assertRaises(ValueError) as e: 60 | IntArray = jpy.get_type('int[]') 61 | self.assertEqual(str(e.exception), "Java class 'int[]' not found") 62 | 63 | def test_issue_74(self): 64 | """ 65 | Try to create enough references to trigger collection by Python. 66 | """ 67 | java_types = ['boolean', 'char', 'byte', 'short', 'int', 'long', 68 | 'float', 'double', 'void', 'java.lang.String'] 69 | 70 | for java_type in java_types: 71 | for i in range(200): 72 | jpy.get_type(java_type) 73 | 74 | def test_cyclic_reference(self): 75 | """ 76 | Test if delaying resolving super classes breaks existing class reference pattern. 77 | """ 78 | j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") 79 | j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") 80 | j_child2 = j_child2_class() 81 | 82 | j_child1 = j_child1_class.of(8) 83 | self.assertEqual(88, j_child1.parentMethod()) 84 | self.assertEqual(888, j_child1.grandParentMethod()) 85 | self.assertIsNone(j_child1.refChild2(j_child2)) 86 | self.assertEqual(8, j_child1.get_x()) 87 | self.assertEqual(10, j_child1.y) 88 | self.assertEqual(100, j_child1.z) 89 | 90 | def test_component_type_resolution(self): 91 | j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") 92 | j_child1 = j_child1_class.of(8) 93 | j_child2s = j_child1.getChild2s() 94 | self.assertIn("[Lorg.jpy.fixtures.CyclicReferenceChild2;", repr(type(j_child2s))) 95 | for j_child2 in j_child2s: 96 | self.assertTrue(j_child2.getName().startswith("Child2")) 97 | 98 | def test_fail_init_supertype(self): 99 | with self.assertRaises(ValueError) as cm: 100 | j_child_class = jpy.get_type("org.jpy.fixtures.GetTypeFailureChild") 101 | 102 | 103 | if __name__ == '__main__': 104 | print('\nRunning ' + __file__) 105 | unittest.main() 106 | -------------------------------------------------------------------------------- /src/test/python/jpy_retval_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import jpyutil 4 | 5 | 6 | jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) 7 | import jpy 8 | 9 | 10 | class TestMethodReturnValues(unittest.TestCase): 11 | def setUp(self): 12 | self.Fixture = jpy.get_type('org.jpy.fixtures.MethodReturnValueTestFixture') 13 | self.assertIsNotNone(self.Fixture) 14 | self.Thing = jpy.get_type('org.jpy.fixtures.Thing') 15 | self.assertIsNotNone(self.Thing) 16 | 17 | def test_void(self): 18 | fixture = self.Fixture() 19 | self.assertEqual(fixture.getVoid(), None) 20 | 21 | def test_primitive_values(self): 22 | fixture = self.Fixture() 23 | self.assertEqual(fixture.getValue_boolean(True), True) 24 | self.assertEqual(fixture.getValue_byte(11), 11) 25 | self.assertEqual(fixture.getValue_short(12), 12) 26 | self.assertEqual(fixture.getValue_int(13), 13) 27 | self.assertEqual(fixture.getValue_long(14), 14) 28 | self.assertAlmostEqual(fixture.getValue_float(15.1), 15.1, places=5) 29 | self.assertEqual(fixture.getValue_double(16.2), 16.2) 30 | 31 | def test_objects(self): 32 | fixture = self.Fixture() 33 | obj = self.Thing() 34 | self.assertEqual(fixture.getString('Hi!'), 'Hi!') 35 | self.assertEqual(fixture.getObject(obj), obj) 36 | 37 | def test_array1d_boolean(self): 38 | fixture = self.Fixture() 39 | array = fixture.getArray1D_boolean(True, False, True) 40 | self.assertEqual(len(array), 3) 41 | self.assertEqual(array[0], True) 42 | self.assertEqual(array[1], False) 43 | self.assertEqual(array[2], True) 44 | 45 | def test_array1d_byte(self): 46 | fixture = self.Fixture() 47 | array = fixture.getArray1D_byte(-10, 20, 30) 48 | self.assertEqual(len(array), 3) 49 | self.assertEqual(array[0], -10) 50 | self.assertEqual(array[1], 20) 51 | self.assertEqual(array[2], 30) 52 | 53 | def test_array1d_short(self): 54 | fixture = self.Fixture() 55 | array = fixture.getArray1D_short(-10001, 20001, 30001) 56 | self.assertEqual(len(array), 3) 57 | self.assertEqual(array[0], -10001) 58 | self.assertEqual(array[1], 20001) 59 | self.assertEqual(array[2], 30001) 60 | 61 | def test_array1d_int(self): 62 | fixture = self.Fixture() 63 | array = fixture.getArray1D_int(-100001, 200001, 300001) 64 | self.assertEqual(len(array), 3) 65 | self.assertEqual(array[0], -100001) 66 | self.assertEqual(array[1], 200001) 67 | self.assertEqual(array[2], 300001) 68 | 69 | def test_array1d_long(self): 70 | fixture = self.Fixture() 71 | array = fixture.getArray1D_long(-10000000001, 20000000001, 30000000001) 72 | self.assertEqual(len(array), 3) 73 | self.assertEqual(array[0], -10000000001) 74 | self.assertEqual(array[1], 20000000001) 75 | self.assertEqual(array[2], 30000000001) 76 | 77 | def test_array1d_float(self): 78 | fixture = self.Fixture() 79 | array = fixture.getArray1D_float(-1.01, 2.01, 3.01) 80 | self.assertEqual(len(array), 3) 81 | self.assertAlmostEqual(array[0], -1.01, places=5) 82 | self.assertAlmostEqual(array[1], 2.01, places=5) 83 | self.assertAlmostEqual(array[2], 3.01, places=5) 84 | 85 | def test_array1d_double(self): 86 | fixture = self.Fixture() 87 | array = fixture.getArray1D_double(-1.01, 2.01, 3.01) 88 | self.assertEqual(len(array), 3) 89 | self.assertEqual(array[0], -1.01) 90 | self.assertEqual(array[1], 2.01) 91 | self.assertEqual(array[2], 3.01) 92 | 93 | def test_array1d_String(self): 94 | fixture = self.Fixture() 95 | array = fixture.getArray1D_String('A', 'B', 'C') 96 | self.assertEqual(len(array), 3) 97 | self.assertEqual(array[0], 'A') 98 | self.assertEqual(array[1], 'B') 99 | self.assertEqual(array[2], 'C') 100 | 101 | def test_array1d_Object(self): 102 | fixture = self.Fixture() 103 | array = fixture.getArray1D_Object(self.Thing(7), self.Thing(8), self.Thing(9)) 104 | self.assertEqual(len(array), 3) 105 | self.assertEqual(array[0], self.Thing(7)) 106 | self.assertEqual(array[1], self.Thing(8)) 107 | self.assertEqual(array[2], self.Thing(9)) 108 | 109 | 110 | if __name__ == '__main__': 111 | print('\nRunning ' + __file__) 112 | unittest.main() 113 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/org/jpy/class-use/DL.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Uses of Class org.jpy.DL (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 | 23 |

JavaScript is disabled on your browser.
25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 43 |
44 | 71 | 72 |
73 |

Uses of Class
org.jpy.DL

74 |
75 |
No usage of org.jpy.DL
76 | 77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 93 |
94 | 121 | 122 |

Copyright © 2014–2022 Brockmann Consult GmbH. All rights reserved.

123 | 124 | 125 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/org/jpy/class-use/PyLib.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Uses of Class org.jpy.PyLib (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 43 |
44 | 71 | 72 |
73 |

Uses of Class
org.jpy.PyLib

74 |
75 |
No usage of org.jpy.PyLib
76 | 77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 93 |
94 | 121 | 122 |

Copyright © 2014–2022 Brockmann Consult GmbH. All rights reserved.

123 | 124 | 125 | -------------------------------------------------------------------------------- /src/test/python/imp/import_ex1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing Python import machinery with the goal to let users import Java class like this: 3 | from jpy.java.io import File 4 | or 5 | from jpy import java.io.File as File 6 | """ 7 | 8 | __author__ = 'Norman' 9 | 10 | import sys 11 | import importlib.abc 12 | 13 | 14 | class JavaClass: 15 | def __init__(self, fullname): 16 | self.fullname = fullname 17 | 18 | def __repr__(self): 19 | return "JavaClass('" + self.fullname + "')" 20 | 21 | def __str__(self): 22 | return self.fullname 23 | 24 | 25 | class JavaPackageImporter(importlib.abc.MetaPathFinder): 26 | 27 | def __init__(self, file): 28 | self.packages = set() 29 | #with open(file, "r") as f: 30 | # for line in f: 31 | # package = line.strip() 32 | # self.register_package(package) 33 | #print(self.packages) 34 | 35 | 36 | def register_package(self, fullname): 37 | package = fullname 38 | while package: 39 | self.packages.add(package) 40 | dot_pos = package.rfind('.') 41 | if dot_pos > 0: 42 | package = package[:dot_pos] 43 | else: 44 | package = None 45 | 46 | 47 | def get_package(self, fullname): 48 | dot_pos = fullname.rfind('.') 49 | if dot_pos > 0: 50 | package = fullname[:dot_pos] 51 | name = fullname[dot_pos + 1:] 52 | else: 53 | package = '' 54 | name = fullname 55 | return (package, name) 56 | 57 | 58 | def load_java_class(self, module, fullname): 59 | print('try loading java class "' + fullname + '"...') 60 | package, name = self.get_package(fullname) 61 | #module.__dict__[name] = JavaClass(fullname) 62 | #return JavaClass(fullname) 63 | return None 64 | 65 | def new_module(self, fullname): 66 | package, name = self.get_package(fullname) 67 | 68 | print('creating module "' + fullname + '"') 69 | module = type(sys)(fullname) 70 | module.__loader__ = self 71 | module.__path__ = None 72 | module.__package__ = package 73 | return module 74 | 75 | 76 | def find_module(self, fullname, path): 77 | print('find_module(fullname="' + str(fullname) + '", path="' + str(path) + '")') 78 | if fullname in self.packages: 79 | return self 80 | 81 | package, name = self.get_package(fullname) 82 | if package in self.packages: 83 | return self 84 | 85 | return None 86 | 87 | 88 | def load_module(self, fullname): 89 | print('load_module(fullname="' + str(fullname) + '")') 90 | is_reload = fullname in sys.modules 91 | if is_reload: 92 | module = sys.modules[fullname] 93 | print('module found!') 94 | # Now, check what to do next...? 95 | else: 96 | if fullname in self.packages: 97 | print('known package: ' + fullname) 98 | module = self.new_module(fullname) 99 | sys.modules[fullname] = module 100 | else: 101 | print('not a known package: ' + fullname) 102 | package, class_name = self.get_package(fullname) 103 | module = sys.modules[package] 104 | if module is None: 105 | return None 106 | module = self.load_java_class(module, fullname) 107 | return module 108 | 109 | 110 | 111 | jpi = JavaPackageImporter('packages-jre7.txt') 112 | #jpi.register_package('org.esa.beam.framework.datamodel') 113 | #jpi.register_package('org.esa.beam.framework.dataio') 114 | #jpi.register_package('org.esa.beam.framework.gpf') 115 | 116 | sys.meta_path += [jpi] 117 | 118 | #import jpy 119 | import bibo 120 | import riser 121 | #from jpy import Riser as Riser 122 | 123 | 124 | import numpy 125 | from numpy import array 126 | 127 | 128 | #import java.io 129 | #import java.lang 130 | #import java.util 131 | #import numpy 132 | #import java.wraaaw 133 | #from java.util import String 134 | #from java.io import File 135 | 136 | #s = String('Hello') 137 | #print(f) 138 | 139 | #f = File('x') 140 | #print(f) 141 | 142 | 143 | #import os.path 144 | # 145 | #print(os.__name__) 146 | #print(os.__package__) 147 | #print(os.__file__) 148 | # 149 | #print(dir(java.io)) 150 | # 151 | # 152 | #print(os.__name__) 153 | #print(os.__package__) 154 | #print(os.__file__) 155 | #print(list(os.__path__)) 156 | #print(os.path.__name__) 157 | #print(os.path.__package__) 158 | #print(os.path.__file__) 159 | #print(list(os.path.__path__)) 160 | # 161 | #print('java.io:', dir(java.io)) 162 | 163 | -------------------------------------------------------------------------------- /doc/_static/java-apidocs/org/jpy/class-use/PyLib.Diag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Uses of Class org.jpy.PyLib.Diag (Java-Python Bridge 0.10.0-SNAPSHOT Java API) 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 43 |
44 | 71 | 72 |
73 |

Uses of Class
org.jpy.PyLib.Diag

74 |
75 |
No usage of org.jpy.PyLib.Diag
76 | 77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 93 |
94 | 121 | 122 |

Copyright © 2014–2022 Brockmann Consult GmbH. All rights reserved.

123 | 124 | 125 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/UseCases.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy; 18 | 19 | import org.junit.After; 20 | import org.junit.Before; 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | 24 | import java.util.Locale; 25 | 26 | import static org.junit.Assert.assertEquals; 27 | import static org.junit.Assert.assertTrue; 28 | 29 | /** 30 | * Some (more complex) tests that represent possible API use cases. 31 | * 32 | * @author Norman Fomferra 33 | */ 34 | public class UseCases { 35 | 36 | @Before 37 | public void setUp() { 38 | PyLib.startPython(); 39 | } 40 | 41 | @After 42 | public void tearDown() { 43 | PyLib.stopPython(); 44 | } 45 | 46 | @Test 47 | public void modifyPythonSysPath() { 48 | 49 | try ( 50 | final PyModule builtinsMod = PyModule.getBuiltins(); 51 | final PyModule sysMod = PyModule.importModule("sys"); 52 | final PyObject pathObj = sysMod.getAttribute("path")) { 53 | 54 | final PyObject lenObj1 = builtinsMod.call("len", pathObj); 55 | pathObj.call("append", "/usr/home/norman/"); 56 | final PyObject lenObj2 = builtinsMod.call("len", pathObj); 57 | 58 | int lenVal1 = lenObj1.getIntValue(); 59 | int lenVal2 = lenObj2.getIntValue(); 60 | String[] pathEntries = pathObj.getObjectArrayValue(String.class); 61 | 62 | lenObj2.close(); 63 | lenObj1.close(); 64 | 65 | ///////////////////////////////////////////////// 66 | 67 | assertEquals(lenVal1 + 1, lenVal2); 68 | assertEquals(pathEntries.length, lenVal2); 69 | //for (int i = 0; i < pathEntries.length; i++) { 70 | // System.out.printf("pathEntries[%d] = %s%n", i, pathEntries[i]); 71 | //} 72 | 73 | ///////////////////////////////////////////////// 74 | } 75 | } 76 | 77 | @Test 78 | public void setAndGetGlobalPythonVariables() throws Exception { 79 | 80 | PyLib.startPython(); 81 | PyLib.execScript("paramInt = 123"); 82 | PyLib.execScript("paramStr = 'abc'"); 83 | try ( 84 | final PyModule mainModule = PyModule.getMain(); 85 | final PyObject paramIntObj = mainModule.getAttribute("paramInt"); 86 | final PyObject paramStrObj = mainModule.getAttribute("paramStr")) { 87 | int paramIntValue = paramIntObj.getIntValue(); 88 | String paramStrValue = paramStrObj.getStringValue(); 89 | 90 | ///////////////////////////////////////////////// 91 | 92 | assertEquals(123, paramIntValue); 93 | assertEquals("abc", paramStrValue); 94 | 95 | ///////////////////////////////////////////////// 96 | } 97 | } 98 | 99 | @Test 100 | public void defAndUseGlobalPythonFunction() throws Exception { 101 | 102 | PyLib.startPython(); 103 | PyLib.execScript("def incByOne(x): return x + 1"); 104 | PyModule mainModule = PyModule.getMain(); 105 | PyObject eleven = mainModule.call("incByOne", 10); 106 | 107 | ///////////////////////////////////////////////// 108 | 109 | assertEquals(11, eleven.getIntValue()); 110 | 111 | ///////////////////////////////////////////////// 112 | // Performance test for TheMegaTB: 113 | 114 | long t0 = System.nanoTime(); 115 | long numCalls = 100000; 116 | PyObject num = eleven; 117 | for (long i = 0; i < numCalls; i++) { 118 | num = mainModule.call("incByOne", num); 119 | } 120 | long t1 = System.nanoTime(); 121 | 122 | assertEquals(11 + numCalls, num.getIntValue()); 123 | 124 | double millis = (t1 - t0) / 1000. / 1000.; 125 | double callsPerMilli = numCalls / millis; 126 | double millisPerCall = millis / numCalls; 127 | 128 | System.out.printf("Performance: %10.1f Python-calls/ms, %2.10f ms/Python-call%n", callsPerMilli, millisPerCall); 129 | assertTrue(callsPerMilli > 1.0); 130 | 131 | ///////////////////////////////////////////////// 132 | } 133 | 134 | static { 135 | Locale.setDefault(Locale.ENGLISH); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/test/java/org/jpy/fixtures/MethodOverloadTestFixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Brockmann Consult GmbH 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.jpy.fixtures; 18 | 19 | import java.lang.reflect.Array; 20 | 21 | /** 22 | * Used as a test class for the test cases in jpy_overload_test.py 23 | * 24 | * @author Norman Fomferra 25 | */ 26 | @SuppressWarnings("UnusedDeclaration") 27 | public class MethodOverloadTestFixture { 28 | 29 | public String join(int a, int b) { 30 | return stringifyArgs(a, b); 31 | } 32 | 33 | public String join(int a, double b) { 34 | return stringifyArgs(a, b); 35 | } 36 | 37 | public String join(int a, String b) { 38 | return stringifyArgs(a, b); 39 | } 40 | 41 | public String join(double a, int b) { 42 | return stringifyArgs(a, b); 43 | } 44 | 45 | public String join(double a, double b) { 46 | return stringifyArgs(a, b); 47 | } 48 | 49 | public String join(double a, String b) { 50 | return stringifyArgs(a, b); 51 | } 52 | 53 | public String join(String a, int b) { 54 | return stringifyArgs(a, b); 55 | } 56 | 57 | public String join(String a, double b) { 58 | return stringifyArgs(a, b); 59 | } 60 | 61 | public String join(String a, String b) { 62 | return stringifyArgs(a, b); 63 | } 64 | 65 | ////////////////////////////////////////////// 66 | 67 | public String join(String a) { 68 | return stringifyArgs(a); 69 | } 70 | 71 | public String join(String a, String b, String c) { 72 | return stringifyArgs(a, b, c); 73 | } 74 | 75 | ////////////////////////////////////////////// 76 | public String join2(Comparable a, int b, String c, String d) { 77 | return stringifyArgs(a, b, c, d); 78 | } 79 | 80 | ////////////////////////////////////////////// 81 | public String join3(Number a, int b) { 82 | return stringifyArgs(a, b); 83 | } 84 | 85 | /** 86 | * Used to test that we also find overloaded methods in class hierarchies 87 | */ 88 | public static class MethodOverloadTestFixture2 extends MethodOverloadTestFixture { 89 | 90 | public String join(String a, String b, String c, String d) { 91 | return stringifyArgs(a, b, c, d); 92 | } 93 | } 94 | 95 | ////////////////////////////////////////////// 96 | 97 | // Should never been found, since 'float' is not present in Python 98 | public String join(int a, float b) { 99 | return stringifyArgs(a, b); 100 | } 101 | 102 | static String stringifyArgs(Object... args) { 103 | StringBuilder argString = new StringBuilder(); 104 | for (int i = 0; i < args.length; i++) { 105 | if (i > 0) { 106 | argString.append(","); 107 | } 108 | Object arg = args[i]; 109 | if (arg != null) { 110 | Class argClass = arg.getClass(); 111 | argString.append(argClass.getSimpleName()); 112 | argString.append('('); 113 | if (argClass.isArray()) { 114 | stringifyArray(arg, argString); 115 | } else { 116 | stringifyObject(arg, argString); 117 | } 118 | argString.append(')'); 119 | } else { 120 | argString.append("null"); 121 | } 122 | } 123 | return argString.toString(); 124 | } 125 | 126 | private static void stringifyObject(Object arg, StringBuilder argString) { 127 | argString.append(String.valueOf(arg)); 128 | } 129 | 130 | private static void stringifyArray(Object arg, StringBuilder argString) { 131 | boolean primitive = arg.getClass().getComponentType().isPrimitive(); 132 | int length = Array.getLength(arg); 133 | for (int i = 0; i < length; i++) { 134 | Object item = Array.get(arg, i); 135 | if (i > 0) { 136 | argString.append(","); 137 | } 138 | if (primitive) { 139 | argString.append(String.valueOf(item)); 140 | } else { 141 | argString.append(stringifyArgs(item)); 142 | } 143 | } 144 | } 145 | 146 | 147 | } 148 | --------------------------------------------------------------------------------