jpy-2.0.0/0000775000175000017500000000000015202503243012473 5ustar alastairalastairjpy-2.0.0/setup.cfg0000664000175000017500000000015615202503234014316 0ustar alastairalastair[global] verbose = 1 [build] force = 1 [install] record = setup.out [metadata] description-file = README.mdjpy-2.0.0/pom.xml0000664000175000017500000001642415202503234014017 0ustar alastairalastair 4.0.0 UTF-8 org.jpyconsortium jpy 2.0.0 jar Java-Python Bridge jpy is a bi-directional Java-Python bridge which you can use to embed Java code in Python programs or the other way round. The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/jpy-consortium/jpy 2014 Brockmann Consult GmbH http://www.brockmann-consult.de Norman Fomferra norman.fomferra@brockmann-consult.de https://github.com/forman Brockmann Consult GmbH http://www.brockmann-consult.de Dave Voutila https://github.com/voutilad Mario Briggs https://github.com/mariobriggs chipkent https://github.com/chipkent scm:git:git@github.com:jpy-consortium/jpy.git scm:git:git@github.com:jpy-consortium/jpy.git git@github.com:jpy-consortium/jpy.git GitHub https://github.com/jpy-consortium/jpy/issues junit junit 4.13.2 test maven-compiler-plugin 3.10.1 11 11 true false UTF-8 maven-javadoc-plugin 3.4.0 ${project.basedir}/doc/_static java-apidocs ${project.name} ${project.version} Java API ${project.name} ${project.version} Java API org.jpy.annotations UTF-8 256m false false true org.jpy.jsr223:org.jpy.annotations https://docs.oracle.com/en/java/javase/11/docs/api/ org.apache.maven.wagon wagon-ssh 1.0-beta-7 jpy-maven-deploy maven-source-plugin 3.2.1 attach-sources jar maven-javadoc-plugin 3.4.0 attach-javadocs jar org.apache.maven.plugins maven-gpg-plugin 3.0.1 sign-artifacts verify sign org.sonatype.central central-publishing-maven-plugin 0.8.0 true central jpy-2.0.0/CODE_OF_CONDUCT.md0000664000175000017500000001262215202503234015275 0ustar alastairalastair # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [opencode@deephaven.io](mailto:opencode@deephaven.io). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations jpy-2.0.0/src/0000775000175000017500000000000015202503234013262 5ustar alastairalastairjpy-2.0.0/src/test/0000775000175000017500000000000015202503234014241 5ustar alastairalastairjpy-2.0.0/src/test/resources/0000775000175000017500000000000015202503234016253 5ustar alastairalastairjpy-2.0.0/src/test/resources/META-INF/0000775000175000017500000000000015202503234017413 5ustar alastairalastairjpy-2.0.0/src/test/resources/META-INF/services/0000775000175000017500000000000015202503234021236 5ustar alastairalastairjpy-2.0.0/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory0000664000175000017500000000004615202503234027516 0ustar alastairalastairorg.jpy.jsr223.ScriptEngineFactoryImpljpy-2.0.0/src/test/resources/pymodules/0000775000175000017500000000000015202503234020274 5ustar alastairalastairjpy-2.0.0/src/test/resources/pymodules/mod_1.py0000664000175000017500000000001315202503234021637 0ustar alastairalastairanswer = 42jpy-2.0.0/src/test/python/0000775000175000017500000000000015202503234015562 5ustar alastairalastairjpy-2.0.0/src/test/python/jpy_typeres_test.py0000664000175000017500000000450015202503234021547 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestTypeResolution(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.TypeResolutionTestFixture') self.assertIsNotNone(self.Fixture) def test_ThatTypeIsResolvedLate(self): self.assertTrue('org.jpy.fixtures.TypeResolutionTestFixture' in jpy.types) self.assertTrue('org.jpy.fixtures.TypeResolutionTestFixture$SuperThing' in jpy.types) self.assertTrue('org.jpy.fixtures.Thing' in jpy.types) fixture = self.Fixture() # Create a thing instance, the type 'org.jpy.fixtures.Thing' is not resolved yet thing = fixture.createSuperThing(2001) # Assert that 'org.jpy.fixtures.Thing' is not resolved yet SuperThing = jpy.types['org.jpy.fixtures.TypeResolutionTestFixture$SuperThing'] self.assertFalse('add' in SuperThing.__dict__) # Assert that 'org.jpy.fixtures.Thing' is not resolved yet Thing = jpy.types['org.jpy.fixtures.Thing'] self.assertFalse('getValue' in Thing.__dict__) # Calling 'add()' on SuperThing will resolve 'org.jpy.fixtures.Thing' thing.add(10) self.assertTrue('add' in SuperThing.__dict__) self.assertTrue('getValue' in Thing.__dict__) value = thing.getValue() self.assertEqual(value, 2011) # see https://github.com/bcdev/jpy/issues/63 def test_ThatJavaTypesHaveAValidClassAttribute(self): Long = jpy.get_type('java.lang.Long') self.assertIsNotNone(Long.jclass) Class = jpy.get_type('java.lang.Class') self.assertIsNotNone(Class.jclass) Object = jpy.get_type('java.lang.Object') self.assertIsNotNone(Object.jclass) # see https://github.com/bcdev/jpy/issues/64 def test_ThatInterfaceTypesIncludeMethodsOfExtendedTypes(self): ObjectInput = jpy.get_type('java.io.ObjectInput', resolve=True) # assert that a method declared of java.io.ObjectInput is in __dict__ self.assertTrue('readObject' in ObjectInput.__dict__) # assert that a method declared of java.io.DataInput is in __dict__ self.assertTrue('readLine' in ObjectInput.__dict__) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_java_embeddable_test.py0000664000175000017500000000141615202503234023124 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes', 'target/classes']) import jpy class TestJavaTests(unittest.TestCase): def testStartingAndStoppingIfAvailable(self): PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') PyLibTest.testStartingAndStoppingIfAvailable() def testPassStatement(self): PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') PyLibTest.testPassStatement() def testPrintStatement(self): PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') PyLibTest.testPrintStatement() def testIncrementByOne(self): PyLibTest = jpy.get_type('org.jpy.EmbeddableTest') PyLibTest.testIncrementByOne() if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_perf_test.py0000664000175000017500000000244115202503234021012 0ustar alastairalastairimport unittest import time import random import jpyutil jpyutil.init_jvm(jvm_maxmem='512M') import jpy class TestPerformance(unittest.TestCase): def test_general_rt_perf(self): Integer = jpy.get_type('java.lang.Integer') String = jpy.get_type('java.lang.String') File = jpy.get_type('java.io.File') HashMap = jpy.get_type('java.util.HashMap') # 1 million N = 1000000 indexes = list(range(N)) random.shuffle(indexes) t0 = time.time() pairs = [(Integer(index), File('path')) for index in indexes] t1 = time.time() print('Integer + File object instantiation took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') map = HashMap() t0 = time.time() for pair in pairs: i, f = pair map.put(i, f) t1 = time.time() print('HashMap.put() took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') t0 = time.time() for pair in pairs: i, f = pair f = map.get(i) t1 = time.time() print('HashMap.get() took', t1-t0, 's for', N, 'calls, this is', 1000*(t1-t0)/N, 'ms per call') if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_overload_test.py0000664000175000017500000002311515202503234021672 0ustar alastairalastair# This file was modified by Deephaven Data Labs. import unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestConstructorOverloads(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.ConstructorOverloadTestFixture') self.assertIsNotNone(self.Fixture) def test_FloatConstructors(self): fixture = self.Fixture() self.assertEqual(fixture.getState(), '') fixture = self.Fixture(12) self.assertEqual(fixture.getState(), 'Integer(12)') fixture = self.Fixture(12, 34) self.assertEqual(fixture.getState(), 'Integer(12),Integer(34)') fixture = self.Fixture(0.12) self.assertEqual(fixture.getState(), 'Float(0.12)') fixture = self.Fixture(0.12, 0.34) self.assertEqual(fixture.getState(), 'Float(0.12),Float(0.34)') fixture = self.Fixture(0.12, 34) self.assertEqual(fixture.getState(), 'Float(0.12),Integer(34)') fixture = self.Fixture(12, 0.34) self.assertEqual(fixture.getState(), 'Integer(12),Float(0.34)') with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: fixture = self.Fixture(12, '34') self.assertEqual(str(e.exception), 'no matching Java method overloads found') class TestMethodOverloads(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.MethodOverloadTestFixture') self.assertIsNotNone(self.Fixture) def test_2ArgOverloadsWithVaryingTypes(self): fixture = self.Fixture() self.assertEqual(fixture.join(12, 32), 'Integer(12),Integer(32)') self.assertEqual(fixture.join(12, 3.2), 'Integer(12),Double(3.2)') self.assertEqual(fixture.join(12, 'abc'), 'Integer(12),String(abc)') self.assertEqual(fixture.join(1.2, 32), 'Double(1.2),Integer(32)') self.assertEqual(fixture.join(1.2, 3.2), 'Double(1.2),Double(3.2)') self.assertEqual(fixture.join(1.2, 'abc'), 'Double(1.2),String(abc)') self.assertEqual(fixture.join('efg', 32), 'String(efg),Integer(32)') self.assertEqual(fixture.join('efg', 3.2), 'String(efg),Double(3.2)') self.assertEqual(fixture.join('efg', 'abc'), 'String(efg),String(abc)') with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: fixture.join(object(), 32) self.assertEqual(str(e.exception), 'no matching Java method overloads found') def test_nArgOverloads(self): fixture = self.Fixture() self.assertEqual(fixture.join('x'), 'String(x)') self.assertEqual(fixture.join('x', 'y'), 'String(x),String(y)') self.assertEqual(fixture.join('x', 'y', 'z'), 'String(x),String(y),String(z)') with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: fixture.join('x', 'y', 'z', 'u') self.assertEqual(str(e.exception), 'no matching Java method overloads found') def test_nArgOverloadsAreFoundInBaseClass(self): Fixture = jpy.get_type('org.jpy.fixtures.MethodOverloadTestFixture$MethodOverloadTestFixture2') fixture = Fixture() self.assertEqual(fixture.join('x'), 'String(x)') self.assertEqual(fixture.join('x', 'y'), 'String(x),String(y)') self.assertEqual(fixture.join('x', 'y', 'z'), 'String(x),String(y),String(z)') self.assertEqual(fixture.join('x', 'y', 'z', 'u'), 'String(x),String(y),String(z),String(u)') with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: fixture.join('x', 'y', 'z', 'u', 'v') self.assertEqual(str(e.exception), 'no matching Java method overloads found') def test_stringAsComparable(self): fixture = self.Fixture() self.assertEqual(fixture.join2("a", 1, "c", "d"), 'String(a),Integer(1),String(c),String(d)') def test_stringAsNumber(self): fixture = self.Fixture() with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: fixture.join3('x', 2) self.assertEqual(str(e.exception), 'no matching Java method overloads found') def test_numbersAsNumber(self): fixture = self.Fixture() self.assertEqual(fixture.join3(1, 2), 'Byte(1),Integer(2)') self.assertEqual(fixture.join3(1.1, 2), 'Double(1.1),Integer(2)') def test_numbersAsComparable(self): fixture = self.Fixture() self.assertEqual(fixture.join2(1, 2, "c", "d"), 'Integer(1),Integer(2),String(c),String(d)') self.assertEqual(fixture.join2(1.1, 2, "c", "d"), 'Double(1.1),Integer(2),String(c),String(d)') class TestVarArgs(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.VarArgsTestFixture') self.assertIsNotNone(self.Fixture) def test_varargsEmpty(self): fixture = self.Fixture() self.assertEqual(fixture.joinFloat("Prefix"), 'String(Prefix),float[]()') with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: res = fixture.join("Prefix") self.assertEqual(str(e.exception), 'ambiguous Java method call, too many matching method overloads found') def test_varargs(self): fixture = self.Fixture() self.assertEqual(fixture.join("Prefix", "a", "b", "c"), 'String(Prefix),String[](String(a),String(b),String(c))') self.assertEqual(fixture.join("Prefix", 1, 2, 3), 'String(Prefix),int[](1,2,3)') self.assertEqual(fixture.join("Prefix", 1.1, 2.1, 3.1), 'String(Prefix),double[](1.1,2.1,3.1)') self.assertEqual(fixture.joinFloat("Prefix", 1.1, 2.1, 3.1), 'String(Prefix),float[](1.1,2.1,3.1)') self.assertEqual(fixture.joinLong("Prefix", 1, 2, 3), 'String(Prefix),long[](1,2,3)') bignum = 8589934592 self.assertEqual(fixture.joinLong("Prefix", 1, 2, 3, bignum), 'String(Prefix),long[](1,2,3,'+str(bignum)+')') self.assertEqual(fixture.joinByte("Prefix", 1, 2, 3), 'String(Prefix),byte[](1,2,3)') self.assertEqual(fixture.joinShort("Prefix", 1, 2, 3, 4), 'String(Prefix),short[](1,2,3,4)') self.assertEqual(fixture.joinChar("Prefix", 65, 66), 'String(Prefix),char[](A,B)') self.assertEqual(fixture.joinBoolean("Prefix", True, False), 'String(Prefix),boolean[](true,false)') self.assertEqual(fixture.joinObjects("Prefix", True, "A String", 3), 'String(Prefix),Object[](Boolean(true),String(A String),Byte(3))') self.assertEqual(fixture.joinObjects("Prefix", True, 0, 127, 128, 32767, 32768, 2147483647, 2147483648), 'String(Prefix),Object[](Boolean(true),Byte(0),Byte(127),Short(128),Short(32767),Integer(32768),Integer(2147483647),Long(2147483648))') def test_fixedArity(self): fixture = self.Fixture() self.assertEqual(fixture.chooseFixedArity(), 1) self.assertEqual(fixture.chooseFixedArity(1), 2) self.assertEqual(fixture.chooseFixedArity(1, 2), 2) def test_stringVsObject(self): fixture = self.Fixture() self.assertEqual(fixture.stringOrObjectVarArgs(["a", "b"]), 3) self.assertEqual(fixture.stringOrObjectVarArgs([1, 2, 3]), 5) class TestOtherMethodResolutionCases(unittest.TestCase): # see https://github.com/bcdev/jpy/issues/55 def test_toReproduceAndFixIssue55(self): Paths = jpy.get_type('java.nio.file.Paths') # The following statement will execute the var args method without any arguments p = Paths.get('testfile') # This is the workaround that was previously required p = Paths.get('testfile', []) # see https://github.com/bcdev/jpy/issues/56 def test_toReproduceAndFixIssue56(self): Paths = jpy.get_type('java.nio.file.Paths') p = Paths.get('testfile', []) s = str(p) self.assertEqual(s, 'testfile') # The following call crashed the Python interpreter with JDK/JRE 1.8.0 < update 60. s = p.toString() self.assertEqual(s, 'testfile') # see https://github.com/bcdev/jpy/issues/57 def test_toReproduceAndFixIssue57(self): HashMap = jpy.get_type('java.util.HashMap') Map = jpy.get_type('java.util.Map') m = HashMap() c = m.getClass() self.assertEqual(c.getName(), 'java.util.HashMap') m = jpy.cast(m, Map) # without the fix, we get "AttributeError: 'java.util.Map' object has no attribute 'getClass'" c = m.getClass() self.assertEqual(c.getName(), 'java.util.HashMap') # see https://github.com/bcdev/jpy/issues/54 def test_toReproduceAndFixIssue54(self): String = jpy.get_type('java.lang.String') Arrays = jpy.get_type('java.util.Arrays') a = jpy.array(String, ['A', 'B', 'C']) # jpy.diag.flags = jpy.diag.F_METH s = Arrays.toString(a) # jpy.diag.flags = 0 # without the fix, we get str(s) = "java.lang.String@xxxxxx" self.assertEqual(str(s), '[A, B, C]') class TestDefaultMethods(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.DefaultInterfaceImplTestFixture') self.assertIsNotNone(self.Fixture) # see https://github.com/bcdev/jpy/issues/102 def test_defaultedInterfaces(self): fixture = self.Fixture() self.assertEqual(fixture.doItPlusOne(), 3) class TestCovariantReturn(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.CovariantOverloadExtendTestFixture') self.assertIsNotNone(self.Fixture) def test_covariantReturn(self): fixture = self.Fixture(1) self.assertEqual(fixture.foo(4, 1).getX(), 6) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_diag_test.py0000664000175000017500000000237615202503234020771 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M') import jpy class TestJavaArrays(unittest.TestCase): def test_diag_flags_constants(self): self.assertIsNotNone(jpy.diag) self.assertIsNotNone(jpy.diag.flags) self.assertEqual(jpy.diag.F_OFF, 0x00) self.assertEqual(jpy.diag.F_TYPE, 0x01) self.assertEqual(jpy.diag.F_METH, 0x02) self.assertEqual(jpy.diag.F_EXEC, 0x04) self.assertEqual(jpy.diag.F_MEM, 0x08) self.assertEqual(jpy.diag.F_JVM, 0x10) self.assertEqual(jpy.diag.F_ERR, 0x20) self.assertEqual(jpy.diag.F_ALL, 0xff) def test_diag_flags_value(self): self.assertIsNotNone(jpy.diag) self.assertEqual(jpy.diag.flags, 0) jpy.diag.flags = 1 self.assertEqual(jpy.diag.flags, 1) jpy.diag.flags = 0 self.assertEqual(jpy.diag.flags, 0) jpy.diag.flags = jpy.diag.F_EXEC + jpy.diag.F_MEM self.assertEqual(jpy.diag.flags, 12) jpy.diag.flags = 0 self.assertEqual(jpy.diag.flags, 0) jpy.diag.flags += jpy.diag.F_EXEC jpy.diag.flags += jpy.diag.F_MEM self.assertEqual(jpy.diag.flags, 12) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_field_test.py0000664000175000017500000001063415202503234021144 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestFields(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.FieldTestFixture') self.assertIsNotNone(self.Fixture) self.Thing = jpy.get_type('org.jpy.fixtures.Thing') self.assertIsNotNone(self.Thing) self.String = jpy.get_type('java.lang.String') self.assertIsNotNone(self.String) def test_static_fields(self): self.assertEqual(self.Fixture.z_STATIC_FIELD, True) self.assertEqual(self.Fixture.c_STATIC_FIELD, 65) self.assertEqual(self.Fixture.b_STATIC_FIELD, 123) self.assertEqual(self.Fixture.s_STATIC_FIELD, 12345) self.assertEqual(self.Fixture.i_STATIC_FIELD, 123456789) self.assertEqual(self.Fixture.j_STATIC_FIELD, 1234567890123456789) self.assertAlmostEqual(self.Fixture.f_STATIC_FIELD, 0.12345, places=5) self.assertAlmostEqual(self.Fixture.d_STATIC_FIELD, 0.123456789) self.assertEqual(self.Fixture.S_OBJ_STATIC_FIELD, 'ABC') self.assertEqual(self.Fixture.l_OBJ_STATIC_FIELD, self.Thing(123)) def test_primitive_instance_fields(self): fixture = self.Fixture() self.assertEqual(fixture.zInstField, False) self.assertEqual(fixture.cInstField, 0) self.assertEqual(fixture.bInstField, 0) self.assertEqual(fixture.sInstField, 0) self.assertEqual(fixture.iInstField, 0) self.assertEqual(fixture.jInstField, 0) self.assertEqual(fixture.fInstField, 0) self.assertEqual(fixture.dInstField, 0) fixture.zInstField = True fixture.cInstField = 65 fixture.bInstField = 123 fixture.sInstField = 12345 fixture.iInstField = 123456789 fixture.jInstField = 1234567890123456789 fixture.fInstField = 0.12345 fixture.dInstField = 0.123456789 self.assertEqual(fixture.zInstField, True) self.assertEqual(fixture.cInstField, 65) self.assertEqual(fixture.bInstField, 123) self.assertEqual(fixture.sInstField, 12345) self.assertEqual(fixture.iInstField, 123456789) self.assertEqual(fixture.jInstField, 1234567890123456789) self.assertAlmostEqual(fixture.fInstField, 0.12345, places=5) self.assertAlmostEqual(fixture.dInstField, 0.123456789) # this seems a bit stretchy, but anyone familiar with strong typed languages shouldn't expect # that values of types other than boolean can be assigned to a boolean field. fixture.zInstField = [] self.assertEqual(fixture.zInstField, False) fixture.zInstField = [None] self.assertEqual(fixture.zInstField, True) def test_object_instance_fields(self): fixture = self.Fixture() self.assertEqual(fixture.zObjInstField, None) self.assertEqual(fixture.cObjInstField, None) self.assertEqual(fixture.bObjInstField, None) self.assertEqual(fixture.sObjInstField, None) self.assertEqual(fixture.iObjInstField, None) self.assertEqual(fixture.jObjInstField, None) self.assertEqual(fixture.fObjInstField, None) self.assertEqual(fixture.dObjInstField, None) self.assertEqual(fixture.SObjInstField, None) self.assertEqual(fixture.lObjInstField, None) fixture.zObjInstField = True fixture.cObjInstField = 65 fixture.bObjInstField = 123 fixture.sObjInstField = 12345 fixture.iObjInstField = 123456789 fixture.jObjInstField = 1234567890123456789 fixture.fObjInstField = 0.12345 fixture.dObjInstField = 0.123456789 fixture.SObjInstField = 'ABC' fixture.lObjInstField = self.Thing(123) self.assertEqual(fixture.zObjInstField, True) self.assertEqual(fixture.cObjInstField, 65) self.assertEqual(fixture.bObjInstField, 123) self.assertEqual(fixture.sObjInstField, 12345) self.assertEqual(fixture.iObjInstField, 123456789) self.assertEqual(fixture.jObjInstField, 1234567890123456789) self.assertAlmostEqual(fixture.fObjInstField, 0.12345, places=5) self.assertAlmostEqual(fixture.dObjInstField, 0.123456789) self.assertEqual(fixture.SObjInstField, 'ABC') self.assertEqual(fixture.lObjInstField, self.Thing(123)) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_rt_test.py0000664000175000017500000001461315202503234020507 0ustar alastairalastairimport unittest import sys import jpyutil jpyutil.init_jvm(jvm_maxmem='512M') import jpy class TestString(unittest.TestCase): """ Tests various Java SE classes from rt.jar """ def setUp(self): self.String = jpy.get_type('java.lang.String') self.assertIsNotNone(self.String) def test_constructor(self): s = self.String('Bibo') self.assertEqual(type(s), self.String) self.assertEqual(str(s), 'Bibo') def test_unicode_constructor_with_py27(self): # This test is actually the same as test_constructor(), but 'str' is not 'unicode' in Python 2.7 s = self.String(u'Bibo') self.assertEqual(type(s), self.String) self.assertEqual(str(s), 'Bibo') def test_toString(self): s = self.String('Bibo') self.assertTrue('toString' in self.String.__dict__) s = s.toString() self.assertEqual(s, 'Bibo') def test_substring(self): s = self.String('Bibo') self.assertTrue('substring' in self.String.__dict__) s2 = s.substring(0, 2) self.assertEqual(s2, 'Bi') s2 = s.substring(2) self.assertEqual(s2, 'bo') def test_split(self): s = self.String('/usr/local/bibo') self.assertTrue('split' in self.String.__dict__) array = s.split('/') self.assertEqual(len(array), 4) self.assertEqual(array[0], '') self.assertEqual(array[1], 'usr') self.assertEqual(array[2], 'local') self.assertEqual(array[3], 'bibo') array = s.split('/', 2) self.assertEqual(array[0], '') self.assertEqual(array[1], 'usr/local/bibo') def test_getBytes(self): s = self.String('Bibo') self.assertTrue('getBytes' in self.String.__dict__) array = s.getBytes() if sys.version_info >= (3, 0, 0): self.assertEqual(str(type(array)), "") else: self.assertEqual(str(type(array)), "") self.assertEqual(len(array), 4) self.assertEqual(array[0], 66) self.assertEqual(array[1], 105) self.assertEqual(array[2], 98) self.assertEqual(array[3], 111) def test_getClass(self): s = self.String() c = s.getClass() self.assertEqual('java.lang.String', c.getName()) import os class TestFile(unittest.TestCase): def setUp(self): self.File = jpy.get_type('java.io.File') self.assertIsNotNone(self.File) def test_constructor(self): f = self.File('/usr/local/bibo') self.assertEqual(type(f), self.File) self.assertEqual(str(f).split(os.sep), ['', 'usr', 'local', 'bibo']) def test_getPath(self): f = self.File('/usr/local/bibo') self.assertTrue('getPath' in self.File.__dict__) path = f.getPath() self.assertEqual(path.split(os.sep), ['', 'usr', 'local', 'bibo']) def test_getName(self): f = self.File('/usr/local/bibo') self.assertTrue('getName' in self.File.__dict__) name = f.getName() self.assertEqual(name, 'bibo') def test_toPath(self): f = self.File('/usr/local/bibo') self.assertTrue('toPath' in self.File.__dict__) path = f.toPath() if sys.version_info >= (3, 0, 0): self.assertEqual(str(type(path)), '') else: self.assertEqual(str(type(path)), '') jpy.get_type('java.nio.file.Path') n1 = path.getName(0) n2 = path.getName(1) n3 = path.getName(2) self.assertEqual(str(n1), 'usr') self.assertEqual(str(n2), 'local') self.assertEqual(str(n3), 'bibo') def test_toString(self): f = self.File('bibo') self.assertTrue('toString' in self.File.__dict__) s = f.toString() self.assertEqual(s, 'bibo') class TestArrayList(unittest.TestCase): def setUp(self): self.ArrayList = jpy.get_type('java.util.ArrayList') self.File = jpy.get_type('java.io.File') def test_ArrayList(self): f = self.File('/usr/local/bibo') array_list = self.ArrayList() array_list.add('A') array_list.add(12) array_list.add(3.4) array_list.add(f) self.assertEqual(array_list.size(), 4) self.assertEqual(array_list.get(0), 'A') self.assertEqual(array_list.get(1), 12) self.assertEqual(array_list.get(2), 3.4) self.assertEqual(array_list.get(3), f) self.assertEqual(type(array_list.get(3)), type(f)) array_list = self.ArrayList(array_list) self.assertEqual(array_list.size(), 4) self.assertEqual(array_list.get(0), 'A') self.assertEqual(array_list.get(1), 12) self.assertEqual(array_list.get(2), 3.4) self.assertEqual(array_list.get(3), f) self.assertEqual(type(array_list.get(3)), type(f)) array = array_list.toArray() self.assertEqual(len(array), 4) self.assertEqual(array[0], 'A') self.assertEqual(array[1], 12) self.assertEqual(array[2], 3.4) self.assertEqual(array[3], f) self.assertEqual(type(array[3]), type(f)) class TestHashMap(unittest.TestCase): def setUp(self): self.HashMap = jpy.get_type('java.util.HashMap') self.File = jpy.get_type('java.io.File') def test_HashMap(self): f = self.File('/usr/local/bibo') fa = jpy.array('java.io.File', 2) fa[0] = f fa[1] = f hash_map = self.HashMap() hash_map.put(0, 'A') hash_map.put(1, 12) hash_map.put(2, 3.4) hash_map.put(3, f) hash_map.put(4, fa) self.assertEqual(hash_map.size(), 5) self.assertEqual(hash_map.get(0), 'A') self.assertEqual(hash_map.get(1), 12) self.assertEqual(hash_map.get(2), 3.4) self.assertEqual(hash_map.get(3), f) self.assertEqual(type(hash_map.get(3)), type(f)) self.assertEqual(hash_map.get(4), fa) hash_map = self.HashMap(hash_map) self.assertEqual(hash_map.size(), 5) self.assertEqual(hash_map.get(0), 'A') self.assertEqual(hash_map.get(1), 12) self.assertEqual(hash_map.get(2), 3.4) self.assertEqual(hash_map.get(3), f) self.assertEqual(type(hash_map.get(3)), type(f)) self.assertEqual(hash_map.get(4), fa) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_eval_exec_test.py0000664000175000017500000000236615202503234022017 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes', 'target/test-classes']) import jpy class TestEvalExec(unittest.TestCase): def setUp(self): self.fixture = jpy.get_type("org.jpy.fixtures.EvalTestFixture") self.assertIsNotNone(self.fixture) def test_foo_42(self): foo = 42 self.assertEqual(self.fixture.expression("foo"), 42) def test_foo_bar(self): foo = "bar" self.assertEqual(self.fixture.expression("foo"), "bar") def test_x_add_y(self): x = 123 y = 456 self.assertEqual(self.fixture.expression("x + y"), 579) def test_inc_baz(self): baz = 15 self.fixture.script("baz = baz + 1; self.assertEqual(baz, 16)") # note: this *is* correct wrt python semantics w/ exec(code, globals(), locals()) # https://bugs.python.org/issue4831 (Note: it's *not* a bug, is working as intended) self.assertEqual(baz, 15) def test_exec_import(self): import sys self.assertTrue("json" not in sys.modules) self.fixture.script("import json") self.assertTrue("json" in sys.modules) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_typeconv_test_pyobj.py0000664000175000017500000000435015202503234023131 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes']) import jpy class TestTypeConversionsPyObj(unittest.TestCase): """ This test covers explicitly converting Python objects to Java PyObject instances. It is separate from jpy_typeconv_test.py because it requires a different classpath. """ def test_convert_toPyObject(self): # Note that this test requires jvm_classpath=['target/classes'] (not jvm_classpath=['target/***test-***classes'] print('Starting test_convert_toPyObject') PyObject_type = jpy.get_type('org.jpy.PyObject') print('test_convert_toPyObject: Got type for PyObject') print('test_convert_toPyObject: Testing value: \'A\'') print('test_convert_toPyObject: Doing first conversion') val = 'A' conv = jpy.convert(val, PyObject_type) print('test_convert_toPyObject: Getting first pointer') ptr = conv.getPointer() print('test_convert_toPyObject: Got first pointer') self.assertEqual(ptr, id(val)) print('test_convert_toPyObject: Passed first assertion') print('test_convert_toPyObject: Testing value: string') val = 'ABCDE' self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('test_convert_toPyObject: Testing value: True') val = True self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('test_convert_toPyObject: Testing value: False') val = False self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('test_convert_toPyObject: Testing value: 12') val = 12 self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('test_convert_toPyObject: Testing value: 12.2') val = 12.2 self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('test_convert_toPyObject: Testing value: [1, 2.0, "ABCDE"]') val = [1, 2.0, "ABCDE"] self.assertEqual(jpy.convert(val, PyObject_type).getPointer(), id(val)) print('Finished test_convert_toPyObject') if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_typeconv_test.py0000664000175000017500000004642515202503234021737 0ustar alastairalastairimport unittest import array import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestTypeConversions(unittest.TestCase): """ This test covers type conversion, including: - Automatic conversions of Python values to Java when calling Java methods from Python - jpy.cast() (See JPy_cast) - jpy.convert() (See JPy_convert_internal / JType_ConvertPythonToJavaObject) """ def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.TypeConversionTestFixture') self.assertTrue('org.jpy.fixtures.TypeConversionTestFixture' in jpy.types) def test_ToObjectConversion(self): """ Test automatic conversion of Python values to Java objects (when passing Python values as arguments to Java methods). """ fixture = self.Fixture() self.assertEqual(fixture.stringifyObjectArg(12), 'Byte(12)') self.assertEqual(fixture.stringifyObjectArg(0.34), 'Double(0.34)') self.assertEqual(fixture.stringifyObjectArg('abc'), 'String(abc)') with self.assertRaises(ValueError) as e: fixture.stringifyObjectArg(1 + 2j) self.assertEqual(str(e.exception), 'cannot convert a Python \'complex\' to a Java \'java.lang.Object\'') def test_cast(self): """ Test casts of Java objects using jpy.cast() """ fixture = self.Fixture() # Create a test String: my_jstr = jpy.get_type('java.lang.String')('testStr') self.assertEqual(type(my_jstr).jclassname, 'java.lang.String') self.assertEqual(fixture.stringifyObjectArg(my_jstr), 'String(testStr)') # Cast to String (this should be a no-op) my_jcharseq1 = jpy.cast(my_jstr, 'java.lang.String') self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq1)) # Should be same Java object... # self.assertTrue(my_jcharseq1 is my_jstr) # and the same Python object. (But currently a new one is returned.) self.assertEqual(type(my_jcharseq1).jclassname, 'java.lang.String') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq1), 'String(testStr)') # Cast to CharSequence (using class name, not explicit jpy.get_type()) my_jcharseq1 = jpy.cast(my_jstr, 'java.lang.CharSequence') self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq1)) # Should be same Java object... self.assertFalse(my_jcharseq1 is my_jstr) # but a new Python object self.assertEqual(type(my_jcharseq1).jclassname, 'java.lang.CharSequence') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq1), 'String(testStr)') # Cast to CharSequence (using explicit jpy.get_type()): my_jcharseq2 = jpy.cast(my_jstr, jpy.get_type('java.lang.CharSequence')) self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq2)) # Should be same Java object... self.assertFalse(my_jcharseq2 is my_jstr) # but a new Python object self.assertEqual(type(my_jcharseq2).jclassname, 'java.lang.CharSequence') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq2), 'String(testStr)') def test_convert_cast(self): """ Test casts of Java objects using jpy.convert() """ fixture = self.Fixture() # Create a test String: my_jstr = jpy.get_type('java.lang.String')('testStr') self.assertEqual(type(my_jstr).jclassname, 'java.lang.String') self.assertEqual(fixture.stringifyObjectArg(my_jstr), 'String(testStr)') # Cast to String (this should be a no-op) my_jcharseq1 = jpy.convert(my_jstr, 'java.lang.String') self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq1)) # Should be same Java object... # self.assertTrue(my_jcharseq1 is my_jstr) # and the same Python object. (But currently a new one is returned.) self.assertEqual(type(my_jcharseq1).jclassname, 'java.lang.String') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq1), 'String(testStr)') # Cast to CharSequence (using class name, not explicit jpy.get_type()) my_jcharseq1 = jpy.convert(my_jstr, 'java.lang.CharSequence') self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq1)) # Should be same Java object... self.assertFalse(my_jcharseq1 is my_jstr) # but a new Python object. self.assertEqual(type(my_jcharseq1).jclassname, 'java.lang.CharSequence') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq1), 'String(testStr)') # Cast to CharSequence (using explicit jpy.get_type()): my_jcharseq2 = jpy.convert(my_jstr, jpy.get_type('java.lang.CharSequence')) self.assertTrue(fixture.isSameObject(my_jstr, my_jcharseq2)) # Should be same Java object... self.assertFalse(my_jcharseq2 is my_jstr) # but a new Python object. self.assertEqual(type(my_jcharseq2).jclassname, 'java.lang.CharSequence') self.assertEqual(fixture.stringifyObjectArg(my_jcharseq2), 'String(testStr)') def test_convert_toBoxedPrimitive(self): fixture = self.Fixture() # Convert Python values to boxed types explicitly (using jpy.get_type() to retrieve the boxed type): self.assertEqual(fixture.stringifyObjectArg(jpy.convert(65, jpy.get_type('java.lang.Character'))), 'Character(A)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Byte'))), 'Byte(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Short'))), 'Short(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Integer'))), 'Integer(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Long'))), 'Long(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Float'))), 'Float(12.0)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('java.lang.Double'))), 'Double(12.0)') # Convert Python values to boxed types (using type name, not explicit jpy.get_type()) self.assertEqual(fixture.stringifyObjectArg(jpy.convert(65, 'java.lang.Character')), 'Character(A)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Byte')), 'Byte(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Short')), 'Short(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Integer')), 'Integer(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Long')), 'Long(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Float')), 'Float(12.0)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, 'java.lang.Double')), 'Double(12.0)') # Convert Python values to boxed types (using primitive type names — but they still get boxed): self.assertEqual(fixture.stringifyObjectArg(jpy.convert(65, jpy.get_type('char'))), 'Character(A)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('byte'))), 'Byte(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('short'))), 'Short(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('int'))), 'Integer(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('long'))), 'Long(12)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('float'))), 'Float(12.0)') self.assertEqual(fixture.stringifyObjectArg(jpy.convert(12, jpy.get_type('double'))), 'Double(12.0)') def test_convert_toPrimitiveArray(self): fixture = self.Fixture() # Convert Python values to arrays of primitive types target_type = type(jpy.array(jpy.get_type('char'), 0)) jobj = jpy.convert([65, 66, 67], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'char[](A,B,C)') target_type = type(jpy.array(jpy.get_type('byte'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'byte[](12,13,14)') target_type = type(jpy.array(jpy.get_type('short'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'short[](12,13,14)') target_type = type(jpy.array(jpy.get_type('int'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'int[](12,13,14)') target_type = type(jpy.array(jpy.get_type('long'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'long[](12,13,14)') target_type = type(jpy.array(jpy.get_type('float'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'float[](12.0,13.0,14.0)') target_type = type(jpy.array(jpy.get_type('double'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'double[](12.0,13.0,14.0)') def test_convert_toBoxedPrimitiveArray(self): fixture = self.Fixture() # Convert Python values to arrays of boxed types target_type = type(jpy.array(jpy.get_type('java.lang.Character'), 0)) jobj = jpy.convert([65, 66, 67], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Character[](Character(A),Character(B),Character(C))') target_type = type(jpy.array(jpy.get_type('java.lang.Byte'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Byte[](Byte(12),Byte(13),Byte(14))') target_type = type(jpy.array(jpy.get_type('java.lang.Short'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Short[](Short(12),Short(13),Short(14))') target_type = type(jpy.array(jpy.get_type('java.lang.Integer'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Integer[](Integer(12),Integer(13),Integer(14))') target_type = type(jpy.array(jpy.get_type('java.lang.Long'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Long[](Long(12),Long(13),Long(14))') target_type = type(jpy.array(jpy.get_type('java.lang.Float'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Float[](Float(12.0),Float(13.0),Float(14.0))') target_type = type(jpy.array(jpy.get_type('java.lang.Double'), 0)) jobj = jpy.convert([12, 13, 14], target_type) self.assertEqual(type(jobj), target_type) self.assertEqual(fixture.stringifyObjectArg(jobj), 'Double[](Double(12.0),Double(13.0),Double(14.0))') def test_convert_toJavaLangObject(self): """ Test converting values to java.lang.Object (and letting the jpy module determine what Java type to convert them to). """ java_lang_object_type = jpy.get_type('java.lang.Object') jobj = jpy.convert('A', java_lang_object_type) expected_type = jpy.get_type('java.lang.String') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).toString(), 'A') jobj = jpy.convert('ABCDE', java_lang_object_type) expected_type = jpy.get_type('java.lang.String') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).toString(), 'ABCDE') jobj = jpy.convert(True, java_lang_object_type) expected_type = jpy.get_type('java.lang.Boolean') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), True) jobj = jpy.convert(False, java_lang_object_type) expected_type = jpy.get_type('java.lang.Boolean') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), False) jobj = jpy.convert(12, java_lang_object_type) expected_type = jpy.get_type('java.lang.Byte') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).byteValue(), 12) jobj = jpy.convert(129, java_lang_object_type) expected_type = jpy.get_type('java.lang.Short') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).shortValue(), 129) jobj = jpy.convert(100_000, java_lang_object_type) expected_type = jpy.get_type('java.lang.Integer') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).intValue(), 100_000) jobj = jpy.convert(10_000_000_000, java_lang_object_type) expected_type = jpy.get_type('java.lang.Long') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).longValue(), 10_000_000_000) jobj = jpy.convert(123.45, java_lang_object_type) expected_type = jpy.get_type('java.lang.Double') # TODO: these go to Double, not Float ? self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).doubleValue(), 123.45) too_big_for_float = jpy.get_type('java.lang.Double').MAX_VALUE jobj = jpy.convert(too_big_for_float, java_lang_object_type) expected_type = jpy.get_type('java.lang.Double') self.assertTrue(type(jobj).jclass.equals(java_lang_object_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).doubleValue(), too_big_for_float) def test_convert_toString(self): jobj = jpy.convert('test string', jpy.get_type('java.lang.Object')) self.assertTrue(type(jobj).jclass.equals(jpy.get_type('java.lang.Object').jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(jpy.get_type('java.lang.String').jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, jpy.get_type('java.lang.String')).toString(), 'test string') # Invalid conversion: with self.assertRaises(ValueError) as e: jpy.convert(12, jpy.get_type('java.lang.String')) actual_message = str(e.exception) expected_message = "cannot convert a Python 'int' to a Java 'java.lang.String'" self.assertEqual(actual_message, expected_message) def test_ToPrimitiveArrayConversion(self): fixture = self.Fixture() # Python int array to Java int array a = array.array('i', [1, 2, 3]) self.assertEqual(fixture.stringifyIntArrayArg(a), 'int[](1,2,3)') # integer list a = [4, 5, 6] self.assertEqual(fixture.stringifyIntArrayArg(a), 'int[](4,5,6)') # integer tuple a = (7, 8, 9) self.assertEqual(fixture.stringifyIntArrayArg(a), 'int[](7,8,9)') with self.assertRaises(RuntimeError) as e: fixture.stringifyIntArrayArg(1 + 2j) self.assertEqual(str(e.exception), 'no matching Java method overloads found') def test_ToObjectArrayConversion(self): fixture = self.Fixture() self.assertEqual(fixture.stringifyObjectArrayArg(('A', 12, 3.4)), 'Object[](String(A),Byte(12),Double(3.4))') self.assertEqual(fixture.stringifyObjectArrayArg(['A', 12, 3.4]), 'Object[](String(A),Byte(12),Double(3.4))') self.assertEqual(fixture.stringifyStringArrayArg(('A', 'B', 'C')), 'String[](String(A),String(B),String(C))') self.assertEqual(fixture.stringifyStringArrayArg(['A', 'B', 'C']), 'String[](String(A),String(B),String(C))') def test_ToBooleanConversion(self): java_boolean_type = jpy.get_type("java.lang.Boolean") expected_type = jpy.get_type('java.lang.Boolean') jobj = jpy.convert(['abc'], java_boolean_type) self.assertTrue(type(jobj).jclass.equals(java_boolean_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), True) jobj = jpy.convert([], java_boolean_type) self.assertTrue(type(jobj).jclass.equals(java_boolean_type.jclass), f'Type is {type(jobj)}') self.assertTrue(jobj.getClass().equals(expected_type.jclass), f'Type is {jobj.getClass()}') self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), False) try: import numpy as np except ImportError: return ba = np.array([True, False]) jobj = jpy.convert(ba[0], java_boolean_type) self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), True) jobj = jpy.convert(ba[1], java_boolean_type) self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), False) jobj = jpy.convert(np.bool_(True), java_boolean_type) self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), True) jobj = jpy.convert(np.bool_(False), java_boolean_type) self.assertEqual(jpy.cast(jobj, expected_type).booleanValue(), False) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_mt_test.py0000664000175000017500000000201615202503234020474 0ustar alastairalastairimport threading import unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M') import jpy class MyThread(threading.Thread): def __init__(self, value): threading.Thread.__init__(self) Integer = jpy.get_type('java.lang.Integer') self.intObj = Integer(value) def run(self): # perform some operation on the local object using a new thread self.intValue = self.intObj.intValue() class TestMultipleThreads(unittest.TestCase): def test_multi_thread_access(self): t1 = MyThread(123) t2 = MyThread(234) t3 = MyThread(345) t4 = MyThread(456) t1.start() t2.start() t3.start() t4.start() t1.join() t2.join() t3.join() t4.join() self.assertEqual(123, t1.intValue) self.assertEqual(234, t2.intValue) self.assertEqual(345, t3.intValue) self.assertEqual(456, t4.intValue) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_translation_test.py0000664000175000017500000000234015202503234022412 0ustar alastairalastair# This file was modified by Deephaven Data Labs. import unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class DummyWrapper: def __init__(self, theThing): self.theThing = theThing def getValue(self): return 2 * self.theThing.getValue() def make_wrapper(type, thing): return DummyWrapper(thing) class TestTypeTranslation(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.TypeTranslationTestFixture') self.assertIsNotNone(self.Fixture) def test_Translation(self): fixture = self.Fixture() thing = fixture.makeThing(7) self.assertEqual(thing.getValue(), 7) self.assertEqual(repr(type(thing)), "") jpy.type_translations['org.jpy.fixtures.Thing'] = make_wrapper thing = fixture.makeThing(8) self.assertEqual(thing.getValue(), 16) self.assertEqual(type(thing), type(DummyWrapper(None))) jpy.type_translations['org.jpy.fixtures.Thing'] = None self.assertEqual(fixture.makeThing(9).getValue(), 9) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_modretparam_test.py0000664000175000017500000002167115202503234022377 0ustar alastairalastairimport unittest import array import sys import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy try: import numpy as np except: np = None def annotate_fixture_methods(type, method): # print('annotate_fixture_methods: type =', type, ', method =', method.name) if method.name == 'modifyThing': method.set_param_mutable(0, True) elif method.name == 'returnThing': method.set_param_return(0, True) elif method.name == 'modifyAndReturnThing': method.set_param_mutable(0, True) method.set_param_return(0, True) elif method.name == 'modifyIntArray': method.set_param_mutable(0, True) elif method.name == 'returnIntArray': method.set_param_return(0, False) method.set_param_return(0, True) elif method.name == 'modifyAndReturnIntArray': method.set_param_mutable(0, True) method.set_param_return(0, True) elif method.name == 'modifyAndOutputIntArray': method.set_param_mutable(0, True) method.set_param_output(0, True) return True jpy.type_callbacks['org.jpy.fixtures.ModifyAndReturnParametersTestFixture'] = annotate_fixture_methods class TestMutableAndReturnParameters(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.ModifyAndReturnParametersTestFixture') self.assertIsNotNone(self.Fixture) self.Thing = jpy.get_type('org.jpy.fixtures.Thing') self.assertIsNotNone(self.Thing) def test_modifyThing(self): fixture = self.Fixture() t = self.Thing() self.assertEqual(t.getValue(), 0) fixture.modifyThing(t, 11) self.assertEqual(t.getValue(), 11) def test_returnThing(self): fixture = self.Fixture() t1 = self.Thing(21) t2 = fixture.returnThing(t1) self.assertEqual(t1.getValue(), 21) self.assertEqual(t2.getValue(), 21) self.assertEqual(t1, t2) self.assertTrue(t1 is t2) t1 = None t2 = fixture.returnThing(t1) self.assertIsNotNone(t2) self.assertEqual(t2.getValue(), 0) def test_modifyAndReturnThing(self): fixture = self.Fixture() t1 = self.Thing() t2 = fixture.modifyAndReturnThing(t1, 31) self.assertEqual(t1.getValue(), 31) self.assertEqual(t2.getValue(), 31) self.assertEqual(t1, t2) self.assertTrue(t1 is t2) t1 = None t2 = fixture.modifyAndReturnThing(t1, 32) self.assertIsNotNone(t2) self.assertEqual(t2.getValue(), 32) def test_modifyIntArray(self): fixture = self.Fixture() # See https://docs.python.org/2/c-api/buffer.html # "An array can only expose its contents via the old-style buffer interface. # This limitation does not apply to Python 3, where memoryview objects can be # constructed from arrays, too." # >>> import array # >>> a = array.array('i', [1,2,3]) # >>> m = memoryview(a) # TypeError: cannot make memory view because object does not have the buffer interface # if sys.version_info >= (3, 0, 0): a = array.array('i', [0, 0, 0]) fixture.modifyIntArray(a, 12, 13, 14) self.assertEqual(a[0], 12) self.assertEqual(a[1], 13) self.assertEqual(a[2], 14) if np: a = np.array([0, 0, 0], dtype='int32') fixture.modifyIntArray(a, 10, 11, 12) self.assertEqual(a[0], 10) self.assertEqual(a[1], 11) self.assertEqual(a[2], 12) a = jpy.array('int', 3) fixture.modifyIntArray(a, 12, 13, 14) self.assertEqual(a[0], 12) self.assertEqual(a[1], 13) self.assertEqual(a[2], 14) a = [0, 0, 0] fixture.modifyIntArray(a, 12, 13, 14) self.assertEqual(a[0], 0) self.assertEqual(a[1], 0) self.assertEqual(a[2], 0) with self.assertRaises(RuntimeError, msg='RuntimeError expected') as e: a = None fixture.modifyIntArray(a, 14, 15, 16) self.assertTrue(str(e.exception).startswith('java.lang.NullPointerException')) def test_returnIntArray(self): fixture = self.Fixture() # See https://docs.python.org/2/c-api/buffer.html # "An array can only expose its contents via the old-style buffer interface. # This limitation does not apply to Python 3, where memoryview objects can be # constructed from arrays, too." # >>> import array # >>> a = array.array('i', [1,2,3]) # >>> m = memoryview(a) # TypeError: cannot make memory view because object does not have the buffer interface # if sys.version_info >= (3, 0, 0): a1 = array.array('i', [0, 0, 0]) a2 = fixture.returnIntArray(a1) # AssertionError: array('i', [0, 0, 0]) is not [I(objectRef=0x0778C364) self.assertIs(a1, a2) if np: a1 = np.array([0, 0, 0], dtype='int32') a2 = fixture.returnIntArray(a1) self.assertIs(a1, a2) a1 = jpy.array('int', 3) a2 = fixture.returnIntArray(a1) self.assertIs(a1, a2) a1 = None a2 = fixture.returnIntArray(a1) self.assertEqual(type(a2), jpy.get_type('[I')) a1 = [0, 0, 0] a2 = fixture.returnIntArray(a1) self.assertEqual(type(a2), jpy.get_type('[I')) def test_modifyAndReturnIntArray(self): fixture = self.Fixture() # See https://docs.python.org/2/c-api/buffer.html # "An array can only expose its contents via the old-style buffer interface. # This limitation does not apply to Python 3, where memoryview objects can be # constructed from arrays, too." # >>> import array # >>> a = array.array('i', [1,2,3]) # >>> m = memoryview(a) # TypeError: cannot make memory view because object does not have the buffer interface # if sys.version_info >= (3, 0, 0): a1 = array.array('i', [0, 0, 0]) # Python 2.7: TypeError: must be impossible, not bool a2 = fixture.modifyAndReturnIntArray(a1, 16, 17, 18) self.assertIs(a1, a2) self.assertEqual(a2[0], 16) self.assertEqual(a2[1], 17) self.assertEqual(a2[2], 18) if np: a1 = np.array([0, 0, 0], dtype='int32') a2 = fixture.modifyAndReturnIntArray(a1, 10, 11, 12) self.assertIs(a1, a2) self.assertEqual(a2[0], 10) self.assertEqual(a2[1], 11) self.assertEqual(a2[2], 12) a1 = jpy.array('int', 3) a2 = fixture.modifyAndReturnIntArray(a1, 16, 17, 18) self.assertIs(a1, a2) self.assertEqual(a2[0], 16) self.assertEqual(a2[1], 17) self.assertEqual(a2[2], 18) a1 = None a2 = fixture.modifyAndReturnIntArray(a1, 16, 17, 18) self.assertEqual(type(a2), jpy.get_type('[I')) self.assertEqual(a2[0], 16) self.assertEqual(a2[1], 17) self.assertEqual(a2[2], 18) a1 = [0, 0, 0] a2 = fixture.modifyAndReturnIntArray(a1, 16, 17, 18) self.assertEqual(type(a2), jpy.get_type('[I')) self.assertEqual(a2[0], 16) self.assertEqual(a2[1], 17) self.assertEqual(a2[2], 18) # See https://github.com/bcdev/jpy/issues/36 # def test_modifyAndOutputIntArray(self): fixture = self.Fixture() # See https://docs.python.org/2/c-api/buffer.html # "An array can only expose its contents via the old-style buffer interface. # This limitation does not apply to Python 3, where memoryview objects can be # constructed from arrays, too." # >>> import array # >>> a = array.array('i', [1,2,3]) # >>> m = memoryview(a) # TypeError: cannot make memory view because object does not have the buffer interface # if sys.version_info >= (3, 0, 0): a = array.array('i', [0, 0, 0]) fixture.modifyAndOutputIntArray(a, 16, 17, 18) self.assertEqual(a[0], 16) self.assertEqual(a[1], 17) self.assertEqual(a[2], 18) if np: a = np.array([0, 0, 0], dtype='int32') fixture.modifyIntArray(a, 10, 11, 12) self.assertEqual(a[0], 10) self.assertEqual(a[1], 11) self.assertEqual(a[2], 12) a = jpy.array('int', 3) fixture.modifyAndOutputIntArray(a, 15, 16, 17) self.assertEqual(a[0], 15) self.assertEqual(a[1], 16) self.assertEqual(a[2], 17) a = None fixture.modifyAndOutputIntArray(a, 16, 17, 18) a = [0, 0, 0] fixture.modifyAndOutputIntArray(a, 16, 17, 18) self.assertEqual(a[0], 0) self.assertEqual(a[1], 0) self.assertEqual(a[2], 0) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_reachability_fence_test.py0000664000175000017500000002156215202503234023663 0ustar alastairalastair""" jpy_reachability_fence_test.py =============================== Stress tests for the Reference.reachabilityFence guards in PyObject. Problem ------- When a PyObject wrapper is used transiently — e.g. the result of getAttribute() is immediately passed to callMethod(), getIntValue(), str(), etc. — JLS §12.6.1 permits the JIT to treat the wrapper as reachability-dead once getPointer() has copied the native pointer into a long. At that point the GC may collect the wrapper and the cleanup thread may Py_DECREF the underlying PyObject* while JNI is still using it, producing a SIGSEGV (use-after-free). Approach -------- Each test method defines a Python object with specific attributes, starts a background GC-pressure thread (continuously allocating to trigger young-gen GC), then hammers a particular PyObject method path at high iteration count. Without the Reference.reachabilityFence fix these tests crash the JVM with SIGSEGV inside libpython. With the fix they must complete without error. """ import unittest import jpyutil # Use a small heap so GC fires frequently, exposing the race window. jpyutil.init_jvm(jvm_maxmem='128M', jvm_classpath=['target/classes', 'target/test-classes']) import jpy ITERATIONS = 500_000 class TestReachabilityFence(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type( 'org.jpy.fixtures.ReachabilityFenceTestFixture') self.assertIsNotNone(self.Fixture) def tearDown(self): self.Fixture.stopAllocator() def _run_with_gc_pressure(self, stress_fn): """Helper: start allocator, run the stress function, stop allocator.""" self.Fixture.startAllocator() try: stress_fn() finally: self.Fixture.stopAllocator() # ------------------------------------------------------------------ # Test: getAttribute + callMethod # ------------------------------------------------------------------ def test_stress_call_method(self): """getAttribute('__call__') -> transient.callMethod('__call__')""" class _Holder: def __call__(self): return 42 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressCallTransient(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + getIntValue # ------------------------------------------------------------------ def test_stress_get_int_value(self): """getAttribute('value') -> transient.getIntValue()""" class _Holder: value = 99 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressGetIntValue(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + getStringValue # ------------------------------------------------------------------ def test_stress_get_string_value(self): """getAttribute('name') -> transient.getStringValue()""" class _Holder: name = "hello" holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressGetStringValue(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + str() # ------------------------------------------------------------------ def test_stress_str(self): """getAttribute('value') -> transient.str()""" class _Holder: value = 42 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressStr(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + repr() # ------------------------------------------------------------------ def test_stress_repr(self): """getAttribute('value') -> transient.repr()""" class _Holder: value = 42 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressRepr(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + hash() # ------------------------------------------------------------------ def test_stress_hash(self): """getAttribute('name') -> transient.hash()""" class _Holder: name = "hashme" holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressHash(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + type-check methods (isInt, isCallable, etc.) # ------------------------------------------------------------------ def test_stress_type_checks(self): """getAttribute('value') -> transient.isInt()/isFloat()/isString()/...""" class _Holder: value = 7 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressTypeChecks(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + hasAttribute # ------------------------------------------------------------------ def test_stress_has_attribute(self): """getAttribute('nested') -> transient.hasAttribute('value')""" class _Nested: value = 1 class _Holder: nested = _Nested() holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressHasAttribute(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + call (function-style) # ------------------------------------------------------------------ def test_stress_call(self): """getAttribute('compute') -> transient.call('__call__', arg)""" class _Holder: @staticmethod def compute(x): return x * 2 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressCall(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: getAttribute + getObjectValue # ------------------------------------------------------------------ def test_stress_get_object_value(self): """getAttribute('value') -> transient.getObjectValue()""" class _Holder: value = 123 holder = _Holder() self._run_with_gc_pressure( lambda: self.Fixture.stressGetObjectValue(holder, ITERATIONS)) # ------------------------------------------------------------------ # Test: createProxy + method call on transient proxy (PyProxyHandler) # ------------------------------------------------------------------ def test_stress_proxy(self): """createProxy(Computable) -> transient proxy.compute(i)""" class _Computable: def compute(self, x): return x * 2 obj = _Computable() self._run_with_gc_pressure( lambda: self.Fixture.stressProxy(obj, ITERATIONS)) # ------------------------------------------------------------------ # Test: asDict().containsKey() on transient PyDictWrapper # ------------------------------------------------------------------ def test_stress_dict_contains_key(self): """asDict().containsKey('key') on a transient wrapper""" d = {"key": 42, "other": 99} # Convert to a PyObject-wrapped dict via jpy py_dict = jpy.get_type('org.jpy.PyObject').executeCode( "{'key': 42, 'other': 99}", jpy.get_type('org.jpy.PyInputMode').EXPRESSION) self._run_with_gc_pressure( lambda: self.Fixture.stressDictContainsKey(py_dict, ITERATIONS)) # ------------------------------------------------------------------ # Test: asDict().keySet() on transient PyDictWrapper # ------------------------------------------------------------------ def test_stress_dict_key_set(self): """asDict().keySet() on a transient wrapper""" py_dict = jpy.get_type('org.jpy.PyObject').executeCode( "{'a': 1, 'b': 2, 'c': 3}", jpy.get_type('org.jpy.PyInputMode').EXPRESSION) self._run_with_gc_pressure( lambda: self.Fixture.stressDictKeySet(py_dict, ITERATIONS)) # ------------------------------------------------------------------ # Test: asDict().copy() on transient PyDictWrapper # ------------------------------------------------------------------ def test_stress_dict_copy(self): """asDict().copy() on a transient wrapper""" py_dict = jpy.get_type('org.jpy.PyObject').executeCode( "{'x': 10}", jpy.get_type('org.jpy.PyInputMode').EXPRESSION) self._run_with_gc_pressure( lambda: self.Fixture.stressDictCopy(py_dict, ITERATIONS)) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_gettype_test.py0000664000175000017500000000765115202503234021547 0ustar alastairalastairimport unittest import sys import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy if sys.version_info >= (3, 0, 0): TYPE_STR_PREFIX = '") IntArray2D = jpy.get_type('[[I') self.assertEqual(str(IntArray2D), TYPE_STR_PREFIX + "'[[I'>") IntArray3D = jpy.get_type('[[[I') self.assertEqual(str(IntArray3D), TYPE_STR_PREFIX + "'[[[I'>") with self.assertRaises(RuntimeError) as e: IntArray1D() self.assertEqual(str(e.exception), "no constructor found (missing JType attribute '__jinit__')") def test_get_class_of_object_array(self): StringArray1D = jpy.get_type('[Ljava.lang.String;') self.assertEqual(str(StringArray1D), TYPE_STR_PREFIX + "'[Ljava.lang.String;'>") StringArray2D = jpy.get_type('[[Ljava.lang.String;') self.assertEqual(str(StringArray2D), TYPE_STR_PREFIX + "'[[Ljava.lang.String;'>") StringArray3D = jpy.get_type('[[[Ljava.lang.String;') self.assertEqual(str(StringArray3D), TYPE_STR_PREFIX + "'[[[Ljava.lang.String;'>") with self.assertRaises(RuntimeError) as e: StringArray1D() self.assertEqual(str(e.exception), "no constructor found (missing JType attribute '__jinit__')") def test_get_class_fromm_inner_class(self): Point2D = jpy.get_type('java.awt.geom.Point2D') self.assertEqual(str(Point2D), TYPE_STR_PREFIX + "'java.awt.geom.Point2D'>") DoublePoint = jpy.get_type('java.awt.geom.Point2D$Double') self.assertEqual(str(DoublePoint), TYPE_STR_PREFIX + "'java.awt.geom.Point2D$Double'>") def test_get_class_of_unknown_type(self): with self.assertRaises(ValueError) as e: String = jpy.get_type('java.lang.Spring') self.assertEqual(str(e.exception), "Java class 'java.lang.Spring' not found") with self.assertRaises(ValueError) as e: IntArray = jpy.get_type('int[]') self.assertEqual(str(e.exception), "Java class 'int[]' not found") def test_issue_74(self): """ Try to create enough references to trigger collection by Python. """ java_types = ['boolean', 'char', 'byte', 'short', 'int', 'long', 'float', 'double', 'void', 'java.lang.String'] for java_type in java_types: for i in range(200): jpy.get_type(java_type) def test_cyclic_reference(self): """ Test if delaying resolving super classes breaks existing class reference pattern. """ j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") j_child2 = j_child2_class() j_child1 = j_child1_class.of(8) self.assertEqual(88, j_child1.parentMethod()) self.assertEqual(888, j_child1.grandParentMethod()) self.assertIsNone(j_child1.refChild2(j_child2)) self.assertEqual(8, j_child1.get_x()) self.assertEqual(10, j_child1.y) self.assertEqual(100, j_child1.z) def test_component_type_resolution(self): j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child1 = j_child1_class.of(8) j_child2s = j_child1.getChild2s() self.assertIn("[Lorg.jpy.fixtures.CyclicReferenceChild2;", repr(type(j_child2s))) for j_child2 in j_child2s: self.assertTrue(j_child2.getName().startswith("Child2")) def test_fail_init_supertype(self): with self.assertRaises(ValueError) as cm: j_child_class = jpy.get_type("org.jpy.fixtures.GetTypeFailureChild") if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_retval_test.py0000664000175000017500000000776215202503234021366 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestMethodReturnValues(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.MethodReturnValueTestFixture') self.assertIsNotNone(self.Fixture) self.Thing = jpy.get_type('org.jpy.fixtures.Thing') self.assertIsNotNone(self.Thing) def test_void(self): fixture = self.Fixture() self.assertEqual(fixture.getVoid(), None) def test_primitive_values(self): fixture = self.Fixture() self.assertEqual(fixture.getValue_boolean(True), True) self.assertEqual(fixture.getValue_byte(11), 11) self.assertEqual(fixture.getValue_short(12), 12) self.assertEqual(fixture.getValue_int(13), 13) self.assertEqual(fixture.getValue_long(14), 14) self.assertAlmostEqual(fixture.getValue_float(15.1), 15.1, places=5) self.assertEqual(fixture.getValue_double(16.2), 16.2) def test_objects(self): fixture = self.Fixture() obj = self.Thing() self.assertEqual(fixture.getString('Hi!'), 'Hi!') self.assertEqual(fixture.getObject(obj), obj) def test_array1d_boolean(self): fixture = self.Fixture() array = fixture.getArray1D_boolean(True, False, True) self.assertEqual(len(array), 3) self.assertEqual(array[0], True) self.assertEqual(array[1], False) self.assertEqual(array[2], True) def test_array1d_byte(self): fixture = self.Fixture() array = fixture.getArray1D_byte(-10, 20, 30) self.assertEqual(len(array), 3) self.assertEqual(array[0], -10) self.assertEqual(array[1], 20) self.assertEqual(array[2], 30) def test_array1d_short(self): fixture = self.Fixture() array = fixture.getArray1D_short(-10001, 20001, 30001) self.assertEqual(len(array), 3) self.assertEqual(array[0], -10001) self.assertEqual(array[1], 20001) self.assertEqual(array[2], 30001) def test_array1d_int(self): fixture = self.Fixture() array = fixture.getArray1D_int(-100001, 200001, 300001) self.assertEqual(len(array), 3) self.assertEqual(array[0], -100001) self.assertEqual(array[1], 200001) self.assertEqual(array[2], 300001) def test_array1d_long(self): fixture = self.Fixture() array = fixture.getArray1D_long(-10000000001, 20000000001, 30000000001) self.assertEqual(len(array), 3) self.assertEqual(array[0], -10000000001) self.assertEqual(array[1], 20000000001) self.assertEqual(array[2], 30000000001) def test_array1d_float(self): fixture = self.Fixture() array = fixture.getArray1D_float(-1.01, 2.01, 3.01) self.assertEqual(len(array), 3) self.assertAlmostEqual(array[0], -1.01, places=5) self.assertAlmostEqual(array[1], 2.01, places=5) self.assertAlmostEqual(array[2], 3.01, places=5) def test_array1d_double(self): fixture = self.Fixture() array = fixture.getArray1D_double(-1.01, 2.01, 3.01) self.assertEqual(len(array), 3) self.assertEqual(array[0], -1.01) self.assertEqual(array[1], 2.01) self.assertEqual(array[2], 3.01) def test_array1d_String(self): fixture = self.Fixture() array = fixture.getArray1D_String('A', 'B', 'C') self.assertEqual(len(array), 3) self.assertEqual(array[0], 'A') self.assertEqual(array[1], 'B') self.assertEqual(array[2], 'C') def test_array1d_Object(self): fixture = self.Fixture() array = fixture.getArray1D_Object(self.Thing(7), self.Thing(8), self.Thing(9)) self.assertEqual(len(array), 3) self.assertEqual(array[0], self.Thing(7)) self.assertEqual(array[1], self.Thing(8)) self.assertEqual(array[2], self.Thing(9)) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_exception_test.py0000664000175000017500000001446015202503234022060 0ustar alastairalastair# This file was modified by Deephaven Data Labs. import unittest import jpyutil import string jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) import jpy class TestExceptions(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.ExceptionTestFixture') self.assertIsNotNone(self.Fixture) def test_NullPointerException(self): fixture = self.Fixture() self.assertEqual(fixture.throwNpeIfArgIsNull("123456"), 6) with self.assertRaises(RuntimeError, msg='Java NullPointerException expected') as e: fixture.throwNpeIfArgIsNull(None) self.assertTrue(str(e.exception).startswith( 'java.lang.NullPointerException')) def test_ArrayIndexOutOfBoundsException(self): fixture = self.Fixture() self.assertEqual(fixture.throwAioobeIfIndexIsNotZero(0), 101) def check_exception(ex_msg: str, expected_idx: int, expected_len: int): """ Verify an ArrayIndexOutOfBoundsException. The exception message can vary by JVM. :param ex_msg: The exception message :param expected_idx: The invalid index expected in the exception message :param expected_len: The expected length of the array that we attempted to access """ valid_ex_msg_1 = f'java.lang.ArrayIndexOutOfBoundsException: {expected_idx}' valid_ex_msg_2 = f'java.lang.ArrayIndexOutOfBoundsException: Index {expected_idx} out of bounds for length {expected_len}' ex_msg_correct = (ex_msg == valid_ex_msg_1 or ex_msg == valid_ex_msg_2) self.assertTrue(ex_msg_correct, f'Exception message \'{ex_msg}\' does not match expectations: either \'{valid_ex_msg_1}\' or \'{valid_ex_msg_2}\'') with self.assertRaises(RuntimeError, msg='Java ArrayIndexOutOfBoundsException expected') as e: fixture.throwAioobeIfIndexIsNotZero(1) check_exception(str(e.exception), 1, 1) with self.assertRaises(RuntimeError, msg='Java ArrayIndexOutOfBoundsException expected') as e: fixture.throwAioobeIfIndexIsNotZero(-1) check_exception(str(e.exception), -1, 1) def test_RuntimeException(self): fixture = self.Fixture() fixture.throwRteIfMessageIsNotNull(None) with self.assertRaises(RuntimeError, msg='Java RuntimeException expected') as e: fixture.throwRteIfMessageIsNotNull("Evil!") self.assertEqual(str(e.exception), 'java.lang.RuntimeException: Evil!') def test_IOException(self): fixture = self.Fixture() fixture.throwIoeIfMessageIsNotNull(None) with self.assertRaises(RuntimeError, msg='Java IOException expected') as e: fixture.throwIoeIfMessageIsNotNull("Evil!") self.assertEqual(str(e.exception), 'java.io.IOException: Evil!') # Checking the exceptions for differences (e.g. in white space) can be a huge pain, this helps) def hexdump(self, s): for i in range(0, len(s), 32): sl = s[i:min(i + 32, len(s))] fsl = map(lambda x: x if (x in string.printable and not x in "\n\t\r") else ".", sl) print("%08d %s %s %s" % (i, " ".join("{:02x}".format(ord(c)) for c in sl), (" ".join(map(lambda x : "", range(32 - len(sl))))), sl)) def test_VerboseException(self): fixture = self.Fixture() jpy.VerboseExceptions.enabled = True self.assertEqual(fixture.throwNpeIfArgIsNull("123456"), 6) with self.assertRaises(RuntimeError) as e: fixture.throwNpeIfArgIsNullNested(None) actual_message = str(e.exception) expected_message = "java.lang.RuntimeException: Nested exception\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested(ExceptionTestFixture.java:43)\n" \ "caused by java.lang.NullPointerException\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNull(ExceptionTestFixture.java:32)\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested(ExceptionTestFixture.java:41)\n" # self.hexdump(actual_message) # self.hexdump(expected_message) # print [i for i in xrange(min(len(expected_message), len(actual_message))) if actual_message[i] != expected_message[i]] self.assertIn("java.lang.NullPointerException", actual_message) with self.assertRaises(RuntimeError) as e: fixture.throwNpeIfArgIsNullNested3(None) actual_message = str(e.exception) expected_message = "java.lang.RuntimeException: Nested exception 3\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested3(ExceptionTestFixture.java:55)\n" \ "caused by java.lang.RuntimeException: Nested exception\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested(ExceptionTestFixture.java:43)\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested2(ExceptionTestFixture.java:48)\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested3(ExceptionTestFixture.java:53)\n" \ "caused by java.lang.NullPointerException\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNull(ExceptionTestFixture.java:32)\n\t" \ "at org.jpy.fixtures.ExceptionTestFixture." \ "throwNpeIfArgIsNullNested(ExceptionTestFixture.java:41)\n\t... 2 more\n" # self.hexdump(actual_message) # self.hexdump(expected_message) # print [i for i in xrange(min(len(expected_message), len(actual_message))) if actual_message[i] != expected_message[i]] self.assertIn("java.lang.NullPointerException", actual_message) jpy.VerboseExceptions.enabled = False if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/fixtures/0000775000175000017500000000000015202503234017433 5ustar alastairalastairjpy-2.0.0/src/test/python/fixtures/proxy_test_classes.py0000664000175000017500000000222015202503234023736 0ustar alastairalastairclass HasStr: def __str__(self): return 'This is my __str__ method' class DoesNotHaveStr: def __init__(self): pass class HasToStringWithArg: def toString(self, arg): return 'weird' class HasHashCodeWithArg: def hashCode(self, arg): return 0 def __hash__(self): return 1 class HasHashNegativeOne: def __hash__(self): return -1 class HasEqualsWithoutArg: def equals(self): return True class EqAlwaysFalse: def __eq__(self, other): return False class EqAlwaysTrue: def __eq__(self, other): return True class NoEqDefined: def __init__(self): pass class ProxyAsArgument: def self_is_other(self, other): return self is other class ThrowsException: def __hash__(self): raise Exception('this __hash__ always raises Exception') def __str__(self): raise Exception('this __str__ always raises Exception') def __eq__(self, other): raise Exception('this __eq__ always raises Exception') class NonBooleanEq: def __eq__(self, other): if self is other: return 1 return 0jpy-2.0.0/src/test/python/fixtures/proc_class.py0000664000175000017500000000107715202503234022142 0ustar alastairalastairclass Processor: def __init__(self): pass def initialize(self): return 'initialize' def computeTile(self, x, y, array): self.spend_some_time() return 'computeTile-' + str(x) + ',' + str(y) def dispose(self): return 'dispose' def spend_some_time(self): l = list(range(10000)) for i in range(10000): l.reverse() def setVal(self, val): self._val = val def getVal(self): return self._val def check1234(self): return self._val == 1234 jpy-2.0.0/src/test/python/fixtures/hasheqstr.py0000664000175000017500000000125415202503234022011 0ustar alastairalastairclass Simple(object): def __init__(self, v): self._v = v def __str__(self): return "Simple: " + str(self._v) def getValue(self): return self._v class HashSimple(object): def __init__(self, v): self._v = v def __str__(self): return "HashSimple: " + str(self._v) def getValue(self): return self._v def __hash__(self): return hash(self._v) def __eq__(self, other): if isinstance(other, self.__class__): return self._v == other._v else: return False # Java can't support NotImplemented jpy-2.0.0/src/test/python/fixtures/special_methods.py0000664000175000017500000000203415202503234023147 0ustar alastairalastairclass Simple: def __init__(self, x): self.x = x def __hash__(self): return hash(self.x) def __str__(self): return str(self.x) def __eq__(self, other): return self.x == other class ThrowsException: def __hash__(self): raise Exception('this __hash__ always raises Exception') def __str__(self): raise Exception('this __str__ always raises Exception') def __eq__(self, other): raise Exception('this __eq__ always raises Exception') # __eq__ *can* return non boolean values class NonBooleanEq: def __eq__(self, other): if self is other: return 1 return 0 # __str__ should *not* return non string values class NonStringStr: def __str__(self): return 1 class NoMethodsDefined: def __init__(self): pass class EqAlwaysFalse: def __eq__(self, other): return False class EqAlwaysTrue: def __eq__(self, other): return True class HashNegativeOne: def __hash__(self): return -1jpy-2.0.0/src/test/python/fixtures/raise_errors.py0000664000175000017500000000012415202503234022501 0ustar alastairalastairdef raise_if_zero(arg): if not arg: raise IndexError("arg wasn't there")jpy-2.0.0/src/test/python/fixtures/proc_module.py0000664000175000017500000000076515202503234022325 0ustar alastairalastair def initialize(): return 'initialize' def computeTile(x, y, array): spend_some_time() return 'computeTile-' + str(x) + ',' + str(y) def dispose(): return 'dispose' def spend_some_time(): l = list(range(10000)) for i in range(10000): l.reverse() _module_val = None def setVal(val): global _module_val _module_val = val def getVal(): global _module_val return _module_val def check1234(): global _module_val return _module_val == 1234 jpy-2.0.0/src/test/python/jpy_mt_eval_exec_test.py0000664000175000017500000000735115202503234022516 0ustar alastairalastairimport math import unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes', 'target/test-classes']) import jpy NUM_THREADS = 20 # A CPU-bound task: computing a large number of prime numbers def is_prime(n: int) -> bool: if n <= 1: return False for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: return False return True def count_primes(start: int, end: int) -> int: count = 0 for i in range(start, end): if is_prime(i): count += 1 return count def use_circular_java_classes(): j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") j_child2 = j_child2_class() j_child1 = j_child1_class.of(8) result = j_child1.parentMethod() assert result == 88 assert 888 == j_child1.grandParentMethod() j_child1.refChild2(j_child2) assert 8 == j_child1.get_x() assert 10 == j_child1.y assert 100 == j_child1.z class MultiThreadedTestEvalExec(unittest.TestCase): def setUp(self): self.fixture = jpy.get_type("org.jpy.fixtures.MultiThreadedEvalTestFixture") self.assertIsNotNone(self.fixture) def test_inc_baz(self): baz = 15 self.fixture.script("baz = baz + 1; self.assertGreater(baz, 15)", NUM_THREADS) # note: this *is* correct wrt python semantics w/ exec(code, globals(), locals()) # https://bugs.python.org/issue4831 (Note: it's *not* a bug, is working as intended) self.assertEqual(baz, 15) def test_exec_import(self): import sys self.assertTrue("json" not in sys.modules) self.fixture.script("import json", NUM_THREADS) self.assertTrue("json" in sys.modules) def test_exec_function_call(self): self.fixture.expression("use_circular_java_classes()", NUM_THREADS) def test_count_primes(self): self.fixture.expression("count_primes(1, 10000)", NUM_THREADS) def test_java_threading_jpy_get_type(self): py_script = """ j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") j_child2 = j_child2_class() j_child1 = j_child1_class.of(8) result = j_child1.parentMethod() assert result == 88 assert 888 == j_child1.grandParentMethod() j_child1.refChild2(j_child2) assert 8 == j_child1.get_x() assert 10 == j_child1.y assert 100 == j_child1.z """ self.fixture.script(py_script, NUM_THREADS) def test_py_threading_jpy_get_type(self): import threading test_self = self class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): barrier.wait() j_child1_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild1") j_child2_class = jpy.get_type("org.jpy.fixtures.CyclicReferenceChild2") j_child2 = j_child2_class() j_child1 = j_child1_class.of(8) test_self.assertEqual(88, j_child1.parentMethod()) test_self.assertEqual(888, j_child1.grandParentMethod()) test_self.assertIsNone(j_child1.refChild2(j_child2)) test_self.assertEqual(8, j_child1.get_x()) test_self.assertEqual(10, j_child1.y) test_self.assertEqual(100, j_child1.z) barrier = threading.Barrier(NUM_THREADS) threads = [] for i in range(NUM_THREADS): t = MyThread() t.start() threads.append(t) for t in threads: t.join() if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_array_test.py0000664000175000017500000002260615202503234021201 0ustar alastairalastairimport unittest import sys import jpyutil jpyutil.init_jvm(jvm_maxmem='8g', jvm_classpath=['target/test-classes']) import jpy class TestJavaArrays(unittest.TestCase): def do_test_basic_array_protocol_with_length(self, type, initial, expected): a = jpy.array(type, 3) self.assertEqual(len(a), 3) self.assertEqual(a[0], initial[0]) self.assertEqual(a[1], initial[1]) self.assertEqual(a[2], initial[2]) a[0] = expected[0] a[1] = expected[1] a[2] = expected[2] return a def do_test_array_with_initializer(self, type, expected): a = jpy.array(type, expected) self.assertEqual(len(a), 3) self.assertEqual(a[0], expected[0]) self.assertEqual(a[1], expected[1]) self.assertEqual(a[2], expected[2]) def do_test_array_protocol(self, type_name, initial, expected): self.do_test_array_protocol2(type_name, initial, expected) self.do_test_array_protocol2(jpy.get_type(type_name), initial, expected) self.do_test_array_with_initializer(type_name, expected) # self.do_test_array_with_initializer(jpy.get_type(type_name), expected) def do_test_array_protocol2(self, type, initial, expected): a = self.do_test_basic_array_protocol_with_length(type, initial, expected) self.assertEqual(a[0], expected[0]) self.assertEqual(a[1], expected[1]) self.assertEqual(a[2], expected[2]) def do_test_array_protocol_float(self, type_name, initial, expected, places): self.do_test_array_protocol_float2(type_name, initial, expected, places) self.do_test_array_protocol_float2(jpy.get_type(type_name), initial, expected, places) def do_test_array_protocol_float2(self, type, initial, expected, places): a = self.do_test_basic_array_protocol_with_length(type, initial, expected) self.assertAlmostEqual(a[0], expected[0], places=places) self.assertAlmostEqual(a[1], expected[1], places=places) self.assertAlmostEqual(a[2], expected[2], places=places) def test_array_boolean(self): self.do_test_array_protocol('boolean', [False, False, False], [True, False, True]) # This seems a bit weird but numpy.ndarray allows this too, also but anyone familiar with strong typed languages # shouldn't expect that values of types other than boolean can be assigned to a boolean array element. jba = jpy.array("boolean", 3) jba[0] = [] jba[1] = [1] jba[2] = False self.assertEqual(jba[0], False) self.assertEqual(jba[1], True) self.assertEqual(jba[2], False) def test_array_char(self): self.do_test_array_protocol('char', [0, 0, 0], [0, 100, 32767]) def test_array_byte(self): self.do_test_array_protocol('byte', [0, 0, 0], [-128, 100, 127]) def test_array_short(self): self.do_test_array_protocol('short', [0, 0, 0], [-32768, 100, 32767]) def test_array_int(self): self.do_test_array_protocol('int', [0, 0, 0], [-2147483648, 100, 2147483647]) def test_array_long(self): self.do_test_array_protocol('long', [0, 0, 0], [-9223372036854775808, 100, 9223372036854775807]) def test_array_float(self): self.do_test_array_protocol_float('float', [0, 0, 0], [-1.001, 0.001, 1.001], places=5) def test_array_double(self): self.do_test_array_protocol_float('double', [0, 0, 0], [-1.001, 0.001, 1.001], places=10) def test_array_object(self): File = jpy.get_type('java.io.File') String = jpy.get_type('java.lang.String') Integer = jpy.get_type('java.lang.Integer') self.do_test_array_protocol('java.lang.Integer', [None, None, None], [1, None, 3]) self.do_test_array_protocol('java.lang.String', [None, None, None], ['A', 'B', 'C']) self.do_test_array_protocol('java.io.File', [None, None, None], [File('A'), File('B'), File('C')]) self.do_test_array_protocol('java.lang.Object', [None, None, None], [None, None, None]) self.do_test_array_protocol('java.lang.Object', [None, None, None], [File('A'), 'B', 3]) # see https://github.com/bcdev/jpy/issues/52 def test_array_item_del(self): Integer = jpy.get_type('java.lang.Integer') a = jpy.array(Integer, 3) try: del a[1] except RuntimeError as err: self.assertEqual(err.args[0], 'cannot delete items of Java arrays') def do_test_basic_buffer_protocol(self, type, itemsize, values): a = jpy.array(type, 4) self.assertEqual(len(a), 4) a[0] = values[0] a[1] = values[1] a[2] = values[2] a[3] = values[3] m = memoryview(a) self.assertEqual(len(m), 4) self.assertEqual(m.ndim, 1) self.assertEqual(m.itemsize, itemsize) self.assertEqual(m.shape, (4,)) self.assertEqual(m.strides, (itemsize,)) self.assertEqual(m.readonly, True) if sys.version_info >= (3, 0, 0): # Python 2.7: AttributeError: 'memoryview' object has no attribute 'nbytes' self.assertEqual(m.nbytes, 4 * itemsize) # Python 2.7: AttributeError: 'memoryview' object has no attribute 'contiguous' self.assertEqual(m.contiguous, True) # Python 2.7: AttributeError: 'memoryview' object has no attribute 'c_contiguous' self.assertEqual(m.c_contiguous, True) return m def do_test_buffer_protocol(self, type_name, itemsize, values): self.do_test_buffer_protocol2(type_name, itemsize, values) self.do_test_buffer_protocol2(jpy.get_type(type_name), itemsize, values) def do_test_buffer_protocol2(self, type, itemsize, values): m = self.do_test_basic_buffer_protocol(type, itemsize, values) # With Python 2.7, we cannot use the returned memoryview object for further tests if sys.version_info >= (3, 0, 0): # Python 2.7: NotImplementedError: tolist() only supports byte views self.assertEqual(m.tolist(), values) # Python 2.7: AttributeError: 'memoryview' object has no attribute 'release' m.release() def do_test_buffer_protocol_float(self, type_name, itemsize, values, places): self.do_test_buffer_protocol_float2(type_name, itemsize, values, places) self.do_test_buffer_protocol_float2(jpy.get_type(type_name), itemsize, values, places) pass def do_test_buffer_protocol_float2(self, type, itemsize, values, places): m = self.do_test_basic_buffer_protocol(type, itemsize, values) # With Python 2.7, we cannot use the returned memoryview object for further tests if sys.version_info >= (3, 0, 0): # Python 2.7: TypeError: unsupported operand type(s) for -: 'float' and 'str' self.assertAlmostEqual(m[0], values[0], places=places) self.assertAlmostEqual(m[1], values[1], places=places) self.assertAlmostEqual(m[2], values[2], places=places) self.assertAlmostEqual(m[3], values[3], places=places) # Python 2.7: AttributeError: 'memoryview' object has no attribute 'release' m.release() def test_buffer_boolean(self): self.do_test_buffer_protocol('boolean', 1, [True, False, True, True]) def test_buffer_char(self): self.do_test_buffer_protocol('char', 2, [65, 0, 67, 32]) def test_buffer_byte(self): self.do_test_buffer_protocol('byte', 1, [65, 0, -110, -1]) def test_buffer_short(self): self.do_test_buffer_protocol('short', 2, [651, 0, -1102, -1]) def test_buffer_int(self): self.do_test_buffer_protocol('int', 4, [65123, 0, -110123, -1]) def test_buffer_long(self): self.do_test_buffer_protocol('long', 8, [65123456789, 0, -110123456789, -1]) def test_buffer_float(self): self.do_test_buffer_protocol_float('float', 4, [0.12345, 0.0, -100.123, 54.3], 5) def test_buffer_double(self): self.do_test_buffer_protocol_float('double', 8, [0.12345678, 0.0, -100.123456, 54.3], 8) def test_large_array_by_size_alloc(self): # 100 * 1MB for _ in range(100): java_array = jpy.array('byte', 1000000) # 1MB def test_large_array_by_sequence_alloc(self): sequence = list(range(250000)) # 1MB # 100 * 1MB for _ in range(100): java_array = jpy.array('int', sequence) def test_java_constructed_array_alloc(self): fixture = jpy.get_type('org.jpy.fixtures.JavaArrayTestFixture') # 100 * 1MB for _ in range(100): java_array = fixture.createByteArray(1000000) # 1MB def test_leak(self): ''' This isn't a very good "unit"-test - the failure of this test depends on the amount of RAM and specifics of the OS. On my machine I've been able to demonstrate failure with the following constants. ''' # skip the test unless you need to stress test array release logic if True: return j_int_array = jpy.array('int', range(100000)) # ensure that the bufferExportCount doesn't go to 0 keep_around = memoryview(j_int_array) for i in range(1000000): memory_view = memoryview(j_int_array) def test_size_greater_than_maxint(self): jarr = jpy.array("int", 2**30) mv = memoryview(jarr) self.assertEqual(mv.nbytes, 2**32) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_obj_test.py0000664000175000017500000000127015202503234020627 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='32M', jvm_classpath=['target/test-classes']) import jpy class TestJavaArrays(unittest.TestCase): def setUp(self): self.Fixture = jpy.get_type('org.jpy.fixtures.ConstructionTestFixture') self.assertIsNotNone(self.Fixture) def test_large_obj_by_constructor_alloc(self): # 100 * 1MB for _ in range(100): fixture = self.Fixture(1000000) # 1MB def test_large_obj_by_static_alloc(self): # 100 * 1MB for _ in range(100): fixture = self.Fixture.viaStatic(1000000) # 1MB if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/jpy_reentrant_test.py0000664000175000017500000000150315202503234022056 0ustar alastairalastairimport unittest import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes']) import jpy class TestReentrant(unittest.TestCase): def test_reentrant_function(self): PyObject = jpy.get_type('org.jpy.PyObject') PyInputMode = jpy.get_type('org.jpy.PyInputMode') PyModule = jpy.get_type('org.jpy.PyModule') PyObject.executeCode('def incByOne(x): return x + 1', PyInputMode.SCRIPT) mainModule = PyModule.getMain() res = mainModule.incByOne(41) self.assertEqual(res, 42) def test_reentrant_statement(self): PyObject = jpy.get_type('org.jpy.PyObject') PyInputMode = jpy.get_type('org.jpy.PyInputMode') PyObject.executeCode('print("hi from test_reentrant_statement")', PyInputMode.STATEMENT) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/python/imp/0000775000175000017500000000000015202503234016347 5ustar alastairalastairjpy-2.0.0/src/test/python/imp/import_ex1.py0000664000175000017500000000770215202503234021016 0ustar alastairalastair""" Testing Python import machinery with the goal to let users import Java class like this: from jpy.java.io import File or from jpy import java.io.File as File """ __author__ = 'Norman' import sys import importlib.abc class JavaClass: def __init__(self, fullname): self.fullname = fullname def __repr__(self): return "JavaClass('" + self.fullname + "')" def __str__(self): return self.fullname class JavaPackageImporter(importlib.abc.MetaPathFinder): def __init__(self, file): self.packages = set() #with open(file, "r") as f: # for line in f: # package = line.strip() # self.register_package(package) #print(self.packages) def register_package(self, fullname): package = fullname while package: self.packages.add(package) dot_pos = package.rfind('.') if dot_pos > 0: package = package[:dot_pos] else: package = None def get_package(self, fullname): dot_pos = fullname.rfind('.') if dot_pos > 0: package = fullname[:dot_pos] name = fullname[dot_pos + 1:] else: package = '' name = fullname return (package, name) def load_java_class(self, module, fullname): print('try loading java class "' + fullname + '"...') package, name = self.get_package(fullname) #module.__dict__[name] = JavaClass(fullname) #return JavaClass(fullname) return None def new_module(self, fullname): package, name = self.get_package(fullname) print('creating module "' + fullname + '"') module = type(sys)(fullname) module.__loader__ = self module.__path__ = None module.__package__ = package return module def find_module(self, fullname, path): print('find_module(fullname="' + str(fullname) + '", path="' + str(path) + '")') if fullname in self.packages: return self package, name = self.get_package(fullname) if package in self.packages: return self return None def load_module(self, fullname): print('load_module(fullname="' + str(fullname) + '")') is_reload = fullname in sys.modules if is_reload: module = sys.modules[fullname] print('module found!') # Now, check what to do next...? else: if fullname in self.packages: print('known package: ' + fullname) module = self.new_module(fullname) sys.modules[fullname] = module else: print('not a known package: ' + fullname) package, class_name = self.get_package(fullname) module = sys.modules[package] if module is None: return None module = self.load_java_class(module, fullname) return module jpi = JavaPackageImporter('packages-jre7.txt') #jpi.register_package('org.esa.beam.framework.datamodel') #jpi.register_package('org.esa.beam.framework.dataio') #jpi.register_package('org.esa.beam.framework.gpf') sys.meta_path += [jpi] #import jpy import bibo import riser #from jpy import Riser as Riser import numpy from numpy import array #import java.io #import java.lang #import java.util #import numpy #import java.wraaaw #from java.util import String #from java.io import File #s = String('Hello') #print(f) #f = File('x') #print(f) #import os.path # #print(os.__name__) #print(os.__package__) #print(os.__file__) # #print(dir(java.io)) # # #print(os.__name__) #print(os.__package__) #print(os.__file__) #print(list(os.__path__)) #print(os.path.__name__) #print(os.path.__package__) #print(os.path.__file__) #print(list(os.path.__path__)) # #print('java.io:', dir(java.io)) jpy-2.0.0/src/test/python/imp/packages-jre6.txt0000664000175000017500000001002415202503234021527 0ustar alastairalastairjava.applet java.awt java.awt.color java.awt.datatransfer java.awt.dnd java.awt.event java.awt.font java.awt.geom java.awt.im java.awt.im.spi java.awt.image java.awt.image.renderable java.awt.print java.beans java.beans.beancontext java.io java.lang java.lang.annotation java.lang.instrument java.lang.management java.lang.ref java.lang.reflect java.math java.net java.nio java.nio.channels java.nio.channels.spi java.nio.charset java.nio.charset.spi java.rmi java.rmi.activation java.rmi.dgc java.rmi.registry java.rmi.server java.security java.security.acl java.security.cert java.security.interfaces java.security.spec java.sql java.text java.text.spi java.util java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks java.util.jar java.util.logging java.util.prefs java.util.regex java.util.spi java.util.zip javax.accessibility javax.activation javax.activity javax.annotation javax.annotation.processing javax.crypto javax.crypto.interfaces javax.crypto.spec javax.imageio javax.imageio.event javax.imageio.metadata javax.imageio.plugins.bmp javax.imageio.plugins.jpeg javax.imageio.spi javax.imageio.stream javax.jws javax.jws.soap javax.lang.model javax.lang.model.element javax.lang.model.type javax.lang.model.util javax.management javax.management.loading javax.management.modelmbean javax.management.monitor javax.management.openmbean javax.management.relation javax.management.remote javax.management.remote.rmi javax.management.timer javax.naming javax.naming.directory javax.naming.event javax.naming.ldap javax.naming.spi javax.net javax.net.ssl javax.print javax.print.attribute javax.print.attribute.standard javax.print.event javax.rmi javax.rmi.CORBA javax.rmi.ssl javax.script javax.security.auth javax.security.auth.callback javax.security.auth.kerberos javax.security.auth.login javax.security.auth.spi javax.security.auth.x500 javax.security.cert javax.security.sasl javax.sound.midi javax.sound.midi.spi javax.sound.sampled javax.sound.sampled.spi javax.sql javax.sql.rowset javax.sql.rowset.serial javax.sql.rowset.spi javax.swing javax.swing.border javax.swing.colorchooser javax.swing.event javax.swing.filechooser javax.swing.plaf javax.swing.plaf.basic javax.swing.plaf.metal javax.swing.plaf.multi javax.swing.plaf.synth javax.swing.table javax.swing.text javax.swing.text.html javax.swing.text.html.parser javax.swing.text.rtf javax.swing.tree javax.swing.undo javax.tools javax.transaction javax.transaction.xa javax.xml javax.xml.bind javax.xml.bind.annotation javax.xml.bind.annotation.adapters javax.xml.bind.attachment javax.xml.bind.helpers javax.xml.bind.util javax.xml.crypto javax.xml.crypto.dom javax.xml.crypto.dsig javax.xml.crypto.dsig.dom javax.xml.crypto.dsig.keyinfo javax.xml.crypto.dsig.spec javax.xml.datatype javax.xml.namespace javax.xml.parsers javax.xml.soap javax.xml.stream javax.xml.stream.events javax.xml.stream.util javax.xml.transform javax.xml.transform.dom javax.xml.transform.sax javax.xml.transform.stax javax.xml.transform.stream javax.xml.validation javax.xml.ws javax.xml.ws.handler javax.xml.ws.handler.soap javax.xml.ws.http javax.xml.ws.soap javax.xml.ws.spi javax.xml.ws.wsaddressing javax.xml.xpath org.ietf.jgss org.omg.CORBA org.omg.CORBA_2_3 org.omg.CORBA_2_3.portable org.omg.CORBA.DynAnyPackage org.omg.CORBA.ORBPackage org.omg.CORBA.portable org.omg.CORBA.TypeCodePackage org.omg.CosNaming org.omg.CosNaming.NamingContextExtPackage org.omg.CosNaming.NamingContextPackage org.omg.Dynamic org.omg.DynamicAny org.omg.DynamicAny.DynAnyFactoryPackage org.omg.DynamicAny.DynAnyPackage org.omg.IOP org.omg.IOP.CodecFactoryPackage org.omg.IOP.CodecPackage org.omg.Messaging org.omg.PortableInterceptor org.omg.PortableInterceptor.ORBInitInfoPackage org.omg.PortableServer org.omg.PortableServer.CurrentPackage org.omg.PortableServer.POAManagerPackage org.omg.PortableServer.POAPackage org.omg.PortableServer.portable org.omg.PortableServer.ServantLocatorPackage org.omg.SendingContext org.omg.stub.java.rmi org.w3c.dom org.w3c.dom.bootstrap org.w3c.dom.events org.w3c.dom.ls org.xml.sax org.xml.sax.ext org.xml.sax.helpers jpy-2.0.0/src/test/python/imp/packages-jre7.txt0000664000175000017500000001021215202503234021527 0ustar alastairalastairjava.applet java.awt java.awt.color java.awt.datatransfer java.awt.dnd java.awt.event java.awt.font java.awt.geom java.awt.im java.awt.im.spi java.awt.image java.awt.image.renderable java.awt.print java.beans java.beans.beancontext java.io java.lang java.lang.annotation java.lang.instrument java.lang.invoke java.lang.management java.lang.ref java.lang.reflect java.math java.net java.nio java.nio.channels java.nio.channels.spi java.nio.charset java.nio.charset.spi java.nio.file java.nio.file.attribute java.nio.file.spi java.rmi java.rmi.activation java.rmi.dgc java.rmi.registry java.rmi.server java.security java.security.acl java.security.cert java.security.interfaces java.security.spec java.sql java.text java.text.spi java.util java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks java.util.jar java.util.logging java.util.prefs java.util.regex java.util.spi java.util.zip javax.accessibility javax.activation javax.activity javax.annotation javax.annotation.processing javax.crypto javax.crypto.interfaces javax.crypto.spec javax.imageio javax.imageio.event javax.imageio.metadata javax.imageio.plugins.bmp javax.imageio.plugins.jpeg javax.imageio.spi javax.imageio.stream javax.jws javax.jws.soap javax.lang.model javax.lang.model.element javax.lang.model.type javax.lang.model.util javax.management javax.management.loading javax.management.modelmbean javax.management.monitor javax.management.openmbean javax.management.relation javax.management.remote javax.management.remote.rmi javax.management.timer javax.naming javax.naming.directory javax.naming.event javax.naming.ldap javax.naming.spi javax.net javax.net.ssl javax.print javax.print.attribute javax.print.attribute.standard javax.print.event javax.rmi javax.rmi.CORBA javax.rmi.ssl javax.script javax.security.auth javax.security.auth.callback javax.security.auth.kerberos javax.security.auth.login javax.security.auth.spi javax.security.auth.x500 javax.security.cert javax.security.sasl javax.sound.midi javax.sound.midi.spi javax.sound.sampled javax.sound.sampled.spi javax.sql javax.sql.rowset javax.sql.rowset.serial javax.sql.rowset.spi javax.swing javax.swing.border javax.swing.colorchooser javax.swing.event javax.swing.filechooser javax.swing.plaf javax.swing.plaf.basic javax.swing.plaf.metal javax.swing.plaf.multi javax.swing.plaf.nimbus javax.swing.plaf.synth javax.swing.table javax.swing.text javax.swing.text.html javax.swing.text.html.parser javax.swing.text.rtf javax.swing.tree javax.swing.undo javax.tools javax.transaction javax.transaction.xa javax.xml javax.xml.bind javax.xml.bind.annotation javax.xml.bind.annotation.adapters javax.xml.bind.attachment javax.xml.bind.helpers javax.xml.bind.util javax.xml.crypto javax.xml.crypto.dom javax.xml.crypto.dsig javax.xml.crypto.dsig.dom javax.xml.crypto.dsig.keyinfo javax.xml.crypto.dsig.spec javax.xml.datatype javax.xml.namespace javax.xml.parsers javax.xml.soap javax.xml.stream javax.xml.stream.events javax.xml.stream.util javax.xml.transform javax.xml.transform.dom javax.xml.transform.sax javax.xml.transform.stax javax.xml.transform.stream javax.xml.validation javax.xml.ws javax.xml.ws.handler javax.xml.ws.handler.soap javax.xml.ws.http javax.xml.ws.soap javax.xml.ws.spi javax.xml.ws.spi.http javax.xml.ws.wsaddressing javax.xml.xpath org.ietf.jgss org.omg.CORBA org.omg.CORBA_2_3 org.omg.CORBA_2_3.portable org.omg.CORBA.DynAnyPackage org.omg.CORBA.ORBPackage org.omg.CORBA.portable org.omg.CORBA.TypeCodePackage org.omg.CosNaming org.omg.CosNaming.NamingContextExtPackage org.omg.CosNaming.NamingContextPackage org.omg.Dynamic org.omg.DynamicAny org.omg.DynamicAny.DynAnyFactoryPackage org.omg.DynamicAny.DynAnyPackage org.omg.IOP org.omg.IOP.CodecFactoryPackage org.omg.IOP.CodecPackage org.omg.Messaging org.omg.PortableInterceptor org.omg.PortableInterceptor.ORBInitInfoPackage org.omg.PortableServer org.omg.PortableServer.CurrentPackage org.omg.PortableServer.POAManagerPackage org.omg.PortableServer.POAPackage org.omg.PortableServer.portable org.omg.PortableServer.ServantLocatorPackage org.omg.SendingContext org.omg.stub.java.rmi org.w3c.dom org.w3c.dom.bootstrap org.w3c.dom.events org.w3c.dom.ls org.xml.sax org.xml.sax.ext org.xml.sax.helpers jpy-2.0.0/src/test/python/imp/import_ex2.py0000664000175000017500000000450215202503234021012 0ustar alastairalastair""" Testing Python import machinery with the goal to let users import Java class like this: from jpy.java.io import File or from jpy import java.io.File as File """ __author__ = 'Norman' import sys import importlib.abc ModuleType = type(sys) yyyy = ModuleType('yyyy') class JavaClass: def __init__(self, fullname): self.fullname = fullname def __repr__(self): return "JavaClass('" + self.fullname + "')" def __str__(self): return self.fullname class JavaPackageImporter(importlib.abc.MetaPathFinder): def get_package(self, fullname): dot_pos = fullname.rfind('.') if dot_pos > 0: package = fullname[:dot_pos] name = fullname[dot_pos + 1:] else: package = '' name = fullname return (package, name) def load_java_class(self, module, fullname): print('try loading java class "' + fullname + '"...') package, name = self.get_package(fullname) #module.__dict__[name] = JavaClass(fullname) #return JavaClass(fullname) return None def new_module(self, fullname): #package, name = self.get_package(fullname) print('creating module "' + fullname + '"') module = type(sys)(fullname) module.__loader__ = self module.__path__ = None #module.__package__ = package return module def find_module(self, fullname, path): print('find_module(fullname="' + str(fullname) + '", path="' + str(path) + '")') if fullname == 'yyyy' or fullname.startswith('yyyy.'): return self return None def load_module(self, fullname): print('load_module(fullname="' + str(fullname) + '")') is_reload = fullname in sys.modules if is_reload: module = sys.modules[fullname] print('module found!') # Now, check what to do next...? else: print('new module: ' + fullname) module = self.new_module(fullname) sys.modules[fullname] = module return module jpi = JavaPackageImporter() sys.meta_path += [jpi] import yyyy.bibo import yyyy.riser.bibo import yyyy.bibo.riser from yyyy.bibo import Riser as Riser import numpy from numpy import array jpy-2.0.0/src/test/python/jpy_py_exception_chain_test.py0000664000175000017500000001064115202503234023727 0ustar alastairalastairimport unittest import jpyutil # target/classes – org.jpy.PyLib / PyObject / PyInputMode used by EvalTestFixture # target/test-classes – EvalTestFixture itself jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/classes', 'target/test-classes']) import jpy class TestPythonExceptionChain(unittest.TestCase): """ Verifies that PyLib_HandlePythonException preserves the full Python exception chain when a Python script is executed via Java (EvalTestFixture). The chain is formatted by Python's own traceback.format_exception(), so all three chaining cases defined by PEP 3134 are exercised: - Explicit cause (raise B from A) → "The above exception was the direct cause of the following exception" - Implicit context (except A: raise B) → "During handling of the above exception, another exception occurred" - Suppressed (raise B from None) → only B should appear; A must be absent """ def setUp(self): self.fixture = jpy.get_type('org.jpy.fixtures.EvalTestFixture') self.assertIsNotNone(self.fixture) def _run_script(self, script: str) -> str: """Execute a Python snippet through Java and return the RuntimeError message.""" with self.assertRaises(RuntimeError) as ctx: self.fixture.script(script) msg = str(ctx.exception) # Every Python exception propagated through Java must carry the base prefix # so it is immediately recognisable in Java stack traces and logs. # Note: the bridge prepends the Java exception class name, so the full # message looks like "java.lang.RuntimeException: Error in Python interpreter:\n..." self.assertIn("Error in Python interpreter:", msg, f"Expected prefix 'Error in Python interpreter:' not found in:\n{msg}") return msg def test_explicit_cause(self): """raise B from A — both exceptions plus the 'direct cause' phrase must appear.""" msg = self._run_script( "try:\n" " raise TypeError('root cause')\n" "except TypeError as e:\n" " raise ValueError('outer error') from e\n" ) self.assertIn("TypeError", msg) self.assertIn("root cause", msg) self.assertIn("ValueError", msg) self.assertIn("outer error", msg) # Python's traceback module uses this exact phrase for __cause__ self.assertIn("direct cause", msg) def test_implicit_context(self): """Implicit context (no 'from') — both exceptions must appear with the right phrase.""" msg = self._run_script( "try:\n" " raise TypeError('original error')\n" "except TypeError:\n" " raise ValueError('replacement error')\n" ) self.assertIn("TypeError", msg) self.assertIn("original error", msg) self.assertIn("ValueError", msg) self.assertIn("replacement error", msg) # Python's traceback module uses this exact phrase for __context__ self.assertIn("During handling of the above exception", msg) def test_suppressed_context(self): """raise B from None — context is suppressed; only B should appear.""" msg = self._run_script( "try:\n" " raise TypeError('hidden cause')\n" "except TypeError:\n" " raise ValueError('only this') from None\n" ) self.assertIn("ValueError", msg) self.assertIn("only this", msg) # The suppressed cause must NOT leak into the message self.assertNotIn("TypeError", msg) self.assertNotIn("hidden cause", msg) def test_three_level_chain(self): """A → B → C explicit chain — all three levels must be present.""" msg = self._run_script( "try:\n" " try:\n" " raise RuntimeError('level 1')\n" " except RuntimeError as e:\n" " raise TypeError('level 2') from e\n" "except TypeError as e:\n" " raise ValueError('level 3') from e\n" ) self.assertIn("RuntimeError", msg) self.assertIn("level 1", msg) self.assertIn("TypeError", msg) self.assertIn("level 2", msg) self.assertIn("ValueError", msg) self.assertIn("level 3", msg) if __name__ == '__main__': print('\nRunning ' + __file__) unittest.main() jpy-2.0.0/src/test/java/0000775000175000017500000000000015202503234015162 5ustar alastairalastairjpy-2.0.0/src/test/java/org/0000775000175000017500000000000015202503234015751 5ustar alastairalastairjpy-2.0.0/src/test/java/org/jpy/0000775000175000017500000000000015202503234016553 5ustar alastairalastairjpy-2.0.0/src/test/java/org/jpy/EmbeddableTest.java0000664000175000017500000000564615202503234022275 0ustar alastairalastairpackage org.jpy; import java.io.Closeable; import java.util.Objects; /** * This class is meant to be invoked both from junit via {@link EmbeddableTestJunit}, and embedded * in python (see jpy_java_embeddable_test.py). * * For simplicity purposes, we don't want to have any external dependencies (ie, junit), so we can * keep the embedded classpath simple. */ public class EmbeddableTest { private static final boolean ON_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); public static void testStartingAndStoppingIfAvailable() { try (PyLibControl controller = new PyLibControl()) { } } public static void testPassStatement() { try (PyLibControl controller = new PyLibControl()) { PyObject object = PyObject.executeCode("pass", PyInputMode.STATEMENT); assertNotNull(object); assertNull(object.getObjectValue()); } } public static void testPrintStatement() { try (PyLibControl controller = new PyLibControl()) { PyObject.executeCode("print('hi')", PyInputMode.STATEMENT); } } public static void testIncrementByOne() { try (PyLibControl controller = new PyLibControl()) { PyObject.executeCode("def incByOne(x): return x + 1", PyInputMode.SCRIPT); PyObject results = PyModule.getMain().call("incByOne", 41); assertNotNull(results); assertTrue(results.isInt() || results.isLong()); assertEquals(results.getIntValue(), 42); } } private static void assertTrue(boolean b) { if (!b) { throw new AssertionError(); } } private static void assertFalse(boolean b) { if (!b) { throw new AssertionError(); } } private static void assertNotNull(Object o) { if (o == null) { throw new AssertionError(); } } private static void assertNull(Object o) { if (o != null) { throw new AssertionError(); } } private static void assertNotEquals(Object a, Object b) { if (Objects.equals(a, b)) { throw new AssertionError(); } } private static void assertEquals(int a, int b) { if (a != b) { throw new AssertionError(); } } private static class PyLibControl implements Closeable { private final boolean weAreEmbeddingPython; PyLibControl() { weAreEmbeddingPython = !PyLib.isPythonRunning(); if (weAreEmbeddingPython) { PyLib.startPython(); } assertTrue(PyLib.isPythonRunning()); } @Override public void close() { if (weAreEmbeddingPython) { PyLib.stopPython(); if (!ON_WINDOWS) { assertFalse(PyLib.isPythonRunning()); } } } } } jpy-2.0.0/src/test/java/org/jpy/jsr223/0000775000175000017500000000000015202503234017600 5ustar alastairalastairjpy-2.0.0/src/test/java/org/jpy/jsr223/Jsr223Test.java0000664000175000017500000000437115202503234022275 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.jsr223; import org.junit.Assert; import org.junit.Test; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class Jsr223Test { @Test public void testThatScriptEngineFactoryIsRegistered() throws Exception { assertNotNull(getScriptEngineFactory()); } @Test public void testThatScriptEngineFactorySupportsMinimumParameterKeys() throws Exception { ScriptEngineFactoryImpl scriptEngineFactory = getScriptEngineFactory(); assertEquals("cpython", scriptEngineFactory.getParameter(ScriptEngine.NAME)); assertEquals("jpy Python Engine", scriptEngineFactory.getParameter(ScriptEngine.ENGINE)); assertEquals("0.1-alpha", scriptEngineFactory.getParameter(ScriptEngine.ENGINE_VERSION)); assertEquals("python", scriptEngineFactory.getParameter(ScriptEngine.LANGUAGE)); assertEquals("3.x", scriptEngineFactory.getParameter(ScriptEngine.LANGUAGE_VERSION)); } private ScriptEngineFactoryImpl getScriptEngineFactory() { ScriptEngineManager engineManager = new ScriptEngineManager(); List engineFactories = engineManager.getEngineFactories(); ScriptEngineFactoryImpl engineFactoryImpl = null; for (ScriptEngineFactory engineFactory : engineFactories) { if (engineFactory instanceof ScriptEngineFactoryImpl) { engineFactoryImpl = (ScriptEngineFactoryImpl) engineFactory; } } return engineFactoryImpl; } } jpy-2.0.0/src/test/java/org/jpy/TestStatePrinter.java0000664000175000017500000000226015202503234022702 0ustar alastairalastairpackage org.jpy; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import java.time.Instant; public class TestStatePrinter extends TestWatcher { @Override protected void starting(Description desc) { System.out.println(Instant.now().toString() + " Starting test: " + desc.getClassName() + ": " + desc.getMethodName()); } @Override protected void succeeded(Description desc) { System.out.println(Instant.now().toString() + " Passed test: " + desc.getClassName() + ": " + desc.getMethodName()); } @Override protected void failed(Throwable e, Description desc) { System.err.println(Instant.now().toString() + " Failed test: " + desc.getClassName() + ": " + desc.getMethodName()); } @Override protected void finished(Description desc) { // TODO: Seems like this makes the tests fail (w/ JVM crash) more reliably. need to figure out why. // (Without the GC, the tests usually fail but sometimes pass. With it, they always fail.) System.out.println(Instant.now().toString() + " Running GC after test: " + desc.getClassName() + ": " + desc.getMethodName()); System.gc(); } } jpy-2.0.0/src/test/java/org/jpy/UseCases.java0000664000175000017500000001022515202503234021131 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import org.junit.After; import org.junit.Before; import org.junit.Assert; import org.junit.Test; import java.util.Locale; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * Some (more complex) tests that represent possible API use cases. * * @author Norman Fomferra */ public class UseCases { @Before public void setUp() { PyLib.startPython(); } @After public void tearDown() { PyLib.stopPython(); } @Test public void modifyPythonSysPath() { try ( final PyModule builtinsMod = PyModule.getBuiltins(); final PyModule sysMod = PyModule.importModule("sys"); final PyObject pathObj = sysMod.getAttribute("path")) { final PyObject lenObj1 = builtinsMod.call("len", pathObj); pathObj.call("append", "/usr/home/norman/"); final PyObject lenObj2 = builtinsMod.call("len", pathObj); int lenVal1 = lenObj1.getIntValue(); int lenVal2 = lenObj2.getIntValue(); String[] pathEntries = pathObj.getObjectArrayValue(String.class); lenObj2.close(); lenObj1.close(); ///////////////////////////////////////////////// assertEquals(lenVal1 + 1, lenVal2); assertEquals(pathEntries.length, lenVal2); //for (int i = 0; i < pathEntries.length; i++) { // System.out.printf("pathEntries[%d] = %s%n", i, pathEntries[i]); //} ///////////////////////////////////////////////// } } @Test public void setAndGetGlobalPythonVariables() throws Exception { PyLib.startPython(); PyLib.execScript("paramInt = 123"); PyLib.execScript("paramStr = 'abc'"); try ( final PyModule mainModule = PyModule.getMain(); final PyObject paramIntObj = mainModule.getAttribute("paramInt"); final PyObject paramStrObj = mainModule.getAttribute("paramStr")) { int paramIntValue = paramIntObj.getIntValue(); String paramStrValue = paramStrObj.getStringValue(); ///////////////////////////////////////////////// assertEquals(123, paramIntValue); assertEquals("abc", paramStrValue); ///////////////////////////////////////////////// } } @Test public void defAndUseGlobalPythonFunction() throws Exception { PyLib.startPython(); PyLib.execScript("def incByOne(x): return x + 1"); PyModule mainModule = PyModule.getMain(); PyObject eleven = mainModule.call("incByOne", 10); ///////////////////////////////////////////////// assertEquals(11, eleven.getIntValue()); ///////////////////////////////////////////////// // Performance test for TheMegaTB: long t0 = System.nanoTime(); long numCalls = 100000; PyObject num = eleven; for (long i = 0; i < numCalls; i++) { num = mainModule.call("incByOne", num); } long t1 = System.nanoTime(); assertEquals(11 + numCalls, num.getIntValue()); double millis = (t1 - t0) / 1000. / 1000.; double callsPerMilli = numCalls / millis; double millisPerCall = millis / numCalls; System.out.printf("Performance: %10.1f Python-calls/ms, %2.10f ms/Python-call%n", callsPerMilli, millisPerCall); assertTrue(callsPerMilli > 1.0); ///////////////////////////////////////////////// } static { Locale.setDefault(Locale.ENGLISH); } } jpy-2.0.0/src/test/java/org/jpy/LifeCycleTest.java0000664000175000017500000000226715202503234022124 0ustar alastairalastairpackage org.jpy; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; public class LifeCycleTest { @Rule public TestRule testStatePrinter = new TestStatePrinter(); private static final boolean ON_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); @Test public void testCanStartAndStopWithoutException() { PyLib.startPython(); Assert.assertTrue(PyLib.isPythonRunning()); PyModule sys1 = PyModule.importModule("sys"); Assert.assertNotNull(sys1); final long sys1Pointer = sys1.getPointer(); PyLib.stopPython(); if (!ON_WINDOWS) { Assert.assertFalse(PyLib.isPythonRunning()); } PyLib.startPython(); Assert.assertTrue(PyLib.isPythonRunning()); PyModule sys2 = PyModule.importModule("sys"); Assert.assertNotNull(sys2); final long sys2Pointer = sys2.getPointer(); if (!ON_WINDOWS) { Assert.assertNotEquals(sys1Pointer, sys2Pointer); } PyLib.stopPython(); if (!ON_WINDOWS) { Assert.assertFalse(PyLib.isPythonRunning()); } } } jpy-2.0.0/src/test/java/org/jpy/PyObjectTest.java0000664000175000017500000005264315202503234022007 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; import java.util.regex.Pattern; import org.junit.*; import org.jpy.fixtures.Processor; import org.junit.rules.TestRule; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.List; import java.util.concurrent.*; import static org.junit.Assert.*; /** * @author Norman Fomferra */ public class PyObjectTest { private PyModule SPECIAL_METHODS; @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Before public void setUp() throws Exception { // System.out.println("PyModuleTest: Current thread: " + // Thread.currentThread()); String importPath = new File("src/test/python/fixtures").getCanonicalPath(); PyLib.startPython(importPath); assertEquals(true, PyLib.isPythonRunning()); PyLib.Diag.setFlags(PyLib.Diag.F_ALL); SPECIAL_METHODS = PyModule.importModule("special_methods"); } @After public void tearDown() throws Exception { PyLib.Diag.setFlags(PyLib.Diag.F_OFF); PyLib.stopPython(); } @Test(expected = IllegalArgumentException.class) public void testNullPointer() throws Exception { new PyObject(0); } @Test public void testPointer() throws Exception { long pointer = PyLib.importModule("sys"); PyObject pyObject = new PyObject(pointer); assertEquals(pointer, pyObject.getPointer()); } @Test public void testToString() throws Exception { long pointer = PyLib.importModule("sys"); PyObject pyObject = new PyObject(pointer); assertEquals("", pyObject.toString()); } @Test public void testEqualsAndHashCode() throws Exception { long pointer1 = PyLib.importModule("sys"); long pointer2 = PyLib.importModule("os"); PyObject pyObject1 = new PyObject(pointer1); PyObject pyObject2 = new PyObject(pointer2); assertEquals(true, pyObject1.equals(pyObject1)); assertEquals(true, pyObject1.equals(new PyObject(pointer1))); assertEquals(false, pyObject1.equals(pyObject2)); assertEquals(false, pyObject1.equals(new PyObject(pointer2))); assertEquals(false, pyObject1.equals((Object) pointer1)); assertTrue(0 != pyObject1.hashCode()); assertTrue(0 != pyObject2.hashCode()); assertEquals(pyObject1.hashCode(), pyObject1.hashCode()); assertEquals(pyObject1.hashCode(), new PyObject(pointer1).hashCode()); assertTrue(pyObject1.hashCode() != pyObject2.hashCode()); } @Test public void testExecuteCode_Stmt() throws Exception { PyObject pyObject = PyObject.executeCode("pass", PyInputMode.STATEMENT); assertNotNull(pyObject); assertNull(pyObject.getObjectValue()); } @Test public void testExecuteCode_IntExpr() throws Exception { PyObject pyObject = PyObject.executeCode("7465", PyInputMode.EXPRESSION); assertNotNull(pyObject); assertEquals(7465, pyObject.getIntValue()); } @Test public void testExecuteCode_DoubleExpr() throws Exception { PyObject pyObject = PyObject.executeCode("3.14", PyInputMode.EXPRESSION); assertNotNull(pyObject); assertEquals(3.14, pyObject.getDoubleValue(), 1e-10); } @Test public void testExecuteCode_StringExpr() throws Exception { PyObject pyObject = PyObject.executeCode("'Hello from Python'", PyInputMode.EXPRESSION); assertNotNull(pyObject); assertEquals("Hello from Python", pyObject.getStringValue()); } @Test public void testExecuteCode_Script() throws Exception { HashMap localMap = new HashMap<>(); PyObject pyVoid = PyObject.executeCode( "" + "import jpy\n" + "File = jpy.get_type('java.io.File')\n" + "f = File('test.txt')", PyInputMode.SCRIPT, null, localMap); assertNotNull(pyVoid); assertEquals(null, pyVoid.getObjectValue()); assertNotNull(localMap.get("jpy")); assertNotNull(localMap.get("File")); assertNotNull(localMap.get("f")); assertEquals(PyObject.class, localMap.get("jpy").getClass()); assertEquals(Class.class, localMap.get("File").getClass()); assertEquals(File.class, localMap.get("f").getClass()); assertEquals(new File("test.txt"), localMap.get("f")); } @Test public void testLocals() throws Exception { HashMap localMap = new HashMap<>(); localMap.put("x", 7); localMap.put("y", 6); PyObject pyVoid = PyObject.executeCode("z = x + y", PyInputMode.STATEMENT, null, localMap); assertEquals(null, pyVoid.getObjectValue()); System.out.println("LocalMap size = " + localMap.size()); for (Map.Entry entry : localMap.entrySet()) { System.out.println("LocalMap[" + entry.getKey() + "]: " + entry.getValue()); } assertNotNull(localMap.get("x")); assertNotNull(localMap.get("y")); assertNotNull(localMap.get("z")); assertEquals((byte) 7, localMap.get("x")); assertEquals((byte) 6, localMap.get("y")); assertEquals((byte) 13, localMap.get("z")); } @Test public void testExecuteScript_ErrorExpr() throws Exception { try { PyObject.executeCode("[1, 2, 3", PyInputMode.EXPRESSION); } catch (RuntimeException e) { assertNotNull(e.getMessage()); assertTrue(e.getMessage().contains("SyntaxError")); } } @Test public void testCall() throws Exception { // Python equivalent: // // >>> import builtins // >>> builtins.max('A', 'Z') // 'Z' // PyModule builtins; try { // Python 3.3 builtins = PyModule.importModule("builtins"); } catch (Exception e) { // Python 2.7 builtins = PyModule.importModule("__builtin__"); } PyObject value = builtins.call("max", "A", "Z"); Assert.assertEquals("Z", value.getStringValue()); } @Test public void testGetSetAttributes() throws Exception { // Python equivalent: // // >>> import imp // >>> myobj = imp.new_module('myobj') // >>> myobj.a = 'Tut tut!' // >>> myobj.a // 'Tut tut!' // try ( final PyModule imp = PyModule.importModule("imp"); final PyObject myobj = imp.call("new_module", "myobj")) { // Call imp.new_module('') module myobj.setAttribute("a", "Tut tut!"); Assert.assertEquals("Tut tut!", myobj.getAttribute("a", String.class)); try (final PyObject a = myobj.getAttribute("a")) { Assert.assertEquals("Tut tut!", a.getStringValue()); } } } private boolean hasKey(Map dict, String key) { for (Map.Entry entry : dict.entrySet()) { if (entry.getKey().isString()) { if (entry.getKey().getObjectValue().equals(key)) { return true; } } } return false; } @Test public void testDictCopy() throws Exception { PyObject globals = PyLib.getMainGlobals(); PyDictWrapper dict = globals.asDict(); PyDictWrapper dictCopy = dict.copy(); PyObject.executeCode("x = 42", PyInputMode.STATEMENT, globals, dictCopy.unwrap()); boolean copyHasX = hasKey(dictCopy, "x"); boolean origHasX = hasKey(dict, "x"); assertTrue(copyHasX); assertFalse(origHasX); } @Test public void testCreateProxyAndCallSingleThreaded() throws Exception { // addTestDirToPythonSysPath(); PyModule procModule = PyModule.importModule("proc_class"); PyObject procObj = procModule.call("Processor"); testCallProxySingleThreaded(procObj); } // see https://github.com/bcdev/jpy/issues/26 @Test public void testCreateProxyAndCallMultiThreaded() throws Exception { // addTestDirToPythonSysPath(); // PyLib.Diag.setFlags(PyLib.Diag.F_ALL); PyModule procModule = PyModule.importModule("proc_class"); PyObject procObj = procModule.call("Processor"); PyLib.Diag.setFlags(PyLib.Diag.F_ALL); testCallProxyMultiThreaded(procObj); // PyLib.Diag.setFlags(PyLib.Diag.F_OFF); } @Test public void testUnwrapProxy() { PyModule procModule = PyModule.importModule("proc_class"); PyObject procObj = procModule.call("Processor"); Processor proxy = procObj.createProxy(Processor.class); PyObject unwrapped = PyObject.unwrapProxy(proxy); assertSame(procObj, unwrapped); } @Test public void testUnwrapProxyNotAProxy() { assertNull(PyObject.unwrapProxy(this)); } static void testCallProxySingleThreaded(PyObject procObject) { // Cast the Python object to a Java object of type 'Processor' Processor processor = procObject.createProxy(Processor.class); assertNotNull(processor); String result; result = processor.initialize(); assertEquals("initialize", result); result = processor.computeTile(100, 100, new float[100 * 100]); assertEquals("computeTile-100,100", result); result = processor.computeTile(200, 100, new float[100 * 100]); assertEquals("computeTile-200,100", result); result = processor.computeTile(100, 200, new float[100 * 100]); assertEquals("computeTile-100,200", result); result = processor.computeTile(200, 200, new float[100 * 100]); assertEquals("computeTile-200,200", result); processor.setVal(1234); int val = processor.getVal(); assertEquals(val, 1234); assertEquals(true, processor.check1234()); result = processor.dispose(); assertEquals("dispose", result); } static void testCallProxyMultiThreaded(PyObject procObject) { testCallProxyMultiThreaded(procObject, Executors.newFixedThreadPool(4)); } private static void testCallProxyMultiThreaded(PyObject procObject, ExecutorService executorService) { // Cast the Python object to a Java object of type 'Processor' final Processor processor = procObject.createProxy(Processor.class); assertNotNull(processor); String result; result = processor.initialize(); assertEquals("initialize", result); List> futures; try { futures = executorService.invokeAll( Arrays.asList(new ProcessorTask(processor, 100, 100), new ProcessorTask(processor, 200, 100), new ProcessorTask(processor, 100, 200), new ProcessorTask(processor, 200, 200)), 10, TimeUnit.SECONDS); result = processor.dispose(); assertEquals("dispose", result); String[] results = new String[] { futures.get(0).get(), futures.get(1).get(), futures.get(2).get(), futures.get(3).get(), }; Arrays.sort(results); result = results[0]; assertEquals("computeTile-100,100", result); result = results[1]; assertEquals("computeTile-100,200", result); result = results[2]; assertEquals("computeTile-200,100", result); result = results[3]; assertEquals("computeTile-200,200", result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); fail(e.getMessage()); } } static void addTestDirToPythonSysPath() throws IOException { // Add module dir to sys.path in order to import file 'proc_class.py' String importPath = new File("src/test/python/fixtures").getCanonicalPath(); // System.out.println("importPath = " + importPath); PyLib.execScript(String.format("import sys; sys.path.append('%s')", importPath.replace("\\", "\\\\"))); } private static class ProcessorTask implements Callable { final Processor processor; int x; int y; public ProcessorTask(Processor processor, int x, int y) { this.processor = processor; this.x = x; this.y = y; } @Override public String call() throws Exception { return processor.computeTile(x, y, new float[100 * 100]); } } private static interface ISimple { public int getValue(); } private static ISimple newTestObj(PyModule pyModule, String pythonClass, int value) { PyObject procObj = pyModule.call(pythonClass, value); ISimple simple = procObj.createProxy(ISimple.class); return simple; } @Test public void testHashEqStr() { PyModule pyModule = PyModule.importModule("hasheqstr"); testOneClass(pyModule, "Simple", false); testOneClass(pyModule, "HashSimple", true); } private static void testOneClass(PyModule pyModule, String pythonClass, boolean eqResExpected) { ISimple simple = newTestObj(pyModule, pythonClass, 1234); int value = simple.getValue(); assertEquals(value, 1234); String rep = simple.toString(); assertEquals(rep, pythonClass + ": 1234"); ISimple simple2 = newTestObj(pyModule, pythonClass, 1234); boolean eqRes = simple.equals(simple2); assertEquals(eqRes, eqResExpected); assertEquals(simple.hashCode() == simple2.hashCode(), eqResExpected); assertEquals(simple.equals(simple), true); } @Test public void strSimple() { PyObject obj = SPECIAL_METHODS.call("Simple", 1); assertEquals("1", obj.str()); } @Test public void hashSimple() { PyObject obj = SPECIAL_METHODS.call("Simple", 1); assertEquals(1L, obj.hash()); } @Test public void eqSimple() { PyObject obj1 = SPECIAL_METHODS.call("Simple", 1); PyObject obj2 = SPECIAL_METHODS.call("Simple", 1); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertTrue(obj1.eq(obj2)); assertTrue(obj2.eq(obj1)); assertFalse(obj1.eq(null)); assertFalse(obj2.eq(null)); assertFalse(obj1.eq(none)); assertFalse(obj2.eq(none)); assertFalse(none.eq(obj1)); assertFalse(none.eq(obj2)); } @Test public void eqSimpleDiff() { PyObject obj1 = SPECIAL_METHODS.call("Simple", 1); PyObject obj2 = SPECIAL_METHODS.call("Simple", 2); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertFalse(obj1.eq(obj2)); assertFalse(obj2.eq(obj1)); assertFalse(obj1.eq(null)); assertFalse(obj2.eq(null)); assertFalse(obj1.eq(none)); assertFalse(obj2.eq(none)); assertFalse(none.eq(obj1)); assertFalse(none.eq(obj2)); } @Test public void noneEqNone() { PyObject obj1 = PyObject.executeCode("None", PyInputMode.EXPRESSION); PyObject obj2 = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertTrue(obj1.eq(obj2)); assertTrue(obj2.eq(obj1)); assertTrue(obj1.eq(null)); assertTrue(obj2.eq(null)); } @Test public void strException() { PyObject obj = SPECIAL_METHODS.call("ThrowsException"); try { obj.str(); fail("Expected exception"); } catch (RuntimeException e) { // expected } } @Test public void hashException() { PyObject obj = SPECIAL_METHODS.call("ThrowsException"); try { obj.hash(); fail("Expected exception"); } catch (RuntimeException e) { // expected } } @Test public void eqException() { PyObject obj = SPECIAL_METHODS.call("ThrowsException"); try { obj.eq(obj); fail("Expected exception"); } catch (RuntimeException e) { // expected } } @Test public void nonBooleanEq() { PyObject obj1 = SPECIAL_METHODS.call("NonBooleanEq"); PyObject obj2 = SPECIAL_METHODS.call("NonBooleanEq"); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertFalse(obj1.eq(obj2)); assertFalse(obj2.eq(obj1)); assertFalse(obj1.eq(null)); assertFalse(obj2.eq(null)); assertFalse(obj1.eq(none)); assertFalse(obj2.eq(none)); assertFalse(none.eq(obj1)); assertFalse(none.eq(obj2)); } @Test public void nonStringStr() { PyObject obj = SPECIAL_METHODS.call("NonStringStr"); try { obj.str(); fail("Expected exception"); } catch (RuntimeException e) { // } } @Test public void strNotDefined() { PyObject obj = SPECIAL_METHODS.call("NoMethodsDefined"); // python2 uses instance, python3 uses object Pattern pattern = Pattern .compile("^$"); assertTrue(pattern.matcher(obj.str()).matches()); } @Test public void hashNotDefined() { PyObject obj = SPECIAL_METHODS.call("NoMethodsDefined"); obj.hash(); } @Test public void eqNotDefined() { PyObject obj1 = SPECIAL_METHODS.call("NoMethodsDefined"); PyObject obj2 = SPECIAL_METHODS.call("NoMethodsDefined"); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertFalse(obj1.eq(obj2)); assertFalse(obj2.eq(obj1)); assertFalse(obj1.eq(null)); assertFalse(obj2.eq(null)); assertFalse(obj1.eq(none)); assertFalse(obj2.eq(none)); assertFalse(none.eq(obj1)); assertFalse(none.eq(obj2)); } @Test public void eqAlwaysFalse() { PyObject obj1 = SPECIAL_METHODS.call("EqAlwaysFalse"); PyObject obj2 = SPECIAL_METHODS.call("EqAlwaysFalse"); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertFalse(obj1.eq(obj1)); assertFalse(obj2.eq(obj2)); assertFalse(obj1.eq(obj2)); assertFalse(obj2.eq(obj1)); assertFalse(obj1.eq(null)); assertFalse(obj2.eq(null)); assertFalse(obj1.eq(none)); assertFalse(obj2.eq(none)); assertFalse(none.eq(obj1)); assertFalse(none.eq(obj2)); } @Test public void eqAlwaysTrue() { PyObject obj1 = SPECIAL_METHODS.call("EqAlwaysTrue"); PyObject obj2 = SPECIAL_METHODS.call("EqAlwaysTrue"); PyObject none = PyObject.executeCode("None", PyInputMode.EXPRESSION); assertTrue(obj1.eq(obj1)); assertTrue(obj2.eq(obj2)); assertTrue(obj1.eq(obj2)); assertTrue(obj2.eq(obj1)); assertTrue(obj1.eq(null)); assertTrue(obj2.eq(null)); assertTrue(obj1.eq(none)); assertTrue(obj2.eq(none)); // this was a bit surprising to me, but None == obj must proxy to obj == None assertTrue(none.eq(obj1)); assertTrue(none.eq(obj2)); } @Test public void eqMixed() { PyObject alwaysFalse = SPECIAL_METHODS.call("EqAlwaysFalse"); PyObject alwaysTrue = SPECIAL_METHODS.call("EqAlwaysTrue"); assertFalse(alwaysFalse.eq(alwaysTrue)); assertTrue(alwaysTrue.eq(alwaysFalse)); } @Test public void hashNegativeOne() { // note: the hash() function treats -1 as a special return value, thus if the underlying // __hash__ returns -1, hash() will change that to -2. PyObject obj = SPECIAL_METHODS.call("HashNegativeOne"); assertEquals(-2L, obj.hash()); PyObject result = obj.callMethod("__hash__"); assertTrue(result.isInt()); assertEquals(-1, result.getIntValue()); } @Test public void closeIsIdempotent() { PyObject obj = SPECIAL_METHODS.call("Simple", 1); obj.close(); obj.close(); } @Test(expected = IllegalStateException.class) public void errorIfUsageAfterClose() { PyObject obj = SPECIAL_METHODS.call("Simple", 1); obj.close(); obj.getPointer(); } } jpy-2.0.0/src/test/java/org/jpy/EmbeddableTestJunit.java0000664000175000017500000000140115202503234023270 0ustar alastairalastairpackage org.jpy; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; /** * Wraps up {@link EmbeddableTest} with JUnit so {@link EmbeddableTest} doesn't have to have the * JUnit dependency. */ public class EmbeddableTestJunit { @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Test public void testStartingAndStoppingIfAvailable() { EmbeddableTest.testStartingAndStoppingIfAvailable(); } @Test public void testPassStatement() { EmbeddableTest.testPassStatement(); } @Test public void testPrintStatement() { EmbeddableTest.testPrintStatement(); } @Test public void testIncrementByOne() { EmbeddableTest.testIncrementByOne(); } } jpy-2.0.0/src/test/java/org/jpy/JavaReflectionTest.java0000664000175000017500000001220715202503234023154 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import org.jpy.annotations.Mutable; import org.jpy.annotations.Return; import org.jpy.fixtures.MethodReturnValueTestFixture; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import static org.junit.Assert.assertEquals; /** * The tests in here are actually not tests; they are only used to clarify the reflection API and JVM properties. * * @author Norman Fomferra */ public class JavaReflectionTest { @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Test public void testPrimitiveAndVoidNames() throws Exception { assertEquals("boolean", Boolean.TYPE.getName()); assertEquals("byte", Byte.TYPE.getName()); assertEquals("char", Character.TYPE.getName()); assertEquals("short", Short.TYPE.getName()); assertEquals("int", Integer.TYPE.getName()); assertEquals("long", Long.TYPE.getName()); assertEquals("float", Float.TYPE.getName()); assertEquals("double", Double.TYPE.getName()); assertEquals("void", Void.TYPE.getName()); } @Test public void testObjectArrays() throws Exception { Class aClass = String[][][].class; assertEquals("[[[Ljava.lang.String;", aClass.getName()); assertEquals(String[][].class, aClass.getComponentType()); assertEquals(String[].class, aClass.getComponentType().getComponentType()); assertEquals(String.class, aClass.getComponentType().getComponentType().getComponentType()); assertEquals(null, aClass.getComponentType().getComponentType().getComponentType().getComponentType()); } @Test public void testPrimitiveArrays() throws Exception { Class aClass = double[][][].class; assertEquals("[[[D", aClass.getName()); assertEquals(double[][].class, aClass.getComponentType()); assertEquals(double[].class, aClass.getComponentType().getComponentType()); assertEquals(Double.TYPE, aClass.getComponentType().getComponentType().getComponentType()); assertEquals(null, aClass.getComponentType().getComponentType().getComponentType().getComponentType()); } @Test public void testAnnotationNames() throws Exception { Method method = getClass().getMethod("iHaveAnAnnotationParam", Object.class); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); assertEquals(1, parameterAnnotations.length); assertEquals(2, parameterAnnotations[0].length); assertEquals("@org.jpy.annotations.Return()", parameterAnnotations[0][0].toString()); assertEquals("@org.jpy.annotations.Mutable()", parameterAnnotations[0][1].toString()); } @SuppressWarnings("UnusedDeclaration") public Object iHaveAnAnnotationParam(@Return @Mutable Object x) { return x; } public static void main(String[] args) { dumpTypeInfo(double[].class); dumpTypeInfo(String.class); dumpTypeInfo(MethodReturnValueTestFixture.class); } private static void dumpTypeInfo(Class type) { dumpBasicInfo(type, "type", ""); Constructor[] constructors = type.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(" constructor = " + constructor.getName()); Class[] parameterTypes = constructor.getParameterTypes(); dumpParameterTypes(parameterTypes); } Method[] methods = type.getDeclaredMethods(); for (Method method : methods) { System.out.println(" method '" + method.getName() + "'"); Class returnType = method.getReturnType(); dumpBasicInfo(returnType, "returnType", " "); Class[] parameterTypes = method.getParameterTypes(); dumpParameterTypes(parameterTypes); } } private static void dumpParameterTypes(Class[] parameterTypes) { for (int i = 0; i < parameterTypes.length; i++) { Class parameterType = parameterTypes[i]; dumpBasicInfo(parameterType, "parameterTypes[" + i + "]", " "); } } private static void dumpBasicInfo(Class type, String property, String indent) { System.out.println(indent + property + ".name: '" + type.getName() + "'"); Class componentType = type.getComponentType(); System.out.println(indent + property + ".componentType: '" + (componentType != null ? componentType.getName() : "null") + "'"); } } jpy-2.0.0/src/test/java/org/jpy/PyProxyTest.java0000664000175000017500000001672715202503234021725 0ustar alastairalastairpackage org.jpy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.util.regex.Pattern; import org.junit.*; import org.junit.rules.TestRule; public class PyProxyTest { private PyModule MODULE; @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Before public void setUp() throws Exception { PyLib.startPython(new File("src/test/python/fixtures").getCanonicalPath()); assertTrue(PyLib.isPythonRunning()); MODULE = PyModule.importModule("proxy_test_classes"); //PyLib.Diag.setFlags(PyLib.Diag.F_ALL); } @After public void tearDown() { //PyLib.Diag.setFlags(Diag.F_OFF); PyLib.stopPython(); } interface HasStr { } interface DoesNotHaveStr { } interface HasToStringWithArg { String toString(Object arg); } interface HasHashCodeWithArg { int hashCode(Object arg); } interface HasHashNegativeOne { } interface HasEqualsWithoutArg { boolean equals(); } interface EqAlwaysTrue { } interface EqAlwaysFalse { } interface ProxyAsArgument { boolean self_is_other(ProxyAsArgument other); } interface ThrowsException { } interface NonBooleanEq { } @Test public void hasStrToString() { PyObject pyObj = MODULE.call("HasStr"); HasStr proxy = pyObj.createProxy(HasStr.class); assertEquals("This is my __str__ method", pyObj.toString()); assertEquals("This is my __str__ method", proxy.toString()); } @Test public void doesNotHaveStrToString() { PyObject pyObj = MODULE.call("DoesNotHaveStr"); DoesNotHaveStr proxy = pyObj.createProxy(DoesNotHaveStr.class); // python2 uses instance, python3 uses object Pattern pattern = Pattern .compile("^$"); String pyObjToString = pyObj.toString(); String proxyToString = proxy.toString(); assertTrue(pattern.matcher(pyObjToString).matches()); assertTrue(pattern.matcher(proxyToString).matches()); assertEquals(pyObjToString, proxyToString); } @Test public void hasToStringWithArg() { PyObject pyObj = MODULE.call("HasToStringWithArg"); HasToStringWithArg proxy = pyObj.createProxy(HasToStringWithArg.class); assertEquals("weird", proxy.toString(this)); assertNotEquals("weird", proxy.toString()); } @Test public void hasHashCodeWithArg() { PyObject pyObj = MODULE.call("HasHashCodeWithArg"); HasHashCodeWithArg proxy = pyObj.createProxy(HasHashCodeWithArg.class); assertEquals(0, proxy.hashCode(this)); assertEquals(1, proxy.hashCode()); } @Test public void hasHashCodeNegativeOne() { /* >>> class Hash: ... def __hash__(self): ... return -1 ... >>> h = Hash() >>> hash(h) -2 >>> h.__hash__() -1 */ PyObject pyObj = MODULE.call("HasHashNegativeOne"); // Python reserves -1 for an error status on PyObject_Hash. When __hash__ returns -1, // python changes it to -2. assertEquals(pyObj.hash(), -2); HasHashNegativeOne proxy = pyObj.createProxy(HasHashNegativeOne.class); assertEquals(proxy.hashCode(), Long.hashCode(-2L)); } @Test public void hasEqualsWithoutArg() { PyObject pyObj = MODULE.call("HasEqualsWithoutArg"); HasEqualsWithoutArg proxy = pyObj.createProxy(HasEqualsWithoutArg.class); assertTrue(proxy.equals()); assertEquals(proxy, proxy); } @Test public void eqAlwaysTrue() { PyObject pyObj1 = MODULE.call("EqAlwaysTrue"); EqAlwaysTrue proxy1 = pyObj1.createProxy(EqAlwaysTrue.class); PyObject pyObj2 = MODULE.call("EqAlwaysTrue"); EqAlwaysTrue proxy2 = pyObj2.createProxy(EqAlwaysTrue.class); assertEquals(proxy1, proxy1); assertEquals(proxy2, proxy2); assertEquals(proxy1, proxy2); assertEquals(proxy2, proxy1); // note: can't call assertEquals, b/c that does null checks assertTrue(proxy1.equals(null)); assertTrue(proxy2.equals(null)); } @Test public void eqAlwaysFalse() { PyObject pyObj1 = MODULE.call("EqAlwaysFalse"); EqAlwaysFalse proxy1 = pyObj1.createProxy(EqAlwaysFalse.class); PyObject pyObj2 = MODULE.call("EqAlwaysFalse"); EqAlwaysFalse proxy2 = pyObj2.createProxy(EqAlwaysFalse.class); assertNotEquals(proxy1, proxy1); assertNotEquals(proxy2, proxy2); assertNotEquals(proxy1, proxy2); assertNotEquals(proxy2, proxy1); // note: can't call assertEquals, b/c that does null checks assertFalse(proxy1.equals(null)); assertFalse(proxy2.equals(null)); } @Test public void eqAlwaysTrueVsFalse() { /* >>> class EqAlwaysTrue: ... def __eq__(self, other): ... return True ... >>> class EqAlwaysFalse: ... def __eq__(self, other): ... return False ... >>> a = A() >>> b = B() >>> a == b True >>> b == a False */ PyObject pyObj1 = MODULE.call("EqAlwaysTrue"); EqAlwaysTrue alwaysTrue = pyObj1.createProxy(EqAlwaysTrue.class); PyObject pyObj2 = MODULE.call("EqAlwaysFalse"); EqAlwaysFalse alwaysFalse = pyObj2.createProxy(EqAlwaysFalse.class); assertEquals(alwaysTrue, alwaysFalse); assertNotEquals(alwaysFalse, alwaysTrue); } @Test public void canUnwrapProxyPassedAsArgument() { PyObject pyObject1 = MODULE.call("ProxyAsArgument"); ProxyAsArgument proxy1 = pyObject1.createProxy(ProxyAsArgument.class); PyObject pyObject2 = MODULE.call("ProxyAsArgument"); ProxyAsArgument proxy2 = pyObject2.createProxy(ProxyAsArgument.class); assertTrue(proxy1.self_is_other(proxy1)); assertTrue(proxy2.self_is_other(proxy2)); assertFalse(proxy1.self_is_other(proxy2)); assertFalse(proxy2.self_is_other(proxy1)); assertEquals(proxy1, proxy1); assertEquals(proxy2, proxy2); assertNotEquals(proxy1, proxy2); assertNotEquals(proxy2, proxy1); } @Test public void equalsException() { PyObject pyObject = MODULE.call("ThrowsException"); ThrowsException throwsException = pyObject .createProxy(ThrowsException.class); try { throwsException.equals(throwsException); fail("Expected an RuntimeException to be thrown"); } catch (RuntimeException e) { //throw e; //assertTrue(e.getMessage().contains("Value: this __eq__ always raises Exception")); } } @Test public void hashCodeException() { PyObject pyObject = MODULE.call("ThrowsException"); ThrowsException throwsException = pyObject .createProxy(ThrowsException.class); try { throwsException.hashCode(); fail("Expected an RuntimeException to be thrown"); } catch (RuntimeException e) { //assertTrue(e.getMessage().contains("Value: this __hash__ always raises Exception")); } } @Test public void toStringException() { PyObject pyObject = MODULE.call("ThrowsException"); ThrowsException throwsException = pyObject .createProxy(ThrowsException.class); try { throwsException.toString(); fail("Expected an RuntimeException to be thrown"); } catch (RuntimeException e) { //assertTrue(e.getMessage().contains("Value: this __str__ always raises Exception")); } } @Test public void nonBooleanEquals() { PyObject pyObject = MODULE.call("NonBooleanEq"); NonBooleanEq proxy = pyObject.createProxy(NonBooleanEq.class); assertEquals(proxy, proxy); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/0000775000175000017500000000000015202503234020424 5ustar alastairalastairjpy-2.0.0/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java0000664000175000017500000000064715202503234025351 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public class CyclicReferenceChild2 extends CyclicReferenceParent { public String getName() { return "Child2: " + this.toString(); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/ReachabilityFenceTestFixture.java0000664000175000017500000001656515202503234027054 0ustar alastairalastairpackage org.jpy.fixtures; import org.jpy.PyObject; import java.util.concurrent.atomic.AtomicBoolean; /** * Fixture for stress-testing the {@code Reference.reachabilityFence} guards * in {@link PyObject}. * *

Problem: When a {@code PyObject} wrapper is used transiently — e.g. * the result of {@code getAttribute} is immediately dereferenced via * {@code callMethod}, {@code getIntValue}, etc. — JLS §12.6.1 permits the JIT * to treat the wrapper as reachability-dead once {@code getPointer()} has copied * the native pointer into a {@code long}. At that point the GC may collect the * wrapper and the cleanup thread may {@code Py_DECREF} the underlying * {@code PyObject*} while JNI is still using it, causing a use-after-free crash. * *

Approach: A background thread continuously allocates short-lived * objects to keep the young-generation GC busy. Meanwhile, the main thread * repeatedly obtains a transient {@code PyObject} via {@code getAttribute} and * immediately invokes a JNI-backed method on it. Without * {@code Reference.reachabilityFence} these patterns crash with SIGSEGV; with * the fence they must complete cleanly. */ public class ReachabilityFenceTestFixture { private static final AtomicBoolean running = new AtomicBoolean(false); private static Thread allocatorThread = null; /** * Starts a background thread that continuously allocates byte arrays to * drive young-generation GC, which is needed to trigger the race. */ public static void startAllocator() { if (!running.compareAndSet(false, true)) { return; } allocatorThread = new Thread(() -> { Object[] sink = new Object[256]; int i = 0; while (running.get()) { sink[i++ & 255] = new byte[64 * 1024]; // 64 KB each } }, "uaf-allocator"); allocatorThread.setDaemon(true); allocatorThread.start(); } /** * Stops the background allocator thread. */ public static void stopAllocator() throws InterruptedException { running.set(false); if (allocatorThread != null) { allocatorThread.join(5_000); allocatorThread = null; } } /** * Stress getAttribute + callMethod on the transient bound method. */ public static void stressCallTransient(PyObject callable, int iterations) { for (int i = 0; i < iterations; i++) { PyObject m = callable.getAttribute("__call__"); m.callMethod("__call__"); } } /** * Stress getAttribute + getIntValue on the transient result. * The object should have a "value" attribute that is an int. */ public static void stressGetIntValue(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("value"); attr.getIntValue(); } } /** * Stress getAttribute + getStringValue on the transient result. * The object should have a "name" attribute that is a string. */ public static void stressGetStringValue(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("name"); attr.getStringValue(); } } /** * Stress getAttribute + str() on the transient result. */ public static void stressStr(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("value"); attr.str(); } } /** * Stress getAttribute + repr() on the transient result. */ public static void stressRepr(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("value"); attr.repr(); } } /** * Stress getAttribute + hash() on the transient result. */ public static void stressHash(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("name"); attr.hash(); } } /** * Stress getAttribute + type-check methods (isInt, isCallable, etc.) * on transient results. Each check uses a fresh getAttribute result so * the transient wrapper is the last use — making it eligible for * premature GC per JLS §12.6.1. */ public static void stressTypeChecks(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { obj.getAttribute("value").isInt(); obj.getAttribute("value").isFloat(); obj.getAttribute("value").isString(); obj.getAttribute("value").isCallable(); obj.getAttribute("value").isNone(); obj.getAttribute("value").isList(); obj.getAttribute("value").isDict(); } } /** * Stress getAttribute + hasAttribute on the transient result. */ public static void stressHasAttribute(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("nested"); attr.hasAttribute("value"); } } /** * Stress call (function-style) on a transient callable. * The object should have a "compute" attribute that accepts one int argument. */ public static void stressCall(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject fn = obj.getAttribute("compute"); fn.call("__call__", i); } } /** * Stress getAttribute + getObjectValue on the transient result. */ public static void stressGetObjectValue(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { PyObject attr = obj.getAttribute("value"); attr.getObjectValue(); } } /** * Stress createProxy + method call on a transient proxy. * The object should implement a "compute" method returning an int. */ public static void stressProxy(PyObject obj, int iterations) { for (int i = 0; i < iterations; i++) { // Create a transient proxy and immediately invoke a method on it. // The proxy (and its PyProxyHandler holding pyObject) can become // reachability-dead after getPointer() in PyProxyHandler.invoke(). Computable proxy = obj.createProxy(Computable.class); proxy.compute(i); } } /** * Stress asDict().containsKey() on a transient PyDictWrapper. * The object should be a Python dict. */ public static void stressDictContainsKey(PyObject dict, int iterations) { for (int i = 0; i < iterations; i++) { dict.asDict().containsKey("key"); } } /** * Stress asDict().keySet() on a transient PyDictWrapper. */ public static void stressDictKeySet(PyObject dict, int iterations) { for (int i = 0; i < iterations; i++) { dict.asDict().keySet(); } } /** * Stress asDict().copy() on a transient PyDictWrapper. */ public static void stressDictCopy(PyObject dict, int iterations) { for (int i = 0; i < iterations; i++) { dict.asDict().copy().close(); } } /** * Interface for proxy stress test. */ public interface Computable { int compute(int x); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/ConstructionTestFixture.java0000664000175000017500000000043515202503234026172 0ustar alastairalastairpackage org.jpy.fixtures; public class ConstructionTestFixture { public static ConstructionTestFixture viaStatic(int len) { return new ConstructionTestFixture(len); } private final byte[] array; public ConstructionTestFixture(int len) { array = new byte[len]; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/MethodReturnValueTestFixture.java0000664000175000017500000000710415202503234027115 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * Used as a test class for the test cases in jpy_retval_test.py * Note: Please make sure to not add any method overloads to this class. * This is done in {@link MethodOverloadTestFixture}. * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class MethodReturnValueTestFixture { public void getVoid() { } public boolean getValue_boolean(boolean value) { return value; } public byte getValue_byte(byte value) { return value; } public short getValue_short(short value) { return value; } public int getValue_int(int value) { return value; } public long getValue_long(long value) { return value; } public float getValue_float(float value) { return value; } public double getValue_double(double value) { return value; } public String getString(String string) { return string; } public Thing getObject(Thing object) { return object; } /////////////////////////////////////////////////////////////////////////////////// // 1D-Array Return Values public boolean[] getArray1D_boolean(boolean item0, boolean item1, boolean item2) { return new boolean[]{item0, item1, item2}; } public byte[] getArray1D_byte(byte item0, byte item1, byte item2) { return new byte[]{item0, item1, item2}; } public short[] getArray1D_short(short item0, short item1, short item2) { return new short[]{item0, item1, item2}; } public int[] getArray1D_int(int item0, int item1, int item2) { return new int[]{item0, item1, item2}; } public long[] getArray1D_long(long item0, long item1, long item2) { return new long[]{item0, item1, item2}; } public float[] getArray1D_float(float item0, float item1, float item2) { return new float[]{item0, item1, item2}; } public double[] getArray1D_double(double item0, double item1, double item2) { return new double[]{item0, item1, item2}; } public String[] getArray1D_String(String item0, String item1, String item2) { return new String[]{item0, item1, item2}; } public Thing[] getArray1D_Object(Thing item0, Thing item1, Thing item2) { return new Thing[]{item0, item1, item2}; } // add other variants /////////////////////////////////////////////////////////////////////////////////// // 2D-Array Return Values public boolean[][] getArray2D_boolean(boolean item00, boolean item01, boolean item10, boolean item11) { return new boolean[][]{{item00, item01}, {item10, item11}}; } public byte[][] getArray2D_byte(byte item00, byte item01, byte item10, byte item11) { return new byte[][]{{item00, item01}, {item10, item11}}; } public int[][] getArray2D_byte(int item00, int item01, int item10, int item11) { return new int[][]{{item00, item01}, {item10, item11}}; } // add other variants } jpy-2.0.0/src/test/java/org/jpy/fixtures/MultiThreadedEvalTestFixture.java0000664000175000017500000000225015202503234027040 0ustar alastairalastairpackage org.jpy.fixtures; import org.jpy.PyInputMode; import org.jpy.PyLib; import org.jpy.PyObject; import java.util.List; public class MultiThreadedEvalTestFixture { public static void expression(String expression, int numThreads) { execute(PyInputMode.EXPRESSION, expression, numThreads); } public static void script(String expression, int numThreads) { execute(PyInputMode.SCRIPT, expression, numThreads); } private static void execute(PyInputMode mode, String expression, int numThreads) { List threads = new java.util.ArrayList<>(); PyObject globals = PyLib.getCurrentGlobals(); PyObject locals = PyLib.getCurrentLocals(); for (int i = 0; i < numThreads; i++) { threads.add(new Thread(() -> { PyObject.executeCode(expression, mode, globals, locals); })); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } jpy-2.0.0/src/test/java/org/jpy/fixtures/DefaultInterfaceTestFixture.java0000664000175000017500000000173415202503234026710 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Charles Wright */ @SuppressWarnings("UnusedDeclaration") public interface DefaultInterfaceTestFixture { int doIt(); default public int doItPlusOne() { return 1 + doIt(); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/Thing.java0000664000175000017500000000260315202503234022341 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * Stands for any Java object. * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class Thing { private int value; public Thing() { } public Thing(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Thing thing = (Thing) o; return value == thing.value; } @Override public int hashCode() { return value; } @Override public String toString() { return "Thing[value=" + value + "]"; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/VarArgsTestFixture.java0000664000175000017500000000740215202503234025046 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy.fixtures; import java.lang.reflect.Array; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class VarArgsTestFixture { public String join(String prefix, int ... a) { return stringifyArgs(prefix, a); } public String join(String prefix, double ... a) { return stringifyArgs(prefix, a); } public String join(String prefix, float ... a) { return stringifyArgs(prefix, a); } public String join(String prefix, String ... a) { return stringifyArgs(prefix, a); } public String joinFloat(String prefix, float ... a) { return stringifyArgs(prefix, a); } public String joinLong(String prefix, long ... a) { return stringifyArgs(prefix, a); } public String joinShort(String prefix, short ... a) { return stringifyArgs(prefix, a); } public String joinByte(String prefix, byte ... a) { return stringifyArgs(prefix, a); } public String joinChar(String prefix, char ... a) { return stringifyArgs(prefix, a); } public String joinBoolean(String prefix, boolean ... a) { return stringifyArgs(prefix, a); } public String joinObjects(String prefix, Object ... a) { return stringifyArgs(prefix, a); } public int chooseFixedArity(int... a) { return 2; } public int chooseFixedArity() { return 1; } public int stringOrObjectVarArgs(String ... a) { return 1 + a.length; } public int stringOrObjectVarArgs(Object ... a) { return 2 + a.length; } static String stringifyArgs(Object... args) { StringBuilder argString = new StringBuilder(); for (int i = 0; i < args.length; i++) { if (i > 0) { argString.append(","); } Object arg = args[i]; if (arg != null) { Class argClass = arg.getClass(); argString.append(argClass.getSimpleName()); argString.append('('); if (argClass.isArray()) { stringifyArray(arg, argString); } else { stringifyObject(arg, argString); } argString.append(')'); } else { argString.append("null"); } } return argString.toString(); } private static void stringifyObject(Object arg, StringBuilder argString) { argString.append(String.valueOf(arg)); } private static void stringifyArray(Object arg, StringBuilder argString) { boolean primitive = arg.getClass().getComponentType().isPrimitive(); int length = Array.getLength(arg); for (int i = 0; i < length; i++) { Object item = Array.get(arg, i); if (i > 0) { argString.append(","); } if (primitive) { argString.append(String.valueOf(item)); } else { argString.append(stringifyArgs(item)); } } } } jpy-2.0.0/src/test/java/org/jpy/fixtures/Processor.java0000664000175000017500000000164415202503234023253 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * Stands for an image data processor. * * Created by Norman on 19.12.13. */ public interface Processor { String initialize(); String computeTile(int w, int h, float[] data); String dispose(); void setVal(int n); int getVal(); boolean check1234(); } jpy-2.0.0/src/test/java/org/jpy/fixtures/TypeConversionTestFixture.java0000664000175000017500000000256415202503234026474 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; /** * Used as a test class for the test cases in jpy_typeconv_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class TypeConversionTestFixture { public String stringifyObjectArg(Object arg) { return stringifyArgs(arg); } public String stringifyIntArrayArg(int[] arg) { return stringifyArgs((Object) arg); } public String stringifyObjectArrayArg(Object[] arg) { return stringifyArgs((Object) arg); } public String stringifyStringArrayArg(String[] arg) { return stringifyArgs((Object) arg); } public boolean isSameObject(Object o1, Object o2) { return o1 == o2; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/MethodOverloadTestFixture.java0000664000175000017500000001012315202503234026407 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; import java.lang.reflect.Array; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class MethodOverloadTestFixture { public String join(int a, int b) { return stringifyArgs(a, b); } public String join(int a, double b) { return stringifyArgs(a, b); } public String join(int a, String b) { return stringifyArgs(a, b); } public String join(double a, int b) { return stringifyArgs(a, b); } public String join(double a, double b) { return stringifyArgs(a, b); } public String join(double a, String b) { return stringifyArgs(a, b); } public String join(String a, int b) { return stringifyArgs(a, b); } public String join(String a, double b) { return stringifyArgs(a, b); } public String join(String a, String b) { return stringifyArgs(a, b); } ////////////////////////////////////////////// public String join(String a) { return stringifyArgs(a); } public String join(String a, String b, String c) { return stringifyArgs(a, b, c); } ////////////////////////////////////////////// public String join2(Comparable a, int b, String c, String d) { return stringifyArgs(a, b, c, d); } ////////////////////////////////////////////// public String join3(Number a, int b) { return stringifyArgs(a, b); } /** * Used to test that we also find overloaded methods in class hierarchies */ public static class MethodOverloadTestFixture2 extends MethodOverloadTestFixture { public String join(String a, String b, String c, String d) { return stringifyArgs(a, b, c, d); } } ////////////////////////////////////////////// // Should never been found, since 'float' is not present in Python public String join(int a, float b) { return stringifyArgs(a, b); } static String stringifyArgs(Object... args) { StringBuilder argString = new StringBuilder(); for (int i = 0; i < args.length; i++) { if (i > 0) { argString.append(","); } Object arg = args[i]; if (arg != null) { Class argClass = arg.getClass(); argString.append(argClass.getSimpleName()); argString.append('('); if (argClass.isArray()) { stringifyArray(arg, argString); } else { stringifyObject(arg, argString); } argString.append(')'); } else { argString.append("null"); } } return argString.toString(); } private static void stringifyObject(Object arg, StringBuilder argString) { argString.append(String.valueOf(arg)); } private static void stringifyArray(Object arg, StringBuilder argString) { boolean primitive = arg.getClass().getComponentType().isPrimitive(); int length = Array.getLength(arg); for (int i = 0; i < length; i++) { Object item = Array.get(arg, i); if (i > 0) { argString.append(","); } if (primitive) { argString.append(String.valueOf(item)); } else { argString.append(stringifyArgs(item)); } } } } jpy-2.0.0/src/test/java/org/jpy/fixtures/ExceptionTestFixture.java0000664000175000017500000000405415202503234025437 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy.fixtures; import java.io.IOException; /** * Used as a test class for the test cases in jpy_exception_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class ExceptionTestFixture { public int throwNpeIfArgIsNull(String arg) { return arg.length(); } public int throwNpeIfArgIsNull2(String arg) { return throwNpeIfArgIsNull(arg); } public int throwNpeIfArgIsNullNested(String arg) { try { return throwNpeIfArgIsNull(arg); } catch (Exception e) { throw new RuntimeException("Nested exception", e); } } public int throwNpeIfArgIsNullNested2(String arg) { return throwNpeIfArgIsNullNested(arg); } public int throwNpeIfArgIsNullNested3(String arg) { try { return throwNpeIfArgIsNullNested2(arg); } catch (Exception e) { throw new RuntimeException("Nested exception 3", e); } } public int throwAioobeIfIndexIsNotZero(int index) { int[] ints = new int[]{101}; return ints[index]; } public void throwRteIfMessageIsNotNull(String message) { if (message != null) { throw new RuntimeException(message); } } public void throwIoeIfMessageIsNotNull(String message) throws IOException { if (message != null) { throw new IOException(message); } } } jpy-2.0.0/src/test/java/org/jpy/fixtures/CyclicReferenceGrandParent.java0000664000175000017500000000112115202503234026435 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public class CyclicReferenceGrandParent { private int x; public int z = 100; public CyclicReferenceGrandParent() { } public void refChild2(CyclicReferenceChild2 child2) { } public int grandParentMethod() { return 888; } public int get_x() { return this.x; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/JavaArrayTestFixture.java0000664000175000017500000000022115202503234025351 0ustar alastairalastairpackage org.jpy.fixtures; public class JavaArrayTestFixture { public static byte[] createByteArray(int len) { return new byte[len]; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/EvalTestFixture.java0000664000175000017500000000112215202503234024361 0ustar alastairalastairpackage org.jpy.fixtures; import org.jpy.PyInputMode; import org.jpy.PyLib; import org.jpy.PyObject; import java.io.File; import java.io.IOException; import static org.junit.Assert.assertEquals; public class EvalTestFixture { public static PyObject expression(String expression) { return PyObject.executeCode(expression,PyInputMode.EXPRESSION, PyLib.getCurrentGlobals(),PyLib.getCurrentLocals()); } public static void script(String expression) { PyObject.executeCode(expression,PyInputMode.SCRIPT, PyLib.getCurrentGlobals(),PyLib.getCurrentLocals()); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/CovariantOverloadTestFixture.java0000664000175000017500000000230115202503234027114 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. */ package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Charles P. Wright */ @SuppressWarnings("UnusedDeclaration") public class CovariantOverloadTestFixture { int x; public CovariantOverloadTestFixture(int x) { this.x = x; } public CovariantOverloadTestFixture foo(Number a, int b) { return new CovariantOverloadTestFixture(a.intValue() + b); } public int getX() { return x; } }jpy-2.0.0/src/test/java/org/jpy/fixtures/GetTypeFailureParent.java0000664000175000017500000000067715202503234025344 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public abstract class GetTypeFailureParent { static { toFail(); } static void toFail() { throw new RuntimeException("Can't be loaded!"); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/TypeTranslationTestFixture.java0000664000175000017500000000207215202503234026637 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy.fixtures; /** * Used as a test class for the test cases in jpy_retval_test.py * Note: Please make sure to not add any method overloads to this class. * This is done in {@link MethodOverloadTestFixture}. * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class TypeTranslationTestFixture { public Thing makeThing(int value) { return new Thing(value); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/GetTypeFailureChild.java0000664000175000017500000000101615202503234025122 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public class GetTypeFailureChild extends GetTypeFailureParent { private int x; private GetTypeFailureChild(int x) { this.x = x; } public static GetTypeFailureChild of(int x) { return new GetTypeFailureChild(x); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/CyclicReferenceParent.java0000664000175000017500000000114715202503234025471 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public abstract class CyclicReferenceParent extends CyclicReferenceGrandParent { private int x; public int y = 10; public static CyclicReferenceChild1 of(int x) { return CyclicReferenceChild1.of(x); } public int parentMethod() { return 88; } public int get_x() { return this.x; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/CovariantOverloadExtendTestFixture.java0000664000175000017500000000230415202503234030267 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. */ package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Charles P. Wright */ @SuppressWarnings("UnusedDeclaration") public class CovariantOverloadExtendTestFixture extends CovariantOverloadTestFixture { public CovariantOverloadExtendTestFixture(int x) { super(x * 2); } public CovariantOverloadExtendTestFixture foo(Number a, int b) { return new CovariantOverloadExtendTestFixture(a.intValue() - b); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/ModifyAndReturnParametersTestFixture.java0000664000175000017500000000433615202503234030602 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; import org.jpy.annotations.Mutable; import org.jpy.annotations.Output; import org.jpy.annotations.Return; /** * Used as a test class for the test cases in jpy_modretparam_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class ModifyAndReturnParametersTestFixture { public void modifyThing(@Mutable Thing thing, int value) { thing.setValue(value); } public Thing returnThing(@Return Thing thing) { if (thing == null) { thing = new Thing(); } return thing; } public Thing modifyAndReturnThing(@Mutable @Return Thing thing, int value) { if (thing == null) { thing = new Thing(); } thing.setValue(value); return thing; } public void modifyIntArray(@Mutable int[] array, int item0, int item1, int item2) { array[0] = item0; array[1] = item1; array[2] = item2; } public int[] returnIntArray(@Return int[] array) { if (array == null) { array = new int[3]; } return array; } public int[] modifyAndReturnIntArray(@Mutable @Return int[] array, int item0, int item1, int item2) { if (array == null) { array = new int[3]; } array[0] = item0; array[1] = item1; array[2] = item2; return array; } public void modifyAndOutputIntArray(@Mutable @Output int[] array, int item0, int item1, int item2) { if (array == null) { return; } array[0] = item0; array[1] = item1; array[2] = item2; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/TypeResolutionTestFixture.java0000664000175000017500000000216115202503234026503 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * Used as a test class for the test cases in jpy_typeres_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class TypeResolutionTestFixture { public SuperThing createSuperThing(int value) { return new SuperThing(value); } public static class SuperThing extends Thing { public SuperThing(int value) { super(value); } public void add(int val) { setValue(getValue() + val); } } } jpy-2.0.0/src/test/java/org/jpy/fixtures/FieldTestFixture.java0000664000175000017500000000365615202503234024533 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * Used as a test class for the test cases in jpy_field_test.py * * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class FieldTestFixture { public static final boolean z_STATIC_FIELD = true; public static final char c_STATIC_FIELD = 'A'; public static final byte b_STATIC_FIELD = (byte) 123; public static final short s_STATIC_FIELD = (short) 12345; public static final int i_STATIC_FIELD = 123456789; public static final long j_STATIC_FIELD = 1234567890123456789L; public static final float f_STATIC_FIELD = 0.12345F; public static final double d_STATIC_FIELD = 0.123456789; public static final String S_OBJ_STATIC_FIELD = "ABC"; public static final Thing l_OBJ_STATIC_FIELD = new Thing(123); public boolean zInstField; public char cInstField; public byte bInstField; public short sInstField; public int iInstField; public long jInstField; public float fInstField; public double dInstField; public Boolean zObjInstField; public Character cObjInstField; public Byte bObjInstField; public Short sObjInstField; public Integer iObjInstField; public Long jObjInstField; public Float fObjInstField; public Double dObjInstField; public String SObjInstField; public Thing lObjInstField; } jpy-2.0.0/src/test/java/org/jpy/fixtures/DefaultInterfaceImplTestFixture.java0000664000175000017500000000172315202503234027530 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; import static org.jpy.fixtures.MethodOverloadTestFixture.stringifyArgs; /** * Used as a test class for the test cases in jpy_overload_test.py * * @author Charles Wright */ @SuppressWarnings("UnusedDeclaration") public class DefaultInterfaceImplTestFixture implements DefaultInterfaceTestFixture { public int doIt() { return 2; } } jpy-2.0.0/src/test/java/org/jpy/fixtures/ConstructorOverloadTestFixture.java0000664000175000017500000000302115202503234027513 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.fixtures; /** * @author Norman Fomferra */ @SuppressWarnings("UnusedDeclaration") public class ConstructorOverloadTestFixture { String state; public ConstructorOverloadTestFixture() { initState(); } public ConstructorOverloadTestFixture(int a) { initState(a); } public ConstructorOverloadTestFixture(int a, int b) { initState(a, b); } public ConstructorOverloadTestFixture(float a) { initState(a); } public ConstructorOverloadTestFixture(float a, float b) { initState(a, b); } public ConstructorOverloadTestFixture(int a, float b) { initState(a, b); } public ConstructorOverloadTestFixture(float a, int b) { initState(a, b); } public String getState() { return state; } private void initState(Object... args) { state = MethodOverloadTestFixture.stringifyArgs(args); } } jpy-2.0.0/src/test/java/org/jpy/fixtures/CyclicReferenceChild1.java0000664000175000017500000000151315202503234025341 0ustar alastairalastair// // Copyright 2024 jpy-consortium // package org.jpy.fixtures; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Used as a test class for the test cases in jpy_gettype_test.py * * @author Jianfeng Mao */ @SuppressWarnings("UnusedDeclaration") public class CyclicReferenceChild1 extends CyclicReferenceParent { private int x; private CyclicReferenceChild1(int x) { this.x = x; } public static CyclicReferenceChild1 of(int x) { return new CyclicReferenceChild1(x); } public int get_x() { return this.x; } public CyclicReferenceChild2[] getChild2s() { CyclicReferenceChild2[] child2s = new CyclicReferenceChild2[2]; child2s[0] = new CyclicReferenceChild2(); child2s[1] = new CyclicReferenceChild2(); return child2s; } } jpy-2.0.0/src/test/java/org/jpy/PyLibWithSysPathTest.java0000664000175000017500000000465515202503234023457 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import org.junit.*; import org.junit.rules.TestRule; import java.io.File; import java.net.URI; import java.security.CodeSource; import static org.junit.Assert.*; public class PyLibWithSysPathTest { @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Before public void setUp() throws Exception { CodeSource codeSource = PyLibWithSysPathTest.class.getProtectionDomain().getCodeSource(); if (codeSource == null) { System.out.println(PyLibWithSysPathTest.class + " not run: no code source found"); return; } URI codeSourceLocation = codeSource.getLocation().toURI(); System.out.println(PyLibWithSysPathTest.class + ": code source: " + codeSourceLocation); File codeSourceDir = new File(codeSourceLocation); if (!codeSourceDir.isDirectory()) { System.out.println(PyLibWithSysPathTest.class + " not run: code source is not a directory: " + codeSourceLocation); return; } File pymodulesDir = new File(codeSourceDir, "pymodules"); //assertFalse(PyLib.isPythonRunning()); System.out.println("PyLibWithSysPathTest: starting Python with 'sys.path' extension: " + pymodulesDir); PyLib.startPython(pymodulesDir.getPath()); //PyLib.startPython("x"); assertTrue(PyLib.isPythonRunning()); } @After public void tearDown() throws Exception { PyLib.stopPython(); } @Test public void testLoadModule() throws Exception { try (final PyModule pyModule = PyModule.importModule("mod_1")) { assertNotNull(pyModule); try (final PyObject pyAnswer = pyModule.getAttribute("answer")) { assertNotNull(pyAnswer); assertEquals(42, pyAnswer.getIntValue()); } } } } jpy-2.0.0/src/test/java/org/jpy/PyModuleTest.java0000664000175000017500000000637415202503234022026 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import org.junit.*; import org.junit.rules.TestRule; import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** * @author Norman Fomferra */ public class PyModuleTest { @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Before public void setUp() throws Exception { //System.out.println("PyModuleTest: Current thread: " + Thread.currentThread()); String importPath = new File("src/test/python/fixtures").getCanonicalPath(); PyLib.startPython(importPath); assertEquals(true, PyLib.isPythonRunning()); //PyLib.Diag.setFlags(PyLib.Diag.F_METH); } @After public void tearDown() throws Exception { PyLib.Diag.setFlags(PyLib.Diag.F_OFF); PyLib.stopPython(); } @Test public void testCreateAndCallProxySingleThreaded() throws Exception { //PyObjectTest.addTestDirToPythonSysPath(); PyModule procModule = PyModule.importModule("proc_module"); PyObjectTest.testCallProxySingleThreaded(procModule); } // see https://github.com/bcdev/jpy/issues/26 @Test public void testCreateAndCallProxyMultiThreaded() throws Exception { //PyObjectTest.addTestDirToPythonSysPath(); PyModule procModule = PyModule.importModule("proc_module"); PyObjectTest.testCallProxyMultiThreaded(procModule); } // see: https://github.com/bcdev/jpy/issues/39: Improve Java exception messages on Python errors #39 @Test public void testPythonErrorMessages() throws Exception { //PyObjectTest.addTestDirToPythonSysPath(); PyModule raiserModule = PyModule.importModule("raise_errors"); for (int i=0;i < 10;i++) { try { raiserModule.call("raise_if_zero", 0); Assert.fail(); } catch (RuntimeException e) { //e.printStackTrace(); String message = e.getMessage(); //System.out.println("message = " + message); assertNotNull(message); assertTrue(message.startsWith("Error in Python interpreter")); assertTrue(message.contains("Type: <")); assertTrue(message.contains("IndexError'>\n")); assertTrue(message.contains("Value: arg wasn't there\n")); assertTrue(message.contains("Line: 3\n")); assertTrue(message.contains("Namespace: raise_if_zero\n")); assertTrue(message.contains("File: ")); } // ok raiserModule.call("raise_if_zero", 1); } } } jpy-2.0.0/src/test/java/org/jpy/PyLibTest.java0000664000175000017500000002352015202503234021277 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; public class PyLibTest { @Rule public TestRule testStatePrinter = new TestStatePrinter(); @Before public void setUp() throws Exception { //PyLib.Diag.setFlags(PyLib.Diag.F_ERR); PyLib.startPython(); assertEquals(true, PyLib.isPythonRunning()); } @After public void tearDown() throws Exception { PyLib.stopPython(); } @Test public void testGettingSysArgv() throws Exception { // Since sys.argv is really part of the C-based sys module, there // are special hooks embedded python systems need to call to set it up. PyModule sys = PyModule.importModule("sys"); String[] argv = sys.getAttribute("argv", String[].class); assertNotNull(argv); assertEquals(1, argv.length); assertTrue(argv[0].isEmpty()); } @Test public void testGetPythonVersion() throws Exception { String pythonVersion = PyLib.getPythonVersion(); System.out.println("pythonVersion = " + pythonVersion); assertNotNull(pythonVersion); } @Test public void testExecScript() throws Exception { int exitCode = PyLib.execScript(String.format("print('%s says: \"Hello Python!\"')", PyLibTest.class.getName())); assertEquals(0, exitCode); } @Test public void testExecScriptInError() throws Exception { int exitCode; exitCode = PyLib.execScript("0 / 1"); assertEquals(0, exitCode); exitCode = PyLib.execScript("1 / 0"); assertEquals(-1, exitCode); } @Test public void testImportModule() throws Exception { long pyModule; pyModule = PyLib.importModule("os"); assertTrue(pyModule != 0); PyLib.decRef(pyModule); pyModule = PyLib.importModule("sys"); assertTrue(pyModule != 0); PyLib.decRef(pyModule); } @Test public void testGetSetAttributeValue() throws Exception { long pyModule; long pyObject; pyModule = PyLib.importModule("jpy"); try { assertTrue(pyModule != 0); long pyObj = PyLib.getAttributeObject(pyModule, "JType"); assertTrue(pyObj != 0); PyLib.decRef(pyObj); PyLib.setAttributeValue(pyModule, "_hello", "Hello Python!", String.class); assertEquals("Hello Python!", PyLib.getAttributeValue(pyModule, "_hello", String.class)); pyObject = PyLib.getAttributeObject(pyModule, "_hello"); try { assertTrue(pyObject != 0); } finally { PyLib.decRef(pyObject); } } finally { PyLib.decRef(pyModule); } } @Test public void testCallAndReturnValue() throws Exception { long builtins; try { //Python 3.3 builtins = PyLib.importModule("builtins"); } catch (Exception e) { //Python 2.7 builtins = PyLib.importModule("__builtin__"); } try { assertTrue(builtins != 0); long max = PyLib.getAttributeObject(builtins, "max"); try { assertTrue(max != 0); } finally { PyLib.decRef(max); } //PyLib.Diag.setFlags(PyLib.Diag.F_ALL); String result = PyLib.callAndReturnValue(builtins, false, "max", 2, new Object[]{"A", "Z"}, new Class[]{String.class, String.class}, String.class); assertEquals("Z", result); } finally { PyLib.decRef(builtins); } } @Test public void testCallAndReturnObject() throws Exception { long builtins; try { //Python 3.3 builtins = PyLib.importModule("builtins"); } catch (Exception e) { //Python 2.7 builtins = PyLib.importModule("__builtin__"); } try { assertTrue(builtins != 0); long max = PyLib.getAttributeObject(builtins, "max"); assertTrue(max != 0); PyLib.decRef(max); long pointer = PyLib.callAndReturnObject(builtins, false, "max", 2, new Object[]{"A", "Z"}, null); try { assertTrue(pointer != 0); //PyLib.Diag.setFlags(PyLib.Diag.F_ALL); assertEquals("Z", new PyObject(pointer).getStringValue()); } finally { PyLib.decRef(pointer); } } finally { PyLib.decRef(builtins); } } @Test public void testGetMainGlobals() throws Exception { PyObject globals = PyLib.getMainGlobals(); Map dict = globals.asDict(); assertFalse(dict.isEmpty()); boolean foundName = false; for (Map.Entry entry : dict.entrySet()) { if (entry.getKey().isString()) { if (entry.getKey().getObjectValue().equals("__name__")) { foundName = true; break; } } } assertTrue(foundName); } @Test public void testNewDict() throws Exception { PyObject dict = PyLib.newDict(); assertTrue(dict.asDict().isEmpty()); PyObject globals = PyLib.getMainGlobals(); PyObject.executeCode("x = 42", PyInputMode.STATEMENT, globals, dict); assertFalse(dict.asDict().isEmpty()); } @Test public void testDictKeys() { PyObject dict = PyLib.newDict(); PyDictWrapper wrapper = dict.asDict(); assertTrue(wrapper.keySet().isEmpty()); PyObject.executeCode("my_key = 42", PyInputMode.STATEMENT, PyLib.getMainGlobals(), dict); PyObject pyKey = PyObject.executeCode("'my_key'", PyInputMode.EXPRESSION); assertEquals(wrapper.keySet(), Collections.singleton(pyKey)); assertTrue(wrapper.containsKey(pyKey)); assertTrue(wrapper.containsKey("my_key")); assertTrue(wrapper.containsKey((Object)pyKey)); assertFalse(wrapper.containsKey(1)); assertFalse(wrapper.containsKey(this)); } @Test public void testDictValues() { PyObject dict = PyLib.newDict(); PyDictWrapper wrapper = dict.asDict(); assertTrue(wrapper.values().isEmpty()); PyObject.executeCode("my_key = 42", PyInputMode.STATEMENT, PyLib.getMainGlobals(), dict); PyObject pyValue = PyObject.executeCode("42", PyInputMode.EXPRESSION); // PyListWrapper doesn't implement the full List interface... can't check direct List equality Collection values = wrapper.values(); assertFalse(values.isEmpty()); assertEquals(values.size(), 1); assertEquals(values.iterator().next(), pyValue); assertTrue(wrapper.containsValue(pyValue)); } @Test public void invalidPyDictKeys() { PyObject pyValue = PyObject.executeCode("42", PyInputMode.EXPRESSION); try { PyLib.pyDictKeys(pyValue.getPointer()); fail("Expected exception"); } catch (UnsupportedOperationException e) { // expected } } @Test public void invalidPyDictValues() { PyObject pyValue = PyObject.executeCode("42", PyInputMode.EXPRESSION); try { PyLib.pyDictValues(pyValue.getPointer()); fail("Expected exception"); } catch (UnsupportedOperationException e) { // expected } } @Test public void invalidPyDictContains() { PyObject pyValue = PyObject.executeCode("42", PyInputMode.EXPRESSION); try { PyLib.pyDictContains(pyValue.getPointer(), pyValue, null); fail("Expected exception"); } catch (UnsupportedOperationException e) { // expected } } @Test public void decRefs() { final long pyObject1 = PyLib.executeCode("4321", PyInputMode.EXPRESSION.value(), null, null); final long pyObject2 = PyLib.executeCode("4322", PyInputMode.EXPRESSION.value(), null, null); PyLib.decRefs(new long[] { pyObject1, pyObject2, 0, 0 }, 2); } @Test public void testEnsureGIL() { assertFalse(PyLib.hasGil()); boolean[] lambdaSuccessfullyRan = {false}; Integer intResult = PyLib.ensureGil(() -> { assertTrue(PyLib.hasGil()); lambdaSuccessfullyRan[0] = true; return 123; }); assertEquals((Integer) 123, intResult); assertTrue(lambdaSuccessfullyRan[0]); try { Object result = PyLib.ensureGil(() -> { throw new IllegalStateException("Error from inside GIL block"); }); fail("Exception expected"); } catch (IllegalStateException expectedException) { assertEquals("Error from inside GIL block", expectedException.getMessage()); }//let anything else rethrow as a failure } } jpy-2.0.0/src/main/0000775000175000017500000000000015202503234014206 5ustar alastairalastairjpy-2.0.0/src/main/c/0000775000175000017500000000000015202503234014430 5ustar alastairalastairjpy-2.0.0/src/main/c/jpy_jarray.h0000664000175000017500000000323515202503234016756 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_JARRAY_H #define JPY_JARRAY_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * The Java primitive array representation in Python. * * IMPORTANT: JPy_JArray must only differ from the JPy_JObj structure by the 'bufferExportCount' member * since we use the same basic type, name JPy_JType for it. DON'T ever change member positions! * @see JPy_JObj */ typedef struct JPy_JArray { PyObject_HEAD jobject objectRef; jint bufferExportCount; void *buf; char javaType; jint bufReadonly; jint isCopy; } JPy_JArray; extern PyBufferProcs JArray_as_buffer_boolean; extern PyBufferProcs JArray_as_buffer_char; extern PyBufferProcs JArray_as_buffer_byte; extern PyBufferProcs JArray_as_buffer_short; extern PyBufferProcs JArray_as_buffer_int; extern PyBufferProcs JArray_as_buffer_long; extern PyBufferProcs JArray_as_buffer_float; extern PyBufferProcs JArray_as_buffer_double; extern void JArray_ReleaseJavaArrayElements(JPy_JArray* self, char javaType); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JARRAY_H */ jpy-2.0.0/src/main/c/jpy_jobj.c0000664000175000017500000007435315202503234016416 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jarray.h" #include "jpy_jbyte_buffer.h" #include "jpy_jtype.h" #include "jpy_jobj.h" #include "jpy_jmethod.h" #include "jpy_jfield.h" #include "jpy_conv.h" PyObject* JObj_New(JNIEnv* jenv, jobject objectRef) { JPy_JType* type; type = JType_GetTypeForObject(jenv, objectRef, JNI_TRUE); if (type == NULL) { return NULL; } return JObj_FromType(jenv, type, objectRef); } PyObject* JObj_FromType(JNIEnv* jenv, JPy_JType* type, jobject objectRef) { PyObject* callable; PyObject* callableResult; JPy_JObj* obj; obj = (JPy_JObj*) PyObject_New(JPy_JObj, JTYPE_AS_PYTYPE(type)); if (obj == NULL) { return NULL; } objectRef = (*jenv)->NewGlobalRef(jenv, objectRef); if (objectRef == NULL) { PyErr_NoMemory(); return NULL; } obj->objectRef = objectRef; // For special treatment of primitive array refer to JType_InitSlots() if (type->componentType != NULL && type->componentType->isPrimitive) { JPy_JArray* array; array = (JPy_JArray*) obj; array->bufferExportCount = 0; array->buf = NULL; } else if (JByteBuffer_Check(type)) { JPy_JByteBufferObj *byteBuffer; byteBuffer = (JPy_JByteBufferObj *) obj; byteBuffer->pyBuffer = NULL; } // we check the type translations dictionary for a callable for this java type name, // and apply the returned callable to the wrapped object #if PY_VERSION_HEX < 0x030D0000 // < 3.13 // borrowed ref callable = PyDict_GetItemString(JPy_Type_Translations, type->javaName); JPy_XINCREF(callable); #else // https://docs.python.org/3/howto/free-threading-extensions.html#borrowed-references // PyDict_GetItemStringRef() is a thread safe version of PyDict_GetItemString() and returns a new reference if (PyDict_GetItemStringRef(JPy_Type_Translations, type->javaName, &callable) != 1) { callable = NULL; } #endif if (callable != NULL) { if (PyCallable_Check(callable)) { callableResult = PyObject_CallFunction(callable, "OO", type, obj); JPy_XDECREF(callable); JPy_XDECREF(obj); if (callableResult == NULL) { Py_RETURN_NONE; } else { return callableResult; } } } JPy_XDECREF(callable); return (PyObject *)obj; } int JObj_init_internal(JNIEnv* jenv, JPy_JObj* self, PyObject* args, PyObject* kwds) { PyTypeObject* type; JPy_JType* jType; PyObject* constructor; JPy_JMethod* jMethod; jobject localObjectRef; jobject globalObjectRef; jvalue* jArgs; JPy_ArgDisposer* jDisposers; int isVarArgsArray; type = ((PyObject*) self)->ob_type; // borrowed ref, no need to replace with PyDict_GetItemStringRef because tp_dict won't be changed concurrently constructor = PyDict_GetItemString(type->tp_dict, JPy_JTYPE_ATTR_NAME_JINIT); if (constructor == NULL) { PyErr_SetString(PyExc_RuntimeError, "no constructor found (missing JType attribute '" JPy_JTYPE_ATTR_NAME_JINIT "')"); return -1; } if (!PyObject_TypeCheck(constructor, &JOverloadedMethod_Type)) { PyErr_SetString(PyExc_RuntimeError, "invalid JType attribute '" JPy_JTYPE_ATTR_NAME_JINIT "': expected type JOverloadedMethod_Type"); return -1; } jType = (JPy_JType*) type; if (jType->classRef == NULL) { PyErr_SetString(PyExc_RuntimeError, "internal error: Java class reference is NULL"); return -1; } jMethod = JOverloadedMethod_FindMethod(jenv, (JPy_JOverloadedMethod*) constructor, args, JNI_FALSE, &isVarArgsArray); if (jMethod == NULL) { return -1; } if (JMethod_CreateJArgs(jenv, jMethod, args, &jArgs, &jDisposers, isVarArgsArray) < 0) { return -1; } JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JObj_init: calling Java constructor %s\n", jType->javaName); localObjectRef = (*jenv)->NewObjectA(jenv, jType->classRef, jMethod->mid, jArgs); JPy_ON_JAVA_EXCEPTION_RETURN(-1); if (localObjectRef == NULL) { PyErr_NoMemory(); return -1; } if (jMethod->paramCount > 0) { JMethod_DisposeJArgs(jenv, jMethod->paramCount, jArgs, jDisposers); } globalObjectRef = (*jenv)->NewGlobalRef(jenv, localObjectRef); if (globalObjectRef == NULL) { PyErr_NoMemory(); return -1; } JPy_DELETE_LOCAL_REF(localObjectRef); // Note: __init__ may be called multiple times, so we have to release the old objectRef if (self->objectRef != NULL) { (*jenv)->DeleteGlobalRef(jenv, self->objectRef); } self->objectRef = globalObjectRef; JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JObj_init: self->objectRef=%p\n", self->objectRef); return 0; } /** * The JObj type's tp_init slot. Called when the type is used to create new instances (constructor). */ int JObj_init(JPy_JObj* self, PyObject* args, PyObject* kwds) { JPy_FRAME(int, -1, JObj_init_internal(jenv, self, args, kwds), 16) } /** * The JObj type's tp_dealloc slot. Called when the reference count reaches zero. */ void JObj_dealloc(JPy_JObj* self) { JNIEnv* jenv; JPy_JType* jtype; JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JObj_dealloc: releasing instance of %s, self->objectRef=%p\n", Py_TYPE(self)->tp_name, self->objectRef); jtype = (JPy_JType *)Py_TYPE(self); if (jtype->componentType != NULL && jtype->componentType->isPrimitive) { JPy_JArray* array; array = (JPy_JArray*) self; assert(array->bufferExportCount == 0); if (array->buf != NULL) { JArray_ReleaseJavaArrayElements(array, array->javaType); } } else if (JByteBuffer_Check(jtype)) { JPy_JByteBufferObj *byteBuffer; byteBuffer = (JPy_JByteBufferObj *) self; if (byteBuffer->pyBuffer != NULL) { PyBuffer_Release(byteBuffer->pyBuffer); PyMem_Free(byteBuffer->pyBuffer); } } jenv = JPy_GetJNIEnv(); if (jenv != NULL) { if (self->objectRef != NULL) { (*jenv)->DeleteGlobalRef(jenv, self->objectRef); } } Py_TYPE(self)->tp_free((PyObject*) self); } int JObj_CompareTo(JNIEnv* jenv, JPy_JObj* obj1, JPy_JObj* obj2) { jobject ref1; jobject ref2; int value; ref1 = obj1->objectRef; ref2 = obj2->objectRef; if (ref1 == ref2 || (*jenv)->IsSameObject(jenv, ref1, ref2)) { return 0; } else if ((*jenv)->IsInstanceOf(jenv, ref1, JPy_Comparable_JClass)) { value = (*jenv)->CallIntMethod(jenv, ref1, JPy_Comparable_CompareTo_MID, ref2); (*jenv)->ExceptionClear(jenv); // we can't deal with exceptions here, so clear any } else { value = (char*) ref1 - (char*) ref2; } return (value == 0) ? 0 : (value < 0) ? -1 : +1; } int JObj_Equals(JNIEnv* jenv, JPy_JObj* obj1, JPy_JObj* obj2) { jobject ref1; jobject ref2; int returnValue; ref1 = obj1->objectRef; ref2 = obj2->objectRef; if ((*jenv)->IsSameObject(jenv, ref1, ref2) || (*jenv)->CallBooleanMethod(jenv, ref1, JPy_Object_Equals_MID, ref2)) { returnValue = 1; } else { returnValue = 0; } (*jenv)->ExceptionClear(jenv); // we can't deal with exceptions here, so clear any return returnValue; } /** * The JObj type's tp_richcompare slot. Python: obj1 obj2 */ PyObject* JObj_richcompare(PyObject* obj1, PyObject* obj2, int opid) { JNIEnv* jenv; if (!JObj_Check(obj1) || !JObj_Check(obj2)) { Py_RETURN_FALSE; } JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL); if (opid == Py_LT) { int value = JObj_CompareTo(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value == -1) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } else if (opid == Py_LE) { int value = JObj_CompareTo(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value == -1 || value == 0) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } else if (opid == Py_GT) { int value = JObj_CompareTo(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value == +1) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } else if (opid == Py_GE) { int value = JObj_CompareTo(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value == +1 || value == 0) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } else if (opid == Py_EQ) { int value = JObj_Equals(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } else if (opid == Py_NE) { int value = JObj_Equals(jenv, (JPy_JObj*) obj1, (JPy_JObj*) obj2); if (value == -2) { return NULL; } else if (value) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; } } else { PyErr_SetString(PyExc_RuntimeError, "internal error: unrecognized opid"); return NULL; } } /** * The JObj type's tp_hash slot. Python: hash(obj) */ long JObj_hash(JPy_JObj* self) { JNIEnv* jenv; jenv = JPy_GetJNIEnv(); if (jenv != NULL) { int returnValue = (*jenv)->CallIntMethod(jenv, self->objectRef, JPy_Object_HashCode_MID); (*jenv)->ExceptionClear(jenv); // we can't deal with exceptions here, so clear any return returnValue; } return -1; } /** * The JObj type's tp_repr slot. Python: repr(obj)) */ PyObject* JObj_repr(JPy_JObj* self) { return JPy_FROM_FORMAT("%s(objectRef=%p)", Py_TYPE(self)->tp_name, self->objectRef); } /** * The JObj type's tp_str slot. Calls Object.toString() on Java Objects. Python: str(obj) */ PyObject* JObj_str(JPy_JObj* self) { JNIEnv* jenv; jstring stringRef; PyObject* returnValue; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL) if (self->objectRef == NULL) { Py_RETURN_NONE; } returnValue = NULL; stringRef = (*jenv)->CallObjectMethod(jenv, self->objectRef, JPy_Object_ToString_MID); JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FromJString(jenv, stringRef); error: JPy_DELETE_LOCAL_REF(stringRef); return returnValue; } /** * The JObj type's tp_setattro slot. */ int JObj_setattro(JPy_JObj* self, PyObject* name, PyObject* value) { PyObject* oldValue; //printf("JObj_setattro: %s.%s\n", Py_TYPE(self)->tp_name, JPy_AS_UTF8(name)); oldValue = PyObject_GenericGetAttr((PyObject*) self, name); if (oldValue != NULL && PyObject_TypeCheck(oldValue, &JField_Type)) { JNIEnv* jenv; JPy_JField* field; JPy_JType* type; field = (JPy_JField*) oldValue; type = field->type; JPy_GET_JNI_ENV_OR_RETURN(jenv, -1) if (type == JPy_JBoolean) { jboolean item = JPy_AS_JBOOLEAN(value); (*jenv)->SetBooleanField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JChar) { jchar item = JPy_AS_JCHAR(value); (*jenv)->SetCharField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JByte) { jbyte item = JPy_AS_JBYTE(value); (*jenv)->SetByteField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JShort) { jshort item = JPy_AS_JSHORT(value); (*jenv)->SetShortField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JInt) { jint item = JPy_AS_JINT(value); (*jenv)->SetIntField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JLong) { jlong item = JPy_AS_JLONG(value); (*jenv)->SetLongField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JFloat) { jfloat item = JPy_AS_JFLOAT(value); (*jenv)->SetFloatField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (type == JPy_JDouble) { jdouble item = JPy_AS_JDOUBLE(value); (*jenv)->SetDoubleField(jenv, self->objectRef, field->fid, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else { jobject objectRef; if (JPy_AsJObjectWithType(jenv, value, &objectRef, field->type) < 0) { return -1; } (*jenv)->SetObjectField(jenv, self->objectRef, field->fid, objectRef); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } return 0; } else { return PyObject_GenericSetAttr((PyObject*) self, name, value); } } /** * The JObj type's tp_getattro slot. * This is important: wrap callable objects of type JOverloadedMethod_Type into python methods so that * a method call to an instance x of class X becomes: x.m() --> X.m(x) */ PyObject* JObj_getattro(JPy_JObj* self, PyObject* name) { JPy_JType* selfType; PyObject* value; //printf("JObj_getattro: %s.%s\n", Py_TYPE(self)->tp_name, JPy_AS_UTF8(name)); // First make sure that the Java type is resolved, otherwise we won't find any methods at all. selfType = (JPy_JType*) Py_TYPE(self); if (!selfType->isResolved) { JNIEnv* jenv; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL) if (JType_ResolveType(jenv, selfType) < 0) { return NULL; } } // todo: implement a special lookup: we need to override __getattro__ of JType (--> JType_getattro) as well so that we know if a method // is called on a class rather than on an instance. Using PyObject_GenericGetAttr will also call JType_getattro, // but then we loose the information that a method is called on an instance and not on a class. value = PyObject_GenericGetAttr((PyObject*) self, name); if (value == NULL) { //printf("JObj_getattro: not found!\n"); return NULL; } if (PyObject_TypeCheck(value, &JOverloadedMethod_Type)) { //JPy_JOverloadedMethod* overloadedMethod = (JPy_JOverloadedMethod*) value; //printf("JObj_getattro: wrapping JOverloadedMethod, overloadCount=%d\n", PyList_Size(overloadedMethod->methodList)); return PyMethod_New(value, (PyObject*) self); } else if (PyObject_TypeCheck(value, &JField_Type)) { JNIEnv* jenv; JPy_JField* field; JPy_JType* type; field = (JPy_JField*) value; type = field->type; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL) if (type == JPy_JBoolean) { jboolean item = (*jenv)->GetBooleanField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JBOOLEAN(item); } else if (type == JPy_JChar) { jchar item = (*jenv)->GetCharField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JCHAR(item); } else if (type == JPy_JByte) { jbyte item = (*jenv)->GetByteField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JBYTE(item); } else if (type == JPy_JShort) { jshort item = (*jenv)->GetShortField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JSHORT(item); } else if (type == JPy_JInt) { jint item = (*jenv)->GetIntField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JINT(item); } else if (type == JPy_JLong) { jlong item = (*jenv)->GetLongField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JLONG(item); } else if (type == JPy_JFloat) { jfloat item = (*jenv)->GetFloatField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JFLOAT(item); } else if (type == JPy_JDouble) { jdouble item = (*jenv)->GetDoubleField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JDOUBLE(item); } else { PyObject* returnValue; jobject item = (*jenv)->GetObjectField(jenv, self->objectRef, field->fid); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); returnValue = JPy_FromJObjectWithType(jenv, item, field->type); JPy_DELETE_LOCAL_REF(item); return returnValue; } } else { //printf("JObj_getattro: passing through\n"); } return value; } /** * The JObj type's sq_length field of the tp_as_sequence slot. Called if len(obj) is called. * Only used for array types (type->componentType != NULL). */ Py_ssize_t JObj_sq_length(JPy_JObj* self) { JNIEnv* jenv; jsize length; JPy_GET_JNI_ENV_OR_RETURN(jenv, -1) length = (*jenv)->GetArrayLength(jenv, self->objectRef); //printf("JObj_sq_length: length=%d\n", length); return (Py_ssize_t) length; } /* * The JObj type's sq_item field of the tp_as_sequence slot. Called if 'item = obj[index]' is used. * Only used for array types (type->componentType != NULL). */ PyObject* JObj_sq_item(JPy_JObj* self, Py_ssize_t index) { JNIEnv* jenv; JPy_JType* type; JPy_JType* componentType; jsize length; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL) //printf("JObj_sq_item: index=%d\n", index); type = (JPy_JType*) Py_TYPE(self); componentType = type->componentType; if (componentType == NULL) { PyErr_SetString(PyExc_RuntimeError, "internal error: object is not an array"); return NULL; } // This is annoying and slow in Python 3.3.2: We must have this check, in order to raise an PyExc_IndexError, // otherwise Python functions such as list(jarr) will not succeed. // Tis is really strange, because n = sq_length() will be called and subsequent sq_item(index=0 ... n) calls will be done. length = (*jenv)->GetArrayLength(jenv, self->objectRef); if (index < 0 || index >= length) { PyErr_SetString(PyExc_IndexError, "Java array index out of bounds"); return NULL; } if (componentType == JPy_JBoolean) { jboolean item; (*jenv)->GetBooleanArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JBOOLEAN(item); } else if (componentType == JPy_JChar) { jchar item; (*jenv)->GetCharArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JCHAR(item); } else if (componentType == JPy_JByte) { jbyte item; (*jenv)->GetByteArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JBYTE(item); } else if (componentType == JPy_JShort) { jshort item; (*jenv)->GetShortArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JSHORT(item); } else if (componentType == JPy_JInt) { jint item; (*jenv)->GetIntArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JINT(item); } else if (componentType == JPy_JLong) { jlong item; (*jenv)->GetLongArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JLONG(item); } else if (componentType == JPy_JFloat) { jfloat item; (*jenv)->GetFloatArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JFLOAT(item); } else if (componentType == JPy_JDouble) { jdouble item; (*jenv)->GetDoubleArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JDOUBLE(item); } else { PyObject* returnValue; jobject item = (*jenv)->GetObjectArrayElement(jenv, self->objectRef, (jsize) index); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); returnValue = JPy_FromJObjectWithType(jenv, item, type->componentType); JPy_DELETE_LOCAL_REF(item); return returnValue; } } /* * The JObj type's sq_ass_item field of the tp_as_sequence slot. Called if 'obj[index] = item' is used. * Only used for array types (type->componentType != NULL). */ int JObj_sq_ass_item(JPy_JObj* self, Py_ssize_t index, PyObject* pyItem) { JNIEnv* jenv; JPy_JType* type; JPy_JType* componentType; JPy_GET_JNI_ENV_OR_RETURN(jenv, -1) type = (JPy_JType*) Py_TYPE(self); componentType = type->componentType; if (type->componentType == NULL) { PyErr_SetString(PyExc_RuntimeError, "object is not a Java array"); return -1; } // fixes https://github.com/bcdev/jpy/issues/52 if (pyItem == NULL) { PyErr_SetString(PyExc_RuntimeError, "cannot delete items of Java arrays"); return -1; } // Note: the following item assignments are not value range checked if (componentType == JPy_JBoolean) { jboolean item = JPy_AS_JBOOLEAN(pyItem); (*jenv)->SetBooleanArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JChar) { jchar item = JPy_AS_JCHAR(pyItem); (*jenv)->SetCharArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JByte) { jbyte item = JPy_AS_JBYTE(pyItem); (*jenv)->SetByteArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JShort) { jshort item = JPy_AS_JSHORT(pyItem); (*jenv)->SetShortArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JInt) { jint item = JPy_AS_JINT(pyItem); (*jenv)->SetIntArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JLong) { jlong item = JPy_AS_JLONG(pyItem); (*jenv)->SetLongArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JFloat) { jfloat item = JPy_AS_JFLOAT(pyItem); (*jenv)->SetFloatArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else if (componentType == JPy_JDouble) { jdouble item = JPy_AS_JDOUBLE(pyItem); (*jenv)->SetDoubleArrayRegion(jenv, self->objectRef, (jsize) index, 1, &item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } else { jobject item; if (JPy_AsJObjectWithType(jenv, pyItem, &item, type->componentType) < 0) { return -1; } (*jenv)->SetObjectArrayElement(jenv, self->objectRef, (jsize) index, item); JPy_ON_JAVA_EXCEPTION_RETURN(-1); } return 0; } /** * The JObj type's tp_as_sequence slot. * Implements the interface for array types (type->componentType != NULL). */ static PySequenceMethods JObj_as_sequence = { (lenfunc) JObj_sq_length, /* sq_length */ NULL, /* sq_concat */ NULL, /* sq_repeat */ (ssizeargfunc) JObj_sq_item, /* sq_item */ NULL, /* was_sq_slice */ (ssizeobjargproc) JObj_sq_ass_item, /* sq_ass_item */ NULL, /* was_sq_ass_slice */ NULL, /* sq_contains */ NULL, /* sq_inplace_concat */ NULL, /* sq_inplace_repeat */ }; int JType_InitSlots(JPy_JType* type) { PyTypeObject* typeObj; jboolean isArray; jboolean isPrimitiveArray; isArray = type->componentType != NULL; isPrimitiveArray = isArray && type->componentType->isPrimitive; typeObj = JTYPE_AS_PYTYPE(type); Py_SET_TYPE(typeObj, NULL); Py_SET_SIZE(typeObj, 0); // todo: The following lines are actually correct, but setting Py_TYPE(type) = &JType_Type results in an interpreter crash. Why? // This is still a problem because all the JType slots are actually never called (especially JType_getattro is // needed to resolve unresolved JTypes and to recognize static field and methods access) //JPy_INCREF(&JType_Type); //Py_SET_TYPE(type, &JType_Type); //Py_SET_SIZE(type, sizeof (JPy_JType)); if (isPrimitiveArray) { typeObj->tp_basicsize = sizeof(JPy_JArray); } else if (JByteBuffer_Check(type)) { typeObj->tp_basicsize = sizeof(JPy_JByteBufferObj); } else { typeObj->tp_basicsize = sizeof(JPy_JObj); } typeObj->tp_itemsize = 0; typeObj->tp_base = type->superType != NULL ? JTYPE_AS_PYTYPE(type->superType) : &JType_Type; //typeObj->tp_base = (PyTypeObject*) type->superType; typeObj->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; // If I uncomment the following line, I get (unpredictable) interpreter crashes // (see also http://stackoverflow.com/questions/8066438/how-to-dynamically-create-a-derived-type-in-the-python-c-api) //typeObj->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; typeObj->tp_getattro = (getattrofunc) JObj_getattro; typeObj->tp_setattro = (setattrofunc) JObj_setattro; // Note: we may later want to add protocol to 'java.lang.String' and 'java.util.List' types. // However, we cannot check directly against these global variable 'JPy_JString' and 'JPy_JList' here because // the current function (JType_InitSlots) is called to compute the actual values for the global // 'JPy_JString' and 'JPy_JList' variables! // So we actually have to check against the Java Object types in order to create and assign slots for the // Python protocols: java.lang.String --> sequence, java.util.Map --> dict, java.util.List --> list, java.util.Set --> set. // If this type is an array type, add support for the protocol if (isArray) { typeObj->tp_as_sequence = &JObj_as_sequence; } if (isPrimitiveArray) { const char* componentTypeName = type->componentType->javaName; if (strcmp(componentTypeName, "boolean") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_boolean; } else if (strcmp(componentTypeName, "char") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_char; } else if (strcmp(componentTypeName, "byte") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_byte; } else if (strcmp(componentTypeName, "short") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_short; } else if (strcmp(componentTypeName, "int") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_int; } else if (strcmp(componentTypeName, "long") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_long; } else if (strcmp(componentTypeName, "float") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_float; } else if (strcmp(componentTypeName, "double") == 0) { typeObj->tp_as_buffer = &JArray_as_buffer_double; } } //printf("JType_InitSlots: typeObj->tp_as_buffer=%p\n", typeObj->tp_as_buffer); typeObj->tp_alloc = PyType_GenericAlloc; typeObj->tp_new = PyType_GenericNew; typeObj->tp_init = (initproc) JObj_init; typeObj->tp_richcompare = (richcmpfunc) JObj_richcompare; typeObj->tp_hash = (hashfunc) JObj_hash; typeObj->tp_repr = (reprfunc) JObj_repr; typeObj->tp_str = (reprfunc) JObj_str; typeObj->tp_dealloc = (destructor) JObj_dealloc; // Check if we should set type.__module__ to the to the first part (up to the last dot) of the tp_name. // See http://docs.python.org/3/c-api/exceptions.html?highlight=pyerr_newexception#PyErr_NewException // Note that PyType_Ready() will set our typeObj->ob_type to &PyType_Type, while JType_New() created // the typeObj with an typeObj->ob_type set to &JType_Type. if (PyType_Ready(typeObj) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_InitSlots: INTERNAL ERROR: PyType_Ready() failed\n"); return -1; } //printf("+++++++++++++++++++++++++++++++++++++++++ typeObj->ob_type=%p\n", ((PyObject*)typeObj)->ob_type); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_InitSlots: typeObj=%p, Py_TYPE(typeObj)=%p, typeObj->tp_name=\"%s\", typeObj->tp_base=%p, typeObj->tp_init=%p, &JType_Type=%p, &PyType_Type=%p, JObj_init=%p\n", typeObj, Py_TYPE(typeObj), typeObj->tp_name, typeObj->tp_base, typeObj->tp_init, &JType_Type, &PyType_Type, JObj_init); return 0; } // This is only a good test as long JObj_init() is not used in other types #define JPY_IS_JTYPE(T) (JTYPE_AS_PYTYPE(T)->tp_init == (initproc) JObj_init) int JObj_Check(PyObject* arg) { return JPY_IS_JTYPE(Py_TYPE(arg)); } int JType_Check(PyObject* arg) { return PyType_Check(arg) && JPY_IS_JTYPE(arg); } int JByteBuffer_Check(JPy_JType* type) { while (type != NULL) { if (type == JPy_JByteBuffer || strcmp(type->javaName, "java.nio.ByteBuffer") == 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JByteBuffer_Check: java ByteBuffer or its sub-type (%s) found.\n", type->javaName); return -1; } type = type->superType; } return 0; } jpy-2.0.0/src/main/c/jpy_module.c0000664000175000017500000015155315202503234016755 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_verboseexcept.h" #include "jpy_jtype.h" #include "jpy_jmethod.h" #include "jpy_jfield.h" #include "jpy_jobj.h" #include "jpy_conv.h" #include "jpy_compat.h" #include "jpy_jbyte_buffer.h" #include #include PyObject* JPy_has_jvm(PyObject* self); PyObject* JPy_create_jvm(PyObject* self, PyObject* args, PyObject* kwds); PyObject* JPy_destroy_jvm(PyObject* self, PyObject* args); PyObject* JPy_get_type(PyObject* self, PyObject* args, PyObject* kwds); PyObject* JPy_cast(PyObject* self, PyObject* args); PyObject* JPy_convert(PyObject* self, PyObject* args); PyObject* JPy_array(PyObject* self, PyObject* args); PyObject* JPy_byte_buffer(PyObject* self, PyObject* args); static PyMethodDef JPy_Functions[] = { {"has_jvm", (PyCFunction) JPy_has_jvm, METH_NOARGS, "has_jvm() - Check if the JVM is available."}, {"create_jvm", (PyCFunction) JPy_create_jvm, METH_VARARGS|METH_KEYWORDS, "create_jvm(options) - Create the Java VM from the given list of options."}, {"destroy_jvm", JPy_destroy_jvm, METH_VARARGS, "destroy_jvm() - Destroy the current Java VM."}, {"get_type", (PyCFunction) JPy_get_type, METH_VARARGS|METH_KEYWORDS, "get_type(name, resolve=True) - Return the Java class with the given name, e.g. 'java.io.File'. " "Loads the Java class from the JVM if not already done. Optionally avoids resolving the class' methods."}, {"cast", JPy_cast, METH_VARARGS, "cast(obj, type) - Cast the given Java object to the given Java type (type name or type object). " "Returns None if the cast is not possible."}, {"convert", JPy_convert, METH_VARARGS, "convert(obj, type) - Convert the given Python object to the given Java type (type name or type object). " "Returns None if the conversion is not possible. If the Java type is a primitive, the returned object " "will be of the corresponding boxed type."}, {"array", JPy_array, METH_VARARGS, "array(name, init) - Return a new Java array of given Java type (type name or type object) and initializer (array length or sequence). " "Possible primitive types are 'boolean', 'byte', 'char', 'short', 'int', 'long', 'float', and 'double'."}, {"byte_buffer", JPy_byte_buffer, METH_VARARGS, "byte_buffer(obj) - Return a new Java direct ByteBuffer sharing the same underlying, contiguous buffer of obj via its implemented Buffer Protocol. The resulting PYObject must live " "longer than the Java object to ensure the underlying data remains valid. In most cases, this means that java functions called in this manner must not keep any references" " to the ByteBuffer"}, {NULL, NULL, 0, NULL} /*Sentinel*/ }; void JPy_free_ptr(void* unused); #define JPY_MODULE_NAME "jpy" #define JPY_MODULE_DOC "Bi-directional Python-Java Bridge" static struct PyModuleDef JPy_ModuleDef = { PyModuleDef_HEAD_INIT, JPY_MODULE_NAME, /* Name of the Python JPy_Module */ JPY_MODULE_DOC, /* Module documentation */ -1, /* Size of per-interpreter state of the JPy_Module, or -1 if the JPy_Module keeps state in global variables. */ JPy_Functions, /* Structure containing global jpy-functions */ NULL, // m_reload NULL, // m_traverse NULL, // m_clear JPy_free_ptr // m_free }; PyObject* JPy_Module = NULL; PyObject* JPy_Types = NULL; PyObject* JPy_Type_Callbacks = NULL; PyObject* JPy_Type_Translations = NULL; PyObject* JException_Type = NULL; // A global reference to a Java VM singleton. JavaVM* JPy_JVM = NULL; // If true, this JVM structure has been initialised from Python jpy.create_jvm() jboolean JPy_MustDestroyJVM = JNI_FALSE; // Global VM Information (maybe better place this in the JPy_JVM structure later) // {{{ JPy_JType* JPy_JBoolean = NULL; JPy_JType* JPy_JChar = NULL; JPy_JType* JPy_JByte = NULL; JPy_JType* JPy_JShort = NULL; JPy_JType* JPy_JInt = NULL; JPy_JType* JPy_JLong = NULL; JPy_JType* JPy_JFloat = NULL; JPy_JType* JPy_JDouble = NULL; JPy_JType* JPy_JVoid = NULL; JPy_JType* JPy_JBooleanObj = NULL; JPy_JType* JPy_JCharacterObj = NULL; JPy_JType* JPy_JByteObj = NULL; JPy_JType* JPy_JShortObj = NULL; JPy_JType* JPy_JIntegerObj = NULL; JPy_JType* JPy_JLongObj = NULL; JPy_JType* JPy_JFloatObj = NULL; JPy_JType* JPy_JDoubleObj = NULL; JPy_JType* JPy_JObject = NULL; JPy_JType* JPy_JClass = NULL; JPy_JType* JPy_JString = NULL; JPy_JType* JPy_JPyObject = NULL; JPy_JType* JPy_JPyModule = NULL; JPy_JType* JPy_JThrowable = NULL; JPy_JType* JPy_JStackTraceElement = NULL; JPy_JType* JPy_JByteBuffer = NULL; // java.lang.Comparable jclass JPy_Comparable_JClass = NULL; jmethodID JPy_Comparable_CompareTo_MID = NULL; // java.lang.Object jclass JPy_Object_JClass = NULL; jmethodID JPy_Object_ToString_MID = NULL; jmethodID JPy_Object_HashCode_MID = NULL; jmethodID JPy_Object_Equals_MID = NULL; // java.lang.Class jclass JPy_Class_JClass = NULL; jmethodID JPy_Class_GetName_MID = NULL; jmethodID JPy_Class_GetDeclaredConstructors_MID = NULL; jmethodID JPy_Class_GetDeclaredFields_MID = NULL; jmethodID JPy_Class_GetDeclaredMethods_MID = NULL; jmethodID JPy_Class_GetFields_MID = NULL; jmethodID JPy_Class_GetMethods_MID = NULL; jmethodID JPy_Class_GetComponentType_MID = NULL; jmethodID JPy_Class_IsPrimitive_MID = NULL; jmethodID JPy_Class_IsInterface_MID = NULL; // java.lang.reflect.Constructor jclass JPy_Constructor_JClass = NULL; jmethodID JPy_Constructor_GetModifiers_MID = NULL; jmethodID JPy_Constructor_GetParameterTypes_MID = NULL; // java.lang.reflect.Method jclass JPy_Method_JClass = NULL; jmethodID JPy_Method_GetName_MID = NULL; jmethodID JPy_Method_GetReturnType_MID = NULL; jmethodID JPy_Method_GetParameterTypes_MID = NULL; jmethodID JPy_Method_GetModifiers_MID = NULL; // java.lang.reflect.Field jclass JPy_Field_JClass = NULL; jmethodID JPy_Field_GetName_MID = NULL; jmethodID JPy_Field_GetModifiers_MID = NULL; jmethodID JPy_Field_GetType_MID = NULL; // java.util.Map jclass JPy_Map_JClass = NULL; jclass JPy_Map_Entry_JClass = NULL; jmethodID JPy_Map_entrySet_MID = NULL; jmethodID JPy_Map_put_MID = NULL; jmethodID JPy_Map_clear_MID = NULL; jmethodID JPy_Map_Entry_getKey_MID = NULL; jmethodID JPy_Map_Entry_getValue_MID = NULL; // java.util.Set jclass JPy_Set_JClass = NULL; jmethodID JPy_Set_Iterator_MID = NULL; // java.util.Iterator jclass JPy_Iterator_JClass = NULL; jmethodID JPy_Iterator_next_MID = NULL; jmethodID JPy_Iterator_hasNext_MID = NULL; jclass JPy_RuntimeException_JClass = NULL; jclass JPy_OutOfMemoryError_JClass = NULL; jclass JPy_UnsupportedOperationException_JClass = NULL; jclass JPy_FileNotFoundException_JClass = NULL; jclass JPy_KeyError_JClass = NULL; jclass JPy_StopIteration_JClass = NULL; // java.lang.Boolean jclass JPy_Boolean_JClass = NULL; jmethodID JPy_Boolean_ValueOf_SMID = NULL; jmethodID JPy_Boolean_BooleanValue_MID = NULL; jclass JPy_Character_JClass = NULL; jmethodID JPy_Character_ValueOf_SMID; jmethodID JPy_Character_CharValue_MID = NULL; jclass JPy_Byte_JClass = NULL; jmethodID JPy_Byte_ValueOf_SMID = NULL; jclass JPy_Short_JClass = NULL; jmethodID JPy_Short_ValueOf_SMID = NULL; jclass JPy_Integer_JClass = NULL; jmethodID JPy_Integer_ValueOf_SMID = NULL; jclass JPy_Long_JClass = NULL; jmethodID JPy_Long_ValueOf_SMID = NULL; jclass JPy_Float_JClass = NULL; jmethodID JPy_Float_ValueOf_SMID = NULL; jclass JPy_Double_JClass = NULL; jmethodID JPy_Double_ValueOf_SMID = NULL; // java.lang.Number jclass JPy_Number_JClass = NULL; jmethodID JPy_Number_IntValue_MID = NULL; jmethodID JPy_Number_LongValue_MID = NULL; jmethodID JPy_Number_DoubleValue_MID = NULL; jclass JPy_Void_JClass = NULL; jclass JPy_String_JClass = NULL; jclass JPy_PyObject_JClass = NULL; jclass JPy_PyDictWrapper_JClass = NULL; jclass JPy_ByteBuffer_JClass = NULL; jmethodID JPy_ByteBuffer_AsReadOnlyBuffer_MID = NULL; jmethodID JPy_PyObject_GetPointer_MID = NULL; jmethodID JPy_PyObject_UnwrapProxy_SMID = NULL; jmethodID JPy_PyObject_Init_MID = NULL; jmethodID JPy_PyModule_Init_MID = NULL; jmethodID JPy_PyDictWrapper_GetPointer_MID = NULL; // java.lang.Throwable jclass JPy_Throwable_JClass = NULL; jmethodID JPy_Throwable_getStackTrace_MID = NULL; jmethodID JPy_Throwable_getCause_MID = NULL; // stack trace element jclass JPy_StackTraceElement_JClass = NULL; // java.util.function.Supplier jclass JPy_Supplier_JClass = NULL; jmethodID JPy_Supplier_get_MID = NULL; // }}} JNIEnv* JPy_GetJNIEnv(void) { JavaVM* jvm; JNIEnv* jenv; jint status; jvm = JPy_JVM; if (jvm == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy: No JVM available."); return NULL; } status = (*jvm)->GetEnv(jvm, (void**) &jenv, JPY_JNI_VERSION); if (status == JNI_EDETACHED) { if ((*jvm)->AttachCurrentThread(jvm, (void**) &jenv, NULL) == 0) { JPy_DIAG_PRINT(JPy_DIAG_F_JVM, "JPy_GetJNIEnv: Attached current thread to JVM: jenv=%p\n", jenv); } else { PyErr_SetString(PyExc_RuntimeError, "jpy: Failed to attach current thread to JVM."); return NULL; } } else if (status == JNI_EVERSION) { PyErr_SetString(PyExc_RuntimeError, "jpy: Failed to attach current thread to JVM: Java version not supported."); return NULL; } else if (status == JNI_OK) { // ok! JPy_DIAG_PRINT(JPy_DIAG_F_JVM, "JPy_GetJNIEnv: jenv=%p\n", jenv); } else { JPy_DIAG_PRINT(JPy_DIAG_F_JVM + JPy_DIAG_F_ERR, "JPy_GetJNIEnv: Received unhandled status code from JVM GetEnv(): status=%d\n", status); } return jenv; } #define JPY_RETURN(V) return V #define JPY_MODULE_INIT_FUNC PyInit_jpy /** * Called by the Python interpreter's import machinery, e.g. using 'import jpy'. */ PyMODINIT_FUNC JPY_MODULE_INIT_FUNC(void) { //printf("PyInit_jpy: JPy_JVM=%p\n", JPy_JVM); ///////////////////////////////////////////////////////////////////////// JPy_Module = PyModule_Create(&JPy_ModuleDef); if (JPy_Module == NULL) { JPY_RETURN(NULL); } #ifdef Py_GIL_DISABLED PyUnstable_Module_SetGIL(JPy_Module, Py_MOD_GIL_NOT_USED); #endif ///////////////////////////////////////////////////////////////////////// if (PyType_Ready(&JType_Type) < 0) { JPY_RETURN(NULL); } JPy_INCREF(&JType_Type); PyModule_AddObject(JPy_Module, "JType", (PyObject*) &JType_Type); ///////////////////////////////////////////////////////////////////////// if (PyType_Ready(&JMethod_Type) < 0) { JPY_RETURN(NULL); } JPy_INCREF(&JMethod_Type); PyModule_AddObject(JPy_Module, "JMethod", (PyObject*) &JMethod_Type); ///////////////////////////////////////////////////////////////////////// if (PyType_Ready(&JOverloadedMethod_Type) < 0) { JPY_RETURN(NULL); } JPy_INCREF(&JOverloadedMethod_Type); PyModule_AddObject(JPy_Module, "JOverloadedMethod", (PyObject*) &JOverloadedMethod_Type); ///////////////////////////////////////////////////////////////////////// if (PyType_Ready(&JField_Type) < 0) { JPY_RETURN(NULL); } JPy_INCREF(&JField_Type); PyModule_AddObject(JPy_Module, "JField", (PyObject*) &JField_Type); ///////////////////////////////////////////////////////////////////////// JException_Type = PyErr_NewException("jpy.JException", NULL, NULL); JPy_INCREF(JException_Type); PyModule_AddObject(JPy_Module, "JException", JException_Type); ///////////////////////////////////////////////////////////////////////// JPy_Types = PyDict_New(); JPy_INCREF(JPy_Types); PyModule_AddObject(JPy_Module, JPy_MODULE_ATTR_NAME_TYPES, JPy_Types); ///////////////////////////////////////////////////////////////////////// JPy_Type_Callbacks = PyDict_New(); JPy_INCREF(JPy_Type_Callbacks); PyModule_AddObject(JPy_Module, JPy_MODULE_ATTR_NAME_TYPE_CALLBACKS, JPy_Type_Callbacks); ///////////////////////////////////////////////////////////////////////// JPy_Type_Translations = PyDict_New(); JPy_INCREF(JPy_Type_Translations); PyModule_AddObject(JPy_Module, JPy_MODULE_ATTR_NAME_TYPE_TRANSLATIONS, JPy_Type_Translations); ///////////////////////////////////////////////////////////////////////// if (PyType_Ready(&Diag_Type) < 0) { JPY_RETURN(NULL); } //JPy_INCREF(&DiagFlags_Type); { PyObject* pyDiag = Diag_New(); JPy_INCREF(pyDiag); PyModule_AddObject(JPy_Module, "diag", pyDiag); } if (PyType_Ready(&VerboseExceptions_Type) < 0) { JPY_RETURN(NULL); } { PyObject* pyVerboseExceptions = VerboseExceptions_New(); JPy_INCREF(pyVerboseExceptions); PyModule_AddObject(JPy_Module, "VerboseExceptions", pyVerboseExceptions); } ///////////////////////////////////////////////////////////////////////// if (JPy_JVM != NULL) { JNIEnv* jenv; jenv = JPy_GetJNIEnv(); if (jenv == NULL) { JPY_RETURN(NULL); } // If we have already a running VM, initialize global variables if (JPy_InitGlobalVars(jenv) < 0) { JPY_RETURN(NULL); } } ///////////////////////////////////////////////////////////////////////// //printf("PyInit_jpy: exit\n"); JPY_RETURN(JPy_Module); } PyObject* JPy_has_jvm(PyObject* self) { return PyBool_FromLong(JPy_JVM != NULL); } PyObject* JPy_create_jvm(PyObject* self, PyObject* args, PyObject* kwds) { static char* keywords[] = {"options", NULL}; PyObject* options; Py_ssize_t optionCount; PyObject* option; JavaVMOption* jvmOptions; JavaVMInitArgs jvmInitArgs; jint jvmErrorCode; JNIEnv* jenv; Py_ssize_t i; //printf("JPy_create_jvm: JPy_JVM=%p\n", JPy_JVM); options = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:create_jvm", keywords, &options)) { return NULL; } if (JPy_JVM != NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_JVM + JPy_DIAG_F_ERR, "JPy_create_jvm: WARNING: Java VM is already running.\n"); JPy_DECREF(options); Py_RETURN_NONE; } if (!PySequence_Check(options)) { PyErr_SetString(PyExc_ValueError, "create_jvm: argument 1 (options) must be a sequence of Java VM option strings"); return NULL; } optionCount = PySequence_Length(options); if (optionCount == -1) { PyErr_SetString(PyExc_ValueError, "create_jvm: failed to determine sequence length of argument 1 (options)"); return NULL; } jvmOptions = PyMem_New(JavaVMOption, optionCount); if (jvmOptions == NULL) { return PyErr_NoMemory(); } for (i = 0; i < optionCount; i++) { option = PySequence_GetItem(options, i); if (option == NULL) { PyMem_Del(jvmOptions); return NULL; } jvmOptions[i].optionString = (char*) JPy_AS_UTF8(option); jvmOptions[i].extraInfo = NULL; JPy_DIAG_PRINT(JPy_DIAG_F_JVM, "JPy_create_jvm: jvmOptions[%d].optionString = '%s'\n", i, jvmOptions[i].optionString); if (jvmOptions[i].optionString == NULL) { PyMem_Del(jvmOptions); return NULL; } JPy_DECREF(option); } jvmInitArgs.version = JPY_JNI_VERSION; jvmInitArgs.options = jvmOptions; jvmInitArgs.nOptions = (size_t) optionCount; jvmInitArgs.ignoreUnrecognized = 0; jvmErrorCode = JNI_CreateJavaVM(&JPy_JVM, (void**) &jenv, &jvmInitArgs); JPy_MustDestroyJVM = JNI_TRUE; JPy_DIAG_PRINT(JPy_DIAG_F_JVM, "JPy_create_jvm: res=%d, JPy_JVM=%p, jenv=%p, JPy_MustDestroyJVM=%d\n", jvmErrorCode, JPy_JVM, jenv, JPy_MustDestroyJVM); PyMem_Del(jvmOptions); if (jvmErrorCode != JNI_OK) { JPy_DIAG_PRINT(JPy_DIAG_F_JVM + JPy_DIAG_F_ERR, "JPy_create_jvm: INTERNAL ERROR: Failed to create Java VM (JNI error code %d). Possible reasons are:\n" "* The Java heap space setting is too high (option -Xmx). Try '256M' first, then increment.\n" "* The JVM shared library (Unix: libjvm.so, Windows: jvm.dll) cannot be found or cannot be loaded.\n" " Make sure the shared library can be found via the 'PATH' environment variable.\n" " Also make sure that the JVM is compiled for the same architecture as Python.\n", jvmErrorCode); PyErr_SetString(PyExc_RuntimeError, "jpy: failed to create Java VM"); return NULL; } if (JPy_InitGlobalVars(jenv) < 0) { return NULL; } Py_RETURN_NONE; } PyObject* JPy_destroy_jvm(PyObject* self, PyObject* args) { JPy_DIAG_PRINT(JPy_DIAG_F_JVM, "JPy_destroy_jvm: JPy_JVM=%p, JPy_MustDestroyJVM=%d\n", JPy_JVM, JPy_MustDestroyJVM); if (JPy_JVM != NULL && JPy_MustDestroyJVM) { JPy_ClearGlobalVars(JPy_GetJNIEnv()); (*JPy_JVM)->DestroyJavaVM(JPy_JVM); JPy_JVM = NULL; } Py_RETURN_NONE; } PyObject* JPy_get_type_internal(JNIEnv* jenv, PyObject* self, PyObject* args, PyObject* kwds) { static char* keywords[] = {"name", "resolve", NULL}; const char* className; int resolve; resolve = 1; // True if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:get_type", keywords, &className, &resolve)) { return NULL; } return (PyObject*) JType_GetTypeForName(jenv, className, (jboolean) (resolve != 0 ? JNI_TRUE : JNI_FALSE)); } PyObject* JPy_get_type(PyObject* self, PyObject* args, PyObject* kwds) { JPy_FRAME(PyObject*, NULL, JPy_get_type_internal(jenv, self, args, kwds), 16) } PyObject* JPy_cast_internal(JNIEnv* jenv, PyObject* self, PyObject* args) { PyObject* obj; PyObject* targetTypeArg; // can be a string PyObject (i.e. JPy_IS_STR) or a JPy_JType JPy_JType* targetTypeParsed; // the actual type that targetTypeArg is processed as jboolean inst; if (!PyArg_ParseTuple(args, "OO:cast", &obj, &targetTypeArg)) { return NULL; } if (obj == Py_None) { Py_RETURN_NONE; } if (!JObj_Check(obj)) { PyErr_SetString(PyExc_ValueError, "cast: argument 1 (obj) must be a Java object"); return NULL; } if (JPy_IS_STR(targetTypeArg)) { const char* typeName = JPy_AS_UTF8(targetTypeArg); targetTypeParsed = JType_GetTypeForName(jenv, typeName, JNI_FALSE); if (targetTypeParsed == NULL) { return NULL; } } else if (JType_Check(targetTypeArg)) { targetTypeParsed = (JPy_JType*) targetTypeArg; } else { PyErr_SetString(PyExc_ValueError, "cast: argument 2 (obj_type) must be a Java type name or Java type object"); return NULL; } inst = (*jenv)->IsInstanceOf(jenv, ((JPy_JObj*) obj)->objectRef, targetTypeParsed->classRef); if (inst) { return (PyObject*) JObj_FromType(jenv, (JPy_JType*) targetTypeParsed, ((JPy_JObj*) obj)->objectRef); } else { Py_RETURN_NONE; } } PyObject* JPy_cast(PyObject* self, PyObject* args) { JPy_FRAME(PyObject*, NULL, JPy_cast_internal(jenv, self, args), 16) } PyObject* JPy_convert_internal(JNIEnv* jenv, PyObject* self, PyObject* args) { PyObject* obj; PyObject* targetTypeArg; // can be a string PyObject (i.e. JPy_IS_STR) or a JPy_JType JPy_JType* targetTypeParsed; // the actual type that targetTypeArg is processed as jboolean inst; PyObject* resultObj; jobject objectRef; // Parse the 'args' from Python into 'obj'/'objType'. if (!PyArg_ParseTuple(args, "OO:convert", &obj, &targetTypeArg)) { return NULL; } if (obj == Py_None) { Py_RETURN_NONE; } if (JPy_IS_STR(targetTypeArg)) { const char* typeName = JPy_AS_UTF8(targetTypeArg); targetTypeParsed = JType_GetTypeForName(jenv, typeName, JNI_FALSE); if (targetTypeParsed == NULL) { return NULL; } // new ref, persists in the global JPy_Types dict and will never be removed, so safe to DECREF JPy_DECREF(targetTypeParsed); } else if (JType_Check(targetTypeArg)) { targetTypeParsed = (JPy_JType*) targetTypeArg; } else { PyErr_SetString(PyExc_ValueError, "cast: argument 2 (obj_type) must be a Java type name or Java type object"); return NULL; } // If the input obj is a Java object, and it is already of the specified target type, // then just cast it. if (JObj_Check(obj)) { inst = (*jenv)->IsInstanceOf(jenv, ((JPy_JObj*) obj)->objectRef, targetTypeParsed->classRef); if (inst) { return (PyObject*) JObj_FromType(jenv, (JPy_JType*) targetTypeParsed, ((JPy_JObj*) obj)->objectRef); } } // Convert the Python object to the specified Java type if (JPy_AsJObjectWithType(jenv, obj, &objectRef, targetTypeParsed) < 0) { return NULL; } return JObj_FromType(jenv, targetTypeParsed, objectRef); } PyObject* JPy_convert(PyObject* self, PyObject* args) { JPy_FRAME(PyObject*, NULL, JPy_convert_internal(jenv, self, args), 16) } PyObject* JPy_array_internal(JNIEnv* jenv, PyObject* self, PyObject* args) { JPy_JType* componentType; jarray arrayRef; PyObject* objType; PyObject* objInit; if (!PyArg_ParseTuple(args, "OO:array", &objType, &objInit)) { return NULL; } if (JPy_IS_STR(objType)) { const char* typeName; typeName = JPy_AS_UTF8(objType); componentType = JType_GetTypeForName(jenv, typeName, JNI_FALSE); if (componentType == NULL) { return NULL; } } else if (JType_Check(objType)) { componentType = (JPy_JType*) objType; } else { PyErr_SetString(PyExc_ValueError, "array: argument 1 (type) must be a type name or Java type object"); return NULL; } if (componentType == JPy_JVoid) { PyErr_SetString(PyExc_ValueError, "array: argument 1 (type) must not be 'void'"); return NULL; } if (JPy_IS_CLONG(objInit)) { jint length = JPy_AS_CLONG(objInit); if (length < 0) { PyErr_SetString(PyExc_ValueError, "array: argument 2 (init) must be either an integer array length or any sequence"); return NULL; } if (componentType == JPy_JBoolean) { arrayRef = (*jenv)->NewBooleanArray(jenv, length); } else if (componentType == JPy_JChar) { arrayRef = (*jenv)->NewCharArray(jenv, length); } else if (componentType == JPy_JByte) { arrayRef = (*jenv)->NewByteArray(jenv, length); } else if (componentType == JPy_JShort) { arrayRef = (*jenv)->NewShortArray(jenv, length); } else if (componentType == JPy_JInt) { arrayRef = (*jenv)->NewIntArray(jenv, length); } else if (componentType == JPy_JLong) { arrayRef = (*jenv)->NewLongArray(jenv, length); } else if (componentType == JPy_JFloat) { arrayRef = (*jenv)->NewFloatArray(jenv, length); } else if (componentType == JPy_JDouble) { arrayRef = (*jenv)->NewDoubleArray(jenv, length); } else { arrayRef = (*jenv)->NewObjectArray(jenv, length, componentType->classRef, NULL); } if (arrayRef == NULL) { return PyErr_NoMemory(); } return JObj_New(jenv, arrayRef); } else if (PySequence_Check(objInit)) { if (JType_CreateJavaArray(jenv, componentType, objInit, &arrayRef, JNI_FALSE) < 0) { return NULL; } return JObj_New(jenv, arrayRef); } else { PyErr_SetString(PyExc_ValueError, "array: argument 2 (init) must be either an integer array length or any sequence"); return NULL; } } PyObject* JPy_array(PyObject* self, PyObject* args) { JPy_FRAME(PyObject*, NULL, JPy_array_internal(jenv, self, args), 16) } PyObject* JType_CreateJavaByteBufferObj(JNIEnv* jenv, PyObject* pyObj) { jobject byteBufferRef, tmpByteBufferRef; Py_buffer *pyBuffer; PyObject *newPyObj; JPy_JByteBufferObj* byteBuffer; pyBuffer = (Py_buffer *)PyMem_Malloc(sizeof(Py_buffer)); if (pyBuffer == NULL) { PyErr_NoMemory(); return NULL; } if (PyObject_GetBuffer(pyObj, pyBuffer, PyBUF_SIMPLE | PyBUF_C_CONTIGUOUS) != 0) { PyErr_SetString(PyExc_ValueError, "JType_CreateJavaByteBufferObj: the Python object failed to return a contiguous buffer."); PyMem_Free(pyBuffer); return NULL; } tmpByteBufferRef = (*jenv)->NewDirectByteBuffer(jenv, pyBuffer->buf, pyBuffer->len); if (tmpByteBufferRef == NULL) { PyBuffer_Release(pyBuffer); PyMem_Free(pyBuffer); PyErr_NoMemory(); return NULL; } byteBufferRef = (*jenv)->CallObjectMethod(jenv, tmpByteBufferRef, JPy_ByteBuffer_AsReadOnlyBuffer_MID); if (byteBufferRef == NULL) { PyBuffer_Release(pyBuffer); PyMem_Free(pyBuffer); JPy_DELETE_LOCAL_REF(tmpByteBufferRef); PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: failed to create a read-only direct ByteBuffer instance."); return NULL; } JPy_DELETE_LOCAL_REF(tmpByteBufferRef); newPyObj = JObj_New(jenv, byteBufferRef); if (newPyObj == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: failed to create a byteBuffer instance."); PyBuffer_Release(pyBuffer); PyMem_Free(pyBuffer); JPy_DELETE_LOCAL_REF(byteBufferRef); return NULL; } JPy_DELETE_LOCAL_REF(byteBufferRef); byteBuffer = (JPy_JByteBufferObj *) newPyObj; byteBuffer->pyBuffer = pyBuffer; return (PyObject *)byteBuffer; } PyObject* JPy_byte_buffer_internal(JNIEnv* jenv, PyObject* self, PyObject* args) { PyObject* pyObj; if (!PyArg_ParseTuple(args, "O:byte_buffer", &pyObj)) { return NULL; } if (PyObject_CheckBuffer(pyObj) == 0) { PyErr_SetString(PyExc_ValueError, "byte_buffer: argument 1 must be a Python object that supports the buffer protocol."); return NULL; } return JType_CreateJavaByteBufferObj(jenv, pyObj); } PyObject* JPy_byte_buffer(PyObject* self, PyObject* args) { JPy_FRAME(PyObject*, NULL, JPy_byte_buffer_internal(jenv, self, args), 16) } JPy_JType* JPy_GetNonObjectJType(JNIEnv* jenv, jclass classRef) { jclass primClassRef; jfieldID fid; JPy_JType* type; if (classRef == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: classRef == NULL"); } fid = (*jenv)->GetStaticFieldID(jenv, classRef, "TYPE", "Ljava/lang/Class;"); if (fid == NULL) { PyErr_SetString(PyExc_RuntimeError, "field 'TYPE' not found"); return NULL; } primClassRef = (*jenv)->GetStaticObjectField(jenv, classRef, fid); if (primClassRef == NULL) { PyErr_SetString(PyExc_RuntimeError, "failed to access field 'TYPE'"); return NULL; } type = JType_GetType(jenv, primClassRef, JNI_FALSE); JPy_DELETE_LOCAL_REF(primClassRef); if (type == NULL) { return NULL; } type->isResolved = JNI_TRUE; // Primitive types are always resolved. JPy_INCREF((PyObject*) type); return type; } jclass JPy_GetClass(JNIEnv* jenv, const char* name) { jclass localClassRef; jclass globalClassRef; localClassRef = (*jenv)->FindClass(jenv, name); if (localClassRef == NULL) { PyErr_Format(PyExc_RuntimeError, "jpy: internal error: Java class '%s' not found", name); return NULL; } globalClassRef = (*jenv)->NewGlobalRef(jenv, localClassRef); JPy_DELETE_LOCAL_REF(localClassRef); if (globalClassRef == NULL) { PyErr_NoMemory(); return NULL; } return globalClassRef; } jmethodID JPy_GetMethod(JNIEnv* jenv, jclass classRef, const char* name, const char* sig) { jmethodID methodID; methodID = (*jenv)->GetMethodID(jenv, classRef, name, sig); if (methodID == NULL) { PyErr_Format(PyExc_RuntimeError, "jpy: internal error: method not found: %s%s", name, sig); return NULL; } return methodID; } jmethodID JPy_GetStaticMethod(JNIEnv *jenv, jclass classRef, const char *name, const char *sig) { jmethodID methodID; methodID = (*jenv)->GetStaticMethodID(jenv, classRef, name, sig); if (methodID == NULL) { PyErr_Format(PyExc_RuntimeError, "jpy: internal error: static method not found: %s%s", name, sig); return NULL; } return methodID; } #define DEFINE_CLASS(C, N) \ C = JPy_GetClass(jenv, N); \ if (C == NULL) { \ return -1; \ } #define DEFINE_METHOD(M, C, N, S) \ M = JPy_GetMethod(jenv, C, N, S); \ if (M == NULL) { \ return -1; \ } #define DEFINE_STATIC_METHOD(M, C, N, S) \ M = JPy_GetStaticMethod(jenv, C, N, S); \ if (M == NULL) { \ return -1; \ } #define DEFINE_NON_OBJECT_TYPE(T, C) \ T = JPy_GetNonObjectJType(jenv, C); \ if (T == NULL) { \ return -1; \ } #define DEFINE_OBJECT_TYPE(T, C) \ T = JType_GetType(jenv, C, JNI_FALSE); \ if (T == NULL) { \ return -1; \ } int initGlobalPyObjectVars(JNIEnv* jenv) { JPy_JType *dictType; JPy_JType *keyErrorType; JPy_JType *stopIterationType; JPy_JPyObject = JType_GetTypeForName(jenv, "org.jpy.PyObject", JNI_FALSE); if (JPy_JPyObject == NULL) { // org.jpy.PyObject may not be on the classpath, which is ok PyErr_Clear(); return -1; } else { JPy_PyObject_JClass = JPy_JPyObject->classRef; DEFINE_METHOD(JPy_PyObject_GetPointer_MID, JPy_PyObject_JClass, "getPointer", "()J"); DEFINE_STATIC_METHOD(JPy_PyObject_UnwrapProxy_SMID, JPy_PyObject_JClass, "unwrapProxy", "(Ljava/lang/Object;)Lorg/jpy/PyObject;"); DEFINE_METHOD(JPy_PyObject_Init_MID, JPy_PyObject_JClass, "", "(JZ)V"); } JPy_JPyModule = JType_GetTypeForName(jenv, "org.jpy.PyModule", JNI_FALSE); if (JPy_JPyModule == NULL) { // org.jpy.PyModule may not be on the classpath, which is ok PyErr_Clear(); return -1; } dictType = JType_GetTypeForName(jenv, "org.jpy.PyDictWrapper", JNI_FALSE); if (dictType == NULL) { PyErr_Clear(); return -1; } else { JPy_PyDictWrapper_JClass = dictType->classRef; DEFINE_METHOD(JPy_PyDictWrapper_GetPointer_MID, JPy_PyDictWrapper_JClass, "getPointer", "()J"); } keyErrorType = JType_GetTypeForName(jenv, "org.jpy.KeyError", JNI_FALSE); if (keyErrorType == NULL) { PyErr_Clear(); return -1; } else { JPy_KeyError_JClass = keyErrorType->classRef; } stopIterationType = JType_GetTypeForName(jenv, "org.jpy.StopIteration", JNI_FALSE); if (stopIterationType == NULL) { PyErr_Clear(); return -1; } else { JPy_StopIteration_JClass = stopIterationType->classRef; } return 0; } int JPy_InitGlobalVars(JNIEnv* jenv) { if (JPy_Comparable_JClass != NULL) { return 0; } DEFINE_CLASS(JPy_Comparable_JClass, "java/lang/Comparable"); DEFINE_METHOD(JPy_Comparable_CompareTo_MID, JPy_Comparable_JClass, "compareTo", "(Ljava/lang/Object;)I"); DEFINE_CLASS(JPy_Object_JClass, "java/lang/Object"); DEFINE_METHOD(JPy_Object_ToString_MID, JPy_Object_JClass, "toString", "()Ljava/lang/String;"); DEFINE_METHOD(JPy_Object_HashCode_MID, JPy_Object_JClass, "hashCode", "()I"); DEFINE_METHOD(JPy_Object_Equals_MID, JPy_Object_JClass, "equals", "(Ljava/lang/Object;)Z"); DEFINE_CLASS(JPy_Class_JClass, "java/lang/Class"); DEFINE_METHOD(JPy_Class_GetName_MID, JPy_Class_JClass, "getName", "()Ljava/lang/String;"); DEFINE_METHOD(JPy_Class_GetDeclaredConstructors_MID, JPy_Class_JClass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); DEFINE_METHOD(JPy_Class_GetDeclaredMethods_MID, JPy_Class_JClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); DEFINE_METHOD(JPy_Class_GetDeclaredFields_MID, JPy_Class_JClass, "getDeclaredFields", "()[Ljava/lang/reflect/Field;"); DEFINE_METHOD(JPy_Class_GetMethods_MID, JPy_Class_JClass, "getMethods", "()[Ljava/lang/reflect/Method;"); DEFINE_METHOD(JPy_Class_GetFields_MID, JPy_Class_JClass, "getFields", "()[Ljava/lang/reflect/Field;"); DEFINE_METHOD(JPy_Class_GetComponentType_MID, JPy_Class_JClass, "getComponentType", "()Ljava/lang/Class;"); DEFINE_METHOD(JPy_Class_IsPrimitive_MID, JPy_Class_JClass, "isPrimitive", "()Z"); DEFINE_METHOD(JPy_Class_IsInterface_MID, JPy_Class_JClass, "isInterface", "()Z"); DEFINE_CLASS(JPy_Constructor_JClass, "java/lang/reflect/Constructor"); DEFINE_METHOD(JPy_Constructor_GetModifiers_MID, JPy_Constructor_JClass, "getModifiers", "()I"); DEFINE_METHOD(JPy_Constructor_GetParameterTypes_MID, JPy_Constructor_JClass, "getParameterTypes", "()[Ljava/lang/Class;"); DEFINE_CLASS(JPy_Field_JClass, "java/lang/reflect/Field"); DEFINE_METHOD(JPy_Field_GetName_MID, JPy_Field_JClass, "getName", "()Ljava/lang/String;"); DEFINE_METHOD(JPy_Field_GetModifiers_MID, JPy_Field_JClass, "getModifiers", "()I"); DEFINE_METHOD(JPy_Field_GetType_MID, JPy_Field_JClass, "getType", "()Ljava/lang/Class;"); DEFINE_CLASS(JPy_Method_JClass, "java/lang/reflect/Method"); DEFINE_METHOD(JPy_Method_GetName_MID, JPy_Method_JClass, "getName", "()Ljava/lang/String;"); DEFINE_METHOD(JPy_Method_GetModifiers_MID, JPy_Method_JClass, "getModifiers", "()I"); DEFINE_METHOD(JPy_Method_GetParameterTypes_MID, JPy_Method_JClass, "getParameterTypes", "()[Ljava/lang/Class;"); DEFINE_METHOD(JPy_Method_GetReturnType_MID, JPy_Method_JClass, "getReturnType", "()Ljava/lang/Class;"); DEFINE_CLASS(JPy_Map_JClass, "java/util/Map"); DEFINE_METHOD(JPy_Map_entrySet_MID, JPy_Map_JClass, "entrySet", "()Ljava/util/Set;"); DEFINE_METHOD(JPy_Map_put_MID, JPy_Map_JClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); DEFINE_METHOD(JPy_Map_clear_MID, JPy_Map_JClass, "clear", "()V"); DEFINE_CLASS(JPy_Map_Entry_JClass, "java/util/Map$Entry"); DEFINE_METHOD(JPy_Map_Entry_getKey_MID, JPy_Map_Entry_JClass, "getKey", "()Ljava/lang/Object;"); DEFINE_METHOD(JPy_Map_Entry_getValue_MID, JPy_Map_Entry_JClass, "getValue", "()Ljava/lang/Object;"); // java.util.Set DEFINE_CLASS(JPy_Set_JClass, "java/util/Set"); DEFINE_METHOD(JPy_Set_Iterator_MID, JPy_Set_JClass, "iterator", "()Ljava/util/Iterator;"); // java.util.Iterator DEFINE_CLASS(JPy_Iterator_JClass, "java/util/Iterator"); DEFINE_METHOD(JPy_Iterator_next_MID, JPy_Iterator_JClass, "next", "()Ljava/lang/Object;"); DEFINE_METHOD(JPy_Iterator_hasNext_MID, JPy_Iterator_JClass, "hasNext", "()Z"); DEFINE_CLASS(JPy_RuntimeException_JClass, "java/lang/RuntimeException"); DEFINE_CLASS(JPy_OutOfMemoryError_JClass, "java/lang/OutOfMemoryError"); DEFINE_CLASS(JPy_FileNotFoundException_JClass, "java/io/FileNotFoundException"); DEFINE_CLASS(JPy_UnsupportedOperationException_JClass, "java/lang/UnsupportedOperationException"); DEFINE_CLASS(JPy_Boolean_JClass, "java/lang/Boolean"); DEFINE_STATIC_METHOD(JPy_Boolean_ValueOf_SMID, JPy_Boolean_JClass, "valueOf", "(Z)Ljava/lang/Boolean;"); DEFINE_METHOD(JPy_Boolean_BooleanValue_MID, JPy_Boolean_JClass, "booleanValue", "()Z"); DEFINE_CLASS(JPy_Character_JClass, "java/lang/Character"); DEFINE_STATIC_METHOD(JPy_Character_ValueOf_SMID, JPy_Character_JClass, "valueOf", "(C)Ljava/lang/Character;"); DEFINE_METHOD(JPy_Character_CharValue_MID, JPy_Character_JClass, "charValue", "()C"); DEFINE_CLASS(JPy_Number_JClass, "java/lang/Number"); DEFINE_CLASS(JPy_Byte_JClass, "java/lang/Byte"); DEFINE_STATIC_METHOD(JPy_Byte_ValueOf_SMID, JPy_Byte_JClass, "valueOf", "(B)Ljava/lang/Byte;"); DEFINE_CLASS(JPy_Short_JClass, "java/lang/Short"); DEFINE_STATIC_METHOD(JPy_Short_ValueOf_SMID, JPy_Short_JClass, "valueOf", "(S)Ljava/lang/Short;"); DEFINE_CLASS(JPy_Integer_JClass, "java/lang/Integer"); DEFINE_STATIC_METHOD(JPy_Integer_ValueOf_SMID, JPy_Integer_JClass, "valueOf", "(I)Ljava/lang/Integer;"); DEFINE_CLASS(JPy_Long_JClass, "java/lang/Long"); DEFINE_STATIC_METHOD(JPy_Long_ValueOf_SMID, JPy_Long_JClass, "valueOf", "(J)Ljava/lang/Long;"); DEFINE_CLASS(JPy_Float_JClass, "java/lang/Float"); DEFINE_STATIC_METHOD(JPy_Float_ValueOf_SMID, JPy_Float_JClass, "valueOf", "(F)Ljava/lang/Float;"); DEFINE_CLASS(JPy_Double_JClass, "java/lang/Double"); DEFINE_STATIC_METHOD(JPy_Double_ValueOf_SMID, JPy_Double_JClass, "valueOf", "(D)Ljava/lang/Double;"); DEFINE_CLASS(JPy_Number_JClass, "java/lang/Number"); DEFINE_METHOD(JPy_Number_IntValue_MID, JPy_Number_JClass, "intValue", "()I"); DEFINE_METHOD(JPy_Number_LongValue_MID , JPy_Number_JClass, "longValue", "()J"); DEFINE_METHOD(JPy_Number_DoubleValue_MID, JPy_Number_JClass, "doubleValue", "()D"); DEFINE_CLASS(JPy_Void_JClass, "java/lang/Void"); DEFINE_CLASS(JPy_String_JClass, "java/lang/String"); DEFINE_CLASS(JPy_Throwable_JClass, "java/lang/Throwable"); DEFINE_CLASS(JPy_StackTraceElement_JClass, "java/lang/StackTraceElement"); DEFINE_CLASS(JPy_ByteBuffer_JClass, "java/nio/ByteBuffer"); DEFINE_METHOD(JPy_ByteBuffer_AsReadOnlyBuffer_MID, JPy_ByteBuffer_JClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;"); // Non-Object types: Primitive types and void. DEFINE_NON_OBJECT_TYPE(JPy_JBoolean, JPy_Boolean_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JChar, JPy_Character_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JByte, JPy_Byte_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JShort, JPy_Short_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JInt, JPy_Integer_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JLong, JPy_Long_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JFloat, JPy_Float_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JDouble, JPy_Double_JClass); DEFINE_NON_OBJECT_TYPE(JPy_JVoid, JPy_Void_JClass); // The Java root object. Make sure, JPy_JObject is the first object type to be initialized. DEFINE_OBJECT_TYPE(JPy_JObject, JPy_Object_JClass); // Immediately after JPy_JObject, we define JPy_JClass. From now on JType_AddClassAttribute() works. DEFINE_OBJECT_TYPE(JPy_JClass, JPy_Class_JClass); // Primitive-Wrapper Objects. DEFINE_OBJECT_TYPE(JPy_JBooleanObj, JPy_Boolean_JClass); DEFINE_OBJECT_TYPE(JPy_JCharacterObj, JPy_Character_JClass); DEFINE_OBJECT_TYPE(JPy_JByteObj, JPy_Byte_JClass); DEFINE_OBJECT_TYPE(JPy_JShortObj, JPy_Short_JClass); DEFINE_OBJECT_TYPE(JPy_JIntegerObj, JPy_Integer_JClass); DEFINE_OBJECT_TYPE(JPy_JLongObj, JPy_Long_JClass); DEFINE_OBJECT_TYPE(JPy_JFloatObj, JPy_Float_JClass); DEFINE_OBJECT_TYPE(JPy_JDoubleObj, JPy_Double_JClass); // Other objects. DEFINE_OBJECT_TYPE(JPy_JString, JPy_String_JClass); DEFINE_OBJECT_TYPE(JPy_JByteBuffer, JPy_ByteBuffer_JClass); DEFINE_OBJECT_TYPE(JPy_JThrowable, JPy_Throwable_JClass); DEFINE_OBJECT_TYPE(JPy_JStackTraceElement, JPy_StackTraceElement_JClass); DEFINE_METHOD(JPy_Throwable_getCause_MID, JPy_Throwable_JClass, "getCause", "()Ljava/lang/Throwable;"); DEFINE_METHOD(JPy_Throwable_getStackTrace_MID, JPy_Throwable_JClass, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); DEFINE_CLASS(JPy_Supplier_JClass, "java/util/function/Supplier"); DEFINE_METHOD(JPy_Supplier_get_MID, JPy_Supplier_JClass, "get", "()Ljava/lang/Object;") // JType_AddClassAttribute is actually called from within JType_GetType(), but not for // JPy_JObject and JPy_JClass for an obvious reason. So we do it now: JType_AddClassAttribute(jenv, JPy_JObject); JType_AddClassAttribute(jenv, JPy_JClass); if (initGlobalPyObjectVars(jenv) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JPy_InitGlobalVars: JPy_JPyObject=%p, JPy_JPyModule=%p\n", JPy_JPyObject, JPy_JPyModule); } return 0; } void JPy_ClearGlobalVars(JNIEnv* jenv) { if (jenv != NULL) { (*jenv)->DeleteGlobalRef(jenv, JPy_Comparable_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Object_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Class_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Constructor_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Method_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Field_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_RuntimeException_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Boolean_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Character_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Byte_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Short_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Integer_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Long_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Float_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Double_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Number_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_Void_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_String_JClass); (*jenv)->DeleteGlobalRef(jenv, JPy_ByteBuffer_JClass); } JPy_Comparable_JClass = NULL; JPy_Object_JClass = NULL; JPy_Class_JClass = NULL; JPy_Constructor_JClass = NULL; JPy_Method_JClass = NULL; JPy_Field_JClass = NULL; JPy_RuntimeException_JClass = NULL; JPy_Boolean_JClass = NULL; JPy_Character_JClass = NULL; JPy_Byte_JClass = NULL; JPy_Short_JClass = NULL; JPy_Integer_JClass = NULL; JPy_Long_JClass = NULL; JPy_Float_JClass = NULL; JPy_Double_JClass = NULL; JPy_Number_JClass = NULL; JPy_Void_JClass = NULL; JPy_String_JClass = NULL; JPy_ByteBuffer_JClass = NULL; JPy_Object_ToString_MID = NULL; JPy_Object_HashCode_MID = NULL; JPy_Object_Equals_MID = NULL; JPy_Class_GetName_MID = NULL; JPy_Class_GetDeclaredConstructors_MID = NULL; JPy_Class_GetDeclaredFields_MID = NULL; JPy_Class_GetDeclaredMethods_MID = NULL; JPy_Class_GetFields_MID = NULL; JPy_Class_GetMethods_MID = NULL; JPy_Class_GetComponentType_MID = NULL; JPy_Class_IsPrimitive_MID = NULL; JPy_Class_IsInterface_MID = NULL; JPy_Constructor_GetModifiers_MID = NULL; JPy_Constructor_GetParameterTypes_MID = NULL; JPy_Method_GetName_MID = NULL; JPy_Method_GetReturnType_MID = NULL; JPy_Method_GetParameterTypes_MID = NULL; JPy_Method_GetModifiers_MID = NULL; JPy_Field_GetName_MID = NULL; JPy_Field_GetModifiers_MID = NULL; JPy_Field_GetType_MID = NULL; JPy_Boolean_ValueOf_SMID = NULL; JPy_Boolean_BooleanValue_MID = NULL; JPy_Character_ValueOf_SMID = NULL; JPy_Character_CharValue_MID = NULL; JPy_Byte_ValueOf_SMID = NULL; JPy_Short_ValueOf_SMID = NULL; JPy_Integer_ValueOf_SMID = NULL; JPy_Long_ValueOf_SMID = NULL; JPy_Float_ValueOf_SMID = NULL; JPy_Double_ValueOf_SMID = NULL; JPy_Number_IntValue_MID = NULL; JPy_Number_LongValue_MID = NULL; JPy_Number_DoubleValue_MID = NULL; JPy_PyObject_GetPointer_MID = NULL; JPy_PyObject_UnwrapProxy_SMID = NULL; JPy_ByteBuffer_AsReadOnlyBuffer_MID = NULL; JPy_XDECREF(JPy_JBoolean); JPy_XDECREF(JPy_JChar); JPy_XDECREF(JPy_JByte); JPy_XDECREF(JPy_JShort); JPy_XDECREF(JPy_JInt); JPy_XDECREF(JPy_JLong); JPy_XDECREF(JPy_JFloat); JPy_XDECREF(JPy_JDouble); JPy_XDECREF(JPy_JVoid); JPy_XDECREF(JPy_JString); JPy_XDECREF(JPy_JByteBuffer); JPy_XDECREF(JPy_JBooleanObj); JPy_XDECREF(JPy_JCharacterObj); JPy_XDECREF(JPy_JByteObj); JPy_XDECREF(JPy_JShortObj); JPy_XDECREF(JPy_JIntegerObj); JPy_XDECREF(JPy_JLongObj); JPy_XDECREF(JPy_JFloatObj); JPy_XDECREF(JPy_JDoubleObj); JPy_XDECREF(JPy_JPyObject); JPy_XDECREF(JPy_JPyModule); JPy_JBoolean = NULL; JPy_JChar = NULL; JPy_JByte = NULL; JPy_JShort = NULL; JPy_JInt = NULL; JPy_JLong = NULL; JPy_JFloat = NULL; JPy_JDouble = NULL; JPy_JVoid = NULL; JPy_JString = NULL; JPy_JByteBuffer = NULL; JPy_JBooleanObj = NULL; JPy_JCharacterObj = NULL; JPy_JByteObj = NULL; JPy_JShortObj = NULL; JPy_JIntegerObj = NULL; JPy_JLongObj = NULL; JPy_JFloatObj = NULL; JPy_JDoubleObj = NULL; JPy_JPyObject = NULL; JPy_JPyModule = NULL; } #define AT_STRING "\tat " #define AT_STRLEN 4 #define CAUSED_BY_STRING "caused by " #define CAUSED_BY_STRLEN 10 #define ELIDED_STRING_MAX_SIZE 30 void JPy_HandleJavaException(JNIEnv* jenv) { jthrowable error = (*jenv)->ExceptionOccurred(jenv); if (error != NULL) { jstring message; int allocError = 0; if (JPy_DiagFlags != 0) { (*jenv)->ExceptionDescribe(jenv); } if (JPy_VerboseExceptions) { char *stackTraceString; size_t stackTraceLength = 0; jthrowable cause = error; jarray enclosingElements = NULL; jint enclosingSize = 0; stackTraceString = strdup(""); do { /* We want the type and the detail string, which is actually what a Throwable toString() does by * default, as does the default printStackTrace(). */ jint ii; jarray stackTrace; jint stackTraceElements; jint lastElementToPrint; jint enclosingIndex; if (stackTraceLength > 0) { char *newStackString; newStackString = realloc(stackTraceString, CAUSED_BY_STRLEN + 1 + stackTraceLength); if (newStackString == NULL) { allocError = 1; break; } stackTraceString = newStackString; strcat(stackTraceString, CAUSED_BY_STRING); stackTraceLength += CAUSED_BY_STRLEN; } message = (jstring) (*jenv)->CallObjectMethod(jenv, cause, JPy_Object_ToString_MID); if (message != NULL) { const char *messageChars = (*jenv)->GetStringUTFChars(jenv, message, NULL); if (messageChars != NULL) { char *newStackString; size_t len = strlen(messageChars); newStackString = realloc(stackTraceString, len + 2 + stackTraceLength); if (newStackString == NULL) { (*jenv)->ReleaseStringUTFChars(jenv, message, messageChars); allocError = 1; break; } stackTraceString = newStackString; strcat(stackTraceString, messageChars); stackTraceString[stackTraceLength + len] = '\n'; stackTraceString[stackTraceLength + len + 1] = '\0'; stackTraceLength += (len + 1); (*jenv)->ReleaseStringUTFChars(jenv, message, messageChars); } else { allocError = 1; break; } JPy_DELETE_LOCAL_REF(message); } /* We should assemble a string based on the stack trace. */ stackTrace = (*jenv)->CallObjectMethod(jenv, cause, JPy_Throwable_getStackTrace_MID); stackTraceElements = (*jenv)->GetArrayLength(jenv, stackTrace); lastElementToPrint = stackTraceElements - 1; enclosingIndex = enclosingSize - 1; while (lastElementToPrint >= 0 && enclosingIndex >= 0) { jobject thisElement = (*jenv)->GetObjectArrayElement(jenv, stackTrace, lastElementToPrint); jobject thatElement = (*jenv)->GetObjectArrayElement(jenv, enclosingElements, enclosingIndex); // if they are equal, let's decrement, otherwise we break jboolean equal = (*jenv)->CallBooleanMethod(jenv, thisElement, JPy_Object_Equals_MID, thatElement); if (!equal) { break; } lastElementToPrint--; enclosingIndex--; } for (ii = 0; ii <= lastElementToPrint; ++ii) { jobject traceElement = (*jenv)->GetObjectArrayElement(jenv, stackTrace, ii); if (traceElement != NULL) { message = (jstring) (*jenv)->CallObjectMethod(jenv, traceElement, JPy_Object_ToString_MID); if (message != NULL) { size_t len; char *newStackString; const char *messageChars = (*jenv)->GetStringUTFChars(jenv, message, NULL); if (messageChars == NULL) { allocError = 1; break; } len = strlen(messageChars); newStackString = realloc(stackTraceString, len + 2 + AT_STRLEN + stackTraceLength); if (newStackString == NULL) { (*jenv)->ReleaseStringUTFChars(jenv, message, messageChars); allocError = 1; break; } stackTraceString = newStackString; strcat(stackTraceString, AT_STRING); strcat(stackTraceString, messageChars); stackTraceString[stackTraceLength + len + AT_STRLEN] = '\n'; stackTraceString[stackTraceLength + len + AT_STRLEN + 1] = '\0'; stackTraceLength += (len + 1 + AT_STRLEN); (*jenv)->ReleaseStringUTFChars(jenv, message, messageChars); } } } if (lastElementToPrint < stackTraceElements - 1) { int written; char *newStackString = realloc(stackTraceString, stackTraceLength + ELIDED_STRING_MAX_SIZE); if (newStackString == NULL) { allocError = 1; break; } stackTraceString = newStackString; stackTraceString[stackTraceLength + ELIDED_STRING_MAX_SIZE - 1] = '\0'; written = snprintf(stackTraceString + stackTraceLength, ELIDED_STRING_MAX_SIZE - 1, "\t... %d more\n", (stackTraceElements - lastElementToPrint) - 1); if (written > (ELIDED_STRING_MAX_SIZE - 1)) { stackTraceLength += (ELIDED_STRING_MAX_SIZE - 1); } else { stackTraceLength += written; } } /** So we can eliminate extra entries. */ enclosingElements = stackTrace; enclosingSize = stackTraceElements; /** Now the next cause. */ cause = (*jenv)->CallObjectMethod(jenv, cause, JPy_Throwable_getCause_MID); } while (cause != NULL && !allocError); if (allocError == 0 && stackTraceString != NULL) { PyErr_Format(PyExc_RuntimeError, "%s", stackTraceString); } else { PyErr_SetString(PyExc_RuntimeError, "Java VM exception occurred, but failed to allocate message text"); } free(stackTraceString); } else { message = (jstring) (*jenv)->CallObjectMethod(jenv, error, JPy_Object_ToString_MID); if (message != NULL) { const char *messageChars; messageChars = (*jenv)->GetStringUTFChars(jenv, message, NULL); if (messageChars != NULL) { PyErr_Format(PyExc_RuntimeError, "%s", messageChars); (*jenv)->ReleaseStringUTFChars(jenv, message, messageChars); } else { PyErr_SetString(PyExc_RuntimeError, "Java VM exception occurred, but failed to allocate message text"); } JPy_DELETE_LOCAL_REF(message); } else { PyErr_SetString(PyExc_RuntimeError, "Java VM exception occurred, no message"); } } JPy_DELETE_LOCAL_REF(error); (*jenv)->ExceptionClear(jenv); } } void JPy_free() { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JPy_free: freeing module data...\n"); JPy_ClearGlobalVars(NULL); JPy_Module = NULL; JPy_Types = NULL; JPy_Type_Callbacks = NULL; JPy_Type_Translations = NULL; JException_Type = NULL; JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JPy_free: done freeing module data\n"); } void JPy_free_ptr(void* unused) { JPy_free(); } jpy-2.0.0/src/main/c/jpy_conv.c0000664000175000017500000001515515202503234016432 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jtype.h" #include "jpy_conv.h" #include "jpy_compat.h" int JPy_AsJObject(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jboolean allowJavaWrapping) { return JType_ConvertPythonToJavaObject(jenv, JPy_JObject, pyObj, objectRef, allowJavaWrapping); } int JPy_AsJObjectWithType(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, JPy_JType* type) { return JType_ConvertPythonToJavaObject(jenv, type, pyObj, objectRef, JNI_FALSE); } int JPy_AsJObjectWithClass(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jclass classRef) { *objectRef = NULL; if (pyObj == Py_None) { return 0; } if (classRef != NULL) { JPy_JType* valueType; valueType = JType_GetType(jenv, classRef, JNI_FALSE); // TODO: we need to xdecref this if (valueType == NULL) { return -1; } if (JPy_AsJObjectWithType(jenv, pyObj, objectRef, valueType) < 0) { return -1; } } else { if (JPy_AsJObject(jenv, pyObj, objectRef, JNI_FALSE) < 0) { return -1; } } return 0; } PyObject* JPy_FromJObject(JNIEnv* jenv, jobject objectRef) { JPy_JType* type; type = JType_GetTypeForObject(jenv, objectRef, JNI_FALSE); if (type == NULL) { return NULL; } return JPy_FromJObjectWithType(jenv, objectRef, type); } PyObject* JPy_FromJObjectWithType(JNIEnv* jenv, jobject objectRef, JPy_JType* type) { return JType_ConvertJavaToPythonObject(jenv, type, objectRef); } /** * Copies the UTF, zero-terminated C-string. * Caller is responsible for freeing the returned string using PyMem_Del(). */ char* JPy_CopyUTFString(const char* utfChars) { char* utfCharsCopy; utfCharsCopy = PyMem_New(char, strlen(utfChars) + 1); if (utfCharsCopy == NULL) { PyErr_NoMemory(); return NULL; } strcpy(utfCharsCopy, utfChars); return utfCharsCopy; } /** * Copies the given jchar string used by Java into a wchar_t string used by Python. * Caller is responsible for freeing the returned string using PyMem_Del(). */ wchar_t* JPy_ConvertToWCharString(const jchar* jChars, jint length) { wchar_t* wChars; jint i; wChars = PyMem_New(wchar_t, length + 1); if (wChars == NULL) { PyErr_NoMemory(); return NULL; } for (i = 0; i < length; i++) { wChars[i] = (wchar_t) jChars[i]; } wChars[length] = 0; return wChars; } /** * Copies the given wchar_t string used by Python into a jchar string used by Python. * Caller is responsible for freeing the returned string using PyMem_Del(). */ jchar* JPy_ConvertToJCharString(const wchar_t* wChars, jint length) { jchar* jChars; jint i; jChars = PyMem_New(jchar, length + 1); if (jChars == NULL) { PyErr_NoMemory(); return NULL; } for (i = 0; i < length; i++) { jChars[i] = (jchar) wChars[i]; } jChars[length] = (jchar) 0; return jChars; } /** * Gets the UTF name of thie given class. * Caller is responsible for freeing the returned string using Py_Del(). */ char* JPy_GetTypeName(JNIEnv* jenv, jclass classRef) { jstring jTypeName; const char* jTypeNameChars; char* typeNameCopy; jTypeName = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetName_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); jTypeNameChars = (*jenv)->GetStringUTFChars(jenv, jTypeName, NULL); if (jTypeNameChars == NULL) { PyErr_NoMemory(); typeNameCopy = NULL; } else { typeNameCopy = JPy_CopyUTFString(jTypeNameChars); (*jenv)->ReleaseStringUTFChars(jenv, jTypeName, jTypeNameChars); } JPy_DELETE_LOCAL_REF(jTypeName); return typeNameCopy; } /** * Gets a string object representing the name of the given class. * Returns a new reference. */ PyObject* JPy_FromTypeName(JNIEnv* jenv, jclass classRef) { PyObject* pyTypeName; jstring jTypeName; const char* jTypeNameChars; jTypeName = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetName_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); jTypeNameChars = (*jenv)->GetStringUTFChars(jenv, jTypeName, NULL); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JPy_FromTypeName: classRef=%p, jTypeNameChars=\"%s\"\n", classRef, jTypeNameChars); if (jTypeNameChars == NULL) { PyErr_NoMemory(); pyTypeName = NULL; } else { pyTypeName = Py_BuildValue("s", jTypeNameChars); (*jenv)->ReleaseStringUTFChars(jenv, jTypeName, jTypeNameChars); } JPy_DELETE_LOCAL_REF(jTypeName); return pyTypeName; } PyObject* JPy_FromJString(JNIEnv* jenv, jstring stringRef) { PyObject* returnValue; const jchar* jChars; jint length; if (stringRef == NULL) { Py_RETURN_NONE; } length = (*jenv)->GetStringLength(jenv, stringRef); if (length == 0) { return Py_BuildValue("s", ""); } jChars = (*jenv)->GetStringChars(jenv, stringRef, NULL); if (jChars == NULL) { PyErr_NoMemory(); return NULL; } returnValue = JPy_FROM_WIDE_CHAR_STR(jChars, length); (*jenv)->ReleaseStringChars(jenv, stringRef, jChars); return returnValue; } /** * Returns a new Java string (a local reference). */ int JPy_AsJString(JNIEnv* jenv, PyObject* arg, jstring* stringRef) { Py_ssize_t length; wchar_t* wChars; if (arg == Py_None) { *stringRef = NULL; return 0; } wChars = JPy_AS_WIDE_CHAR_STR(arg, &length); if (wChars == NULL) { *stringRef = NULL; return -1; } if (sizeof(wchar_t) == sizeof(jchar)) { *stringRef = (*jenv)->NewString(jenv, (const jchar*) wChars, length); } else { jchar* jChars; jChars = JPy_ConvertToJCharString(wChars, length); if (jChars == NULL) { goto error; } *stringRef = (*jenv)->NewString(jenv, jChars, length); PyMem_Del(jChars); } if (*stringRef == NULL) { PyMem_Del(wChars); PyErr_NoMemory(); return -1; } error: PyMem_Del(wChars); return 0; } jpy-2.0.0/src/main/c/jpy_verboseexcept.c0000664000175000017500000000702215202503234020335 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #include #include "jpy_verboseexcept.h" int JPy_VerboseExceptions = 0; PyObject* VerboseExceptions_New(void) { return PyObject_New(PyObject, &VerboseExceptions_Type); } PyObject* VerboseExceptions_getattro(PyObject* self, PyObject *attr_name) { if (strcmp(JPy_AS_UTF8(attr_name), "enabled") == 0) { return PyBool_FromLong(JPy_VerboseExceptions); } else { return PyObject_GenericGetAttr(self, attr_name); } } int VerboseExceptions_setattro(PyObject* self, PyObject *attr_name, PyObject *v) { if (strcmp(JPy_AS_UTF8(attr_name), "enabled") == 0) { if (PyBool_Check(v)) { JPy_VerboseExceptions = v == Py_True; } else { PyErr_SetString(PyExc_ValueError, "value for 'flags' must be a boolean"); return -1; } return 0; } else { return PyObject_GenericSetAttr(self, attr_name, v); } } PyTypeObject VerboseExceptions_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.VerboseExceptions", /* tp_name */ sizeof (VerboseExceptions_Type), /* tp_basicsize */ 0, /* tp_itemsize */ NULL, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ NULL, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ NULL, /* tp_str */ (getattrofunc) VerboseExceptions_getattro, /* tp_getattro */ (setattrofunc) VerboseExceptions_setattro, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "Controls python exception verbosity", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ NULL, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) NULL, /* tp_init */ NULL, /* tp_alloc */ NULL, /* tp_new */ }; jpy-2.0.0/src/main/c/jni/0000775000175000017500000000000015202503234015210 5ustar alastairalastairjpy-2.0.0/src/main/c/jni/org_jpy_PyLib.c0000664000175000017500000023153715202503234020137 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #include #include #include "frameobject.h" #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jtype.h" #include "jpy_jobj.h" #include "jpy_conv.h" #include "org_jpy_PyLib.h" #include "org_jpy_PyLib_Diag.h" // Note: Native org.jpy.PyLib function definition headers in this file are formatted according to the header // generated by javah. This makes it easier to follow up changes in the header. PyObject* PyLib_GetAttributeObject(JNIEnv* jenv, PyObject* pyValue, jstring jName); PyObject* PyLib_CallAndReturnObject(JNIEnv *jenv, PyObject* pyValue, jboolean isMethodCall, jstring jName, jint argCount, jobjectArray jArgs, jobjectArray jParamClasses); void PyLib_HandlePythonException(JNIEnv* jenv); void PyLib_ThrowOOM(JNIEnv* jenv); void PyLib_ThrowFNFE(JNIEnv* jenv, const char *file); void PyLib_ThrowUOE(JNIEnv* jenv, const char *message); void PyLib_ThrowRTE(JNIEnv* jenv, const char *message); void PyLib_RedirectStdOut(void); int copyPythonDictToJavaMap(JNIEnv *jenv, PyObject *pyDict, jobject jMap); static PyThreadState *_save = NULL; //#define JPy_JNI_DEBUG 1 #define JPy_JNI_DEBUG 0 // Make sure the following contants are same as in enum org.jpy.PyInputMode #define JPy_IM_STATEMENT 256 #define JPy_IM_SCRIPT 257 #define JPy_IM_EXPRESSION 258 #if PY_VERSION_HEX >= 0x030D0000 #define JPy_Py_IsFinalizing Py_IsFinalizing #else #define JPy_Py_IsFinalizing _Py_IsFinalizing #endif // Checking if Python is in the middle of finalization to make sure that we do not attempt to interact with the Python // runtime while it is shutting down. This helps prevent undefined behavior, such as thread hanging/crashing in // PyGILState_Ensure() as described in https://docs.python.org/3.14/c-api/init.html#c.PyGILState_Ensure. Note that this // approach has some limitations: // 1. it doesn't completely prevent the race condition from happening(TOCTOU), but it mitigates the risk significantly. // 2. it adds some overhead to calling Python from Java, but this is usually negligible compared to the cost of acquiring // and releasing the GIL itself. #define JPy_BEGIN_GIL_STATE(retval) { \ if (JPy_Py_IsFinalizing() != 0) { \ JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Thread attempted to call into Python during interpreter shutdown. Raising exception.\n"); \ (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, "Thread attempted to call into Python during interpreter shutdown"); \ return (retval); \ } \ PyGILState_STATE gilState = PyGILState_Ensure(); #define JPy_BEGIN_GIL_STATE_VOID() { \ if (JPy_Py_IsFinalizing() != 0) { \ JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Thread attempted to call into Python during interpreter shutdown. Raising exception.\n"); \ (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, "Thread attempted to call into Python during interpreter shutdown"); \ return; \ } \ PyGILState_STATE gilState = PyGILState_Ensure(); #define JPy_END_GIL_STATE PyGILState_Release(gilState); } /** * Called if the JVM loads this module. * Will only called if this module's code is linked into a shared library and loaded by a Java VM. */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) { if (JPy_JNI_DEBUG) printf("JNI_OnLoad: enter: jvm=%p, JPy_JVM=%p, JPy_MustDestroyJVM=%d, Py_IsInitialized()=%d\n", jvm, JPy_JVM, JPy_MustDestroyJVM, Py_IsInitialized()); if (JPy_JVM == NULL) { JPy_JVM = jvm; JPy_MustDestroyJVM = JNI_FALSE; } else if (JPy_JVM == jvm) { if (JPy_JNI_DEBUG) printf("JNI_OnLoad: warning: same JVM already running\n"); } else { if (JPy_JNI_DEBUG) printf("JNI_OnLoad: warning: different JVM already running (expect weird things!)\n"); } if (JPy_JNI_DEBUG) printf("JNI_OnLoad: exit: jvm=%p, JPy_JVM=%p, JPy_MustDestroyJVM=%d, Py_IsInitialized()=%d\n", jvm, JPy_JVM, JPy_MustDestroyJVM, Py_IsInitialized()); if (JPy_JNI_DEBUG) fflush(stdout); return JPY_JNI_VERSION; } /** * Called if the JVM unloads this module. * Will only called if this module's code is linked into a shared library and loaded by a Java VM. */ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* jvm, void* reserved) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JNI_OnUnload: enter: jvm=%p, JPy_JVM=%p, JPy_MustDestroyJVM=%d, Py_IsInitialized()=%d\n", jvm, JPy_JVM, JPy_MustDestroyJVM, Py_IsInitialized()); Py_Finalize(); if (!JPy_MustDestroyJVM) { JPy_ClearGlobalVars(JPy_GetJNIEnv()); JPy_JVM = NULL; } JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JNI_OnUnload: exit: jvm=%p, JPy_JVM=%p, JPy_MustDestroyJVM=%d, Py_IsInitialized()=%d\n", jvm, JPy_JVM, JPy_MustDestroyJVM, Py_IsInitialized()); } /* * Class: org_jpy_PyLib * Method: isPythonRunning * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_isPythonRunning (JNIEnv* jenv, jclass jLibClass) { int init; init = Py_IsInitialized(); return init && JPy_Module != NULL; } #define MAX_PYTHON_HOME 256 wchar_t staticPythonHome[MAX_PYTHON_HOME]; /* * Class: org_jpy_PyLib * Method: setPythonHome * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_setPythonHome (JNIEnv* jenv, jclass jLibClass, jstring jPythonHome) { const wchar_t* pythonHome = NULL; const char *nonWidePythonHome = NULL; jboolean result = JNI_FALSE; nonWidePythonHome = (*jenv)->GetStringUTFChars(jenv, jPythonHome, NULL); if (nonWidePythonHome != NULL) { pythonHome = Py_DecodeLocale(nonWidePythonHome, NULL); if (pythonHome != NULL) { if (wcslen(pythonHome) < MAX_PYTHON_HOME) { wcscpy(staticPythonHome, pythonHome); result = JNI_TRUE; } else { PyMem_RawFree(pythonHome); } } if (result) { Py_SetPythonHome(staticPythonHome); PyMem_RawFree(pythonHome); } (*jenv)->ReleaseStringUTFChars(jenv, jPythonHome, nonWidePythonHome); } return result; } #define MAX_PROGRAM_NAME 256 wchar_t staticProgramName[MAX_PROGRAM_NAME]; /* * Class: org_jpy_PyLib * Method: setProgramName * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_setProgramName (JNIEnv* jenv, jclass jLibClass, jstring jProgramName) { const wchar_t* programName = NULL; const char *nonWideProgramName = NULL; jboolean result = JNI_FALSE; nonWideProgramName = (*jenv)->GetStringUTFChars(jenv, jProgramName, NULL); if (nonWideProgramName != NULL) { programName = Py_DecodeLocale(nonWideProgramName, NULL); if (programName != NULL) { if (wcslen(programName) < MAX_PROGRAM_NAME) { wcscpy(staticProgramName, programName); result = JNI_TRUE; } else { PyMem_RawFree(programName); } } if (result) { Py_SetProgramName(staticProgramName); PyMem_RawFree(programName); } (*jenv)->ReleaseStringUTFChars(jenv, jProgramName, nonWideProgramName); } return result; } /* * Class: org_jpy_PyLib * Method: startPython0 * Signature: ([Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_startPython0 (JNIEnv* jenv, jclass jLibClass, jobjectArray jPathArray) { int pyInit = Py_IsInitialized(); JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_startPython: entered: jenv=%p, pyInit=%d, JPy_Module=%p\n", jenv, pyInit, JPy_Module); if (!pyInit) { Py_Initialize(); // See https://github.com/bcdev/jpy/issues/81 PySys_SetArgvEx(0, NULL, 0); PyLib_RedirectStdOut(); pyInit = Py_IsInitialized(); // todo: assert pyInit == 1? PyEval_InitThreads(); _save = PyEval_SaveThread(); // todo: assert not null } if (pyInit) { if (JPy_DiagFlags != 0) { printf("PyLib_startPython: global Python interpreter information:\n"); printf(" Py_GetProgramName() = \"%ls\"\n", Py_GetProgramName()); printf(" Py_GetPrefix() = \"%ls\"\n", Py_GetPrefix()); printf(" Py_GetExecPrefix() = \"%ls\"\n", Py_GetExecPrefix()); printf(" Py_GetProgramFullPath() = \"%ls\"\n", Py_GetProgramFullPath()); printf(" Py_GetPath() = \"%ls\"\n", Py_GetPath()); printf(" Py_GetPythonHome() = \"%ls\"\n", Py_GetPythonHome()); printf(" Py_GetVersion() = \"%s\"\n", Py_GetVersion()); printf(" Py_GetPlatform() = \"%s\"\n", Py_GetPlatform()); printf(" Py_GetCompiler() = \"%s\"\n", Py_GetCompiler()); printf(" Py_GetBuildInfo() = \"%s\"\n", Py_GetBuildInfo()); } // If we've got jPathArray, add all entries to Python's "sys.path" // if (jPathArray != NULL) { PyObject* pyPathList; PyObject* pyPath; jstring jPath; jsize i, pathCount; pathCount = (*jenv)->GetArrayLength(jenv, jPathArray); //printf(">> pathCount=%d\n", pathCount); if (pathCount > 0) { JPy_BEGIN_GIL_STATE(JNI_FALSE) pyPathList = PySys_GetObject("path"); //printf(">> pyPathList=%p, len=%ld\n", pyPathList, PyList_Size(pyPathList)); if (pyPathList != NULL) { JPy_INCREF(pyPathList); for (i = pathCount - 1; i >= 0; i--) { jPath = (*jenv)->GetObjectArrayElement(jenv, jPathArray, i); //printf(">> i=%d, jPath=%p\n", i, jPath); if (jPath != NULL) { pyPath = JPy_FromJString(jenv, jPath); //printf(">> i=%d, pyPath=%p\n", i, pyPath); if (pyPath != NULL) { PyList_Insert(pyPathList, 0, pyPath); } } } JPy_DECREF(pyPathList); } //printf(">> pyPathList=%p, len=%ld\n", pyPathList, PyList_Size(pyPathList)); //printf(">> pyPathList=%p, len=%ld (check)\n", PySys_GetObject("path"), PyList_Size(PySys_GetObject("path"))); JPy_END_GIL_STATE } } // if the global JPy_Module is NULL, then the 'jpy' extension module has not been imported yet. // if (JPy_Module == NULL) { PyObject* pyModule; JPy_BEGIN_GIL_STATE(JNI_FALSE) // We import 'jpy' so that Python can call our PyInit_jpy() which sets up a number of // required global variables (including JPy_Module, see above). // pyModule = PyImport_ImportModule("jpy"); //printf(">> pyModule=%p\n", pyModule); if (pyModule == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_startPython: failed to import module 'jpy'\n"); if (JPy_DiagFlags != 0 && PyErr_Occurred()) { PyErr_Print(); } PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE } } JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_startPython: exiting: jenv=%p, pyInit=%d, JPy_Module=%p\n", jenv, pyInit, JPy_Module); //printf(">> JPy_Module=%p\n", JPy_Module); if (!pyInit) { (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, "Failed to initialize Python interpreter."); return JNI_FALSE; } if (JPy_Module == NULL) { (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, "Failed to initialize Python 'jpy' module."); return JNI_FALSE; } return JNI_TRUE; } /* * Class: org_jpy_PyLib * Method: stopPython0 * Signature: ()V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_stopPython0 (JNIEnv* jenv, jclass jLibClass) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_stopPython: entered: JPy_Module=%p\n", JPy_Module); if (Py_IsInitialized()) { // Cleanup the JPY stateful structures and shut down the interpreter. JPy_BEGIN_GIL_STATE_VOID() JPy_free(); JPy_END_GIL_STATE PyEval_RestoreThread(_save); _save = NULL; Py_Finalize(); } JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_stopPython: exiting: JPy_Module=%p\n", JPy_Module); } /* * Class: org_jpy_PyLib * Method: getPythonVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_getPythonVersion (JNIEnv* jenv, jclass jLibClass) { const char* version; version = Py_GetVersion(); if (version == NULL) { return NULL; } return (*jenv)->NewStringUTF(jenv, version); } /* * Class: org_jpy_python_PyLib * Method: execScript * Signature: (Ljava/lang/String;)J */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_execScript (JNIEnv* jenv, jclass jLibClass, jstring jScript) { const char* scriptChars; int retCode = -1; JPy_BEGIN_GIL_STATE(-1) scriptChars = (*jenv)->GetStringUTFChars(jenv, jScript, NULL); if (scriptChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_execScript: script='%s'\n", scriptChars); retCode = PyRun_SimpleString(scriptChars); if (retCode < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_execScript: error: PyRun_SimpleString(\"%s\") returned %d\n", scriptChars, retCode); // Note that we cannot retrieve last Python exception after a calling PyRun_SimpleString, see documentation of PyRun_SimpleString. } error: if (scriptChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jScript, scriptChars); } JPy_END_GIL_STATE return retCode; } PyObject* PyLib_ConvertJavaToPythonObject(JNIEnv* jenv, jobject jObject) { JPy_JType* type; if (jObject == NULL) { return JPy_FROM_JNULL(); } type = JType_GetTypeForObject(jenv, jObject, JNI_FALSE); return JType_ConvertJavaToPythonObject(jenv, type, jObject); } int PyLib_ConvertPythonToJavaObject(JNIEnv* jenv, PyObject* pyObject, jobject* jObject) { return JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, pyObject, jObject, JNI_FALSE); } void dumpDict(const char* dictName, PyObject* dict) { Py_ssize_t size; PyObject *key = NULL, *value = NULL; Py_ssize_t pos = 0; Py_ssize_t i = 0; if (!PyDict_Check(dict)) { printf(">> dumpDict: %s is not a dictionary!\n", dictName); return; } size = PyDict_Size(dict); printf(">> dumpDict: %s.size = %ld\n", dictName, size); #if PY_VERSION_HEX >= 0x030D0000 // >=3.13 // PyDict_Next is not thread-safe, so we need to protect it with a critical section // https://docs.python.org/3/howto/free-threading-extensions.html#pydict-next Py_BEGIN_CRITICAL_SECTION(dict); #endif while (PyDict_Next(dict, &pos, &key, &value)) { const char* name; name = JPy_AS_UTF8(key); printf(">> dumpDict: %s[%ld].name = '%s'\n", dictName, i, name); i++; } #if PY_VERSION_HEX >= 0x030D0000 // >=3.13 Py_END_CRITICAL_SECTION(); #endif } /** * Get the globals from the __main__ module. */ PyObject *getMainGlobals() { PyObject* pyMainModule; PyObject* pyGlobals; pyMainModule = PyImport_AddModule("__main__"); // borrowed ref if (pyMainModule == NULL) { return NULL; } pyGlobals = PyModule_GetDict(pyMainModule); // borrowed ref JPy_XINCREF(pyGlobals); return pyGlobals; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getMainGlobals (JNIEnv *jenv, jclass libClass) { jobject objectRef = NULL; PyObject *globals; JPy_BEGIN_GIL_STATE(NULL) globals = getMainGlobals(); // new ref if (globals == NULL) { goto error; } if (JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, globals, &objectRef, JNI_FALSE) < 0) { objectRef = NULL; goto error; } error: JPy_XDECREF(globals); JPy_END_GIL_STATE return objectRef; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getCurrentGlobals (JNIEnv *jenv, jclass libClass) { jobject objectRef = NULL; PyObject *globals; JPy_BEGIN_GIL_STATE(NULL) #if PY_VERSION_HEX < 0x030D0000 // < 3.13 globals = PyEval_GetGlobals(); // borrowed ref JPy_XINCREF(globals); #else // See https://peps.python.org/pep-0667 for the change in Python 3.13 globals = PyEval_GetFrameGlobals(); // new ref #endif if (globals == NULL) { goto error; } if (JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, globals, &objectRef, JNI_FALSE) < 0) { objectRef = NULL; goto error; } error: JPy_XDECREF(globals); JPy_END_GIL_STATE return objectRef; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getCurrentLocals (JNIEnv *jenv, jclass libClass) { jobject objectRef = NULL; PyObject *locals; JPy_BEGIN_GIL_STATE(NULL) #if PY_VERSION_HEX < 0x030D0000 // < 3.13 locals = PyEval_GetLocals(); // borrowed ref JPy_XINCREF(locals); #else // See https://peps.python.org/pep-0667 for the change in Python 3.13 locals = PyEval_GetFrameLocals(); // new ref #endif if (locals == NULL) { goto error; } if (JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, locals, &objectRef, JNI_FALSE) < 0) { objectRef = NULL; goto error; } error: JPy_XDECREF(locals); JPy_END_GIL_STATE return objectRef; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_copyDict (JNIEnv *jenv, jclass libClass, jlong pyPointer) { jobject objectRef = NULL; PyObject* copy = NULL; PyObject* src = (PyObject*)pyPointer; JPy_BEGIN_GIL_STATE(NULL) if (!PyDict_Check(src)) { PyLib_ThrowUOE(jenv, "Not a dictionary!"); goto error; } copy = PyDict_Copy(src); if (JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, copy, &objectRef, JNI_FALSE) < 0) { objectRef = NULL; goto error; } error: JPy_XDECREF(copy); JPy_END_GIL_STATE return objectRef; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_newDict (JNIEnv *jenv, jclass libClass) { jobject objectRef = NULL; PyObject *dict; JPy_BEGIN_GIL_STATE(NULL) dict = PyDict_New(); if (JType_ConvertPythonToJavaObject(jenv, JPy_JPyObject, dict, &objectRef, JNI_FALSE) < 0) { objectRef = NULL; goto error; } error: JPy_XDECREF(dict); JPy_END_GIL_STATE return objectRef; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_pyDictKeys (JNIEnv *jenv, jclass libClass, jlong pyDict) { jobject result = NULL; PyObject* keys = NULL; PyObject* src = (PyObject*)pyDict; JPy_BEGIN_GIL_STATE(NULL) if (!PyDict_Check(src)) { PyLib_ThrowUOE(jenv, "Not a dictionary!"); goto error; } keys = PyDict_Keys(src); if (JType_CreateJavaPyObject(jenv, JPy_JPyObject, keys, &result) < 0) { result = NULL; goto error; } error: JPy_XDECREF(keys); JPy_END_GIL_STATE return result; } JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_pyDictValues (JNIEnv *jenv, jclass libClass, jlong pyDict) { jobject result = NULL; PyObject* values = NULL; PyObject* src = (PyObject*)pyDict; JPy_BEGIN_GIL_STATE(NULL) if (!PyDict_Check(src)) { PyLib_ThrowUOE(jenv, "Not a dictionary!"); goto error; } values = PyDict_Values(src); if (JType_CreateJavaPyObject(jenv, JPy_JPyObject, values, &result) < 0) { result = NULL; goto error; } error: JPy_XDECREF(values); JPy_END_GIL_STATE return result; } JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyDictContains (JNIEnv *jenv, jclass libClass, jlong pyDict, jobject jKey, jclass jKeyClass) { PyObject* src = (PyObject*)pyDict; int result = 0; JPy_JType* keyType; PyObject* pyKey; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (!PyDict_Check(src)) { result = -1; PyLib_ThrowUOE(jenv, "Not a dictionary!"); goto error; } if (jKeyClass != NULL) { keyType = JType_GetType(jenv, jKeyClass, JNI_FALSE); if (keyType == NULL) { result = -1; JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_pyDictContains: failed to retrieve type\n"); PyLib_HandlePythonException(jenv); goto error; } pyKey = JPy_FromJObjectWithType(jenv, jKey, keyType); } else { pyKey = JPy_FromJObject(jenv, jKey); } result = PyDict_Contains(src, pyKey); if (result < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_pyDictContains: PyDict_Contains error\n"); PyLib_HandlePythonException(jenv); goto error; } error: JPy_END_GIL_STATE return result == 1 ? JNI_TRUE : JNI_FALSE; } /** * Copies a Java Map into a new Python dictionary. */ PyObject *copyJavaStringObjectMapToPyDict(JNIEnv *jenv, jobject jMap) { PyObject* result = NULL; jobject entrySet, iterator, mapEntry; jboolean hasNext; result = PyDict_New(); if (result == NULL) { return result; } entrySet = (*jenv)->CallObjectMethod(jenv, jMap, JPy_Map_entrySet_MID); if (entrySet == NULL) { goto error; } iterator = (*jenv)->CallObjectMethod(jenv, entrySet, JPy_Set_Iterator_MID); if (iterator == NULL) { goto error; } hasNext = (*jenv)->CallBooleanMethod(jenv, iterator, JPy_Iterator_hasNext_MID); while (hasNext) { jobject key, value; char const *keyChars; PyObject *pyKey; PyObject *pyValue; JPy_JType* type; mapEntry = (*jenv)->CallObjectMethod(jenv, iterator, JPy_Iterator_next_MID); if (mapEntry == NULL) { goto error; } key = (*jenv)->CallObjectMethod(jenv, mapEntry, JPy_Map_Entry_getKey_MID); if (key == NULL) { goto error; } // we require string keys if (!(*jenv)->IsInstanceOf(jenv, key, JPy_String_JClass)) { goto error; } keyChars = (*jenv)->GetStringUTFChars(jenv, (jstring)key, NULL); if (keyChars == NULL) { goto error; } pyKey = JPy_FROM_CSTR(keyChars); (*jenv)->ReleaseStringUTFChars(jenv, (jstring)key, keyChars); value = (*jenv)->CallObjectMethod(jenv, mapEntry, JPy_Map_Entry_getValue_MID); type = JType_GetTypeForObject(jenv, value, JNI_FALSE); pyValue = JType_ConvertJavaToPythonObject(jenv, type, value); PyDict_SetItem(result, pyKey, pyValue); hasNext = (*jenv)->CallBooleanMethod(jenv, iterator, JPy_Iterator_hasNext_MID); } return result; error: JPy_XDECREF(result); return NULL; } int copyPythonDictToJavaMap(JNIEnv *jenv, PyObject *pyDict, jobject jMap) { PyObject *pyKey, *pyValue; Py_ssize_t pos = 0; Py_ssize_t dictSize; jobject *jValues = NULL; jobject *jKeys = NULL; int ii; jboolean exceptionAlready = JNI_FALSE; jthrowable savedException = NULL; int retcode = -1; if (!PyDict_Check(pyDict)) { PyLib_ThrowUOE(jenv, "PyObject is not a dictionary!"); return -1; } dictSize = PyDict_Size(pyDict); jKeys = malloc(dictSize * sizeof(jobject)); jValues = malloc(dictSize * sizeof(jobject)); if (jKeys == NULL || jValues == NULL) { PyLib_ThrowOOM(jenv); goto error; } exceptionAlready = (*jenv)->ExceptionCheck(jenv); if (exceptionAlready) { // save the exception away, because otherwise the conversion methods might spuriously fail savedException = (*jenv)->ExceptionOccurred(jenv); (*jenv)->ExceptionClear(jenv); } // first convert everything ii = 0; while (PyDict_Next(pyDict, &pos, &pyKey, &pyValue)) { if (JPy_AsJObjectWithClass(jenv, pyKey, &(jKeys[ii]), JPy_String_JClass) < 0) { // an error occurred goto error; } if (JPy_AsJObject(jenv, pyValue, &(jValues[ii]), JNI_TRUE) < 0) { // an error occurred goto error; } ii++; } // now that we've converted, clear out the map and repopulate it (*jenv)->CallVoidMethod(jenv, jMap, JPy_Map_clear_MID); for (ii = 0; ii < dictSize; ++ii) { // since the map is cleared, we want to plow through all of the put operations (*jenv)->CallObjectMethod(jenv, jMap, JPy_Map_put_MID, jKeys[ii], jValues[ii]); } // and we are successful! retcode = 0; error: if (exceptionAlready) { // restore our original exception (*jenv)->Throw(jenv, savedException); } free(jKeys); free(jValues); return retcode; } typedef PyObject * (*DoRun)(const void *,int,PyObject*,PyObject*); /** * After setting up the globals and locals, calls the provided function. * * jStart must be JPy_IM_STATEMENT, JPy_IM_SCRIPT, JPy_IM_EXPRESSION; matching what you are trying to * run. If you use a statement or expression instead of a script; some of your code may be ignored. * * If jGlobals is not specified, then the main module globals are used. * * If jLocals is not specified, then the globals are used. * * jGlobals and jLocals may be a PyObject, in which case they are used without translation. Otherwise, * they must be a map from String to Object, and will be copied to a new python dictionary. After execution * completes the dictionary entries will be copied back. * */ jlong executeInternal(JNIEnv* jenv, jclass jLibClass, jint jStart, jobject jGlobals, jobject jLocals, DoRun runFunction, void *runArg) { PyObject *pyReturnValue; PyObject *pyGlobals; PyObject *pyLocals; int start; jboolean decGlobals, decLocals, copyGlobals, copyLocals; JPy_BEGIN_GIL_STATE(0) decGlobals = decLocals = JNI_FALSE; copyGlobals = copyLocals = JNI_FALSE; pyGlobals = NULL; pyLocals = NULL; pyReturnValue = NULL; if (jGlobals == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using main globals\n"); pyGlobals = getMainGlobals(); // new ref decGlobals = JNI_TRUE; if (pyGlobals == NULL) { PyLib_HandlePythonException(jenv); goto error; } } else if ((*jenv)->IsInstanceOf(jenv, jGlobals, JPy_PyObject_JClass)) { // if we are an instance of PyObject, just use the object pyGlobals = (PyObject *)((*jenv)->CallLongMethod(jenv, jGlobals, JPy_PyObject_GetPointer_MID)); JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using PyObject globals\n"); } else if ((*jenv)->IsInstanceOf(jenv, jGlobals, JPy_PyDictWrapper_JClass)) { // if we are an instance of a wrapped dictionary, just use the underlying dictionary pyGlobals = (PyObject *)((*jenv)->CallLongMethod(jenv, jGlobals, JPy_PyDictWrapper_GetPointer_MID)); JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using PyDictWrapper globals\n"); } else if ((*jenv)->IsInstanceOf(jenv, jGlobals, JPy_Map_JClass)) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using Java Map globals\n"); // this is a java Map and we need to convert it pyGlobals = copyJavaStringObjectMapToPyDict(jenv, jGlobals); if (pyGlobals == NULL) { PyLib_ThrowRTE(jenv, "Could not convert globals from Java Map to Python dictionary"); goto error; } copyGlobals = decGlobals = JNI_TRUE; } else { PyLib_ThrowUOE(jenv, "Unsupported globals type"); goto error; } // if we have no locals specified, using globals for it matches the behavior of eval, "The expression argument is // parsed and evaluated as a Python expression (technically speaking, a condition list) using the globals and // locals dictionaries as global and local namespace. ... If the locals dictionary is omitted it defaults to the // globals dictionary. If both dictionaries are omitted, the expression is executed in the environment where eval() // is called. The return value is the result of the evaluated expression. if (jLocals == NULL) { pyLocals = pyGlobals; JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using globals for locals\n"); } else if ((*jenv)->IsInstanceOf(jenv, jLocals, JPy_PyObject_JClass)) { // if we are an instance of PyObject, just use the object JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using PyObject locals\n"); pyLocals = (PyObject *)((*jenv)->CallLongMethod(jenv, jLocals, JPy_PyObject_GetPointer_MID)); } else if ((*jenv)->IsInstanceOf(jenv, jLocals, JPy_PyDictWrapper_JClass)) { // if we are an instance of a wrapped dictionary, just use the underlying dictionary pyLocals = (PyObject *)((*jenv)->CallLongMethod(jenv, jLocals, JPy_PyDictWrapper_GetPointer_MID)); JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using PyDictWrapper locals\n"); } else if ((*jenv)->IsInstanceOf(jenv, jLocals, JPy_Map_JClass)) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: using Java Map locals\n"); // this is a java Map and we need to convert it pyLocals = copyJavaStringObjectMapToPyDict(jenv, jLocals); if (pyLocals == NULL) { PyLib_ThrowRTE(jenv, "Could not convert locals from Java Map to Python dictionary"); goto error; } copyLocals = decLocals = JNI_TRUE; } else { PyLib_ThrowUOE(jenv, "Unsupported locals type"); goto error; } start = jStart == JPy_IM_STATEMENT ? Py_single_input : jStart == JPy_IM_SCRIPT ? Py_file_input : Py_eval_input; pyReturnValue = runFunction(runArg, start, pyGlobals, pyLocals); if (pyReturnValue == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: Handle Python Exception\n"); PyLib_HandlePythonException(jenv); goto error; } error: if (copyGlobals) { copyPythonDictToJavaMap(jenv, pyGlobals, jGlobals); JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: copied back Java global\n"); } if (copyLocals) { copyPythonDictToJavaMap(jenv, pyLocals, jLocals); JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeInternal: copied back Java locals\n"); } if (decGlobals) { JPy_XDECREF(pyGlobals); } if (decLocals) { JPy_XDECREF(pyLocals); } JPy_END_GIL_STATE return (jlong) pyReturnValue; } PyObject *pyRunStringWrapper(const char *code, int start, PyObject *globals, PyObject *locals) { PyObject *result = PyRun_String(code, start, globals, locals); return result; } /** * Calls PyRun_String under the covers to execute the string contents. * * jStart must be JPy_IM_STATEMENT, JPy_IM_SCRIPT, JPy_IM_EXPRESSION; matching what you are trying to * run. If you use a statement or expression instead of a script; some of your code may be ignored. * * If jGlobals is not specified, then the main module globals are used. * * If jLocals is not specified, then the globals are used. * * jGlobals and jLocals may be a PyObject, in which case they are used without translation. Otherwise, * they must be a map from String to Object, and will be copied to a new python dictionary. After execution * completes the dictionary entries will be copied back. */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_executeCode (JNIEnv* jenv, jclass jLibClass, jstring jCode, jint jStart, jobject jGlobals, jobject jLocals) { const char *codeChars; jlong result; codeChars = (*jenv)->GetStringUTFChars(jenv, jCode, NULL); if (codeChars == NULL) { PyLib_ThrowOOM(jenv); return 0; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_executeCode: code='%s'\n", codeChars); result = executeInternal(jenv, jLibClass, jStart, jGlobals, jLocals, (DoRun)pyRunStringWrapper, codeChars); if (codeChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jCode, codeChars); } return result; } typedef struct { FILE *fp; const char *filechars; } RunFileArgs; PyObject *pyRunFileWrapper(RunFileArgs *args, int start, PyObject *globals, PyObject *locals) { return PyRun_File(args->fp, args->filechars, start, globals, locals); } /** * Calls PyRun_Script under the covers to execute the script contents. * * jStart must be JPy_IM_STATEMENT, JPy_IM_SCRIPT, JPy_IM_EXPRESSION; matching what you are trying to * run. If you use a statement or expression instead of a script; some of your code may be ignored. * * If jGlobals is not specified, then the main module globals are used. * * If jLocals is not specified, then the globals are used. * * jGlobals and jLocals may be a PyObject, in which case they are used without translation. Otherwise, * they must be a map from String to Object, and will be copied to a new python dictionary. After execution * completes the dictionary entries will be copied back. */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_executeScript (JNIEnv* jenv, jclass jLibClass, jstring jFile, jint jStart, jobject jGlobals, jobject jLocals) { RunFileArgs runFileArgs; jlong result = 0; runFileArgs.fp = NULL; runFileArgs.filechars = NULL; runFileArgs.filechars = (*jenv)->GetStringUTFChars(jenv, jFile, NULL); if (runFileArgs.filechars == NULL) { PyLib_ThrowOOM(jenv); goto error; } runFileArgs.fp = fopen(runFileArgs.filechars, "r"); if (!runFileArgs.fp) { PyLib_ThrowFNFE(jenv, runFileArgs.filechars); goto error; } result = executeInternal(jenv, jLibClass, jStart, jGlobals, jLocals, (DoRun)pyRunFileWrapper, &runFileArgs); error: if (runFileArgs.filechars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jFile, runFileArgs.filechars); } if (runFileArgs.fp != NULL) { fclose(runFileArgs.fp); } return result; } /* * Class: org_jpy_python_PyLib * Method: incRef * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_incRef (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; Py_ssize_t refCount; pyObject = (PyObject*) objId; if (Py_IsInitialized()) { JPy_BEGIN_GIL_STATE_VOID() refCount = Py_REFCNT(pyObject); JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "Java_org_jpy_PyLib_incRef: pyObject=%p, refCount=%d, type='%s'\n", pyObject, refCount, Py_TYPE(pyObject)->tp_name); JPy_INCREF(pyObject); JPy_END_GIL_STATE } else { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_incRef: error: no interpreter: pyObject=%p\n", pyObject); } } /* * Class: org_jpy_python_PyLib * Method: decRef * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_decRef (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; Py_ssize_t refCount; pyObject = (PyObject*) objId; if (Py_IsInitialized()) { JPy_BEGIN_GIL_STATE_VOID() refCount = Py_REFCNT(pyObject); if (refCount <= 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_decRef: error: refCount <= 0: pyObject=%p, refCount=%d\n", pyObject, refCount); } else { JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "Java_org_jpy_PyLib_decRef: pyObject=%p, refCount=%d, type='%s'\n", pyObject, refCount, Py_TYPE(pyObject)->tp_name); JPy_DECREF(pyObject); } JPy_END_GIL_STATE } else { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_decRef: error: no interpreter: pyObject=%p\n", pyObject); } } JNIEXPORT void JNICALL Java_org_jpy_PyLib_decRefs (JNIEnv* jenv, jclass jLibClass, jlongArray objIds, jsize len) { PyObject* pyObject; Py_ssize_t refCount; jsize i; jboolean isCopy; jlong* buf; if (Py_IsInitialized()) { JPy_BEGIN_GIL_STATE_VOID() // Note: it *may* be desirable to instead force a local copy using GetLongArrayRegion, TBD. // It is *not* a good idea to use a critical array here, as JPy_DECREF may trigger the python // object destructor, which can run arbitrary code. buf = (*jenv)->GetLongArrayElements(jenv, objIds, &isCopy); for (i = 0; i < len; i++) { pyObject = (PyObject*) buf[i]; refCount = Py_REFCNT(pyObject); if (refCount <= 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_decRefs: error: refCount <= 0: pyObject=%p, refCount=%d\n", pyObject, refCount); } else { JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "Java_org_jpy_PyLib_decRefs: pyObject=%p, refCount=%d, type='%s'\n", pyObject, refCount, Py_TYPE(pyObject)->tp_name); JPy_DECREF(pyObject); } } (*jenv)->ReleaseLongArrayElements(jenv, objIds, buf, JNI_ABORT); JPy_END_GIL_STATE } else { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_decRefs: error: no interpreter\n"); } } /* * Class: org_jpy_python_PyLib * Method: getIntValue * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_getIntValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jint value; JPy_BEGIN_GIL_STATE(-1) pyObject = (PyObject*) objId; value = (jint) JPy_AS_CLONG(pyObject); // Note: we are not handling the overflow case, but there might be a need to have getIntValueSafe() in the future if (value == -1 && PyErr_Occurred()) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getIntValue: error: failed to convert Python object to Java int\n"); PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return value; } /* * Class: org_jpy_python_PyLib * Method: getLongValue * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getLongValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jlong value; JPy_BEGIN_GIL_STATE(-1) pyObject = (PyObject*) objId; value = JPy_AS_CLONG(pyObject); if (value == -1 && PyErr_Occurred()) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getLongValue: error: failed to convert Python object to Java long\n"); PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return value; } /** * Used to convert a python object into it's corresponding boolean. If the PyObject is not a boolean; * then return false. */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_getBooleanValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jboolean value; JPy_BEGIN_GIL_STATE(JNI_FALSE) pyObject = (PyObject*) objId; if (PyBool_Check(pyObject)) { value = (pyObject == Py_True) ? JNI_TRUE : JNI_FALSE; } else { value = JNI_FALSE; } JPy_END_GIL_STATE return value; } /* * Class: org_jpy_python_PyLib * Method: getDoubleValue * Signature: (J)D */ JNIEXPORT jdouble JNICALL Java_org_jpy_PyLib_getDoubleValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jdouble value; JPy_BEGIN_GIL_STATE(-1.0) pyObject = (PyObject*) objId; value = (jdouble) PyFloat_AsDouble(pyObject); if (value == -1.0 && PyErr_Occurred()) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getDoubleValue: error: failed to convert Python object to Java double\n"); PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return value; } /* * Class: org_jpy_python_PyLib * Method: getStringValue * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_getStringValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jstring jString; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject*) objId; if (JPy_AsJString(jenv, pyObject, &jString) < 0) { jString = NULL; JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getStringValue: error: failed to convert Python object to Java String\n"); PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return jString; } /* * Class: org_jpy_python_PyLib * Method: getObjectValue * Signature: (J)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getObjectValue (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jobject jObject; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject*) objId; if (JObj_Check(pyObject)) { jObject = ((JPy_JObj*) pyObject)->objectRef; } else { if (JPy_AsJObject(jenv, pyObject, &jObject, JNI_FALSE) < 0) { jObject = NULL; JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getObjectValue: error: failed to convert Python object to Java Object\n"); PyLib_HandlePythonException(jenv); } } JPy_END_GIL_STATE return jObject; } /** * Returns true if this object can be converted from a Python object into a Java object (or primitive); * if this returns false, when you fetch an Object from Python it will be a PyObject wrapper. * * objId is a pointer to a PyObject. */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_isConvertible (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) pyObject = (PyObject*) objId; result = pyObject == Py_None || JObj_Check(pyObject) || PyBool_Check(pyObject) || JPy_IS_CLONG(pyObject) || PyFloat_Check(pyObject) || JPy_IS_STR(pyObject) ? JNI_TRUE : JNI_FALSE; JPy_END_GIL_STATE return result; } /** * Gets the Python type object of specified objId. * * objId is a pointer to a PyObject. */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getType (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; JPy_BEGIN_GIL_STATE(0) pyObject = ((PyObject*) objId)->ob_type; JPy_INCREF(pyObject); JPy_END_GIL_STATE return (jlong)pyObject; } /** * Evaluate PyDict_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass the PyLib class object * @param objId a pointer to a python object * @return true if objId is a python dictionary */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyDictCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyDict_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyList_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass the PyLib class object * @param objId a pointer to a python object * @return true if objId is a python list */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyListCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyList_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyBool_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python boolean */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyBoolCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyBool_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Check equality against Py_None and a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a None */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyNoneCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (Py_None == (((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyInt_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python int */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyIntCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { int check; JPy_BEGIN_GIL_STATE(JNI_FALSE) check = JPy_IS_CLONG(((PyObject*) objId)); JPy_END_GIL_STATE return check ? (jboolean)JNI_TRUE : (jboolean)JNI_FALSE; } /** * Evaluate PyLong_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python long */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyLongCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyLong_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyFloat_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python float */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyFloatCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyFloat_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyString_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python String */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyStringCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (JPy_IS_STR(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyCallable_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python callable */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyCallableCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyCallable_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyFunction_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python function */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyFunctionCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyFunction_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyModule_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python module */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyModuleCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyModule_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Evaluate PyTuple_Check against a Python object. * * @param jenv JNI environment. * @param jLibClass * @param objId a pointer to a python object * @return true if objId is a python tuple */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyTupleCheck (JNIEnv* jenv, jclass jLibClass, jlong objId) { jboolean result; JPy_BEGIN_GIL_STATE(JNI_FALSE) if (PyTuple_Check(((PyObject*) objId))) { result = JNI_TRUE; } else { result = JNI_FALSE; } JPy_END_GIL_STATE return result; } /** * Runs the str function on a Python object. * * @param jenv JNI environment. * @param jLibClass the class object for PyLib * @param objId a pointer to a python object * @return the Python toString of this object */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_str (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject *pyObject; jobject jObject; PyObject *pyStr; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject *) objId; pyStr = PyObject_Str(pyObject); if (pyStr != NULL) { jObject = (*jenv)->NewStringUTF(jenv, JPy_AS_UTF8(pyStr)); JPy_DECREF(pyStr); } else { jObject = NULL; PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return jObject; } /** * Runs the repr function on a Python object. * * @param jenv JNI environment. * @param jLibClass the class object for PyLib * @param objId a pointer to a python object * @return the Python representation string of this object */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_repr (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject *pyObject; jobject jObject; PyObject *pyStr; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject *) objId; pyStr = PyObject_Repr(pyObject); if (pyStr) { jObject = (*jenv)->NewStringUTF(jenv, JPy_AS_UTF8(pyStr)); JPy_DECREF(pyStr); } else { jObject = NULL; } JPy_END_GIL_STATE return jObject; } /* * Compute and return the hash value of an object o. On failure, return -1. * This is the equivalent of the Python expression hash(o). * * @param jenv JNI environment. * @param jLibClass the class object for PyLib * @param objId a pointer to a python object * @return the hash code, -1 on failure */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_hash (JNIEnv* jenv, jclass jLibClass, jlong objId) { PyObject* pyObject; long hash; JPy_BEGIN_GIL_STATE(-1) pyObject = (PyObject *) objId; if ((hash = PyObject_Hash(pyObject)) == -1) { PyLib_HandlePythonException(jenv); } JPy_END_GIL_STATE return (jlong)hash; } JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_eq (JNIEnv* jenv, jclass jLibClass, jlong objId1, jobject other) { PyObject* pyObject1; PyObject* pyObject2; PyObject* eq; jboolean result = JNI_FALSE; JPy_BEGIN_GIL_STATE(JNI_FALSE) pyObject1 = (PyObject *) objId1; pyObject2 = PyLib_ConvertJavaToPythonObject(jenv, other); // Note: there are subtle differences between PyObject_RichCompare and PyObject_RichCompareBool // that make PyObject_RichCompareBool unacceptable to use here. // // Specifically, PyObject_RichCompareBool says that it will return True for the same object // https://docs.python.org/2/c-api/object.html#c.PyObject_RichCompareBool // We *DON'T* want that, we want to delegate to the implementations __eq__ function instead. // i.e. /* >>> class AlwaysFalse: ... def __eq__(self, other): ... return False ... >>> a == a False */ eq = PyObject_RichCompare(pyObject1, pyObject2, Py_EQ); JPy_DECREF(pyObject2); if (eq == NULL) { PyLib_HandlePythonException(jenv); } else if (PyBool_Check(eq)) { result = (eq == Py_True) ? JNI_TRUE : JNI_FALSE; JPy_DECREF(eq); } else { int val = PyObject_IsTrue(eq); JPy_DECREF(eq); if (val == -1) { PyLib_HandlePythonException(jenv); } else { result = val ? JNI_TRUE : JNI_FALSE; } } JPy_END_GIL_STATE return result; } // TODO: this implementation *should* use JType_CreateJavaArray instead... /* * Class: org_jpy_PyLib * Method: getObjectArrayValue * Signature: (JLjava/lang/Class;)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL Java_org_jpy_PyLib_getObjectArrayValue (JNIEnv* jenv, jclass jLibClass, jlong objId, jclass itemClassRef) { PyObject* pyObject; jobject jObject; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject*) objId; if (pyObject == Py_None) { jObject = NULL; } else if (JObj_Check(pyObject)) { jObject = ((JPy_JObj*) pyObject)->objectRef; } else if (PySequence_Check(pyObject)) { PyObject* pyItem; jobject jItem; jint i, length; length = PySequence_Length(pyObject); jObject = (*jenv)->NewObjectArray(jenv, length, itemClassRef, NULL); for (i = 0; i < length; i++) { pyItem = PySequence_GetItem(pyObject, i); if (pyItem == NULL) { JPy_DELETE_LOCAL_REF(jObject); goto error; } if (JPy_AsJObject(jenv, pyItem, &jItem, JNI_FALSE) < 0) { JPy_DELETE_LOCAL_REF(jObject); JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getObjectArrayValue: error: failed to convert Python item to Java Object\n"); PyLib_HandlePythonException(jenv); goto error; } JPy_XDECREF(pyItem); (*jenv)->SetObjectArrayElement(jenv, jObject, i, jItem); if ((*jenv)->ExceptionCheck(jenv)) { JPy_DELETE_LOCAL_REF(jObject); goto error; } } } else { jObject = NULL; (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, "python object cannot be converted to Object[]"); } error: JPy_END_GIL_STATE return jObject; } /* * Class: org_jpy_python_PyLib * Method: getModule * Signature: (Ljava/lang/String;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_importModule (JNIEnv* jenv, jclass jLibClass, jstring jName) { PyObject* pyName; PyObject* pyModule = NULL; const char* nameChars; JPy_BEGIN_GIL_STATE(0) nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_importModule: name='%s'\n", nameChars); /* Note: pyName is a new reference */ pyName = JPy_FROM_CSTR(nameChars); /* Note: pyModule is a new reference */ pyModule = PyImport_Import(pyName); if (pyModule == NULL) { PyLib_HandlePythonException(jenv); } JPy_XDECREF(pyName); error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } JPy_END_GIL_STATE return (jlong) pyModule; } /* * Class: org_jpy_python_PyLib * Method: getAttributeValue * Signature: (JLjava/lang/String;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getAttributeObject (JNIEnv* jenv, jclass jLibClass, jlong objId, jstring jName) { PyObject* pyObject; PyObject* pyValue; JPy_BEGIN_GIL_STATE(0) pyObject = (PyObject*) objId; pyValue = PyLib_GetAttributeObject(jenv, pyObject, jName); JPy_END_GIL_STATE return (jlong) pyValue; } /* * Class: org_jpy_python_PyLib * Method: getAttributeValue * Signature: (JLjava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getAttributeValue (JNIEnv* jenv, jclass jLibClass, jlong objId, jstring jName, jclass jValueClass) { PyObject* pyObject; PyObject* pyValue; jobject jReturnValue; JPy_BEGIN_GIL_STATE(NULL) pyObject = (PyObject*) objId; pyValue = PyLib_GetAttributeObject(jenv, pyObject, jName); if (pyValue == NULL) { jReturnValue = NULL; goto error; } if (JPy_AsJObjectWithClass(jenv, pyValue, &jReturnValue, jValueClass) < 0) { jReturnValue = NULL; JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_getAttributeValue: error: failed to convert attribute value\n"); PyLib_HandlePythonException(jenv); } error: JPy_XDECREF(pyValue); JPy_END_GIL_STATE return jReturnValue; } /* * Class: org_jpy_python_PyLib * Method: setAttributeValue * Signature: (JLjava/lang/String;J)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_setAttributeValue (JNIEnv* jenv, jclass jLibClass, jlong objId, jstring jName, jobject jValue, jclass jValueClass) { PyObject* pyObject; const char* nameChars; PyObject* pyValue; JPy_JType* valueType; JPy_BEGIN_GIL_STATE_VOID() pyObject = (PyObject*) objId; nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_setAttributeValue: objId=%p, name='%s', jValue=%p, jValueClass=%p\n", pyObject, nameChars, jValue, jValueClass); if (jValueClass != NULL) { valueType = JType_GetType(jenv, jValueClass, JNI_FALSE); } else { valueType = NULL; } if (valueType != NULL) { pyValue = JPy_FromJObjectWithType(jenv, jValue, valueType); } else { pyValue = JPy_FromJObject(jenv, jValue); } if (pyValue == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_setAttributeValue: error: attribute '%s': Java object not convertible\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } // PyObject_SetAttrString will increase ref count if (PyObject_SetAttrString(pyObject, nameChars, pyValue) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_setAttributeValue: error: PyObject_SetAttrString failed on attribute '%s'\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } JPy_XDECREF(pyValue); JPy_END_GIL_STATE } /** * Deletes an attribute from an object. * * @param jenv JNI environment. * @param jLibClass the PyLib class object * @param objId a pointer to a python object * @param jName the java string naming the attribute to delete */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_delAttribute (JNIEnv* jenv, jclass jLibClass, jlong objId, jstring jName) { PyObject* pyObject; const char* nameChars; JPy_BEGIN_GIL_STATE_VOID() pyObject = (PyObject*) objId; nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_delAttribute: objId=%p, name='%s'\n", pyObject, nameChars); if (PyObject_DelAttrString(pyObject, nameChars) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_delAttribute: error: PyObject_DelAttrString failed on attribute '%s'\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } JPy_END_GIL_STATE } /* * Checks for an attribute's existence. * * @param jenv JNI environment. * @param jLibClass the PyLib class object * @param objId a pointer to a python object * @param jName the java string naming the attribute to delete * * @return true if the attribute exists on this object */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_hasAttribute (JNIEnv* jenv, jclass jLibClass, jlong objId, jstring jName) { PyObject* pyObject; const char* nameChars; jboolean result = JNI_FALSE; JPy_BEGIN_GIL_STATE(JNI_FALSE) pyObject = (PyObject*) objId; nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "Java_org_jpy_PyLib_delAttribute: objId=%p, name='%s'\n", pyObject, nameChars); result = PyObject_HasAttrString(pyObject, nameChars) ? JNI_TRUE : JNI_FALSE; error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } JPy_END_GIL_STATE return result; } /* * Class: org_jpy_PyLib * Method: hasGil * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_hasGil (JNIEnv* jenv, jclass jLibClass) { jboolean result; // Note: we *don't* need the GIL to inquire if we have the GIL, that would be silly. result = PyGILState_Check() ? JNI_TRUE : JNI_FALSE; return result; } /* * Class: org_jpy_PyLib * Method: ensureGil * Signature: (Ljava/util/function/Supplier;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_ensureGil (JNIEnv* jenv, jclass jLibClass, jobject supplier) { jobject result; JPy_BEGIN_GIL_STATE(NULL) result = (*jenv)->CallObjectMethod(jenv, supplier, JPy_Supplier_get_MID); JPy_END_GIL_STATE return result; } /* * Class: org_jpy_python_PyLib * Method: call * Signature: (JZLjava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object; */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_callAndReturnObject (JNIEnv *jenv, jclass jLibClass, jlong objId, jboolean isMethodCall, jstring jName, jint argCount, jobjectArray jArgs, jobjectArray jParamClasses) { PyObject* pyObject; PyObject* pyReturnValue; JPy_BEGIN_GIL_STATE(0) pyObject = (PyObject*) objId; pyReturnValue = PyLib_CallAndReturnObject(jenv, pyObject, isMethodCall, jName, argCount, jArgs, jParamClasses); JPy_END_GIL_STATE return (jlong) pyReturnValue; } /* * Class: org_jpy_python_PyLib * Method: callAndReturnValue * Signature: (JZLjava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_callAndReturnValue (JNIEnv *jenv, jclass jLibClass, jlong objId, jboolean isMethodCall, jstring jName, jint argCount, jobjectArray jArgs, jobjectArray jParamClasses, jclass jReturnClass) { PyObject* pyObject = (PyObject*) objId; PyObject* pyReturnValue; jobject jReturnValue = NULL; JPy_BEGIN_GIL_STATE(NULL) pyReturnValue = PyLib_CallAndReturnObject(jenv, pyObject, isMethodCall, jName, argCount, jArgs, jParamClasses); if (pyReturnValue == NULL) { goto error; } if (JPy_AsJObjectWithClass(jenv, pyReturnValue, &jReturnValue, jReturnClass) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "Java_org_jpy_PyLib_callAndReturnValue: error: failed to convert attribute value\n"); PyLib_HandlePythonException(jenv); jReturnValue = NULL; goto error; } error: JPy_XDECREF(pyReturnValue); JPy_END_GIL_STATE return jReturnValue; } /* * Class: org_jpy_python_PyLib * Method: getDiagFlags * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_00024Diag_getFlags (JNIEnv *jenv, jclass classRef) { return JPy_DiagFlags; } /* * Class: org_jpy_python_PyLib * Method: setDiagFlags * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_00024Diag_setFlags (JNIEnv *jenv, jclass classRef, jint flags) { JPy_DiagFlags = flags; } //////////////////////////////////////////////////////////////////////////////////// // Helpers that also throw Java exceptions PyObject* PyLib_GetAttributeObject(JNIEnv* jenv, PyObject* pyObject, jstring jName) { PyObject* pyValue = NULL; const char* nameChars; nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "PyLib_GetAttributeObject: objId=%p, name='%s'\n", pyObject, nameChars); /* Note: pyValue is a new reference */ pyValue = PyObject_GetAttrString(pyObject, nameChars); if (pyValue == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_GetAttributeObject: error: attribute not found '%s'\n", nameChars); PyLib_HandlePythonException(jenv); } error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } return pyValue; } PyObject* PyLib_FromJObjectForTuple(JNIEnv *jenv, jobject jArg, jclass jParamClass, char* nameChars, jint index) { JPy_JType* implicitParamType; JPy_JType* explicitParamType; PyObject* pyReturnValue; // if jArg is NULL, we don't care about the implicit / explicit types, we'll return None if (jArg == NULL) { return JPy_FROM_JNULL(); } pyReturnValue = NULL; explicitParamType = NULL; implicitParamType = JType_GetTypeForObject(jenv, jArg, JNI_FALSE); if (implicitParamType == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_FromJObjectForTuple: error: callable '%s': argument %d: failed to retrieve implicit-type\n", nameChars, index); PyLib_HandlePythonException(jenv); goto error; } if (jParamClass != NULL) { explicitParamType = JType_GetType(jenv, jParamClass, JNI_FALSE); if (explicitParamType == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_FromJObjectForTuple: error: callable '%s': argument %d: failed to retrieve explicit-type\n", nameChars, index); PyLib_HandlePythonException(jenv); goto error; } pyReturnValue = JPy_FromJObjectWithType(jenv, jArg, explicitParamType); } else { pyReturnValue = JPy_FromJObjectWithType(jenv, jArg, implicitParamType); } error: JPy_XDECREF(explicitParamType); JPy_XDECREF(implicitParamType); return pyReturnValue; } PyObject* PyLib_CallAndReturnObject(JNIEnv *jenv, PyObject* pyObject, jboolean isMethodCall, jstring jName, jint argCount, jobjectArray jArgs, jobjectArray jParamClasses) { PyObject* pyCallable = NULL; PyObject* pyArgs = NULL; PyObject* pyArg; PyObject* pyReturnValue = NULL; const char* nameChars; jint i; jobject jArg; jclass jParamClass = NULL; JPy_JType* explicitParamType = NULL; JPy_JType* jArgParamType; nameChars = (*jenv)->GetStringUTFChars(jenv, jName, NULL); if (nameChars == NULL) { PyLib_ThrowOOM(jenv); goto error; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "PyLib_CallAndReturnObject: objId=%p, isMethodCall=%d, name='%s', argCount=%d\n", pyObject, isMethodCall, nameChars, argCount); // Note: pyCallable is a new reference pyCallable = PyObject_GetAttrString(pyObject, nameChars); if (pyCallable == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_CallAndReturnObject: error: function or method not found: '%s'\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } if (!PyCallable_Check(pyCallable)) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_CallAndReturnObject: error: object is not callable: '%s'\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } pyArgs = PyTuple_New(argCount); for (i = 0; i < argCount; i++) { // get the implicit java type (the real type of the object) jArg = (*jenv)->GetObjectArrayElement(jenv, jArgs, i); if (jParamClasses != NULL) { jParamClass = (*jenv)->GetObjectArrayElement(jenv, jParamClasses, i); } pyArg = PyLib_FromJObjectForTuple(jenv, jArg, jParamClass, nameChars, i); if (jParamClass != NULL) { JPy_DELETE_LOCAL_REF(jParamClass); } JPy_DELETE_LOCAL_REF(jArg); if (pyArg == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_CallAndReturnObject: error: callable '%s': argument %d: failed to convert Java into Python object\n", nameChars, i); PyLib_HandlePythonException(jenv); goto error; } // pyArg reference stolen here PyTuple_SetItem(pyArgs, i, pyArg); } // Check why: for some reason, we don't need the following code to invoke object methods. /* if (isMethodCall) { PyObject* pyMethod; pyMethod = PyMethod_New(pyCallable, pyObject); if (pyMethod == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "PyLib_CallAndReturnObject: error: callable '%s': no memory\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } JPy_DECREF(pyCallable); pyCallable = pyMethod; } */ pyReturnValue = PyObject_CallObject(pyCallable, argCount > 0 ? pyArgs : NULL); if (pyReturnValue == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "PyLib_CallAndReturnObject: error: callable '%s': call returned NULL\n", nameChars); PyLib_HandlePythonException(jenv); goto error; } error: if (nameChars != NULL) { (*jenv)->ReleaseStringUTFChars(jenv, jName, nameChars); } JPy_XDECREF(pyCallable); JPy_XDECREF(pyArgs); return pyReturnValue; } #define JPY_ERR_BASE_MSG "Error in Python interpreter" #define JPY_NO_INFO_MSG JPY_ERR_BASE_MSG ", no information available" /** * Translates the current Python exception — including its full __cause__ / __context__ * chain — into a Java exception and throws it. * * Formatting is delegated entirely to Python's own traceback.format_exception(), which * faithfully reproduces the output that the Python interpreter would print itself: * chained exceptions, ExceptionGroups (3.11+), and any future exception-display changes * are all handled automatically without any C-side formatting logic. * * On Python >= 3.12 the deprecated PyErr_Fetch / PyErr_NormalizeException trio is * replaced with PyErr_GetRaisedException(), which returns a single already-normalized * exception object with its __traceback__ already attached. * * Note on stack-frame limits and recursive-line cutoffs * ------------------------------------------------------ * The previous hand-rolled C formatter imposed a hard frame limit (1024) and a * recursive-line cutoff (3) because it managed a growable C string buffer manually * and needed to bound memory growth. Neither constant is carried forward here: * * - traceback.format_exception() manages all string memory through the Python * allocator, so there is no C buffer to protect. * - Python's interpreter already enforces sys.getrecursionlimit() (default 1000), * which practically caps traceback depth without any extra limit on our side. * - The traceback module natively compresses repeated frames into * "[Previous line repeated N more times]", making deep recursive tracebacks * compact without truncating useful information. * - Imposing our own limit would silently drop the outermost frames, which are * often the most useful ones for diagnosing the root cause. */ void PyLib_HandlePythonException(JNIEnv* jenv) { if (PyErr_Occurred() == NULL) { return; } /* pyValue is an alias used only for Java exception-class selection below. */ PyObject* pyValue = NULL; jclass jExceptionClass; int threw = 0; #if PY_VERSION_HEX >= 0x030C0000 /* >= 3.12 */ /* Returns a single normalized exception object with __traceback__ already attached, * and clears the error indicator. Replaces the deprecated PyErr_Fetch / * PyErr_NormalizeException / PyErr_Restore trio. */ PyObject* pyExc = PyErr_GetRaisedException(); pyValue = pyExc; #else PyObject* pyType = NULL; PyObject* pyTraceback = NULL; PyErr_Fetch(&pyType, &pyValue, &pyTraceback); PyErr_NormalizeException(&pyType, &pyValue, &pyTraceback); #endif /* Select the Java exception class to throw. */ if (pyValue != NULL && PyObject_TypeCheck(pyValue, (PyTypeObject*) PyExc_KeyError)) { jExceptionClass = JPy_KeyError_JClass; } else if (pyValue != NULL && PyObject_TypeCheck(pyValue, (PyTypeObject*) PyExc_StopIteration)) { jExceptionClass = JPy_StopIteration_JClass; } else { jExceptionClass = JPy_RuntimeException_JClass; } /* Delegate to Python's own traceback module to format the full exception chain. * This handles __cause__, __context__, __suppress_context__, ExceptionGroups, etc. * — everything the previous hand-rolled C formatter silently discarded. */ PyObject* tbModule = PyImport_ImportModule("traceback"); if (tbModule != NULL) { PyObject* formatted = NULL; #if PY_VERSION_HEX >= 0x030C0000 /* single-argument form available since 3.10, safe here */ formatted = PyObject_CallMethod(tbModule, "format_exception", "O", pyExc); #else /* Three-argument form works on all supported Python versions. */ formatted = PyObject_CallMethod(tbModule, "format_exception", "OOO", pyType, pyValue, pyTraceback != NULL ? pyTraceback : Py_None); #endif JPy_DECREF(tbModule); if (formatted != NULL) { /* format_exception returns a list of strings; join, prefix, and encode them. * Each step is conditional on the previous succeeding; all temporaries are * released together in the flat cleanup block below. */ PyObject* sep = PyUnicode_FromString(""); PyObject* joined = sep != NULL ? PyUnicode_Join(sep, formatted) : NULL; PyObject* prefixed = joined != NULL ? PyUnicode_FromFormat(JPY_ERR_BASE_MSG ":\n%U", joined) : NULL; PyObject* joinedUtf8 = prefixed != NULL ? PyUnicode_AsEncodedString(prefixed, "utf-8", "replace") : NULL; if (joinedUtf8 != NULL) { (*jenv)->ThrowNew(jenv, jExceptionClass, PyBytes_AsString(joinedUtf8)); threw = 1; } JPy_XDECREF(joinedUtf8); JPy_XDECREF(prefixed); JPy_XDECREF(joined); JPy_XDECREF(sep); JPy_DECREF(formatted); } } if (!threw) { /* Fallback: traceback module unavailable or an intermediate step failed. */ (*jenv)->ThrowNew(jenv, jExceptionClass, JPY_NO_INFO_MSG); } #if PY_VERSION_HEX >= 0x030C0000 JPy_XDECREF(pyExc); #else JPy_XDECREF(pyType); JPy_XDECREF(pyValue); JPy_XDECREF(pyTraceback); #endif PyErr_Clear(); } /** * Throw an OutOfMemoryError. * @param jenv the jni environment */ void PyLib_ThrowOOM(JNIEnv* jenv) { (*jenv)->ThrowNew(jenv, JPy_OutOfMemoryError_JClass, "Out of memory"); } /** * Throw a FileNotFoundException. * @param jenv the jni environment */ void PyLib_ThrowFNFE(JNIEnv* jenv, const char *file) { (*jenv)->ThrowNew(jenv, JPy_FileNotFoundException_JClass, file); } /** * Throw an UnsupportedOperationException. * @param jenv the jni environment * @param message the exception message */ void PyLib_ThrowUOE(JNIEnv* jenv, const char *message) { (*jenv)->ThrowNew(jenv, JPy_UnsupportedOperationException_JClass, message); } /** * Throw an UnsupportedOperationException. * @param jenv the jni environment * @param message the exception message */ void PyLib_ThrowRTE(JNIEnv* jenv, const char *message) { (*jenv)->ThrowNew(jenv, JPy_RuntimeException_JClass, message); } //////////////////////////////////////////////////////////////////////////////////////////////// // Redirect stdout static PyObject* JPrint_write(PyObject* self, PyObject* args) { if (stdout != NULL) { const char* text; if (!PyArg_ParseTuple(args, "s", &text)) { return NULL; } fprintf(stdout, "%s", text); } Py_RETURN_NONE; } static PyObject* JPrint_flush(PyObject* self, PyObject* args) { if (stdout != NULL) { fflush(stdout); } Py_RETURN_NONE; } static PyMethodDef JPrint_Functions[] = { {"write", (PyCFunction) JPrint_write, METH_VARARGS, "Internal function. Used to print to stdout in embedded mode."}, {"flush", (PyCFunction) JPrint_flush, METH_VARARGS, "Internal function. Used to flush to stdout in embedded mode."}, {NULL, NULL, 0, NULL} /*Sentinel*/ }; #define JPY_STDOUT_MODULE_NAME "jpy_stdout" #define JPY_STDOUT_MODULE_DOC "Redirect 'stdout' to the console in embedded mode" static struct PyModuleDef JPrint_ModuleDef = { PyModuleDef_HEAD_INIT, JPY_STDOUT_MODULE_NAME, /* Name of the Python JPy_Module */ JPY_STDOUT_MODULE_DOC, /* Module documentation */ -1, /* Size of per-interpreter state of the JPy_Module, or -1 if the JPy_Module keeps state in global variables. */ JPrint_Functions, /* Structure containing global jpy-functions */ NULL, // m_reload NULL, // m_traverse NULL, // m_clear NULL // m_free }; void PyLib_RedirectStdOut(void) { PyObject* module; module = PyModule_Create(&JPrint_ModuleDef); PySys_SetObject("stdout", module); PySys_SetObject("stderr", module); } jpy-2.0.0/src/main/c/jni/org_jpy_DL.c0000664000175000017500000000563015202503234017410 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "org_jpy_DL.h" #if !defined(_WIN32) && !defined(__CYGWIN__) #include /* * Class: org_jpy_DL * Method: dlopen * Signature: (Ljava/lang/String;I)J */ JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen (JNIEnv *jenv, jclass dlClass, jstring jPath, jint mode) { const char* path; int flags; if (jPath == NULL) { return 0; } path = (*jenv)->GetStringUTFChars(jenv, jPath, NULL); if (path == NULL) { return 0; } flags = 0; if ((mode & org_jpy_DL_RTLD_LAZY) != 0) { flags |= RTLD_LAZY; } if ((mode & org_jpy_DL_RTLD_NOW) != 0) { flags |= RTLD_NOW; } if ((mode & org_jpy_DL_RTLD_LOCAL) != 0) { flags |= RTLD_LOCAL; } if ((mode & org_jpy_DL_RTLD_GLOBAL) != 0) { flags |= RTLD_GLOBAL; } return (jlong) dlopen(path, flags); } /* * Class: org_jpy_DL * Method: dlclose * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose (JNIEnv *jenv, jclass dlClass, jlong handle) { return dlclose((void*) handle); } /* * Class: org_jpy_DL * Method: dlerror * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror (JNIEnv *jenv, jclass dlClass) { const char* message; message = dlerror(); if (message != NULL) { return (*jenv)->NewStringUTF(jenv, message); } else { return NULL; } } #else /* !defined(_WIN32) && !defined(__CYGWIN__) */ // Dummy DLL entry point for Python 2.7 (Windows requires it) __declspec(dllexport) void initjdl(void) { } // Dummy DLL entry point for Python 3.3+ (Windows requires it) __declspec(dllexport) void* PyInit_jdl(void) { return NULL; } /* * Class: org_jpy_DL * Method: dlopen * Signature: (Ljava/lang/String;I)J */ JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen (JNIEnv *jenv, jclass dlClass, jstring jPath, jint mode) { return 0; } /* * Class: org_jpy_DL * Method: dlclose * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose (JNIEnv *jenv, jclass dlClass, jlong handle) { return 0; } /* * Class: org_jpy_DL * Method: dlerror * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror (JNIEnv *jenv, jclass dlClass) { return NULL; } #endif /* !defined(_WIN32) && !defined(__CYGWIN__) */ jpy-2.0.0/src/main/c/jni/org_jpy_DL.h0000664000175000017500000000171115202503234017411 0ustar alastairalastair/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_jpy_DL */ #ifndef _Included_org_jpy_DL #define _Included_org_jpy_DL #ifdef __cplusplus extern "C" { #endif #undef org_jpy_DL_RTLD_LAZY #define org_jpy_DL_RTLD_LAZY 1L #undef org_jpy_DL_RTLD_NOW #define org_jpy_DL_RTLD_NOW 2L #undef org_jpy_DL_RTLD_LOCAL #define org_jpy_DL_RTLD_LOCAL 4L #undef org_jpy_DL_RTLD_GLOBAL #define org_jpy_DL_RTLD_GLOBAL 8L /* * Class: org_jpy_DL * Method: dlopen * Signature: (Ljava/lang/String;I)J */ JNIEXPORT jlong JNICALL Java_org_jpy_DL_dlopen (JNIEnv *, jclass, jstring, jint); /* * Class: org_jpy_DL * Method: dlclose * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_jpy_DL_dlclose (JNIEnv *, jclass, jlong); /* * Class: org_jpy_DL * Method: dlerror * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_DL_dlerror (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif jpy-2.0.0/src/main/c/jni/org_jpy_PyLib_Diag.h0000664000175000017500000000220115202503234021050 0ustar alastairalastair/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_jpy_PyLib_Diag */ #ifndef _Included_org_jpy_PyLib_Diag #define _Included_org_jpy_PyLib_Diag #ifdef __cplusplus extern "C" { #endif #undef org_jpy_PyLib_Diag_F_OFF #define org_jpy_PyLib_Diag_F_OFF 0L #undef org_jpy_PyLib_Diag_F_TYPE #define org_jpy_PyLib_Diag_F_TYPE 1L #undef org_jpy_PyLib_Diag_F_METH #define org_jpy_PyLib_Diag_F_METH 2L #undef org_jpy_PyLib_Diag_F_EXEC #define org_jpy_PyLib_Diag_F_EXEC 4L #undef org_jpy_PyLib_Diag_F_MEM #define org_jpy_PyLib_Diag_F_MEM 8L #undef org_jpy_PyLib_Diag_F_JVM #define org_jpy_PyLib_Diag_F_JVM 16L #undef org_jpy_PyLib_Diag_F_ERR #define org_jpy_PyLib_Diag_F_ERR 32L #undef org_jpy_PyLib_Diag_F_ALL #define org_jpy_PyLib_Diag_F_ALL 255L /* * Class: org_jpy_PyLib_Diag * Method: getFlags * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_00024Diag_getFlags (JNIEnv *, jclass); /* * Class: org_jpy_PyLib_Diag * Method: setFlags * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_00024Diag_setFlags (JNIEnv *, jclass, jint); #ifdef __cplusplus } #endif #endif jpy-2.0.0/src/main/c/jni/org_jpy_PyLib_CallableKind.h0000664000175000017500000000043615202503234022521 0ustar alastairalastair/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_jpy_PyLib_CallableKind */ #ifndef _Included_org_jpy_PyLib_CallableKind #define _Included_org_jpy_PyLib_CallableKind #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif jpy-2.0.0/src/main/c/jni/org_jpy_PyLib.h0000664000175000017500000002452515202503234020141 0ustar alastairalastair/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_jpy_PyLib */ #ifndef _Included_org_jpy_PyLib #define _Included_org_jpy_PyLib #ifdef __cplusplus extern "C" { #endif /* * Class: org_jpy_PyLib * Method: isPythonRunning * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_isPythonRunning (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: startPython0 * Signature: ([Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_startPython0 (JNIEnv *, jclass, jobjectArray); /* * Class: org_jpy_PyLib * Method: setPythonHome * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_setPythonHome (JNIEnv *, jclass, jstring); /* * Class: org_jpy_PyLib * Method: setProgramName * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_setProgramName (JNIEnv *, jclass, jstring); /* * Class: org_jpy_PyLib * Method: getPythonVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_getPythonVersion (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: stopPython0 * Signature: ()V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_stopPython0 (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: execScript * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_execScript (JNIEnv *, jclass, jstring); /* * Class: org_jpy_PyLib * Method: executeCode * Signature: (Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_executeCode (JNIEnv *, jclass, jstring, jint, jobject, jobject); /* * Class: org_jpy_PyLib * Method: executeScript * Signature: (Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_executeScript (JNIEnv *, jclass, jstring, jint, jobject, jobject); /* * Class: org_jpy_PyLib * Method: getMainGlobals * Signature: ()Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getMainGlobals (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: getCurrentGlobals * Signature: ()Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getCurrentGlobals (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: getCurrentLocals * Signature: ()Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getCurrentLocals (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: copyDict * Signature: (J)Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_copyDict (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: incRef * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_incRef (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: decRef * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_decRef (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: decRefs * Signature: ([JI)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_decRefs (JNIEnv *, jclass, jlongArray, jint); /* * Class: org_jpy_PyLib * Method: getIntValue * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_jpy_PyLib_getIntValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getLongValue * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getLongValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getBooleanValue * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_getBooleanValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getDoubleValue * Signature: (J)D */ JNIEXPORT jdouble JNICALL Java_org_jpy_PyLib_getDoubleValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getStringValue * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_getStringValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getObjectValue * Signature: (J)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getObjectValue (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: isConvertible * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_isConvertible (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyNoneCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyNoneCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyDictCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyDictCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyListCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyListCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyBoolCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyBoolCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyIntCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyIntCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyLongCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyLongCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyFloatCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyFloatCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyStringCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyStringCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyCallableCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyCallableCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyFunctionCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyFunctionCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyModuleCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyModuleCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyTupleCheck * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyTupleCheck (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: getType * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getType (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: str * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_str (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: repr * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_jpy_PyLib_repr (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: hash * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_hash (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: eq * Signature: (JLjava/lang/Object;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_eq (JNIEnv *, jclass, jlong, jobject); /* * Class: org_jpy_PyLib * Method: newDict * Signature: ()Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_newDict (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: pyDictKeys * Signature: (J)Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_pyDictKeys (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyDictValues * Signature: (J)Lorg/jpy/PyObject; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_pyDictValues (JNIEnv *, jclass, jlong); /* * Class: org_jpy_PyLib * Method: pyDictContains * Signature: (JLjava/lang/Object;Ljava/lang/Class;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_pyDictContains (JNIEnv *, jclass, jlong, jobject, jclass); /* * Class: org_jpy_PyLib * Method: getObjectArrayValue * Signature: (JLjava/lang/Class;)[Ljava/lang/Object; */ JNIEXPORT jobjectArray JNICALL Java_org_jpy_PyLib_getObjectArrayValue (JNIEnv *, jclass, jlong, jclass); /* * Class: org_jpy_PyLib * Method: importModule * Signature: (Ljava/lang/String;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_importModule (JNIEnv *, jclass, jstring); /* * Class: org_jpy_PyLib * Method: getAttributeObject * Signature: (JLjava/lang/String;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_getAttributeObject (JNIEnv *, jclass, jlong, jstring); /* * Class: org_jpy_PyLib * Method: getAttributeValue * Signature: (JLjava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_getAttributeValue (JNIEnv *, jclass, jlong, jstring, jclass); /* * Class: org_jpy_PyLib * Method: setAttributeValue * Signature: (JLjava/lang/String;Ljava/lang/Object;Ljava/lang/Class;)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_setAttributeValue (JNIEnv *, jclass, jlong, jstring, jobject, jclass); /* * Class: org_jpy_PyLib * Method: delAttribute * Signature: (JLjava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_jpy_PyLib_delAttribute (JNIEnv *, jclass, jlong, jstring); /* * Class: org_jpy_PyLib * Method: hasAttribute * Signature: (JLjava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_hasAttribute (JNIEnv *, jclass, jlong, jstring); /* * Class: org_jpy_PyLib * Method: hasGil * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_jpy_PyLib_hasGil (JNIEnv *, jclass); /* * Class: org_jpy_PyLib * Method: ensureGil * Signature: (Ljava/util/function/Supplier;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_ensureGil (JNIEnv *, jclass, jobject); /* * Class: org_jpy_PyLib * Method: callAndReturnObject * Signature: (JZLjava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Class;)J */ JNIEXPORT jlong JNICALL Java_org_jpy_PyLib_callAndReturnObject (JNIEnv *, jclass, jlong, jboolean, jstring, jint, jobjectArray, jobjectArray); /* * Class: org_jpy_PyLib * Method: callAndReturnValue * Signature: (JZLjava/lang/String;I[Ljava/lang/Object;[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_jpy_PyLib_callAndReturnValue (JNIEnv *, jclass, jlong, jboolean, jstring, jint, jobjectArray, jobjectArray, jclass); #ifdef __cplusplus } #endif #endif jpy-2.0.0/src/main/c/jpy_verboseexcept.h0000664000175000017500000000171615202503234020346 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #ifndef JPY_VERBOSEEXCEPT_H #define JPY_VERBOSEEXCEPT_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" extern PyTypeObject VerboseExceptions_Type; extern int JPy_VerboseExceptions; PyObject* VerboseExceptions_New(void); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_DIAG_H */jpy-2.0.0/src/main/c/jpy_jtype.c0000664000175000017500000031764715202503234016633 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jtype.h" #include "jpy_jfield.h" #include "jpy_jmethod.h" #include "jpy_jobj.h" #include "jpy_jbyte_buffer.h" #include "jpy_conv.h" #include "jpy_compat.h" #ifdef Py_GIL_DISABLED // Reentrant lock for the recursive JType_GetType() and JType_ResolveType() in free-threaded environments // Note that in order to avoid a fatal circular reference issues, JType_InitSuperType() no longer tries to resolve // the super types. This means it is impossible for a thread to hold a get_type lock while trying to acquire the // resolve_type lock but it can still hold a resolve_type lock while trying to acquire the get_type lock. This allows // maximum concurrency but also avoids deadlocks at the same time. typedef struct { PyMutex lock; PyThreadState* owner; int recursion_level; } ReentrantLock; static void acquire_lock(ReentrantLock* rlock) { PyThreadState* current_thread = PyThreadState_Get(); if (rlock->owner == current_thread) { rlock->recursion_level++; return; } PyMutex_Lock(&(rlock->lock)); rlock->owner = current_thread; rlock->recursion_level = 1; } static void release_lock(ReentrantLock* rlock) { if (rlock->owner != PyThreadState_Get()) { PyErr_SetString(PyExc_RuntimeError, "Lock not owned by current thread"); return; } rlock->recursion_level--; if (rlock->recursion_level == 0) { rlock->owner = NULL; PyMutex_Unlock(&(rlock->lock)); } } static ReentrantLock get_type_rlock = {{0}, NULL, 0}; static ReentrantLock resolve_type_rlock = {{0}, NULL, 0}; #define ACQUIRE_GET_TYPE_LOCK() acquire_lock(&get_type_rlock) #define RELEASE_GET_TYPE_LOCK() release_lock(&get_type_rlock) #define ACQUIRE_RESOLVE_TYPE_LOCK() acquire_lock(&resolve_type_rlock) #define RELEASE_RESOLVE_TYPE_LOCK() release_lock(&resolve_type_rlock) #else #define ACQUIRE_GET_TYPE_LOCK() #define RELEASE_GET_TYPE_LOCK() #define ACQUIRE_RESOLVE_TYPE_LOCK() #define RELEASE_RESOLVE_TYPE_LOCK() #endif JPy_JType* JType_New(JNIEnv* jenv, jclass classRef, jboolean resolve); int JType_ResolveType(JNIEnv* jenv, JPy_JType* type); int JType_InitComponentType(JNIEnv* jenv, JPy_JType* type, jboolean resolve); int JType_InitSuperType(JNIEnv* jenv, JPy_JType* type, jboolean resolve); int JType_ProcessClassConstructors(JNIEnv* jenv, JPy_JType* type); int JType_ProcessClassFields(JNIEnv* jenv, JPy_JType* type); int JType_ProcessClassMethods(JNIEnv* jenv, JPy_JType* type); int JType_AddMethod(JPy_JType* type, JPy_JMethod* method); JPy_ReturnDescriptor* JType_CreateReturnDescriptor(JNIEnv* jenv, jclass returnType); JPy_ParamDescriptor* JType_CreateParamDescriptors(JNIEnv* jenv, int paramCount, jarray paramTypes); void JType_InitParamDescriptorFunctions(JPy_ParamDescriptor* paramDescriptor, jboolean isLastVarArg); void JType_InitMethodParamDescriptorFunctions(JPy_JType* type, JPy_JMethod* method); int JType_ProcessField(JNIEnv* jenv, JPy_JType* declaringType, PyObject* fieldKey, const char* fieldName, jclass fieldClassRef, jboolean isStatic, jboolean isFinal, jfieldID fid); void JType_DisposeLocalObjectRefArg(JNIEnv* jenv, jvalue* value, void* data); void JType_DisposeReadOnlyBufferArg(JNIEnv* jenv, jvalue* value, void* data); void JType_DisposeWritableBufferArg(JNIEnv* jenv, jvalue* value, void* data); static int JType_MatchVarArgPyArgAsFPType(const JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx, struct JPy_JType *expectedType, int floatMatch); static int JType_MatchVarArgPyArgIntType(const JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx, struct JPy_JType *expectedComponentType); JPy_JType* JType_GetTypeForObject(JNIEnv* jenv, jobject objectRef, jboolean resolve) { JPy_JType* type; jclass classRef; classRef = (*jenv)->GetObjectClass(jenv, objectRef); type = JType_GetType(jenv, classRef, resolve); JPy_DELETE_LOCAL_REF(classRef); return type; } JPy_JType* JType_GetTypeForName(JNIEnv* jenv, const char* typeName, jboolean resolve) { const char* resourceName; jclass classRef; JPy_JType *result; JPy_JType* javaType = NULL; if (strcmp(typeName, "boolean") == 0) { javaType = JPy_JBoolean; } else if (strcmp(typeName, "char") == 0) { javaType = JPy_JChar; } else if (strcmp(typeName, "byte") == 0) { javaType = JPy_JByte; } else if (strcmp(typeName, "short") == 0) { javaType = JPy_JShort; } else if (strcmp(typeName, "int") == 0) { javaType = JPy_JInt; } else if (strcmp(typeName, "long") == 0) { javaType = JPy_JLong; } else if (strcmp(typeName, "float") == 0) { javaType = JPy_JFloat; } else if (strcmp(typeName, "double") == 0) { javaType = JPy_JDouble; } else if (strcmp(typeName, "void") == 0) { javaType = JPy_JVoid; } if (javaType != NULL) { JPy_INCREF(javaType); return javaType; } if (strchr(typeName, '.') != NULL) { // resourceName: Replace dots '.' by slashes '/' char* c; resourceName = PyMem_New(char, strlen(typeName) + 1); if (resourceName == NULL) { PyErr_NoMemory(); return NULL; } strcpy((char*) resourceName, typeName); c = (char*) resourceName; while ((c = strchr(c, '.')) != NULL) { *c = '/'; } } else { resourceName = typeName; } JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetTypeForName: typeName='%s', resourceName='%s'\n", typeName, resourceName); classRef = (*jenv)->FindClass(jenv, resourceName); if (typeName != resourceName) { PyMem_Del((char*) resourceName); } if (classRef == NULL || (*jenv)->ExceptionCheck(jenv)) { (*jenv)->ExceptionClear(jenv); PyErr_Format(PyExc_ValueError, "Java class '%s' not found", typeName); return NULL; } result = JType_GetType(jenv, classRef, resolve); JPy_DELETE_LOCAL_REF(classRef); return result; } /** * Returns a new reference. */ JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve) { PyObject* typeKey; PyObject* typeValue; JPy_JType* type; jboolean found; if (JPy_Types == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: module 'jpy' not initialized"); return NULL; } typeKey = JPy_FromTypeName(jenv, classRef); if (typeKey == NULL) { return NULL; } ACQUIRE_GET_TYPE_LOCK(); // borrowed ref, no need to replace with PyDict_GetItemRef because it is protected by the lock typeValue = PyDict_GetItem(JPy_Types, typeKey); if (typeValue == NULL) { found = JNI_FALSE; // Create a new type instance, the refcount is set to 1 if this call succeeds type = JType_New(jenv, classRef, resolve); if (type == NULL) { JPy_DECREF(typeKey); RELEASE_GET_TYPE_LOCK(); return NULL; } //printf("T1: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // In order to avoid infinite recursion, we have to register the new (but yet incomplete) type first... // it will increment the refcount of type PyDict_SetItem(JPy_Types, typeKey, (PyObject*) type); //printf("T2: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // ... before we can continue processing the super type ... // Note, at this point, the Python type has been registered-but-yet-finalized (as done in JType_InitSlots). // We need to delay resolving the super classes to when they are actually referenced, so that in a cyclic // reference scenario, the super classes can still be finalized (reference-able by child classes), but NOT RESOLVED // (its methods/fields remain absent in the type object). This is to avoid the case where its members reference some other // registered-but-yet-finalized classes in the same class hierarchy, and these classes in turn have // registered-but-yet-finalized super classes, causing JType_InitSlots to fail for one of them because its super // class is not finalized. When this happens, the affected classes will not be properly resolved. if (JType_InitSuperType(jenv, type, JNI_FALSE) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitSuperType() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); JPy_DECREF(typeKey); JPy_DECREF(type); RELEASE_GET_TYPE_LOCK(); return NULL; } //printf("T3: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // ... and processing the component type. if (JType_InitComponentType(jenv, type, JNI_FALSE) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitComponentType() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); JPy_DECREF(typeKey); JPy_DECREF(type); RELEASE_GET_TYPE_LOCK(); return NULL; } //printf("T4: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); // Finally we initialise the type's slots, so that our JObj instances behave pythonic. if (JType_InitSlots(type) < 0) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: error: JType_InitSlots() failed for javaName=\"%s\"\n", type->javaName); PyDict_DelItem(JPy_Types, typeKey); JPy_DECREF(typeKey); JPy_DECREF(type); RELEASE_GET_TYPE_LOCK(); return NULL; } JType_AddClassAttribute(jenv, type); // 'type' will be refcount incremented and returned as a new reference later. 'typeKey' needs to be released. JPy_DECREF(typeKey); JPy_DECREF(type); //printf("T5: type->tp_init=%p\n", ((PyTypeObject*)type)->tp_init); } else { jboolean isTypeInProgress = typeValue->ob_type == &JType_Type; jboolean isFinalizedType = PyType_Check(typeValue); found = JNI_TRUE; if (!isTypeInProgress && !isFinalizedType) { JPy_DIAG_PRINT(JPy_DIAG_F_ALL, "JType_GetType: INTERNAL ERROR: illegal typeValue=%p (type '%s') for typeKey=%p (type '%s') in 'jpy.%s'\n", typeValue, Py_TYPE(typeValue)->tp_name, typeKey, Py_TYPE(typeKey)->tp_name, JPy_MODULE_ATTR_NAME_TYPES); PyErr_Format(PyExc_RuntimeError, "jpy internal error: attributes in 'jpy.%s' must be of type '%s', but found a '%s'", JPy_MODULE_ATTR_NAME_TYPES, JType_Type.tp_name, Py_TYPE(typeValue)->tp_name); JPy_DECREF(typeKey); RELEASE_GET_TYPE_LOCK(); return NULL; } type = (JPy_JType*) typeValue; // 'type' will be refcount incremented and returned as a new reference. 'typeKey' needs to be released. JPy_DECREF(typeKey); } JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_GetType: javaName=\"%s\", found=%d, resolve=%d, resolved=%d, type=%p\n", type->javaName, found, resolve, type->isResolved, type); RELEASE_GET_TYPE_LOCK(); if (!type->isResolved && resolve) { if (JType_ResolveType(jenv, type) < 0) { return NULL; } } JPy_INCREF(type); return type; } /** * Creates a type instance of the meta type 'JType_Type'. * Such type instances are used as types for Java Objects in Python. */ JPy_JType* JType_New(JNIEnv* jenv, jclass classRef, jboolean resolve) { PyTypeObject* metaType; JPy_JType* type; metaType = &JType_Type; type = (JPy_JType*) metaType->tp_alloc(metaType, 0); if (type == NULL) { return NULL; } // printf("=============================================== JType_New: type->ob_type=%p\n", ((PyObject*)type)->ob_type); type->classRef = NULL; type->isResolved = JNI_FALSE; type->isResolving = JNI_FALSE; type->javaName = JPy_GetTypeName(jenv, classRef); if (type->javaName == NULL) { metaType->tp_free(type); return NULL; } // The object type's name. Note that the reference is borrowed from type->javaName. type->typeObj.tp_name = type->javaName; // tp_init is used to identify objects instances of type jpy.JType. Make sure it is initially NULL. type->typeObj.tp_init = NULL; type->classRef = (*jenv)->NewGlobalRef(jenv, classRef); if (type->classRef == NULL) { PyMem_Del(type->javaName); type->javaName = NULL; metaType->tp_free(type); PyErr_NoMemory(); return NULL; } type->isPrimitive = (*jenv)->CallBooleanMethod(jenv, type->classRef, JPy_Class_IsPrimitive_MID); if ((*jenv)->ExceptionCheck(jenv)) { (*jenv)->ExceptionClear(jenv); PyMem_Del(type->javaName); type->javaName = NULL; metaType->tp_free(type); return NULL; } type->isInterface = (*jenv)->CallBooleanMethod(jenv, type->classRef, JPy_Class_IsInterface_MID); if ((*jenv)->ExceptionCheck(jenv)) { (*jenv)->ExceptionClear(jenv); PyMem_Del(type->javaName); type->javaName = NULL; metaType->tp_free(type); return NULL; } JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_New: javaName=\"%s\", resolve=%d, type=%p\n", type->javaName, resolve, type); return type; } PyObject* JType_ConvertJavaToPythonObject(JNIEnv* jenv, JPy_JType* type, jobject objectRef) { if (objectRef == NULL) { return JPy_FROM_JNULL(); } if (type->componentType == NULL) { // Scalar type, not an array, try to convert to Python equivalent if (type == JPy_JBooleanObj || type == JPy_JBoolean) { jboolean value = (*jenv)->CallBooleanMethod(jenv, objectRef, JPy_Boolean_BooleanValue_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JBOOLEAN(value); } else if (type == JPy_JCharacterObj || type == JPy_JChar) { jchar value = (*jenv)->CallCharMethod(jenv, objectRef, JPy_Character_CharValue_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JCHAR(value); } else if (type == JPy_JByteObj || type == JPy_JShortObj || type == JPy_JIntegerObj || type == JPy_JShort || type == JPy_JInt) { jint value = (*jenv)->CallIntMethod(jenv, objectRef, JPy_Number_IntValue_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JINT(value); } else if (type == JPy_JLongObj || type == JPy_JLong) { jlong value = (*jenv)->CallLongMethod(jenv, objectRef, JPy_Number_LongValue_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JLONG(value); } else if (type == JPy_JFloatObj || type == JPy_JDoubleObj || type == JPy_JFloat || type == JPy_JDouble) { jdouble value = (*jenv)->CallDoubleMethod(jenv, objectRef, JPy_Number_DoubleValue_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); return JPy_FROM_JDOUBLE(value); } else if (type == JPy_JPyObject || type == JPy_JPyModule) { jlong value = (*jenv)->CallLongMethod(jenv, objectRef, JPy_PyObject_GetPointer_MID); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); PyObject* pyObj = (PyObject*) value; JPy_INCREF(pyObj); return pyObj; } else if (type == JPy_JString) { return JPy_FromJString(jenv, objectRef); } else if (type == JPy_JObject) { type = JType_GetTypeForObject(jenv, objectRef, JNI_FALSE); if (type != JPy_JObject) { return JType_ConvertJavaToPythonObject(jenv, type, objectRef); } } else if (JPy_JPyObject != NULL) { // Note: JPy_JPyObject == NULL means that org.jpy.PyObject has not been loaded from the // java classpath, which is OK. This is common when using jpy from Python. // If org.jpy.PyObject has not been loaded, we know the object isn't a proxy. // If this object was created by PyObject#createProxy, let's unwrap it and get back original PyObject. jobject jPyObject = (*jenv)->CallStaticObjectMethod(jenv, JPy_JPyObject->classRef, JPy_PyObject_UnwrapProxy_SMID, objectRef); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); if (jPyObject != NULL) { // We know that jPyObject is of the proper type, no need to check it. jlong value = (*jenv)->CallLongMethod(jenv, jPyObject, JPy_PyObject_GetPointer_MID); JPy_DELETE_LOCAL_REF(jPyObject); JPy_ON_JAVA_EXCEPTION_RETURN(NULL); PyObject* pyObj = (PyObject*) value; JPy_INCREF(pyObj); return pyObj; } } } // For all other types than the ones handled above (namely primitive types, PyObject+PyModule, and String), // we create a Java object wrapper return (PyObject*) JObj_FromType(jenv, type, objectRef); // Tried the following in order to return objects that have the actual type instead of the declared return type. // Problem occurs if Java collections are used: These return java.lang.Object items and must be explicitly // cast to the desired type in Python using jpy.cast(obj, type). // Anyway, JObj_New() is 2x slower than JObj_FromType() and it may require definition of Java type wrappers for // actually private implementation classes. For example: A java.util.List is filled with java.nio.file.Path objects, // but List.get() returns a java.lang.Object. Returning the actual type is platform dependent. E.g. on // Windows, it returns an instance of the internal sun.nio.fs.WindowsPath class. // //return (PyObject*) JObj_New(jenv, objectRef); } int JType_PythonToJavaConversionError(JPy_JType* type, PyObject* pyArg) { PyErr_Format(PyExc_ValueError, "cannot convert a Python '%s' to a Java '%s'", Py_TYPE(pyArg)->tp_name, type->javaName); return -1; } int JType_CreateJavaObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jclass classRef, jmethodID initMID, jvalue value, jobject* objectRef) { Py_BEGIN_ALLOW_THREADS; *objectRef = (*jenv)->NewObjectA(jenv, classRef, initMID, &value); Py_END_ALLOW_THREADS; if (*objectRef == NULL) { PyErr_NoMemory(); return -1; } JPy_ON_JAVA_EXCEPTION_RETURN(-1); return 0; } int JType_CreateJavaObject_2(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jclass classRef, jmethodID initMID, jvalue value1, jvalue value2, jobject* objectRef) { Py_BEGIN_ALLOW_THREADS; *objectRef = (*jenv)->NewObject(jenv, classRef, initMID, value1, value2); Py_END_ALLOW_THREADS; if (*objectRef == NULL) { PyErr_NoMemory(); return -1; } JPy_ON_JAVA_EXCEPTION_RETURN(-1); return 0; } #define JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(TARGET_TYPE, STATIC_METHOD_ID, ARG, JOBJECT_PTR) \ Py_BEGIN_ALLOW_THREADS; \ *JOBJECT_PTR = (*jenv)->CallStaticObjectMethod(jenv, TARGET_TYPE, STATIC_METHOD_ID, ARG); \ Py_END_ALLOW_THREADS; \ if (*objectRef == NULL) { \ PyErr_NoMemory(); \ return -1; \ } \ JPy_ON_JAVA_EXCEPTION_RETURN(-1); \ return 0; int JType_CreateJavaBooleanObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jboolean value; value = JPy_AS_JBOOLEAN(pyArg); if ((signed char)value == -1) { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Boolean_JClass, JPy_Boolean_ValueOf_SMID, value, objectRef); } int JType_CreateJavaCharacterObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jchar value; if (JPy_IS_CLONG(pyArg)) { value = JPy_AS_JCHAR(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Character_JClass, JPy_Character_ValueOf_SMID, value, objectRef); } int JType_CreateJavaByteObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jbyte value; if (JPy_IS_CLONG(pyArg)) { value = JPy_AS_JBYTE(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Byte_JClass, JPy_Byte_ValueOf_SMID, value, objectRef); } int JType_CreateJavaShortObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jshort value; if (JPy_IS_CLONG(pyArg)) { value = JPy_AS_JSHORT(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Short_JClass, JPy_Short_ValueOf_SMID, value, objectRef); } int JType_CreateJavaNumberFromPythonInt(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { signed char b; short s; long i; long long j; j = JPy_AS_JLONG(pyArg); i = (int) j; s = (short) j; b = (signed char) j; if (i != j) { JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Long_JClass, JPy_Long_ValueOf_SMID, j, objectRef); } if (s != i) { JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Integer_JClass, JPy_Integer_ValueOf_SMID, i, objectRef); } if (b != s) { JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Short_JClass, JPy_Short_ValueOf_SMID, s, objectRef); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Byte_JClass, JPy_Byte_ValueOf_SMID, b, objectRef); } int JType_CreateJavaIntegerObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jint value; if (JPy_IS_CLONG(pyArg)) { value = JPy_AS_JINT(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Integer_JClass, JPy_Integer_ValueOf_SMID, value, objectRef); } int JType_CreateJavaLongObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jlong value; if (JPy_IS_CLONG(pyArg)) { value = JPy_AS_JLONG(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Long_JClass, JPy_Long_ValueOf_SMID, value, objectRef); } int JType_CreateJavaFloatObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jfloat value; if (JPy_IS_CLONG(pyArg)) { value = (jfloat) JPy_AS_JLONG(pyArg); } else if (PyFloat_Check(pyArg)) { value = JPy_AS_JFLOAT(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Float_JClass, JPy_Float_ValueOf_SMID, value, objectRef); } int JType_CreateJavaDoubleObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jdouble value; if (JPy_IS_CLONG(pyArg)) { value = (jdouble) JPy_AS_JLONG(pyArg); } else if (PyFloat_Check(pyArg)) { value = JPy_AS_JDOUBLE(pyArg); } else { return JType_PythonToJavaConversionError(type, pyArg); } JType_CALL_STATIC_OBJECT_METHOD_1_AND_RETURN(JPy_Double_JClass, JPy_Double_ValueOf_SMID, value, objectRef); } int JType_CreateJavaPyObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef) { jvalue value1; jvalue value2; value1.j = (jlong) pyArg; value2.z = JNI_TRUE; JPy_INCREF(pyArg); return JType_CreateJavaObject_2(jenv, type, pyArg, type->classRef, JPy_PyObject_Init_MID, value1, value2, objectRef); } int JType_CreateJavaArray(JNIEnv* jenv, JPy_JType* componentType, PyObject* pyArg, jobject* objectRef, jboolean allowObjectWrapping) { jint itemCount; jarray arrayRef; jint index; PyObject* pyItem; if (pyArg == Py_None) { itemCount = 0; } else if (PySequence_Check(pyArg)) { itemCount = PySequence_Length(pyArg); if (itemCount < 0) { return -1; } } else { PyErr_Format(PyExc_ValueError, "cannot convert a Python '%s' to a Java array of type '%s'", Py_TYPE(pyArg)->tp_name, componentType->javaName); return -1; } arrayRef = NULL; if (componentType == JPy_JBoolean) { arrayRef = (*jenv)->NewBooleanArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jboolean* items = (*jenv)->GetBooleanArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseBooleanArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JBOOLEAN(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseBooleanArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseBooleanArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JByte) { arrayRef = (*jenv)->NewByteArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jbyte* items = (*jenv)->GetByteArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseByteArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JBYTE(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseByteArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseByteArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JChar) { arrayRef = (*jenv)->NewCharArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jchar* items = (*jenv)->GetCharArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseCharArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JCHAR(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseCharArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseCharArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JShort) { arrayRef = (*jenv)->NewShortArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jshort* items = (*jenv)->GetShortArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseShortArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JSHORT(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseShortArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseShortArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JInt) { arrayRef = (*jenv)->NewIntArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jint* items = (*jenv)->GetIntArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseIntArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JINT(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseIntArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseIntArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JLong) { arrayRef = (*jenv)->NewLongArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jlong* items = (*jenv)->GetLongArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseLongArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JLONG(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseLongArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseLongArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JFloat) { arrayRef = (*jenv)->NewFloatArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jfloat* items = (*jenv)->GetFloatArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseFloatArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JFLOAT(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseFloatArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseFloatArrayElements(jenv, arrayRef, items, 0); } } else if (componentType == JPy_JDouble) { arrayRef = (*jenv)->NewDoubleArray(jenv, itemCount); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } if (itemCount > 0) { jdouble* items = (*jenv)->GetDoubleArrayElements(jenv, arrayRef, NULL); if (items == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); PyErr_NoMemory(); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { (*jenv)->ReleaseDoubleArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } items[index] = JPy_AS_JDOUBLE(pyItem); JPy_DECREF(pyItem); if (PyErr_Occurred()) { (*jenv)->ReleaseDoubleArrayElements(jenv, arrayRef, items, 0); JPy_DELETE_LOCAL_REF(arrayRef); return -1; } } (*jenv)->ReleaseDoubleArrayElements(jenv, arrayRef, items, 0); } } else if (!componentType->isPrimitive) { jobject jItem; arrayRef = (*jenv)->NewObjectArray(jenv, itemCount, componentType->classRef, NULL); if (arrayRef == NULL || (*jenv)->ExceptionCheck(jenv)) { JPy_HandleJavaException(jenv); return -1; } for (index = 0; index < itemCount; index++) { pyItem = PySequence_GetItem(pyArg, index); if (pyItem == NULL) { JPy_DELETE_LOCAL_REF(arrayRef); return -1; } if (JType_ConvertPythonToJavaObject(jenv, componentType, pyItem, &jItem, allowObjectWrapping) < 0) { JPy_DELETE_LOCAL_REF(arrayRef); JPy_DECREF(pyItem); return -1; } JPy_DECREF(pyItem); (*jenv)->SetObjectArrayElement(jenv, arrayRef, index, jItem); if ((*jenv)->ExceptionCheck(jenv)) { JPy_DELETE_LOCAL_REF(arrayRef); JPy_DELETE_LOCAL_REF(jItem); JPy_HandleJavaException(jenv); return -1; } } } else { PyErr_Format(PyExc_ValueError, "illegal Java array component type %s", componentType->javaName); return -1; } *objectRef = arrayRef; return 0; } int JType_ConvertPythonToJavaObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef, jboolean allowObjectWrapping) { // Note: There may be a potential memory leak here. // If a new local reference is created in this function and assigned to *objectRef, the reference may escape. // If the reference is created for an argument to a JNI call, we already delete the ref (see JMethod_InvokeMethod()). // In other cases we actually have to call (*jenv)->DeleteLocalRef(jenv, *objectRef) some time later. if (pyArg == Py_None) { // None converts into NULL *objectRef = NULL; // todo: if the return type is PyObject, we should return Py_None instead return 0; } // If it is already a Java object wrapper JObj, and assignable, then we are done if (JObj_Check(pyArg)) { jobject jobj = ((JPy_JObj*) pyArg)->objectRef; jclass jobjclass = (*jenv)->GetObjectClass(jenv, jobj); if ((*jenv)->IsAssignableFrom(jenv, jobjclass, type->classRef)) { JPy_DELETE_LOCAL_REF(jobjclass); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ConvertPythonToJavaObject: unwrapping JObj into type->javaName=\"%s\"\n", type->javaName); jobj = (*jenv)->NewLocalRef(jenv, jobj); // need a new reference to it *objectRef = jobj; if (jobj == NULL) { PyErr_NoMemory(); return -1; } return 0; } JPy_DELETE_LOCAL_REF(jobjclass); } // If it is already a Java type wrapper JType, and assignable, then we are done if (JType_Check(pyArg)) { jclass jobj = ((JPy_JType*)pyArg)->classRef; jclass jobjclass = (*jenv)->GetObjectClass(jenv, jobj); if ((*jenv)->IsAssignableFrom(jenv, jobjclass, type->classRef)) { JPy_DELETE_LOCAL_REF(jobjclass); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ConvertPythonToJavaObject: unwrapping JType into type->javaName=\"%s\"\n", type->javaName); jobj = (*jenv)->NewLocalRef(jenv, jobj); // need a new reference to it *objectRef = jobj; if (jobj == NULL) { PyErr_NoMemory(); return -1; } return 0; } JPy_DELETE_LOCAL_REF(jobjclass); } if (type->componentType != NULL) { // For any other Python argument create a Java object (a new local reference) return JType_CreateJavaArray(jenv, type->componentType, pyArg, objectRef, allowObjectWrapping); } else if (type == JPy_JBoolean || type == JPy_JBooleanObj) { return JType_CreateJavaBooleanObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JChar || type == JPy_JCharacterObj) { return JType_CreateJavaCharacterObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JByte || type == JPy_JByteObj) { return JType_CreateJavaByteObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JShort || type == JPy_JShortObj) { return JType_CreateJavaShortObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JInt || type == JPy_JIntegerObj) { return JType_CreateJavaIntegerObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JLong || type == JPy_JLongObj) { return JType_CreateJavaLongObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JFloat || type == JPy_JFloatObj) { return JType_CreateJavaFloatObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JDouble || type == JPy_JDoubleObj) { return JType_CreateJavaDoubleObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JPyObject) { return JType_CreateJavaPyObject(jenv, type, pyArg, objectRef); } else if (JPy_IS_STR(pyArg) && (type == JPy_JString || type == JPy_JObject || (*jenv)->IsAssignableFrom(jenv, JPy_JString->classRef, type->classRef))) { return JPy_AsJString(jenv, pyArg, objectRef); } else if (PyBool_Check(pyArg) && (type == JPy_JObject || (*jenv)->IsAssignableFrom(jenv, JPy_Boolean_JClass, type->classRef))) { return JType_CreateJavaBooleanObject(jenv, type, pyArg, objectRef); } else if (JPy_IS_CLONG(pyArg) && (type == JPy_JObject || (*jenv)->IsAssignableFrom(jenv, JPy_Number_JClass, type->classRef))) { return JType_CreateJavaNumberFromPythonInt(jenv, type, pyArg, objectRef); } else if (JPy_IS_CLONG(pyArg) && ((*jenv)->IsAssignableFrom(jenv, JPy_Integer_JClass, type->classRef))) { return JType_CreateJavaIntegerObject(jenv, type, pyArg, objectRef); } else if (JPy_IS_CLONG(pyArg) && (*jenv)->IsAssignableFrom(jenv, JPy_Long_JClass, type->classRef)) { return JType_CreateJavaLongObject(jenv, type, pyArg, objectRef); } else if (PyFloat_Check(pyArg) && (type == JPy_JObject || (*jenv)->IsAssignableFrom(jenv, JPy_Double_JClass, type->classRef))) { return JType_CreateJavaDoubleObject(jenv, type, pyArg, objectRef); } else if (PyFloat_Check(pyArg) && (*jenv)->IsAssignableFrom(jenv, JPy_Float_JClass, type->classRef)) { return JType_CreateJavaFloatObject(jenv, type, pyArg, objectRef); } else if (type == JPy_JObject && allowObjectWrapping) { return JType_CreateJavaPyObject(jenv, JPy_JPyObject, pyArg, objectRef); } return JType_PythonToJavaConversionError(type, pyArg); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The following functions deal with type creation, initialisation, and resolution. /** * Fill the type __dict__ with our Java class constructors and methods. * Constructors will be available using the key named __jinit__. * Methods will be available using their method name. */ int JType_ResolveType(JNIEnv* jenv, JPy_JType* type) { PyTypeObject* typeObj; ACQUIRE_RESOLVE_TYPE_LOCK(); if (type->isResolved || type->isResolving) { RELEASE_RESOLVE_TYPE_LOCK(); return 0; } type->isResolving = JNI_TRUE; typeObj = JTYPE_AS_PYTYPE(type); if (typeObj->tp_base != NULL && JType_Check((PyObject*) typeObj->tp_base)) { JPy_JType* baseType = (JPy_JType*) typeObj->tp_base; if (!baseType->isResolved) { if (JType_ResolveType(jenv, baseType) < 0) { type->isResolving = JNI_FALSE; RELEASE_RESOLVE_TYPE_LOCK(); return -1; } } } //printf("JType_ResolveType 1\n"); if (JType_ProcessClassConstructors(jenv, type) < 0) { type->isResolving = JNI_FALSE; RELEASE_RESOLVE_TYPE_LOCK(); return -1; } //printf("JType_ResolveType 2\n"); if (JType_ProcessClassMethods(jenv, type) < 0) { type->isResolving = JNI_FALSE; RELEASE_RESOLVE_TYPE_LOCK(); return -1; } //printf("JType_ResolveType 3\n"); if (JType_ProcessClassFields(jenv, type) < 0) { type->isResolving = JNI_FALSE; RELEASE_RESOLVE_TYPE_LOCK(); return -1; } //printf("JType_ResolveType 4\n"); type->isResolving = JNI_FALSE; type->isResolved = JNI_TRUE; RELEASE_RESOLVE_TYPE_LOCK(); return 0; } jboolean JType_AcceptMethod(JPy_JType* declaringClass, JPy_JMethod* method) { PyObject* callable; PyObject* callableResult; //printf("JType_AcceptMethod: javaName='%s'\n", overloadedMethod->declaringClass->javaName); #if PY_VERSION_HEX < 0x030D0000 // < 3.13 // borrowed ref callable = PyDict_GetItemString(JPy_Type_Callbacks, declaringClass->javaName); JPy_XINCREF(callable); #else // https://docs.python.org/3/howto/free-threading-extensions.html#borrowed-references // PyDict_GetItemStringRef() is a thread safe version of PyDict_GetItemString() and returns a new reference if (PyDict_GetItemStringRef(JPy_Type_Callbacks, declaringClass->javaName, &callable) != 1) { callable = NULL; } #endif if (callable != NULL) { if (PyCallable_Check(callable)) { callableResult = PyObject_CallFunction(callable, "OO", declaringClass, method); if (callableResult == Py_None || callableResult == Py_False) { JPy_XDECREF(callable); return JNI_FALSE; } else if (callableResult == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_AcceptMethod: warning: failed to invoke callback on method addition\n"); // Ignore this problem and continue } } } JPy_XDECREF(callable); return JNI_TRUE; } int JType_ProcessMethod(JNIEnv* jenv, JPy_JType* type, PyObject* methodKey, const char* methodName, jclass returnType, jarray paramTypes, jboolean isStatic, jboolean isVarArgs, jmethodID mid) { JPy_ParamDescriptor* paramDescriptors = NULL; JPy_ReturnDescriptor* returnDescriptor = NULL; jint paramCount; JPy_JMethod* method; paramCount = (*jenv)->GetArrayLength(jenv, paramTypes); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ProcessMethod: methodName=\"%s\", paramCount=%d, isStatic=%d, isVarArgs=%d, mid=%p\n", methodName, paramCount, isStatic, isVarArgs, mid); if (paramCount > 0) { paramDescriptors = JType_CreateParamDescriptors(jenv, paramCount, paramTypes); if (paramDescriptors == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessMethod: WARNING: Java method '%s' rejected because an error occurred during parameter type processing\n", methodName); return -1; } } else { paramDescriptors = NULL; } if (returnType != NULL) { returnDescriptor = JType_CreateReturnDescriptor(jenv, returnType); if (returnDescriptor == NULL) { PyMem_Del(paramDescriptors); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessMethod: WARNING: Java method '%s' rejected because an error occurred during return type processing\n", methodName); return -1; } } else { returnDescriptor = NULL; } method = JMethod_New(type, methodKey, paramCount, paramDescriptors, returnDescriptor, isStatic, isVarArgs, mid); if (method == NULL) { PyMem_Del(paramDescriptors); PyMem_Del(returnDescriptor); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessMethod: WARNING: Java method '%s' rejected because an error occurred during method instantiation\n", methodName); return -1; } if (JType_AcceptMethod(type, method)) { JType_InitMethodParamDescriptorFunctions(type, method); JType_AddMethod(type, method); } else { JMethod_Del(method); } return 0; } int JType_InitComponentType(JNIEnv* jenv, JPy_JType* type, jboolean resolve) { jclass componentTypeRef; componentTypeRef = (jclass) (*jenv)->CallObjectMethod(jenv, type->classRef, JPy_Class_GetComponentType_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); if (componentTypeRef != NULL) { type->componentType = JType_GetType(jenv, componentTypeRef, resolve); JPy_DELETE_LOCAL_REF(componentTypeRef); if (type->componentType == NULL) { return -1; } } else { type->componentType = NULL; } return 0; } int JType_InitSuperType(JNIEnv* jenv, JPy_JType* type, jboolean resolve) { jclass superClassRef; superClassRef = (*jenv)->GetSuperclass(jenv, type->classRef); if (superClassRef != NULL) { type->superType = JType_GetType(jenv, superClassRef, resolve); if (type->superType == NULL) { return -1; } JPy_DELETE_LOCAL_REF(superClassRef); } else if (type->isInterface && JPy_JObject != NULL) { // This solves the problems that java.lang.Object methods can not be called on interfaces (https://github.com/bcdev/jpy/issues/57) type->superType = JPy_JObject; JPy_INCREF(type->superType); } else { type->superType = NULL; } return 0; } int JType_ProcessClassConstructors(JNIEnv* jenv, JPy_JType* type) { jclass classRef; jobject constructors; jobject constructor; jobject parameterTypes; jint modifiers; jint constrCount; jint i; jboolean isPublic; jboolean isVarArg; jmethodID mid; PyObject* methodKey; classRef = type->classRef; methodKey = Py_BuildValue("s", JPy_JTYPE_ATTR_NAME_JINIT); constructors = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetDeclaredConstructors_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); constrCount = (*jenv)->GetArrayLength(jenv, constructors); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ProcessClassConstructors: constrCount=%d\n", constrCount); for (i = 0; i < constrCount; i++) { constructor = (*jenv)->GetObjectArrayElement(jenv, constructors, i); modifiers = (*jenv)->CallIntMethod(jenv, constructor, JPy_Constructor_GetModifiers_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); isPublic = (modifiers & 0x0001) != 0; isVarArg = (modifiers & 0x0080) != 0; if (isPublic) { parameterTypes = (*jenv)->CallObjectMethod(jenv, constructor, JPy_Constructor_GetParameterTypes_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); mid = (*jenv)->FromReflectedMethod(jenv, constructor); JType_ProcessMethod(jenv, type, methodKey, JPy_JTYPE_ATTR_NAME_JINIT, NULL, parameterTypes, 1, isVarArg, mid); JPy_DELETE_LOCAL_REF(parameterTypes); } JPy_DELETE_LOCAL_REF(constructor); } JPy_DELETE_LOCAL_REF(constructors); return 0; } int JType_ProcessClassFields(JNIEnv* jenv, JPy_JType* type) { jclass classRef; jobject fields; jobject field; jobject fieldNameStr; jobject fieldTypeObj; jint modifiers; jint fieldCount; jint i; jboolean isStatic; jboolean isPublic; jboolean isFinal; const char * fieldName; jfieldID fid; PyObject* fieldKey; classRef = type->classRef; if (type->isInterface) { fields = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetFields_MID); } else { fields = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetDeclaredFields_MID); } JPy_ON_JAVA_EXCEPTION_RETURN(-1); fieldCount = (*jenv)->GetArrayLength(jenv, fields); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ProcessClassFields: fieldCount=%d\n", fieldCount); for (i = 0; i < fieldCount; i++) { field = (*jenv)->GetObjectArrayElement(jenv, fields, i); modifiers = (*jenv)->CallIntMethod(jenv, field, JPy_Field_GetModifiers_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); // see http://docs.oracle.com/javase/6/docs/api/constant-values.html#java.lang.reflect.Modifier.PUBLIC isPublic = (modifiers & 0x0001) != 0; isStatic = (modifiers & 0x0008) != 0; isFinal = (modifiers & 0x0010) != 0; if (isPublic) { fieldNameStr = (*jenv)->CallObjectMethod(jenv, field, JPy_Field_GetName_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); fieldTypeObj = (*jenv)->CallObjectMethod(jenv, field, JPy_Field_GetType_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); fid = (*jenv)->FromReflectedField(jenv, field); fieldName = (*jenv)->GetStringUTFChars(jenv, fieldNameStr, NULL); fieldKey = Py_BuildValue("s", fieldName); JType_ProcessField(jenv, type, fieldKey, fieldName, fieldTypeObj, isStatic, isFinal, fid); (*jenv)->ReleaseStringUTFChars(jenv, fieldNameStr, fieldName); JPy_DELETE_LOCAL_REF(fieldTypeObj); JPy_DELETE_LOCAL_REF(fieldNameStr); } JPy_DELETE_LOCAL_REF(field); } JPy_DELETE_LOCAL_REF(fields); return 0; } int JType_ProcessClassMethods(JNIEnv* jenv, JPy_JType* type) { jclass classRef; jobject methods; jobject method; jobject methodNameStr; jobject returnType; jobject parameterTypes; jint modifiers; jint methodCount; jint i; jboolean isStatic; jboolean isVarArg; jboolean isPublic; jboolean isBridge; const char *methodName; jmethodID mid; PyObject *methodKey; classRef = type->classRef; methods = (*jenv)->CallObjectMethod(jenv, classRef, JPy_Class_GetMethods_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); methodCount = (*jenv)->GetArrayLength(jenv, methods); JPy_ON_JAVA_EXCEPTION_RETURN(-1); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ProcessClassMethods: methodCount=%d\n", methodCount); for (i = 0; i < methodCount; i++) { method = (*jenv)->GetObjectArrayElement(jenv, methods, i); modifiers = (*jenv)->CallIntMethod(jenv, method, JPy_Method_GetModifiers_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); // see http://docs.oracle.com/javase/6/docs/api/constant-values.html#java.lang.reflect.Modifier.PUBLIC isPublic = (modifiers & 0x0001) != 0; isStatic = (modifiers & 0x0008) != 0; isVarArg = (modifiers & 0x0080) != 0; isBridge = (modifiers & 0x0040) != 0; // we exclude bridge methods; as covariant return types will result in bridge methods that cause ambiguity if (isPublic && !isBridge) { methodNameStr = (*jenv)->CallObjectMethod(jenv, method, JPy_Method_GetName_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); returnType = (*jenv)->CallObjectMethod(jenv, method, JPy_Method_GetReturnType_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); parameterTypes = (*jenv)->CallObjectMethod(jenv, method, JPy_Method_GetParameterTypes_MID); JPy_ON_JAVA_EXCEPTION_RETURN(-1); mid = (*jenv)->FromReflectedMethod(jenv, method); methodName = (*jenv)->GetStringUTFChars(jenv, methodNameStr, NULL); methodKey = Py_BuildValue("s", methodName); JType_ProcessMethod(jenv, type, methodKey, methodName, returnType, parameterTypes, isStatic, isVarArg, mid); (*jenv)->ReleaseStringUTFChars(jenv, methodNameStr, methodName); JPy_DELETE_LOCAL_REF(parameterTypes); JPy_DELETE_LOCAL_REF(returnType); JPy_DELETE_LOCAL_REF(methodNameStr); } JPy_DELETE_LOCAL_REF(method); } JPy_DELETE_LOCAL_REF(methods); return 0; } jboolean JType_AcceptField(JPy_JType* declaringClass, JPy_JField* field) { return JNI_TRUE; } int JType_AddField(JPy_JType* declaringClass, JPy_JField* field) { PyObject* typeDict; typeDict = declaringClass->typeObj.tp_dict; if (typeDict == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: missing attribute '__dict__' in JType"); return -1; } PyDict_SetItem(typeDict, field->name, (PyObject*) field); return 0; } int JType_AddClassAttribute(JNIEnv* jenv, JPy_JType* declaringClass) { PyObject* typeDict; if (JPy_JClass != NULL) { typeDict = declaringClass->typeObj.tp_dict; if (typeDict == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: missing attribute '__dict__' in JType"); return -1; } PyDict_SetItem(typeDict, Py_BuildValue("s", "jclass"), (PyObject*) JObj_FromType(jenv, JPy_JClass, declaringClass->classRef)); PyDict_SetItem(typeDict, Py_BuildValue("s", "jclassname"), (PyObject*) JPy_FROM_CSTR(declaringClass->typeObj.tp_name)); } return 0; } int JType_AddFieldAttribute(JNIEnv* jenv, JPy_JType* declaringClass, PyObject* fieldName, JPy_JType* fieldType, jfieldID fid) { PyObject* typeDict; PyObject* fieldValue; typeDict = declaringClass->typeObj.tp_dict; if (typeDict == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: missing attribute '__dict__' in JType"); return -1; } if (fieldType == JPy_JBoolean) { jboolean item = (*jenv)->GetStaticBooleanField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JBOOLEAN(item); } else if (fieldType == JPy_JChar) { jchar item = (*jenv)->GetStaticCharField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JCHAR(item); } else if (fieldType == JPy_JByte) { jbyte item = (*jenv)->GetStaticByteField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JBYTE(item); } else if (fieldType == JPy_JShort) { jshort item = (*jenv)->GetStaticShortField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JSHORT(item); } else if (fieldType == JPy_JInt) { jint item = (*jenv)->GetStaticIntField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JINT(item); } else if (fieldType == JPy_JLong) { jlong item = (*jenv)->GetStaticLongField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JLONG(item); } else if (fieldType == JPy_JFloat) { jfloat item = (*jenv)->GetStaticFloatField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JFLOAT(item); } else if (fieldType == JPy_JDouble) { jdouble item = (*jenv)->GetStaticDoubleField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FROM_JDOUBLE(item); } else if (fieldType == JPy_JString) { jstring objectRef = (*jenv)->GetStaticObjectField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FromJString(jenv, objectRef); JPy_DELETE_LOCAL_REF(objectRef); } else { jobject objectRef = (*jenv)->GetStaticObjectField(jenv, declaringClass->classRef, fid); fieldValue = JPy_FromJObjectWithType(jenv, objectRef, (JPy_JType*) fieldType); JPy_DELETE_LOCAL_REF(objectRef); } PyDict_SetItem(typeDict, fieldName, fieldValue); return 0; } int JType_ProcessField(JNIEnv* jenv, JPy_JType* declaringClass, PyObject* fieldKey, const char* fieldName, jclass fieldClassRef, jboolean isStatic, jboolean isFinal, jfieldID fid) { JPy_JField* field; JPy_JType* fieldType; fieldType = JType_GetType(jenv, fieldClassRef, JNI_FALSE); if (fieldType == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessField: WARNING: Java field '%s' rejected because an error occurred during type processing\n", fieldName); return -1; } if (isStatic && isFinal) { // Add static final values to the JPy_JType's tp_dict. // todo: Note that this is a workaround only, because the JPy_JType's tp_getattro slot is not called. if (JType_AddFieldAttribute(jenv, declaringClass, fieldKey, fieldType, fid) < 0) { return -1; } } else if (!isStatic) { // Add instance field accessor to the JPy_JType's tp_dict, this will be evaluated in the JPy_JType's tp_setattro and tp_getattro slots. field = JField_New(declaringClass, fieldKey, fieldType, isStatic, isFinal, fid); if (field == NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessField: WARNING: Java field '%s' rejected because an error occurred during field instantiation\n", fieldName); return -1; } if (JType_AcceptField(declaringClass, field)) { JType_AddField(declaringClass, field); } else { JField_Del(field); } } else { JPy_DIAG_PRINT(JPy_DIAG_F_TYPE + JPy_DIAG_F_ERR, "JType_ProcessField: WARNING: Java field '%s' rejected because is is static, but not final\n", fieldName); } return 0; } void JType_InitMethodParamDescriptorFunctions(JPy_JType* type, JPy_JMethod* method) { int index; for (index = 0; index < method->paramCount; index++) { JType_InitParamDescriptorFunctions(method->paramDescriptors + index, index == method->paramCount - 1 && method->isVarArgs); } } int JType_AddMethod(JPy_JType* type, JPy_JMethod* method) { PyObject* typeDict; PyObject* methodValue; JPy_JOverloadedMethod* overloadedMethod; typeDict = type->typeObj.tp_dict; if (typeDict == NULL) { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: missing attribute '__dict__' in JType"); return -1; } // borrowed ref, no need to replace with PyDict_GetItemRef because typeDict won't be changed concurrently methodValue = PyDict_GetItem(typeDict, method->name); if (methodValue == NULL) { overloadedMethod = JOverloadedMethod_New(type, method->name, method); return PyDict_SetItem(typeDict, method->name, (PyObject*) overloadedMethod); } else if (PyObject_TypeCheck(methodValue, &JOverloadedMethod_Type)) { overloadedMethod = (JPy_JOverloadedMethod*) methodValue; return JOverloadedMethod_AddMethod(overloadedMethod, method); } else { PyErr_SetString(PyExc_RuntimeError, "jpy internal error: expected type 'JOverloadedMethod' in '__dict__' of a JType"); return -1; } } /** * Returns NULL (error), Py_None (borrowed ref), or a JPy_JOverloadedMethod* (borrowed ref) */ PyObject* JType_GetOverloadedMethod(JNIEnv* jenv, JPy_JType* type, PyObject* methodName, jboolean useSuperClass) { PyObject* typeDict; PyObject* methodValue; typeDict = type->typeObj.tp_dict; if (typeDict == NULL) { PyErr_SetString(PyExc_RuntimeError, "internal error: missing attribute '__dict__' in JType"); return NULL; } // borrowed ref, no need to replace with PyDict_GetItemRef because typeDict won't be changed concurrently methodValue = PyDict_GetItem(typeDict, methodName); if (methodValue == NULL) { if (useSuperClass) { // type->superType == NULL means that type->superType is either an interface or a java.lang.Object // If it is an interface (and not type java.lang.Object) also look for java.lang.Object methods // See https://github.com/bcdev/jpy/issues/57. if (type->superType != NULL) { return JType_GetOverloadedMethod(jenv, type->superType, methodName, JNI_TRUE); } else if (type != JPy_JObject && JPy_JObject != NULL) { return JType_GetOverloadedMethod(jenv, JPy_JObject, methodName, JNI_FALSE); } else { Py_RETURN_NONE; } } else { Py_RETURN_NONE; } } if (PyObject_TypeCheck(methodValue, &JOverloadedMethod_Type)) { return methodValue; } else { PyErr_SetString(PyExc_RuntimeError, "internal error: expected type 'JOverloadedMethod' in '__dict__' of a JType"); return NULL; } } JPy_ReturnDescriptor* JType_CreateReturnDescriptor(JNIEnv* jenv, jclass returnClass) { JPy_ReturnDescriptor* returnDescriptor; JPy_JType* type; returnDescriptor = PyMem_New(JPy_ReturnDescriptor, 1); if (returnDescriptor == NULL) { PyErr_NoMemory(); return NULL; } type = JType_GetType(jenv, returnClass, JNI_FALSE); if (type == NULL) { return NULL; } returnDescriptor->type = type; returnDescriptor->paramIndex = -1; JPy_INCREF((PyObject*) type); JPy_DIAG_PRINT(JPy_DIAG_F_TYPE, "JType_ProcessReturnType: type->javaName=\"%s\", type=%p\n", type->javaName, type); return returnDescriptor; } JPy_ParamDescriptor* JType_CreateParamDescriptors(JNIEnv* jenv, int paramCount, jarray paramClasses) { JPy_ParamDescriptor* paramDescriptors; JPy_ParamDescriptor* paramDescriptor; JPy_JType* type; jclass paramClass; int i; paramDescriptors = PyMem_New(JPy_ParamDescriptor, paramCount); if (paramDescriptors == NULL) { PyErr_NoMemory(); return NULL; } for (i = 0; i < paramCount; i++) { paramClass = (*jenv)->GetObjectArrayElement(jenv, paramClasses, i); paramDescriptor = paramDescriptors + i; type = JType_GetType(jenv, paramClass, JNI_FALSE); JPy_DELETE_LOCAL_REF(paramClass); if (type == NULL) { return NULL; } paramDescriptor->type = type; JPy_INCREF((PyObject*) paramDescriptor->type); paramDescriptor->isMutable = 0; paramDescriptor->isOutput = 0; paramDescriptor->isReturn = 0; paramDescriptor->MatchPyArg = NULL; paramDescriptor->MatchVarArgPyArg = NULL; paramDescriptor->ConvertPyArg = NULL; paramDescriptor->ConvertVarArgPyArg = NULL; } return paramDescriptors; } int JType_MatchPyArgAsJBooleanParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (PyBool_Check(pyArg)) return 100; else if (JPy_IS_CLONG(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJBooleanArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->z = JPy_AS_JBOOLEAN(pyArg); return 0; } int JType_MatchPyArgAsJByteParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (JPy_IS_CLONG(pyArg)) return 100; else if (PyBool_Check(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJByteArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->b = JPy_AS_JBYTE(pyArg); return 0; } int JType_MatchPyArgAsJCharParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (JPy_IS_CLONG(pyArg)) return 100; else if (PyBool_Check(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJCharArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->c = JPy_AS_JCHAR(pyArg); return 0; } int JType_MatchPyArgAsJShortParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (JPy_IS_CLONG(pyArg)) return 100; else if (PyBool_Check(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJShortArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->s = JPy_AS_JSHORT(pyArg); return 0; } int JType_MatchPyArgAsJIntParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (JPy_IS_CLONG(pyArg)) return 100; else if (PyBool_Check(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJIntArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->i = JPy_AS_JINT(pyArg); return 0; } int JType_MatchPyArgAsJLongParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (JPy_IS_CLONG(pyArg)) return 100; else if (PyBool_Check(pyArg)) return 10; else return 0; } int JType_ConvertPyArgToJLongArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->j = JPy_AS_JLONG(pyArg); return 0; } int JType_MatchPyArgAsJFloatParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (PyFloat_Check(pyArg)) return 90; // not 100, in order to give 'double' a chance else if (PyNumber_Check(pyArg)) return 50; else if (JPy_IS_CLONG(pyArg)) return 10; else if (PyBool_Check(pyArg)) return 1; else return 0; } int JType_ConvertPyArgToJFloatArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->f = JPy_AS_JFLOAT(pyArg); return 0; } int JType_MatchPyArgAsJDoubleParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (PyFloat_Check(pyArg)) return 100; else if (PyNumber_Check(pyArg)) return 50; else if (JPy_IS_CLONG(pyArg)) return 10; else if (PyBool_Check(pyArg)) return 1; else return 0; } int JType_ConvertPyArgToJDoubleArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { value->d = JPy_AS_JDOUBLE(pyArg); return 0; } int JType_MatchPyArgAsJStringParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { if (pyArg == Py_None) { // Signal it is possible, but give low priority since we cannot perform any type checks on 'None' return 1; } if (JPy_IS_STR(pyArg)) { return 100; } return 0; } int JType_ConvertPyArgToJStringArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { disposer->data = NULL; disposer->DisposeArg = JType_DisposeLocalObjectRefArg; return JPy_AsJString(jenv, pyArg, &value->l); } int JType_ConvertPyArgToJPyObjectArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { disposer->data = NULL; disposer->DisposeArg = JType_DisposeLocalObjectRefArg; return JType_CreateJavaPyObject(jenv, JPy_JPyObject, pyArg, &value->l); } int JType_MatchPyArgAsJPyObjectParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { // We can always turn a python object into a PyObject // But it has a lower matching value to give a more specific type a better chance // ie, against: // void method1(String s) { ... } // void method1(PyObject o) { ... } // // the Python code: obj.method1('a string') should prefer the String specific version return 10; // yeah - EZ PZ } int JType_MatchPyArgAsJObjectParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg) { return JType_MatchPyArgAsJObject(jenv, paramDescriptor->type, pyArg); } int JType_MatchVarArgPyArgAsJObjectParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, int idx) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType == NULL) { return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue = JType_MatchPyArgAsJObject(jenv, componentType, unpack); if (matchValue == 0) { return 0; } minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_MatchVarArgPyArgAsJStringParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, int idx) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType != JPy_JString) { return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue = JType_MatchPyArgAsJStringParam(jenv, paramDescriptor, unpack); if (matchValue == 0) { return 0; } minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_MatchVarArgPyArgAsJPyObjectParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, int idx) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType != JPy_JPyObject) { return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue = JType_MatchPyArgAsJPyObjectParam(jenv, paramDescriptor, unpack); if (matchValue == 0) { return 0; } minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_MatchVarArgPyArgAsJBooleanParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType != JPy_JBoolean) { // something is horribly wrong here! return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue; if (PyBool_Check(unpack)) matchValue = 100; else if (JPy_IS_CLONG(unpack)) matchValue = 10; else return 0; minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_MatchVarArgPyArgAsJIntParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgIntType(paramDescriptor, pyArg, idx, JPy_JInt); } int JType_MatchVarArgPyArgAsJLongParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgIntType(paramDescriptor, pyArg, idx, JPy_JLong); } int JType_MatchVarArgPyArgAsJShortParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgIntType(paramDescriptor, pyArg, idx, JPy_JShort); } int JType_MatchVarArgPyArgAsJByteParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgIntType(paramDescriptor, pyArg, idx, JPy_JByte); } int JType_MatchVarArgPyArgAsJCharParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgIntType(paramDescriptor, pyArg, idx, JPy_JChar); } int JType_MatchVarArgPyArgIntType(const JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx, struct JPy_JType *expectedComponentType) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType != expectedComponentType) { // something is horribly wrong here! return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue; if (JPy_IS_CLONG(unpack)) matchValue = 100; else if (PyBool_Check(unpack)) matchValue = 10; else return 0; minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_MatchVarArgPyArgAsJDoubleParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { return JType_MatchVarArgPyArgAsFPType(paramDescriptor, pyArg, idx, JPy_JDouble, 100); } int JType_MatchVarArgPyArgAsJFloatParam(JNIEnv *jenv, JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx) { // float gets a match of 90, so that double has a better chance return JType_MatchVarArgPyArgAsFPType(paramDescriptor, pyArg, idx, JPy_JFloat, 90); } /* The float and double match functions are almost identical, but for the expected componentType and the match value * for floating point numbers should give a preference to double over float. */ int JType_MatchVarArgPyArgAsFPType(const JPy_ParamDescriptor *paramDescriptor, PyObject *pyArg, int idx, struct JPy_JType *expectedType, int floatMatch) { Py_ssize_t argCount = PyTuple_Size(pyArg); Py_ssize_t remaining = (argCount - idx); JPy_JType *componentType = paramDescriptor->type->componentType; int minMatch = 100; int ii; if (componentType != expectedType) { // something is horribly wrong here! return 0; } if (remaining == 0) { return 10; } for (ii = 0; ii < remaining; ii++) { PyObject *unpack = PyTuple_GetItem(pyArg, idx + ii); int matchValue; if (PyFloat_Check(unpack)) matchValue = floatMatch; else if (PyNumber_Check(unpack)) matchValue = 50; else if (JPy_IS_CLONG(unpack)) matchValue = 10; else if (PyBool_Check(unpack)) matchValue = 1; else return 0; minMatch = matchValue < minMatch ? matchValue : minMatch; } return minMatch; } int JType_ConvertVarArgPyArgToJObjectArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArgOrig, int offset, jvalue* value, JPy_ArgDisposer* disposer) { Py_ssize_t size = PyTuple_Size(pyArgOrig); PyObject *pyArg = PyTuple_GetSlice(pyArgOrig, offset, size); if (pyArg == Py_None) { // Py_None maps to (Java) NULL value->l = NULL; disposer->data = NULL; disposer->DisposeArg = NULL; } else if (JObj_Check(pyArg)) { // If it is a wrapped Java object, it is always a global reference, so don't dispose it JPy_JObj* obj = (JPy_JObj*) pyArg; value->l = obj->objectRef; disposer->data = NULL; disposer->DisposeArg = NULL; } else { // For any other Python argument, we first check if the formal parameter is a primitive array // and the Python argument is a buffer object JPy_JType* paramType = paramDescriptor->type; JPy_JType* paramComponentType = paramType->componentType; if (paramComponentType != NULL && paramComponentType->isPrimitive && PyObject_CheckBuffer(pyArg)) { Py_buffer* pyBuffer; int flags; Py_ssize_t itemCount; jarray jArray; void* arrayItems; jint itemSize; pyBuffer = PyMem_New(Py_buffer, 1); if (pyBuffer == NULL) { PyErr_NoMemory(); JPy_DECREF(pyArg); return -1; } flags = paramDescriptor->isMutable ? PyBUF_WRITABLE : PyBUF_SIMPLE; if (PyObject_GetBuffer(pyArg, pyBuffer, flags) < 0) { PyMem_Del(pyBuffer); JPy_DECREF(pyArg); return -1; } itemCount = pyBuffer->len / pyBuffer->itemsize; if (paramComponentType == JPy_JBoolean) { jArray = (*jenv)->NewBooleanArray(jenv, itemCount); itemSize = sizeof(jboolean); } else if (paramComponentType == JPy_JByte) { jArray = (*jenv)->NewByteArray(jenv, itemCount); itemSize = sizeof(jbyte); } else if (paramComponentType == JPy_JChar) { jArray = (*jenv)->NewCharArray(jenv, itemCount); itemSize = sizeof(jchar); } else if (paramComponentType == JPy_JShort) { jArray = (*jenv)->NewShortArray(jenv, itemCount); itemSize = sizeof(jshort); } else if (paramComponentType == JPy_JInt) { jArray = (*jenv)->NewIntArray(jenv, itemCount); itemSize = sizeof(jint); } else if (paramComponentType == JPy_JLong) { jArray = (*jenv)->NewLongArray(jenv, itemCount); itemSize = sizeof(jlong); } else if (paramComponentType == JPy_JFloat) { jArray = (*jenv)->NewFloatArray(jenv, itemCount); itemSize = sizeof(jfloat); } else if (paramComponentType == JPy_JDouble) { jArray = (*jenv)->NewDoubleArray(jenv, itemCount); itemSize = sizeof(jdouble); } else { JPy_DECREF(pyArg); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_SetString(PyExc_RuntimeError, "internal error: illegal primitive Java type"); return -1; } if (pyBuffer->len != itemCount * itemSize) { Py_ssize_t bufferLen = pyBuffer->len; Py_ssize_t bufferItemSize = pyBuffer->itemsize; //printf("%ld, %ld, %ld, %ld\n", pyBuffer->len , pyBuffer->itemsize, itemCount, itemSize); JPy_DECREF(pyArg); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_Format(PyExc_ValueError, "illegal buffer argument: expected size was %ld bytes, but got %ld (expected item size was %d bytes, got %ld)", itemCount * itemSize, bufferLen, itemSize, bufferItemSize); return -1; } if (jArray == NULL) { JPy_DECREF(pyArg); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_NoMemory(); return -1; } if (!paramDescriptor->isOutput) { arrayItems = (*jenv)->GetPrimitiveArrayCritical(jenv, jArray, NULL); if (arrayItems == NULL) { JPy_DECREF(pyArg); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_NoMemory(); return -1; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC|JPy_DIAG_F_MEM, "JType_ConvertPyArgToJObjectArg: moving Python buffer into Java array: pyBuffer->buf=%p, pyBuffer->len=%d\n", pyBuffer->buf, pyBuffer->len); memcpy(arrayItems, pyBuffer->buf, itemCount * itemSize); (*jenv)->ReleasePrimitiveArrayCritical(jenv, jArray, arrayItems, 0); } value->l = jArray; disposer->data = pyBuffer; disposer->DisposeArg = paramDescriptor->isMutable ? JType_DisposeWritableBufferArg : JType_DisposeReadOnlyBufferArg; } else { jobject objectRef; if (JType_ConvertPythonToJavaObject(jenv, paramType, pyArg, &objectRef, JNI_FALSE) < 0) { JPy_DECREF(pyArg); return -1; } value->l = objectRef; disposer->data = NULL; disposer->DisposeArg = JType_DisposeLocalObjectRefArg; } } JPy_DECREF(pyArg); return 0; } int JType_MatchPyArgAsJObject(JNIEnv* jenv, JPy_JType* paramType, PyObject* pyArg) { JPy_JType* argType; JPy_JType* paramComponentType; JPy_JType* argComponentType; JPy_JObj* argValue; if (pyArg == Py_None) { // Signal it is possible, but give low priority since we cannot perform any type checks on 'None' return 1; } paramComponentType = paramType->componentType; if (JObj_Check(pyArg)) { // pyArg is a Java object argType = (JPy_JType*) Py_TYPE(pyArg); if (argType == paramType) { // pyArg has same type as the parameter return 100; } argValue = (JPy_JObj*) pyArg; if ((*jenv)->IsInstanceOf(jenv, argValue->objectRef, paramType->classRef)) { argComponentType = argType->componentType; if (argComponentType == paramComponentType) { // pyArg is an instance of parameter type, and they both have the same component types (which may be null) return 90; } if (argComponentType != NULL && paramComponentType != NULL) { // Determines whether an object of clazz1 can be safely cast to clazz2. if ((*jenv)->IsAssignableFrom(jenv, argComponentType->classRef, paramComponentType->classRef)) { // pyArg is an instance of parameter array type, and component types are compatible return 80; } } // Honour that pyArg is compatible with paramType, but better matches may exist. return 10; } // pyArg type does not match parameter type return 0; } // pyArg is not a Java object if (paramComponentType != NULL) { // The parameter type is an array type if (paramComponentType->isPrimitive && PyObject_CheckBuffer(pyArg)) { Py_buffer view; // The parameter type is a primitive array type, pyArg is a Python buffer object if (PyObject_GetBuffer(pyArg, &view, PyBUF_FORMAT) == 0) { JPy_JType* type; int matchValue; //printf("JType_AssessToJObject: buffer len=%d, itemsize=%d, format=%s\n", view.len, view.itemsize, view.format); type = paramComponentType; matchValue = 0; if (view.format != NULL) { char format = *view.format; if (type == JPy_JBoolean) { matchValue = format == 'b' || format == 'B' ? 100 : view.itemsize == 1 ? 10 : 0; } else if (type == JPy_JByte) { matchValue = format == 'b' ? 100 : format == 'B' ? 90 : view.itemsize == 1 ? 10 : 0; } else if (type == JPy_JChar) { matchValue = format == 'u' ? 100 : format == 'H' ? 90 : format == 'h' ? 80 : view.itemsize == 2 ? 10 : 0; } else if (type == JPy_JShort) { matchValue = format == 'h' ? 100 : format == 'H' ? 90 : view.itemsize == 2 ? 10 : 0; } else if (type == JPy_JInt) { matchValue = format == 'i' ? 100 : format == 'I' ? 90 : view.itemsize == 4 ? 10 : 0; } else if (type == JPy_JLong) { matchValue = format == 'q' || format == 'l' ? 100 : format == 'Q' || format == 'L' ? 90 : view.itemsize == 8 ? 10 : 0; } else if (type == JPy_JFloat) { matchValue = format == 'f' ? 100 : view.itemsize == 4 ? 10 : 0; } else if (type == JPy_JDouble) { matchValue = format == 'd' ? 100 : view.itemsize == 8 ? 10 : 0; } } else { if (type == JPy_JBoolean) { matchValue = view.itemsize == 1 ? 10 : 0; } else if (type == JPy_JByte) { matchValue = view.itemsize == 1 ? 10 : 0; } else if (type == JPy_JChar) { matchValue = view.itemsize == 2 ? 10 : 0; } else if (type == JPy_JShort) { matchValue = view.itemsize == 2 ? 10 : 0; } else if (type == JPy_JInt) { matchValue = view.itemsize == 4 ? 10 : 0; } else if (type == JPy_JLong) { matchValue = view.itemsize == 8 ? 10 : 0; } else if (type == JPy_JFloat) { matchValue = view.itemsize == 4 ? 10 : 0; } else if (type == JPy_JDouble) { matchValue = view.itemsize == 8 ? 10 : 0; } } PyBuffer_Release(&view); return matchValue; } } else if (PySequence_Check(pyArg)) { // if we know the type of the array is a string, we should preferentially match it if ((*jenv)->IsAssignableFrom(jenv, paramComponentType->classRef, JPy_String_JClass)) { // it's a string array Py_ssize_t len = PySequence_Length(pyArg); Py_ssize_t ii; for (ii = 0; ii < len; ++ii) { PyObject *element = PySequence_GetItem(pyArg, ii); if (!JPy_IS_STR(element)) { // if the element is not a string, this is not a good match return 0; } } // a String sequence is a good match for a String array return 80; } return 10; } } else if (paramType == JPy_JObject) { // Parameter type is not an array type, but any other Java object type return 10; } else if (paramType == JPy_JBooleanObj) { if (PyBool_Check(pyArg)) { return 100; } else if (JPy_IS_CLONG(pyArg)) { return 10; } } else if (paramType == JPy_JCharacterObj || paramType == JPy_JByteObj || paramType == JPy_JShortObj || paramType == JPy_JIntegerObj || paramType == JPy_JLongObj) { if (JPy_IS_CLONG(pyArg)) { return 100; } else if (PyBool_Check(pyArg)) { return 10; } } else if (paramType == JPy_JFloatObj || paramType == JPy_JDoubleObj) { if (PyFloat_Check(pyArg)) { return 100; } else if (JPy_IS_CLONG(pyArg)) { return 90; } else if (PyBool_Check(pyArg)) { return 10; } } else { if (JPy_IS_STR(pyArg)) { if ((*jenv)->IsAssignableFrom(jenv, JPy_JString->classRef, paramType->classRef)) { return 80; } } else if (PyBool_Check(pyArg)) { if ((*jenv)->IsAssignableFrom(jenv, JPy_Boolean_JClass, paramType->classRef)) { return 80; } } else if (JPy_IS_CLONG(pyArg)) { if ((*jenv)->IsAssignableFrom(jenv, JPy_Integer_JClass, paramType->classRef)) { return 80; } else if ((*jenv)->IsAssignableFrom(jenv, JPy_Long_JClass, paramType->classRef)) { return 80; } } else if (PyFloat_Check(pyArg)) { if ((*jenv)->IsAssignableFrom(jenv, JPy_Double_JClass, paramType->classRef)) { return 80; } else if ((*jenv)->IsAssignableFrom(jenv, JPy_Float_JClass, paramType->classRef)) { return 80; } } } return 0; } int JType_ConvertPyArgToJObjectArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer) { if (pyArg == Py_None) { // Py_None maps to (Java) NULL value->l = NULL; disposer->data = NULL; disposer->DisposeArg = NULL; } else if (JObj_Check(pyArg)) { // If it is a wrapped Java object, it is always a global reference, so don't dispose it JPy_JObj* obj = (JPy_JObj*) pyArg; value->l = obj->objectRef; disposer->data = NULL; disposer->DisposeArg = NULL; } else { // For any other Python argument, we first check if the formal parameter is a primitive array // and the Python argument is a buffer object JPy_JType* paramType = paramDescriptor->type; JPy_JType* paramComponentType = paramType->componentType; if (paramComponentType != NULL && paramComponentType->isPrimitive && PyObject_CheckBuffer(pyArg)) { Py_buffer* pyBuffer; int flags; Py_ssize_t itemCount; jarray jArray; void* arrayItems; jint itemSize; pyBuffer = PyMem_New(Py_buffer, 1); if (pyBuffer == NULL) { PyErr_NoMemory(); return -1; } flags = paramDescriptor->isMutable ? PyBUF_WRITABLE : PyBUF_SIMPLE; if (PyObject_GetBuffer(pyArg, pyBuffer, flags) < 0) { PyMem_Del(pyBuffer); return -1; } itemCount = pyBuffer->len / pyBuffer->itemsize; if (paramComponentType == JPy_JBoolean) { jArray = (*jenv)->NewBooleanArray(jenv, itemCount); itemSize = sizeof(jboolean); } else if (paramComponentType == JPy_JByte) { jArray = (*jenv)->NewByteArray(jenv, itemCount); itemSize = sizeof(jbyte); } else if (paramComponentType == JPy_JChar) { jArray = (*jenv)->NewCharArray(jenv, itemCount); itemSize = sizeof(jchar); } else if (paramComponentType == JPy_JShort) { jArray = (*jenv)->NewShortArray(jenv, itemCount); itemSize = sizeof(jshort); } else if (paramComponentType == JPy_JInt) { jArray = (*jenv)->NewIntArray(jenv, itemCount); itemSize = sizeof(jint); } else if (paramComponentType == JPy_JLong) { jArray = (*jenv)->NewLongArray(jenv, itemCount); itemSize = sizeof(jlong); } else if (paramComponentType == JPy_JFloat) { jArray = (*jenv)->NewFloatArray(jenv, itemCount); itemSize = sizeof(jfloat); } else if (paramComponentType == JPy_JDouble) { jArray = (*jenv)->NewDoubleArray(jenv, itemCount); itemSize = sizeof(jdouble); } else { PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_SetString(PyExc_RuntimeError, "internal error: illegal primitive Java type"); return -1; } if (pyBuffer->len != itemCount * itemSize) { Py_ssize_t bufferLen = pyBuffer->len; Py_ssize_t bufferItemSize = pyBuffer->itemsize; //printf("%ld, %ld, %ld, %ld\n", pyBuffer->len , pyBuffer->itemsize, itemCount, itemSize); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_Format(PyExc_ValueError, "illegal buffer argument: expected size was %ld bytes, but got %ld (expected item size was %d bytes, got %ld)", itemCount * itemSize, bufferLen, itemSize, bufferItemSize); return -1; } if (jArray == NULL) { PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_NoMemory(); return -1; } if (!paramDescriptor->isOutput) { arrayItems = (*jenv)->GetPrimitiveArrayCritical(jenv, jArray, NULL); if (arrayItems == NULL) { PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); PyErr_NoMemory(); return -1; } JPy_DIAG_PRINT(JPy_DIAG_F_EXEC|JPy_DIAG_F_MEM, "JType_ConvertPyArgToJObjectArg: moving Python buffer into Java array: pyBuffer->buf=%p, pyBuffer->len=%d\n", pyBuffer->buf, pyBuffer->len); memcpy(arrayItems, pyBuffer->buf, itemCount * itemSize); (*jenv)->ReleasePrimitiveArrayCritical(jenv, jArray, arrayItems, 0); } value->l = jArray; disposer->data = pyBuffer; disposer->DisposeArg = paramDescriptor->isMutable ? JType_DisposeWritableBufferArg : JType_DisposeReadOnlyBufferArg; } else { jobject objectRef; if (JType_ConvertPythonToJavaObject(jenv, paramType, pyArg, &objectRef, JNI_FALSE) < 0) { return -1; } value->l = objectRef; disposer->data = NULL; disposer->DisposeArg = JType_DisposeLocalObjectRefArg; } } return 0; } void JType_DisposeLocalObjectRefArg(JNIEnv* jenv, jvalue* value, void* data) { jobject objectRef = value->l; if (objectRef != NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JType_DisposeLocalObjectRefArg: objectRef=%p\n", objectRef); JPy_DELETE_LOCAL_REF(objectRef); } } void JType_DisposeReadOnlyBufferArg(JNIEnv* jenv, jvalue* value, void* data) { Py_buffer* pyBuffer; jarray jArray; pyBuffer = (Py_buffer*) data; jArray = (jarray) value->l; JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JType_DisposeReadOnlyBufferArg: pyBuffer=%p, jArray=%p\n", pyBuffer, jArray); if (pyBuffer != NULL) { PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); } if (jArray != NULL) { JPy_DELETE_LOCAL_REF(jArray); } } void JType_DisposeWritableBufferArg(JNIEnv* jenv, jvalue* value, void* data) { Py_buffer* pyBuffer; jarray jArray; void* arrayItems; pyBuffer = (Py_buffer*) data; jArray = (jarray) value->l; JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JType_DisposeWritableBufferArg: pyBuffer=%p, jArray=%p\n", pyBuffer, jArray); if (pyBuffer != NULL && jArray != NULL) { // Copy modified array content back into buffer view arrayItems = (*jenv)->GetPrimitiveArrayCritical(jenv, jArray, NULL); if (arrayItems != NULL) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC|JPy_DIAG_F_MEM, "JType_DisposeWritableBufferArg: moving Java array into Python buffer: pyBuffer->buf=%p, pyBuffer->len=%d\n", pyBuffer->buf, pyBuffer->len); memcpy(pyBuffer->buf, arrayItems, pyBuffer->len); (*jenv)->ReleasePrimitiveArrayCritical(jenv, jArray, arrayItems, 0); } JPy_DELETE_LOCAL_REF(jArray); PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); } else if (pyBuffer != NULL) { PyBuffer_Release(pyBuffer); PyMem_Del(pyBuffer); } else if (jArray != NULL) { JPy_DELETE_LOCAL_REF(jArray); } } void JType_InitParamDescriptorFunctions(JPy_ParamDescriptor* paramDescriptor, jboolean isLastVarArg) { JPy_JType* paramType = paramDescriptor->type; if (paramType == JPy_JVoid) { paramDescriptor->MatchPyArg = NULL; paramDescriptor->ConvertPyArg = NULL; } else if (paramType == JPy_JBoolean) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJBooleanParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJBooleanArg; } else if (paramType == JPy_JByte) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJByteParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJByteArg; } else if (paramType == JPy_JChar) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJCharParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJCharArg; } else if (paramType == JPy_JShort) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJShortParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJShortArg; } else if (paramType == JPy_JInt) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJIntParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJIntArg; } else if (paramType == JPy_JLong) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJLongParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJLongArg; } else if (paramType == JPy_JFloat) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJFloatParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJFloatArg; } else if (paramType == JPy_JDouble) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJDoubleParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJDoubleArg; } else if (paramType == JPy_JString) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJStringParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJStringArg; //} else if (paramType == JPy_JMap) { //} else if (paramType == JPy_JList) { //} else if (paramType == JPy_JSet) { } else if (paramType == JPy_JPyObject) { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJPyObjectParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJPyObjectArg; } else { paramDescriptor->MatchPyArg = JType_MatchPyArgAsJObjectParam; paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJObjectArg; } if (isLastVarArg) { paramDescriptor->ConvertVarArgPyArg = JType_ConvertVarArgPyArgToJObjectArg; if (paramType->componentType == JPy_JBoolean) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJBooleanParam; } else if (paramType->componentType == JPy_JByte) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJByteParam; } else if (paramType->componentType == JPy_JChar) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJCharParam; } else if (paramType->componentType == JPy_JShort) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJShortParam; } else if (paramType->componentType == JPy_JInt) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJIntParam; } else if (paramType->componentType == JPy_JLong) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJLongParam; } else if (paramType->componentType == JPy_JFloat) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJFloatParam; } else if (paramType->componentType == JPy_JDouble) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJDoubleParam; } else if (paramType->componentType == JPy_JString) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJStringParam; } else if (paramType->componentType == JPy_JPyObject) { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJPyObjectParam; } else { paramDescriptor->MatchVarArgPyArg = JType_MatchVarArgPyArgAsJObjectParam; } } } /** * The JType's tp_repr slot. */ PyObject* JType_repr(JPy_JType* self) { //printf("JType_repr: self=%p\n", self); return JPy_FROM_FORMAT("%s(%p)", self->javaName, self->classRef); } /** * The JType's tp_str slot. */ PyObject* JType_str(JPy_JType* self) { JNIEnv* jenv; jstring strJObj; PyObject* strPyObj; jboolean isCopy; const char * utfChars; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL) //printf("JType_str: self=%p\n", self); strJObj = (*jenv)->CallObjectMethod(jenv, self->classRef, JPy_Object_ToString_MID); utfChars = (*jenv)->GetStringUTFChars(jenv, strJObj, &isCopy); strPyObj = JPy_FROM_FORMAT("%s", utfChars); (*jenv)->ReleaseStringUTFChars(jenv, strJObj, utfChars); JPy_DELETE_LOCAL_REF(strJObj); return strPyObj; } /** * The JType's tp_dealloc slot. */ void JType_dealloc(JPy_JType* self) { JNIEnv* jenv = JPy_GetJNIEnv(); //printf("JType_dealloc: self->javaName='%s', self->classRef=%p\n", self->javaName, self->classRef); PyMem_Del(self->javaName); self->javaName = NULL; if (jenv != NULL && self->classRef != NULL) { (*jenv)->DeleteGlobalRef(jenv, self->classRef); self->classRef = NULL; } JPy_XDECREF(self->superType); self->superType = NULL; JPy_XDECREF(self->componentType); self->componentType = NULL; Py_TYPE(self)->tp_free((PyObject*) self); } /** * The JType's JType_getattro slot. */ PyObject* JType_getattro(JPy_JType* self, PyObject* name) { //printf("JType_getattro: %s.%s\n", Py_TYPE(self)->tp_name, JPy_AS_UTF8(name)); if (!self->isResolved && !self->isResolving) { JNIEnv* jenv; JPy_GET_JNI_ENV_OR_RETURN(jenv, NULL); JType_ResolveType(jenv, self); } return PyObject_GenericGetAttr((PyObject*) self, name); } /** * The jpy.JType singleton. */ PyTypeObject JType_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.JType", /* tp_name */ sizeof (JPy_JType), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) JType_dealloc, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ (reprfunc) JType_repr, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ (reprfunc) JType_str, /* tp_str */ (getattrofunc)JType_getattro, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ "Java Meta Type", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ NULL, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) NULL, /* tp_init */ NULL, /* tp_alloc */ (newfunc) NULL, /* tp_new=NULL --> JType instances cannot be created from Python. */ }; jpy-2.0.0/src/main/c/jpy_jmethod.c0000664000175000017500000011545715202503234017125 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jtype.h" #include "jpy_jobj.h" #include "jpy_jmethod.h" #include "jpy_conv.h" #include "jpy_compat.h" JPy_JMethod* JMethod_New(JPy_JType* declaringClass, PyObject* name, int paramCount, JPy_ParamDescriptor* paramDescriptors, JPy_ReturnDescriptor* returnDescriptor, jboolean isStatic, jboolean isVarArgs, jmethodID mid) { PyTypeObject* type = &JMethod_Type; JPy_JMethod* method; method = (JPy_JMethod*) type->tp_alloc(type, 0); method->declaringClass = declaringClass; method->name = name; method->paramCount = paramCount; method->paramDescriptors = paramDescriptors; method->returnDescriptor = returnDescriptor; method->isStatic = isStatic; method->isVarArgs = isVarArgs; method->mid = mid; JPy_INCREF(declaringClass); JPy_INCREF(method->name); return method; } /** * The JMethod type's tp_dealloc slot. */ void JMethod_dealloc(JPy_JMethod* self) { JNIEnv* jenv; JPy_DECREF(self->declaringClass); JPy_DECREF(self->name); jenv = JPy_GetJNIEnv(); if (jenv != NULL) { int i; for (i = 0; i < self->paramCount; i++) { JPy_DECREF((self->paramDescriptors + i)->type); } JPy_DECREF((self->returnDescriptor + i)->type); } PyMem_Del(self->paramDescriptors); PyMem_Del(self->returnDescriptor); Py_TYPE(self)->tp_free((PyObject*) self); } void JMethod_Del(JPy_JMethod* method) { JMethod_dealloc(method); } /** * Matches the give Python argument tuple against the Java method's formal parameters. * Returns the sum of the i-th argument against the i-th Java parameter. * The maximum match value returned is 100 * method->paramCount. * * The isVarArgsArray pointer is set to 1 if this is a varargs match for an object array * argument. */ int JMethod_MatchPyArgs(JNIEnv* jenv, JPy_JType* declaringClass, JPy_JMethod* method, int argCount, PyObject* pyArgs, int *isVarArgArray) { JPy_ParamDescriptor* paramDescriptor; PyObject* pyArg; int matchValueSum; int matchValue; int i; int i0; int iLast; *isVarArgArray = 0; if (method->isStatic) { if (method->isVarArgs) { if(argCount < method->paramCount - 1) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: var args argument count mismatch java=%d, python=%d (matchValue=0)\n", method->paramCount, argCount); // argument count mismatch return 0; } iLast = method->paramCount - 1; } else { if (method->paramCount != argCount) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: argument count mismatch (matchValue=0)\n"); // argument count mismatch return 0; } if (method->paramCount == 0) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: no-argument static method (matchValue=100)\n"); // There can't be any other static method overloads with no parameters return 100; } iLast = argCount; } matchValueSum = 0; i0 = 0; } else { PyObject* self; //printf("Non-Static! method->paramCount=%d, argCount=%d, isVarArg=%d\n", method->paramCount, argCount, method->isVarArgs); if (method->isVarArgs) { if (argCount < method->paramCount) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: var args argument count mismatch java=%d, python=%d (matchValue=0)\n", method->paramCount, argCount); // argument count mismatch return 0; } iLast = method->paramCount; } else if (method->paramCount != argCount - 1) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: argument count mismatch (matchValue=0)\n"); // argument count mismatch return 0; } else { iLast = method->paramCount + 1; } self = PyTuple_GetItem(pyArgs, 0); if (self == Py_None) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: self argument is None (matchValue=0)\n"); return 0; } if (!JObj_Check(self)) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: self argument is not a Java object (matchValue=0)\n"); return 0; } i0 = 1; matchValueSum = JType_MatchPyArgAsJObject(jenv, declaringClass, self); if (matchValueSum == 0) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: self argument does not match required Java class (matchValue=0)\n"); return 0; } if (method->paramCount == 0) { JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: no-argument non-static method (matchValue=%d)\n", matchValueSum); // There can't be any other method overloads with no parameters return matchValueSum; } } paramDescriptor = method->paramDescriptors; for (i = i0; i < iLast; i++) { pyArg = PyTuple_GetItem(pyArgs, i); matchValue = paramDescriptor->MatchPyArg(jenv, paramDescriptor, pyArg); JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: pyArgs[%d]: paramDescriptor->type->javaName='%s', matchValue=%d\n", i, paramDescriptor->type->javaName, matchValue); if (matchValue == 0) { //printf("JMethod_MatchPyArgs 6\n"); // current pyArg does not match parameter type at all return 0; } matchValueSum += matchValue; paramDescriptor++; } if (method->isVarArgs) { int singleMatchValue = 0; JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: isVarArgs, argCount = %d, i=%d\n", argCount, i); if (argCount - i == 0) { matchValueSum += 10; JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: isVarArgs, argCount = %d, paramCount = %d, matchValueSum=%d\n", argCount, method->paramCount, matchValueSum); } else if (argCount - i == 1) { // if we have exactly one argument, which matches our array type, then we can use that as an array pyArg = PyTuple_GetItem(pyArgs, i); singleMatchValue = paramDescriptor->MatchPyArg(jenv, paramDescriptor, pyArg); JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: isVarArgs, argCount = %d, paramCount = %d, starting singleMatchValue=%d\n", argCount, method->paramCount, singleMatchValue); } JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: isVarArgs, argCount = %d, paramCount = %d, starting matchValue=%d\n", argCount, method->paramCount, matchValueSum); matchValue = paramDescriptor->MatchVarArgPyArg(jenv, paramDescriptor, pyArgs, i); JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JMethod_MatchPyArgs: isVarArgs, paramDescriptor->type->javaName='%s', matchValue=%d\n", paramDescriptor->type->javaName, matchValue); if (matchValue == 0 && singleMatchValue == 0) { return 0; } if (matchValue > singleMatchValue) { matchValueSum += matchValue; } else { matchValueSum += singleMatchValue; *isVarArgArray = 1; } } //printf("JMethod_MatchPyArgs 7\n"); return matchValueSum; } #define JPy_SUPPORT_RETURN_PARAMETER 1 PyObject* JMethod_FromJObject(JNIEnv* jenv, JPy_JMethod* method, PyObject* pyArgs, jvalue* jArgs, int argOffset, JPy_JType* returnType, jobject jReturnValue) { #ifdef JPy_SUPPORT_RETURN_PARAMETER if (method->returnDescriptor->paramIndex >= 0) { jint paramIndex = method->returnDescriptor->paramIndex; PyObject* pyReturnArg = PyTuple_GetItem(pyArgs, paramIndex + argOffset); jobject jArg = jArgs[paramIndex].l; //printf("JMethod_FromJObject: paramIndex=%d, jArg=%p, isNone=%d\n", paramIndex, jArg, pyReturnArg == Py_None); if ((JObj_Check(pyReturnArg) || PyObject_CheckBuffer(pyReturnArg)) && (*jenv)->IsSameObject(jenv, jReturnValue, jArg)) { JPy_INCREF(pyReturnArg); return pyReturnArg; } } #endif return JPy_FromJObjectWithType(jenv, jReturnValue, returnType); } /** * Invoke a method. We have already ensured that the Python arguments and expected Java parameters match. */ PyObject* JMethod_InvokeMethod(JNIEnv* jenv, JPy_JMethod* method, PyObject* pyArgs, int isVarArgsArray) { jvalue* jArgs; JPy_ArgDisposer* argDisposers; PyObject* returnValue; JPy_JType* declaringClass; JPy_JType* returnType; jclass classRef; //printf("JMethod_InvokeMethod 1: typeCode=%c\n", typeCode); if (JMethod_CreateJArgs(jenv, method, pyArgs, &jArgs, &argDisposers, isVarArgsArray) < 0) { return NULL; } //printf("JMethod_InvokeMethod 2: typeCode=%c\n", typeCode); returnType = method->returnDescriptor->type; returnValue = NULL; declaringClass = method->declaringClass; classRef = declaringClass->classRef; if (method->isStatic) { JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "JMethod_InvokeMethod: calling static Java method %s#%s\n", declaringClass->javaName, JPy_AS_UTF8(method->name)); if (returnType == JPy_JVoid) { Py_BEGIN_ALLOW_THREADS; (*jenv)->CallStaticVoidMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JVOID(); } else if (returnType == JPy_JBoolean) { jboolean v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticBooleanMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JBOOLEAN(v); } else if (returnType == JPy_JChar) { jchar v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticCharMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JCHAR(v); } else if (returnType == JPy_JByte) { jbyte v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticByteMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JBYTE(v); } else if (returnType == JPy_JShort) { jshort v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticShortMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JSHORT(v); } else if (returnType == JPy_JInt) { jint v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticIntMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JINT(v); } else if (returnType == JPy_JLong) { jlong v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticLongMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JLONG(v); } else if (returnType == JPy_JFloat) { jfloat v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticFloatMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JFLOAT(v); } else if (returnType == JPy_JDouble) { jdouble v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticDoubleMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JDOUBLE(v); } else if (returnType == JPy_JString) { jstring v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticObjectMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FromJString(jenv, v); JPy_DELETE_LOCAL_REF(v); } else { jobject v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallStaticObjectMethodA(jenv, classRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JMethod_FromJObject(jenv, method, pyArgs, jArgs, 0, returnType, v); JPy_DELETE_LOCAL_REF(v); } } else { jobject objectRef; PyObject* self; JPy_DIAG_PRINT(JPy_DIAG_F_EXEC, "JMethod_InvokeMethod: calling Java method %s#%s\n", declaringClass->javaName, JPy_AS_UTF8(method->name)); self = PyTuple_GetItem(pyArgs, 0); // Note it is already ensured that self is a JPy_JObj* objectRef = ((JPy_JObj*) self)->objectRef; if (returnType == JPy_JVoid) { Py_BEGIN_ALLOW_THREADS; (*jenv)->CallVoidMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JVOID(); } else if (returnType == JPy_JBoolean) { jboolean v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallBooleanMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JBOOLEAN(v); } else if (returnType == JPy_JChar) { jchar v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallCharMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JCHAR(v); } else if (returnType == JPy_JByte) { jbyte v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallByteMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JBYTE(v); } else if (returnType == JPy_JShort) { jshort v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallShortMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JSHORT(v); } else if (returnType == JPy_JInt) { jint v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallIntMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JINT(v); } else if (returnType == JPy_JLong) { jlong v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallLongMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JLONG(v); } else if (returnType == JPy_JFloat) { jfloat v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallFloatMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JFLOAT(v); } else if (returnType == JPy_JDouble) { jdouble v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallDoubleMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FROM_JDOUBLE(v); } else if (returnType == JPy_JString) { jstring v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallObjectMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JPy_FromJString(jenv, v); JPy_DELETE_LOCAL_REF(v); } else { jobject v; Py_BEGIN_ALLOW_THREADS; v = (*jenv)->CallObjectMethodA(jenv, objectRef, method->mid, jArgs); Py_END_ALLOW_THREADS; JPy_ON_JAVA_EXCEPTION_GOTO(error); returnValue = JMethod_FromJObject(jenv, method, pyArgs, jArgs, 1, returnType, v); JPy_DELETE_LOCAL_REF(v); } } error: if (jArgs != NULL) { JMethod_DisposeJArgs(jenv, method->paramCount, jArgs, argDisposers); } return returnValue; } int JMethod_CreateJArgs(JNIEnv* jenv, JPy_JMethod* method, PyObject* pyArgs, jvalue** argValuesRet, JPy_ArgDisposer** argDisposersRet, int isVarArgsArray) { JPy_ParamDescriptor* paramDescriptor; Py_ssize_t i, i0, iLast; Py_ssize_t argCount; PyObject* pyArg; jvalue* jValue; jvalue* jValues; JPy_ArgDisposer* argDisposer; JPy_ArgDisposer* argDisposers; if (method->paramCount == 0) { *argValuesRet = NULL; *argDisposersRet = NULL; return 0; } argCount = PyTuple_Size(pyArgs); if (method->isVarArgs) { // need to know if we expect a self parameter i0 = method->isStatic ? 0 : 1; iLast = method->isStatic ? method->paramCount - 1 : method->paramCount; } else { i0 = argCount - method->paramCount; if (!(i0 == 0 || i0 == 1)) { PyErr_SetString(PyExc_RuntimeError, "internal error"); return -1; } iLast = argCount; } jValues = PyMem_New(jvalue, method->paramCount); if (jValues == NULL) { PyErr_NoMemory(); return -1; } argDisposers = PyMem_New(JPy_ArgDisposer, method->paramCount); if (argDisposers == NULL) { PyMem_Del(jValues); PyErr_NoMemory(); return -1; } paramDescriptor = method->paramDescriptors; jValue = jValues; argDisposer = argDisposers; for (i = i0; i < iLast; i++) { pyArg = PyTuple_GetItem(pyArgs, i); jValue->l = 0; argDisposer->data = NULL; argDisposer->DisposeArg = NULL; if (paramDescriptor->ConvertPyArg(jenv, paramDescriptor, pyArg, jValue, argDisposer) < 0) { PyMem_Del(jValues); PyMem_Del(argDisposers); return -1; } paramDescriptor++; jValue++; argDisposer++; } if (method->isVarArgs) { if (isVarArgsArray) { pyArg = PyTuple_GetItem(pyArgs, i); jValue->l = 0; argDisposer->data = NULL; argDisposer->DisposeArg = NULL; if (paramDescriptor->ConvertPyArg(jenv, paramDescriptor, pyArg, jValue, argDisposer) < 0) { PyMem_Del(jValues); PyMem_Del(argDisposers); return -1; } } else { jValue->l = 0; argDisposer->data = NULL; argDisposer->DisposeArg = NULL; if (paramDescriptor->ConvertVarArgPyArg(jenv, paramDescriptor, pyArgs, i, jValue, argDisposer) < 0) { PyMem_Del(jValues); PyMem_Del(argDisposers); return -1; } } } *argValuesRet = jValues; *argDisposersRet = argDisposers; return 0; } void JMethod_DisposeJArgs(JNIEnv* jenv, int paramCount, jvalue* jArgs, JPy_ArgDisposer* argDisposers) { jvalue* jArg; JPy_ArgDisposer* argDisposer; int index; jArg = jArgs; argDisposer = argDisposers; for (index = 0; index < paramCount; index++) { if (argDisposer->DisposeArg != NULL) { argDisposer->DisposeArg(jenv, jArg, argDisposer->data); } jArg++; argDisposer++; } PyMem_Del(jArgs); PyMem_Del(argDisposers); } PyObject* JMethod_repr(JPy_JMethod* self) { const char* name = JPy_AS_UTF8(self->name); return JPy_FROM_FORMAT("%s(name='%s', param_count=%d, is_static=%d, mid=%p)", ((PyObject*)self)->ob_type->tp_name, name, self->paramCount, self->isStatic, self->mid); } PyObject* JMethod_str(JPy_JMethod* self) { JPy_INCREF(self->name); return self->name; } static PyMemberDef JMethod_members[] = { {"name", T_OBJECT_EX, offsetof(JPy_JMethod, name), READONLY, "Method name"}, {"param_count", T_INT, offsetof(JPy_JMethod, paramCount), READONLY, "Number of method parameters"}, {"is_static", T_BOOL, offsetof(JPy_JMethod, isStatic), READONLY, "Tests if this is a static method"}, {NULL} /* Sentinel */ }; #define JMethod_CHECK_PARAMETER_INDEX(self, index) \ if (index < 0 || index >= self->paramCount) { \ PyErr_SetString(PyExc_IndexError, "invalid parameter index"); \ return NULL; \ } PyObject* JMethod_get_param_type(JPy_JMethod* self, PyObject* args) { PyObject* type; int index; if (!PyArg_ParseTuple(args, "i:get_param_type", &index)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); type = (PyObject*) self->paramDescriptors[index].type; JPy_INCREF(type); return type; } PyObject* JMethod_is_param_mutable(JPy_JMethod* self, PyObject* args) { int index; int value; if (!PyArg_ParseTuple(args, "i:is_param_mutable", &index)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); value = self->paramDescriptors[index].isMutable; return PyBool_FromLong(value); } PyObject* JMethod_set_param_mutable(JPy_JMethod* self, PyObject* args) { int index; int value; if (!PyArg_ParseTuple(args, "ip:set_param_mutable", &index, &value)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); self->paramDescriptors[index].isMutable = value; Py_RETURN_NONE; } PyObject* JMethod_is_param_output(JPy_JMethod* self, PyObject* args) { int index = 0; int value = 0; if (!PyArg_ParseTuple(args, "i:is_param_output", &index)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); value = self->paramDescriptors[index].isOutput; return PyBool_FromLong(value); } PyObject* JMethod_set_param_output(JPy_JMethod* self, PyObject* args) { int index = 0; int value = 0; if (!PyArg_ParseTuple(args, "ip:set_param_output", &index, &value)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); self->paramDescriptors[index].isOutput = value; Py_RETURN_NONE; } PyObject* JMethod_is_param_return(JPy_JMethod* self, PyObject* args) { int index = 0; int value = 0; if (!PyArg_ParseTuple(args, "i:is_param_return", &index)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); value = self->paramDescriptors[index].isReturn; return PyBool_FromLong(value); } PyObject* JMethod_set_param_return(JPy_JMethod* self, PyObject* args) { int index = 0; int value = 0; if (!PyArg_ParseTuple(args, "ip:set_param_return", &index, &value)) { return NULL; } JMethod_CHECK_PARAMETER_INDEX(self, index); self->paramDescriptors[index].isReturn = value; if (value) { self->returnDescriptor->paramIndex = index; } Py_RETURN_NONE; } static PyMethodDef JMethod_methods[] = { {"get_param_type", (PyCFunction) JMethod_get_param_type, METH_VARARGS, "Gets the type of the parameter given by index"}, {"is_param_mutable", (PyCFunction) JMethod_is_param_mutable, METH_VARARGS, "Tests if the method parameter given by index is mutable"}, {"is_param_output", (PyCFunction) JMethod_is_param_output, METH_VARARGS, "Tests if the method parameter given by index is a mere output value (and not read from)"}, {"is_param_return", (PyCFunction) JMethod_is_param_return, METH_VARARGS, "Tests if the method parameter given by index is the return value"}, {"set_param_mutable", (PyCFunction) JMethod_set_param_mutable, METH_VARARGS, "Sets whether the method parameter given by index is mutable"}, {"set_param_output", (PyCFunction) JMethod_set_param_output, METH_VARARGS, "Sets whether the method parameter given by index is a mere output value (and not read from)"}, {"set_param_return", (PyCFunction) JMethod_set_param_return, METH_VARARGS, "Sets whether the method parameter given by index is the return value"}, {NULL} /* Sentinel */ }; /** * Implements the BeamPy_JObjectType class singleton. */ PyTypeObject JMethod_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.JMethod", /* tp_name */ sizeof (JPy_JMethod), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)JMethod_dealloc, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ (reprfunc)JMethod_repr, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ (reprfunc)JMethod_str, /* tp_str */ NULL, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "Java Method Wrapper", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ JMethod_methods, /* tp_methods */ JMethod_members, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ NULL, /* tp_new */ }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JOverloadedMethod typedef struct JPy_MethodFindResult { JPy_JMethod* method; int matchValue; int matchCount; int isVarArgsArray; } JPy_MethodFindResult; JPy_JMethod* JOverloadedMethod_FindMethod0(JNIEnv* jenv, JPy_JOverloadedMethod* overloadedMethod, PyObject* pyArgs, JPy_MethodFindResult* result) { Py_ssize_t overloadCount; Py_ssize_t argCount; int matchCount; int matchValue; int matchValueMax; JPy_JMethod* currMethod; JPy_JMethod* bestMethod; int i; int currentIsVarArgsArray; int bestIsVarArgsArray; result->method = NULL; result->matchValue = 0; result->matchCount = 0; overloadCount = PyList_Size(overloadedMethod->methodList); if (overloadCount <= 0) { PyErr_SetString(PyExc_RuntimeError, "internal error: invalid overloadedMethod->methodList"); return NULL; } argCount = PyTuple_Size(pyArgs); matchCount = 0; matchValueMax = -1; bestMethod = NULL; bestIsVarArgsArray = 0; JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JOverloadedMethod_FindMethod0: method '%s#%s': overloadCount=%d, argCount=%d\n", overloadedMethod->declaringClass->javaName, JPy_AS_UTF8(overloadedMethod->name), overloadCount, argCount); for (i = 0; i < overloadCount; i++) { // borrowed ref, no need to replace with PyList_GetItemRef() because the list won't be changed concurrently currMethod = (JPy_JMethod*) PyList_GetItem(overloadedMethod->methodList, i); if (currMethod->isVarArgs && matchValueMax > 0 && !bestMethod->isVarArgs) { // we should not process varargs if we have already found a suitable fixed arity method break; } matchValue = JMethod_MatchPyArgs(jenv, overloadedMethod->declaringClass, currMethod, argCount, pyArgs, ¤tIsVarArgsArray); JPy_DIAG_PRINT(JPy_DIAG_F_METH, "JOverloadedMethod_FindMethod0: methodList[%d]: paramCount=%d, matchValue=%d, isVarArgs=%d\n", i, currMethod->paramCount, matchValue, currMethod->isVarArgs); if (matchValue > 0) { if (matchValue > matchValueMax) { matchValueMax = matchValue; bestMethod = currMethod; matchCount = 1; bestIsVarArgsArray = currentIsVarArgsArray; } else if (matchValue == matchValueMax) { matchCount++; } if (!currMethod->isVarArgs && (matchValue >= 100 * argCount)) { // we can't get any better (if so, we have an internal problem) break; } } } if (bestMethod == NULL) { matchValueMax = 0; matchCount = 0; bestIsVarArgsArray = 0; } result->method = bestMethod; result->matchValue = matchValueMax; result->matchCount = matchCount; result->isVarArgsArray = bestIsVarArgsArray; return bestMethod; } JPy_JMethod* JOverloadedMethod_FindMethod(JNIEnv* jenv, JPy_JOverloadedMethod* overloadedMethod, PyObject* pyArgs, jboolean visitSuperClass, int *isVarArgsArray) { JPy_JOverloadedMethod* currentOM; JPy_MethodFindResult result; JPy_MethodFindResult bestResult; JPy_JType* superClass; PyObject* superOM; int argCount; argCount = PyTuple_Size(pyArgs); if ((JPy_DiagFlags & JPy_DIAG_F_METH) != 0) { int i; printf("JOverloadedMethod_FindMethod: argCount=%d, visitSuperClass=%d\n", argCount, visitSuperClass); for (i = 0; i < argCount; i++) { PyObject* pyArg = PyTuple_GetItem(pyArgs, i); printf("\tPy_TYPE(pyArgs[%d])->tp_name = %s\n", i, Py_TYPE(pyArg)->tp_name); } } bestResult.method = NULL; bestResult.matchValue = 0; bestResult.matchCount = 0; bestResult.isVarArgsArray = 0; currentOM = overloadedMethod; while (1) { if (JOverloadedMethod_FindMethod0(jenv, currentOM, pyArgs, &result) < 0) { // oops, error return NULL; } if (result.method != NULL) { // in the case where we have a match count that is perfect, but more than one match; the super class might // have a better match count, because varargs can have fewer arguments than actual parameters. if (result.matchValue >= 100 * argCount && result.matchCount == 1) { // We can't get any better. *isVarArgsArray = result.isVarArgsArray; return result.method; } else if (result.matchValue > 0 && result.matchValue > bestResult.matchValue) { // We may have better matching methods overloads in the super class (if any) bestResult = result; } } if (visitSuperClass) { superClass = currentOM->declaringClass->superType; if (superClass != NULL) { superOM = JType_GetOverloadedMethod(jenv, superClass, currentOM->name, JNI_TRUE); } else { superOM = Py_None; } } else { superOM = Py_None; } if (superOM == NULL) { // oops, error return NULL; } else if (superOM == Py_None) { // no overloaded methods found in super class, so return best result found so far if (bestResult.method == NULL) { PyErr_SetString(PyExc_RuntimeError, "no matching Java method overloads found"); return NULL; } else if (bestResult.matchCount > 1) { PyErr_SetString(PyExc_RuntimeError, "ambiguous Java method call, too many matching method overloads found"); return NULL; } else { *isVarArgsArray = bestResult.isVarArgsArray; return bestResult.method; } } else { // Continue trying with overloads from super type currentOM = (JPy_JOverloadedMethod*) superOM; } } // Should never come here PyErr_SetString(PyExc_RuntimeError, "internal error"); return NULL; } JPy_JOverloadedMethod* JOverloadedMethod_New(JPy_JType* declaringClass, PyObject* name, JPy_JMethod* method) { PyTypeObject* methodType = &JOverloadedMethod_Type; JPy_JOverloadedMethod* overloadedMethod; overloadedMethod = (JPy_JOverloadedMethod*) methodType->tp_alloc(methodType, 0); overloadedMethod->declaringClass = declaringClass; overloadedMethod->name = name; overloadedMethod->methodList = PyList_New(0); JPy_INCREF((PyObject*) overloadedMethod->declaringClass); JPy_INCREF((PyObject*) overloadedMethod->name); JPy_INCREF((PyObject*) overloadedMethod); JOverloadedMethod_AddMethod(overloadedMethod, method); return overloadedMethod; } int JOverloadedMethod_AddMethod(JPy_JOverloadedMethod* overloadedMethod, JPy_JMethod* method) { Py_ssize_t destinationIndex = -1; if (!method->isVarArgs) { Py_ssize_t ii; // we need to insert this before the first varargs method Py_ssize_t size = PyList_Size(overloadedMethod->methodList); for (ii = 0; ii < size; ii++) { // borrowed ref, no need to replace with PyList_GetItemRef() because the list won't be changed concurrently PyObject *check = PyList_GetItem(overloadedMethod->methodList, ii); if (((JPy_JMethod *) check)->isVarArgs) { // this is the first varargs method, so we should go before it destinationIndex = ii; break; } } } if (destinationIndex >= 0) { return PyList_Insert(overloadedMethod->methodList, destinationIndex, (PyObject *) method); } else { return PyList_Append(overloadedMethod->methodList, (PyObject *) method); } } /** * The 'JOverloadedMethod' type's tp_dealloc slot. */ void JOverloadedMethod_dealloc(JPy_JOverloadedMethod* self) { JPy_DECREF((PyObject*) self->declaringClass); JPy_DECREF((PyObject*) self->name); JPy_DECREF((PyObject*) self->methodList); Py_TYPE(self)->tp_free((PyObject*) self); } PyObject* JOverloadedMethod_call_internal(JNIEnv* jenv, JPy_JOverloadedMethod* self, PyObject *args, PyObject *kw) { JPy_JMethod* method; int isVarArgsArray; method = JOverloadedMethod_FindMethod(jenv, self, args, JNI_TRUE, &isVarArgsArray); if (method == NULL) { return NULL; } return JMethod_InvokeMethod(jenv, method, args, isVarArgsArray); } /** * The 'JOverloadedMethod' type's tp_call slot. Makes instances of the 'JOverloadedMethod' type callable. */ PyObject* JOverloadedMethod_call(JPy_JOverloadedMethod* self, PyObject *args, PyObject *kw) { JPy_FRAME(PyObject*, NULL, JOverloadedMethod_call_internal(jenv, self, args, kw), 16) } /** * The 'JOverloadedMethod' type's tp_repr slot. */ PyObject* JOverloadedMethod_repr(JPy_JOverloadedMethod* self) { const char* className = self->declaringClass->javaName; const char* name = JPy_AS_UTF8(self->name); int methodCount = PyList_Size(self->methodList); return JPy_FROM_FORMAT("%s(class='%s', name='%s', methodCount=%d)", ((PyObject*)self)->ob_type->tp_name, className, name, methodCount); } static PyMemberDef JOverloadedMethod_members[] = { {"decl_class", T_OBJECT_EX, offsetof(JPy_JOverloadedMethod, declaringClass), READONLY, "Declaring Java class"}, {"name", T_OBJECT_EX, offsetof(JPy_JOverloadedMethod, name), READONLY, "Overloaded method name"}, {"methods", T_OBJECT_EX, offsetof(JPy_JOverloadedMethod, methodList), READONLY, "List of methods"}, {NULL} /* Sentinel */ }; /** * The 'JOverloadedMethod' type's tp_str slot. */ PyObject* JOverloadedMethod_str(JPy_JOverloadedMethod* self) { JPy_INCREF(self->name); return self->name; } PyTypeObject JOverloadedMethod_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.JOverloadedMethod", /* tp_name */ sizeof (JPy_JOverloadedMethod), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)JOverloadedMethod_dealloc, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ (reprfunc)JOverloadedMethod_repr, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ (ternaryfunc)JOverloadedMethod_call, /* tp_call */ (reprfunc)JOverloadedMethod_str, /* tp_str */ NULL, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "Java Overloaded Method", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ JOverloadedMethod_members, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ NULL, /* tp_new */ }; jpy-2.0.0/src/main/c/jpy_jarray.c0000664000175000017500000002502615202503234016753 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jarray.h" #define PRINT_FLAG(F) printf("JArray_GetBufferProc: %s = %d\n", #F, (flags & F) != 0); #define PRINT_MEMB(F, M) printf("JArray_GetBufferProc: %s = " ## F ## "\n", #M, M); //#define JPy_USE_GET_PRIMITIVE_ARRAY_CRITICAL 1 /* * Implements the getbuffer() method of the buffer protocol for JPy_JArray objects. * Regarding the format parameter, refer to the Python 'struct' module documentation: * http://docs.python.org/2/library/struct.html#module-struct */ int JArray_GetBufferProc(JPy_JArray* self, Py_buffer* view, int flags, char javaType, jint itemSize, const char* format) { JNIEnv* jenv; jint itemCount; jboolean isCopy; void* buf; JPy_GET_JNI_ENV_OR_RETURN(jenv, -1) /* printf("JArray_GetBufferProc:\n"); PRINT_FLAG(PyBUF_ANY_CONTIGUOUS); PRINT_FLAG(PyBUF_CONTIG); PRINT_FLAG(PyBUF_CONTIG_RO); PRINT_FLAG(PyBUF_C_CONTIGUOUS); PRINT_FLAG(PyBUF_FORMAT); PRINT_FLAG(PyBUF_FULL); PRINT_FLAG(PyBUF_FULL_RO); PRINT_FLAG(PyBUF_F_CONTIGUOUS); PRINT_FLAG(PyBUF_INDIRECT); PRINT_FLAG(PyBUF_ND); PRINT_FLAG(PyBUF_READ); PRINT_FLAG(PyBUF_RECORDS); PRINT_FLAG(PyBUF_RECORDS_RO); PRINT_FLAG(PyBUF_SIMPLE); PRINT_FLAG(PyBUF_STRIDED); PRINT_FLAG(PyBUF_STRIDED_RO); PRINT_FLAG(PyBUF_STRIDES); PRINT_FLAG(PyBUF_WRITE); PRINT_FLAG(PyBUF_WRITEABLE); */ itemCount = (*jenv)->GetArrayLength(jenv, self->objectRef); // According to Python documentation, // buffer allocation shall be done in the 5 following steps; // Step 1/5 #ifdef JPy_USE_GET_PRIMITIVE_ARRAY_CRITICAL buf = (*jenv)->GetPrimitiveArrayCritical(jenv, self->objectRef, &isCopy); #else if (self->buf == NULL) { if (javaType == 'Z') { buf = (*jenv)->GetBooleanArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'C') { buf = (*jenv)->GetCharArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'B') { buf = (*jenv)->GetByteArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'S') { buf = (*jenv)->GetShortArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'I') { buf = (*jenv)->GetIntArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'J') { buf = (*jenv)->GetLongArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'F') { buf = (*jenv)->GetFloatArrayElements(jenv, self->objectRef, &isCopy); } else if (javaType == 'D') { buf = (*jenv)->GetDoubleArrayElements(jenv, self->objectRef, &isCopy); } else { PyErr_Format(PyExc_RuntimeError, "internal error: illegal Java array type '%c'", javaType); return -1; } self->buf = buf; self->javaType = javaType; self->isCopy = isCopy; self->bufReadonly = (flags & (PyBUF_WRITE | PyBUF_WRITEABLE)) == 0; } else { buf = self->buf; } #endif if (buf == NULL) { PyErr_NoMemory(); return -1; } JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JArray_GetBufferProc: buf=%p, bufferExportCount=%d, type='%s', format='%s', itemSize=%d, itemCount=%d, isCopy=%d\n", buf, self->bufferExportCount, Py_TYPE(self)->tp_name, format, itemSize, itemCount, isCopy); // Step 2/5 view->buf = buf; view->len = (Py_ssize_t)itemCount * (Py_ssize_t)itemSize; view->itemsize = itemSize; view->readonly = (flags & (PyBUF_WRITE | PyBUF_WRITEABLE)) == 0; self->bufReadonly &= view->readonly; view->ndim = 1; view->shape = PyMem_New(Py_ssize_t, 1); *view->shape = itemCount; view->strides = PyMem_New(Py_ssize_t, 1); *view->strides = itemSize; view->suboffsets = NULL; if ((flags & PyBUF_FORMAT) != 0) { view->format = (char*) format; } else { view->format = (char*) "B"; } /* PRINT_MEMB("%d", view->len); PRINT_MEMB("%d", view->ndim); PRINT_MEMB("%s", view->format); PRINT_MEMB("%d", view->itemsize); PRINT_MEMB("%d", view->readonly); PRINT_MEMB("%d", view->shape[0]); PRINT_MEMB("%d", view->strides[0]); */ // Step 3/5 self->bufferExportCount++; // Step 4/5 view->obj = (PyObject*) self; JPy_INCREF(view->obj); // Step 5/5 return 0; } int JArray_getbufferproc_boolean(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'Z', 1, "B"); } int JArray_getbufferproc_char(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'C', 2, "H"); } int JArray_getbufferproc_byte(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'B', 1, "b"); } int JArray_getbufferproc_short(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'S', 2, "h"); } int JArray_getbufferproc_int(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'I', 4, "i"); } int JArray_getbufferproc_long(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'J', 8, "q"); } int JArray_getbufferproc_float(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'F', 4, "f"); } int JArray_getbufferproc_double(JPy_JArray* self, Py_buffer* view, int flags) { return JArray_GetBufferProc(self, view, flags, 'D', 8, "d"); } /* * */ void JArray_ReleaseJavaArrayElements(JPy_JArray* self, char javaType) { JNIEnv* jenv = JPy_GetJNIEnv(); if (!self->buf) return; if (jenv != NULL) { #ifdef JPy_USE_GET_PRIMITIVE_ARRAY_CRITICAL (*jenv)->ReleasePrimitiveArrayCritical(jenv, self->objectRef, view->buf, view->readonly ? JNI_ABORT : 0); #else if (javaType == 'Z') { (*jenv)->ReleaseBooleanArrayElements(jenv, self->objectRef, (jboolean*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'C') { (*jenv)->ReleaseCharArrayElements(jenv, self->objectRef, (jchar*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'B') { (*jenv)->ReleaseByteArrayElements(jenv, self->objectRef, (jbyte*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'S') { (*jenv)->ReleaseShortArrayElements(jenv, self->objectRef, (jshort*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'I') { (*jenv)->ReleaseIntArrayElements(jenv, self->objectRef, (jint*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'J') { (*jenv)->ReleaseLongArrayElements(jenv, self->objectRef, (jlong*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'F') { (*jenv)->ReleaseFloatArrayElements(jenv, self->objectRef, (jfloat*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } else if (javaType == 'D') { (*jenv)->ReleaseDoubleArrayElements(jenv, self->objectRef, (jdouble*) self->buf, self->bufReadonly ? JNI_ABORT : 0); } #endif } } /* * Implements the releasebuffer() method the buffer protocol for JPy_JArray objects */ void JArray_ReleaseBufferProc(JPy_JArray* self, Py_buffer* view, char javaType) { // Step 1 self->bufferExportCount--; JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JArray_ReleaseBufferProc: buf=%p, bufferExportCount=%d\n", view->buf, self->bufferExportCount); // Step 2 // defer the release of Java buffer to dealloc // Note: this function is *not* responsible for PyDECREF of view->obj // https://docs.python.org/3/c-api/typeobj.html#c.PyBufferProcs.bf_releasebuffer } void JArray_releasebufferproc_boolean(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'Z'); } void JArray_releasebufferproc_char(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'C'); } void JArray_releasebufferproc_byte(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'B'); } void JArray_releasebufferproc_short(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'S'); } void JArray_releasebufferproc_int(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'I'); } void JArray_releasebufferproc_long(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'J'); } void JArray_releasebufferproc_float(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'F'); } void JArray_releasebufferproc_double(JPy_JArray* self, Py_buffer* view) { JArray_ReleaseBufferProc(self, view, 'D'); } // PyBufferProcs 3.x // // struct PyBufferProcs { // getbufferproc bf_getbuffer; // releasebufferproc bf_releasebuffer; // } PyBufferProcs JArray_as_buffer_boolean = { (getbufferproc) JArray_getbufferproc_boolean, (releasebufferproc) JArray_releasebufferproc_boolean }; PyBufferProcs JArray_as_buffer_char = { (getbufferproc) JArray_getbufferproc_char, (releasebufferproc) JArray_releasebufferproc_char }; PyBufferProcs JArray_as_buffer_byte = { (getbufferproc) JArray_getbufferproc_byte, (releasebufferproc) JArray_releasebufferproc_byte }; PyBufferProcs JArray_as_buffer_short = { (getbufferproc) JArray_getbufferproc_short, (releasebufferproc) JArray_releasebufferproc_short }; PyBufferProcs JArray_as_buffer_int = { (getbufferproc) JArray_getbufferproc_int, (releasebufferproc) JArray_releasebufferproc_int }; PyBufferProcs JArray_as_buffer_long = { (getbufferproc) JArray_getbufferproc_long, (releasebufferproc) JArray_releasebufferproc_long }; PyBufferProcs JArray_as_buffer_float = { (getbufferproc) JArray_getbufferproc_float, (releasebufferproc) JArray_releasebufferproc_float }; PyBufferProcs JArray_as_buffer_double = { (getbufferproc) JArray_getbufferproc_double, (releasebufferproc) JArray_releasebufferproc_double }; jpy-2.0.0/src/main/c/jpy_compat.h0000664000175000017500000000310015202503234016740 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_COMPAT_H #define JPY_COMPAT_H #ifdef __cplusplus extern "C" { #endif #include #include "frameobject.h" #define JPY_VERSION_ERROR "jpy requires Python 3.9+" #if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 9 #error JPY_VERSION_ERROR #endif #define JPy_IS_CLONG(pyArg) PyLong_Check(pyArg) #define JPy_AS_CLONG(pyArg) PyLong_AsLong(pyArg) #define JPy_AS_CLONGLONG(pyArg) PyLong_AsLongLong(pyArg) #define JPy_FROM_CLONG(cl) PyLong_FromLong(cl) #define JPy_IS_STR(pyArg) PyUnicode_Check(pyArg) #define JPy_FROM_CSTR(cstr) PyUnicode_FromString(cstr) #define JPy_FROM_FORMAT PyUnicode_FromFormat #define JPy_AS_UTF8(unicode) PyUnicode_AsUTF8(unicode) #define JPy_AS_WIDE_CHAR_STR(unicode, size) PyUnicode_AsWideCharString(unicode, size) #define JPy_FROM_WIDE_CHAR_STR(wc, size) PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, wc, size) #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_COMPAT_H */ jpy-2.0.0/src/main/c/jpy_jtype.h0000664000175000017500000001132115202503234016614 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * * This file was modified by Deephaven Data Labs. * */ #ifndef JPY_JTYPE_H #define JPY_JTYPE_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * The Python type 'JType' representing a Java type. */ typedef struct JPy_JType { // Since this is a type object, inherit everything from from PyTypeObject. // It is absolutely essential that this is the first struct member! // typeObj.tp_name is this type's fully qualified Java name (same as 'javaName' field). PyTypeObject typeObj; // The Java type name. char* javaName; // The JNI class reference (global reference). jclass classRef; // The super type of this type. This will be NULL for primitive types, 'void', and 'java.lang.Object'. struct JPy_JType* superType; // If component type of this type if this type is an array, NULL otherwise. struct JPy_JType* componentType; // If TRUE, 'classRef' refers to a Java primitive type or 'void'. char isPrimitive; // If TRUE, 'classRef' refers to a Java interface type. char isInterface; // If TRUE, the type is currently being resolved. char isResolving; // If TRUE, all the class constructors and methods have already been resolved. char isResolved; } JPy_JType; #define JTYPE_AS_PYTYPE(T) ((PyTypeObject*) T) /** * The 'JType' singleton. */ extern PyTypeObject JType_Type; typedef void (*JPy_DisposeArg)(JNIEnv*, jvalue* value, void* data); /** * ArgDisposers are used to dispose arguments after invocation of Java methods. * We need to dispose those arguments which have been created as local references, * e.g. jenv->NewString(), jenv->NewObjectArray(), jenv->NewArray(). */ typedef struct JPy_ArgDisposer { void* data; JPy_DisposeArg DisposeArg; } JPy_ArgDisposer; struct JPy_ParamDescriptor; typedef int (*JPy_MatchPyArg)(JNIEnv*, struct JPy_ParamDescriptor*, PyObject*); typedef int (*JPy_MatchVarArgPyArg)(JNIEnv*, struct JPy_ParamDescriptor*, PyObject*, int); typedef int (*JPy_ConvertPyArg)(JNIEnv*, struct JPy_ParamDescriptor*, PyObject*, jvalue*, JPy_ArgDisposer*); typedef int (*JPy_ConvertVarArgPyArg)(JNIEnv*, struct JPy_ParamDescriptor*, PyObject*, int, jvalue*, JPy_ArgDisposer*); /** * Method return value descriptor. */ typedef struct JPy_ReturnDescriptor { /** * The return type. */ JPy_JType* type; /** * If JPy_ParamDescriptor.isReturnIndex == TRUE the index of the parameter, whose argument will serve as return value. * If JPy_ParamDescriptor.isReturnIndex == FALSE it will be -1. */ jint paramIndex; } JPy_ReturnDescriptor; /** * Method parameter descriptor. */ typedef struct JPy_ParamDescriptor { JPy_JType* type; jboolean isMutable; jboolean isOutput; jboolean isReturn; JPy_MatchPyArg MatchPyArg; JPy_MatchVarArgPyArg MatchVarArgPyArg; JPy_ConvertPyArg ConvertPyArg; JPy_ConvertVarArgPyArg ConvertVarArgPyArg; } JPy_ParamDescriptor; int JType_Check(PyObject* obj); JPy_JType* JType_GetTypeForObject(JNIEnv* jenv, jobject objectRef, jboolean resolve); JPy_JType* JType_GetTypeForName(JNIEnv* jenv, const char* typeName, jboolean resolve); JPy_JType* JType_GetType(JNIEnv* jenv, jclass classRef, jboolean resolve); PyObject* JType_ConvertJavaToPythonObject(JNIEnv* jenv, JPy_JType* type, jobject objectRef); int JType_ConvertPythonToJavaObject(JNIEnv* jenv, JPy_JType* type, PyObject* arg, jobject* objectRef, jboolean allowObjectWrapping); PyObject* JType_GetOverloadedMethod(JNIEnv* jenv, JPy_JType* type, PyObject* methodName, jboolean useSuperClass); int JType_MatchPyArgAsJObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg); int JType_CreateJavaPyObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, jobject* objectRef); int JType_CreateJavaArray(JNIEnv* jenv, JPy_JType* componentType, PyObject* pyArg, jobject* objectRef, jboolean allowObjectWrapping); // Non-API. Defined in jpy_jobj.c int JType_InitSlots(JPy_JType* type); // Non-API. Defined in jpy_jtype.c int JType_ResolveType(JNIEnv* jenv, JPy_JType* type); int JType_AddClassAttribute(JNIEnv* jenv, JPy_JType* type); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JTYPE_H */jpy-2.0.0/src/main/c/jpy_jmethod.h0000664000175000017500000000646515202503234017130 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. */ #ifndef JPY_JMETHOD_H #define JPY_JMETHOD_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * Python object representing a Java method. It's type is 'JMethod'. */ typedef struct { PyObject_HEAD // The declaring class. JPy_JType* declaringClass; // Method name. PyObject* name; // Method parameter count. int paramCount; // Method is static? char isStatic; // Method is varargs? char isVarArgs; // Method parameter types. Will be NULL, if parameter_count == 0. JPy_ParamDescriptor* paramDescriptors; // Method return type. Will be NULL for constructors. JPy_ReturnDescriptor* returnDescriptor; // The JNI method ID obtained from the declaring class. jmethodID mid; } JPy_JMethod; /** * The Python 'JMethod' type singleton. */ extern PyTypeObject JMethod_Type; /** * Python object representing an overloaded Java method. It's type is 'JOverloadedMethod'. */ typedef struct { PyObject_HEAD // The declaring class. JPy_JType* declaringClass; // Method name. PyObject* name; // List of method overloads (a PyList with items of type JPy_JMethod). PyObject* methodList; } JPy_JOverloadedMethod; /** * The Python 'JOverloadedMethod' type singleton. */ extern PyTypeObject JOverloadedMethod_Type; JPy_JMethod* JOverloadedMethod_FindMethod(JNIEnv* jenv, JPy_JOverloadedMethod* overloadedMethod, PyObject* argTuple, jboolean visitSuperClass, int *isVarArgsArray); JPy_JMethod* JOverloadedMethod_FindStaticMethod(JPy_JOverloadedMethod* overloadedMethod, PyObject* argTuple); JPy_JOverloadedMethod* JOverloadedMethod_New(JPy_JType* declaringClass, PyObject* name, JPy_JMethod* method); int JOverloadedMethod_AddMethod(JPy_JOverloadedMethod* overloadedMethod, JPy_JMethod* method); JPy_JMethod* JMethod_New(JPy_JType* declaringClass, PyObject* name, int paramCount, JPy_ParamDescriptor* paramDescriptors, JPy_ReturnDescriptor* returnDescriptor, jboolean isStatic, jboolean isVarArgs, jmethodID mid); void JMethod_Del(JPy_JMethod* method); int JMethod_ConvertToJavaValues(JNIEnv* jenv, JPy_JMethod* jMethod, int argCount, PyObject* argTuple, jvalue* jArgs); int JMethod_CreateJArgs(JNIEnv* jenv, JPy_JMethod* jMethod, PyObject* argTuple, jvalue** jValues, JPy_ArgDisposer** jDisposers, int isVarArgsArray); void JMethod_DisposeJArgs(JNIEnv* jenv, int paramCount, jvalue* jValues, JPy_ArgDisposer* jDisposers); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JMETHOD_H */jpy-2.0.0/src/main/c/jpy_module.h0000664000175000017500000002041415202503234016751 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #ifndef JPY_MODULE_H #define JPY_MODULE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "jpy_compat.h" #if 1 #define JPy_DECREF(x) Py_DECREF(x) #define JPy_INCREF(x) Py_INCREF(x) #define JPy_XINCREF(x) Py_XINCREF(x) #define JPy_XDECREF(x) Py_XDECREF(x) #else #include "jpy_diag.h" #define JPy_DECREF(x) do { \ JPy_DiagPrint(JPy_DIAG_F_ALL, "JPy_DECREF: %s:%d %p %ld\n", __FILE__, __LINE__, x, ((PyObject *)x)->ob_refcnt);\ Py_DECREF(x);\ } while (0) #define JPy_INCREF(x) do { \ JPy_DiagPrint(JPy_DIAG_F_ALL, "JPy_INCREF: %s:%d %p %ld\n", __FILE__, __LINE__, x, ((PyObject *)x)->ob_refcnt);\ Py_INCREF(x);\ } while (0) #define JPy_XDECREF(x) do { \ JPy_DiagPrint(JPy_DIAG_F_ALL, "JPy_XDECREF: %s:%d %p %ld\n", __FILE__, __LINE__, x, x == NULL ? -1 : ((PyObject *)x)->ob_refcnt);\ Py_XDECREF(x);\ } while (0) #endif #define JPY_JNI_VERSION JNI_VERSION_1_6 extern PyObject* JPy_Module; extern PyObject* JPy_Types; extern PyObject* JPy_Type_Callbacks; extern PyObject* JPy_Type_Translations; extern PyObject* JException_Type; extern JavaVM* JPy_JVM; extern jboolean JPy_MustDestroyJVM; #define JPy_JTYPE_ATTR_NAME_JINIT "__jinit__" #define JPy_MODULE_ATTR_NAME_TYPES "types" #define JPy_MODULE_ATTR_NAME_TYPE_CALLBACKS "type_callbacks" #define JPy_MODULE_ATTR_NAME_TYPE_TRANSLATIONS "type_translations" /** * Gets the current JNI environment pointer. * Returns NULL, if the JVM is down. * * General jpy design guideline: Use the JPy_GetJNIEnv function only in entry points from Python calls into C. * Add a JNIEnv* as first parameter to all functions that require it. */ JNIEnv* JPy_GetJNIEnv(void); int JPy_InitGlobalVars(JNIEnv* jenv); void JPy_ClearGlobalVars(JNIEnv* jenv); void JPy_free(); /** * Gets the current JNI environment pointer JENV. If this is NULL, it returns the given RET_VALUE. * Warning: This method may immediately return, so make sure there will be no memory leaks in this case. * * General jpy design guideline: Use the JPy_GET_JNI_ENV_OR_RETURN macro only in entry points from Python calls into C. * Add a JNIEnv* as first parameter to all functions that require it. */ #define JPy_GET_JNI_ENV_OR_RETURN(JENV, RET_VALUE) \ if ((JENV = JPy_GetJNIEnv()) == NULL) { \ return (RET_VALUE); \ } else { \ } /** * Fetches the last Java exception occurred and raises a new Python exception. */ void JPy_HandleJavaException(JNIEnv* jenv); #define JPy_DELETE_LOCAL_REF(VALUE) \ (*jenv)->DeleteLocalRef(jenv, VALUE); \ VALUE = NULL; #define JPy_ON_JAVA_EXCEPTION_GOTO(LABEL) \ if ((*jenv)->ExceptionCheck(jenv)) { \ JPy_HandleJavaException(jenv); \ goto LABEL; \ } #define JPy_ON_JAVA_EXCEPTION_RETURN(VALUE) \ if ((*jenv)->ExceptionCheck(jenv)) { \ JPy_HandleJavaException(jenv); \ return VALUE; \ } #define JPy_FRAME(TYPE, ON_ERROR, FUNCTION, FRAME_SIZE) \ JNIEnv* jenv; \ TYPE result; \ if ((jenv = JPy_GetJNIEnv()) == NULL) { \ return ON_ERROR; \ } \ if ((*jenv)->PushLocalFrame(jenv, FRAME_SIZE) < 0) { \ JPy_HandleJavaException(jenv); \ return ON_ERROR; \ } \ result = FUNCTION; \ (*jenv)->PopLocalFrame(jenv, NULL); \ return result; struct JPy_JType; extern struct JPy_JType* JPy_JBoolean; extern struct JPy_JType* JPy_JChar; extern struct JPy_JType* JPy_JByte; extern struct JPy_JType* JPy_JShort; extern struct JPy_JType* JPy_JInt; extern struct JPy_JType* JPy_JLong; extern struct JPy_JType* JPy_JFloat; extern struct JPy_JType* JPy_JDouble; extern struct JPy_JType* JPy_JVoid; extern struct JPy_JType* JPy_JBooleanObj; extern struct JPy_JType* JPy_JCharacterObj; extern struct JPy_JType* JPy_JByteObj; extern struct JPy_JType* JPy_JShortObj; extern struct JPy_JType* JPy_JIntegerObj; extern struct JPy_JType* JPy_JLongObj; extern struct JPy_JType* JPy_JFloatObj; extern struct JPy_JType* JPy_JDoubleObj; extern struct JPy_JType* JPy_JObject; extern struct JPy_JType* JPy_JClass; extern struct JPy_JType* JPy_JString; extern struct JPy_JType* JPy_JPyObject; extern struct JPy_JType* JPy_JPyModule; extern struct JPy_JType* JPy_JByteBuffer; // java.lang.Comparable extern jclass JPy_Comparable_JClass; extern jmethodID JPy_Comparable_CompareTo_MID; // java.lang.Object extern jclass JPy_Object_JClass; extern jmethodID JPy_Object_ToString_MID; extern jmethodID JPy_Object_HashCode_MID; extern jmethodID JPy_Object_Equals_MID; // java.lang.Class extern jclass JPy_Class_JClass; extern jmethodID JPy_Class_GetName_MID; extern jmethodID JPy_Class_GetDeclaredConstructors_MID; extern jmethodID JPy_Class_GetDeclaredFields_MID; extern jmethodID JPy_Class_GetDeclaredMethods_MID; extern jmethodID JPy_Class_GetFields_MID; extern jmethodID JPy_Class_GetMethods_MID; extern jmethodID JPy_Class_GetComponentType_MID; extern jmethodID JPy_Class_IsPrimitive_MID; extern jmethodID JPy_Class_IsInterface_MID; // java.lang.reflect.Constructor extern jclass JPy_Constructor_JClass; extern jmethodID JPy_Constructor_GetModifiers_MID; extern jmethodID JPy_Constructor_GetParameterTypes_MID; // java.lang.reflect.Method extern jclass JPy_Method_JClass; extern jmethodID JPy_Method_GetName_MID; extern jmethodID JPy_Method_GetModifiers_MID; extern jmethodID JPy_Method_GetParameterTypes_MID; extern jmethodID JPy_Method_GetReturnType_MID; // java.lang.reflect.Field extern jclass JPy_Field_JClass; extern jmethodID JPy_Field_GetName_MID; extern jmethodID JPy_Field_GetModifiers_MID; extern jmethodID JPy_Field_GetType_MID; // java.util.Map extern jclass JPy_Map_JClass; extern jclass JPy_Map_Entry_JClass; extern jmethodID JPy_Map_entrySet_MID; extern jmethodID JPy_Map_put_MID; extern jmethodID JPy_Map_clear_MID; extern jmethodID JPy_Map_Entry_getKey_MID; extern jmethodID JPy_Map_Entry_getValue_MID; // java.util.Set extern jclass JPy_Set_JClass; extern jmethodID JPy_Set_Iterator_MID; // java.util.Iterator extern jclass JPy_Iterator_JClass; extern jmethodID JPy_Iterator_next_MID; extern jmethodID JPy_Iterator_hasNext_MID; extern jclass JPy_RuntimeException_JClass; extern jclass JPy_OutOfMemoryError_JClass; extern jclass JPy_FileNotFoundException_JClass; extern jclass JPy_UnsupportedOperationException_JClass; extern jclass JPy_KeyError_JClass; extern jclass JPy_StopIteration_JClass; extern jclass JPy_Boolean_JClass; extern jmethodID JPy_Boolean_ValueOf_SMID; extern jmethodID JPy_Boolean_BooleanValue_MID; extern jclass JPy_Character_JClass; extern jmethodID JPy_Character_ValueOf_SMID; extern jmethodID JPy_Character_CharValue_MID; extern jclass JPy_Number_JClass; extern jclass JPy_Byte_JClass; extern jmethodID JPy_Byte_ValueOf_SMID; extern jclass JPy_Short_JClass; extern jmethodID JPy_Short_ValueOf_SMID; extern jclass JPy_Integer_JClass; extern jmethodID JPy_Integer_ValueOf_SMID; extern jclass JPy_Long_JClass; extern jmethodID JPy_Long_ValueOf_SMID; extern jclass JPy_Float_JClass; extern jmethodID JPy_Float_ValueOf_SMID; extern jclass JPy_Double_JClass; extern jmethodID JPy_Double_ValueOf_SMID; extern jclass JPy_Number_JClass; extern jmethodID JPy_Number_IntValue_MID; extern jmethodID JPy_Number_LongValue_MID; extern jmethodID JPy_Number_DoubleValue_MID; extern jclass JPy_String_JClass; extern jclass JPy_Void_JClass; extern jclass JPy_ByteBuffer_JClass; extern jmethodID JPy_ByteBuffer_AsReadOnlyBuffer_MID; extern jclass JPy_PyObject_JClass; extern jmethodID JPy_PyObject_GetPointer_MID; extern jmethodID JPy_PyObject_UnwrapProxy_SMID; extern jmethodID JPy_PyObject_Init_MID; extern jclass JPy_PyDictWrapper_JClass; extern jmethodID JPy_PyDictWrapper_GetPointer_MID; extern jclass JPy_Supplier_JClass; extern jmethodID JPy_Supplier_get_MID; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_MODULE_H */jpy-2.0.0/src/main/c/jpy_conv.h0000664000175000017500000000705615202503234016440 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_CONV_H #define JPY_CONV_H #include "jpy_compat.h" #ifdef __cplusplus extern "C" { #endif #define JPy_AS_JBOOLEAN(pyArg) (jboolean) (pyArg == Py_True ? 1 : (pyArg == Py_False || pyArg == Py_None) ? 0 : PyObject_IsTrue(pyArg)) #define JPy_AS_JCHAR(pyArg) (jchar) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) #define JPy_AS_JBYTE(pyArg) (jbyte) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) #define JPy_AS_JSHORT(pyArg) (jshort) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) #define JPy_AS_JINT(pyArg) (jint) (pyArg == Py_None ? 0 : JPy_AS_CLONG(pyArg)) #define JPy_AS_JLONG(pyArg) (jlong) (pyArg == Py_None ? 0 : JPy_AS_CLONGLONG(pyArg)) #define JPy_AS_JFLOAT(pyArg) (jfloat) (pyArg == Py_None ? 0 : PyFloat_AsDouble(pyArg)) #define JPy_AS_JDOUBLE(pyArg) (jdouble) (pyArg == Py_None ? 0 : PyFloat_AsDouble(pyArg)) #define JPy_FROM_JBOOLEAN(jArg) PyBool_FromLong(jArg) #define JPy_FROM_JCHAR(jArg) PyLong_FromLong(jArg) #define JPy_FROM_JBYTE(jArg) PyLong_FromLong(jArg) #define JPy_FROM_JSHORT(jArg) PyLong_FromLong(jArg) #define JPy_FROM_JINT(jArg) PyLong_FromLong(jArg) #define JPy_FROM_JLONG(jArg) PyLong_FromLongLong(jArg) #define JPy_FROM_JFLOAT(jArg) PyFloat_FromDouble(jArg) #define JPy_FROM_JDOUBLE(jArg) PyFloat_FromDouble(jArg) #define JPy_PY_NONE() Py_BuildValue("") // Builds a Python 'None' object #define JPy_FROM_JVOID() JPy_PY_NONE() #define JPy_FROM_JNULL() JPy_PY_NONE() /** * Convert Java string to Python string/unicode object. */ PyObject* JPy_FromJString(JNIEnv* jenv, jstring stringRef); /** * Convert any Java Object to Python Object. */ PyObject* JPy_FromJObject(JNIEnv* jenv, jobject objectRef); /** * Convert any Java Object of known type to Python Object. */ PyObject* JPy_FromJObjectWithType(JNIEnv* jenv, jobject objectRef, JPy_JType* type); /** * Convert Python unicode object to Java String. */ int JPy_AsJString(JNIEnv* jenv, PyObject* pyObj, jstring* stringRef); /** * Convert any Python objects to Java object. * * @param allowObjectWrapping if true, may return a PyObject for unrecognized object types */ int JPy_AsJObject(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jboolean allowObjectWrapping); /** * Convert Python objects to Java object with known type. */ int JPy_AsJObjectWithType(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, JPy_JType* type); /** * Convert Python objects to Java object with known type. */ int JPy_AsJObjectWithClass(JNIEnv* jenv, PyObject* pyObj, jobject* objectRef, jclass classRef); /** * Creates a Python unicode object representing the name of the given class. * Returns a new reference. */ PyObject* JPy_FromTypeName(JNIEnv* jenv, jclass classRef); /** * Gets the UTF8-encoded name of the given Java type. * Caller is responsible for freeing the returned string using PyMem_Del(). */ char* JPy_GetTypeName(JNIEnv* jenv, jclass classRef); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_CONV_H */jpy-2.0.0/src/main/c/jpy_jfield.c0000664000175000017500000001065715202503234016724 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jtype.h" #include "jpy_jobj.h" #include "jpy_jfield.h" #include "jpy_conv.h" #include "jpy_compat.h" JPy_JField* JField_New(JPy_JType* declaringClass, PyObject* fieldName, JPy_JType* fieldType, jboolean isStatic, jboolean isFinal, jfieldID fid) { PyTypeObject* type = &JField_Type; JPy_JField* field; field = (JPy_JField*) type->tp_alloc(type, 0); field->declaringClass = declaringClass; field->name = fieldName; field->type = fieldType; field->isStatic = isStatic; field->isFinal = isFinal; field->fid = fid; JPy_INCREF(field->name); JPy_INCREF(field->type); return field; } /** * The JField type's tp_dealloc slot. */ void JField_dealloc(JPy_JField* self) { JPy_DECREF(self->name); JPy_DECREF(self->type); Py_TYPE(self)->tp_free((PyObject*) self); } void JField_Del(JPy_JField* field) { JField_dealloc(field); } /** * The JField type's tp_repr slot. */ PyObject* JField_repr(JPy_JField* self) { const char* name = JPy_AS_UTF8(self->name); return JPy_FROM_FORMAT("%s(name='%s', is_static=%d, is_final=%d, fid=%p)", ((PyObject*)self)->ob_type->tp_name, name, self->isStatic, self->isFinal, self->fid); } /** * The JField type's tp_str slot. */ PyObject* JField_str(JPy_JField* self) { JPy_INCREF(self->name); return self->name; } static PyMemberDef JField_members[] = { {"name", T_OBJECT_EX, offsetof(JPy_JField, name), READONLY, "Field name"}, {"is_static", T_BOOL, offsetof(JPy_JField, isStatic), READONLY, "Tests if this is a static field"}, {"is_final", T_BOOL, offsetof(JPy_JField, isFinal), READONLY, "Tests if this is a final field"}, {NULL} /* Sentinel */ }; /** * Implements the BeamPy_JObjectType class singleton. */ PyTypeObject JField_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.JField", /* tp_name */ sizeof (JPy_JField), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)JField_dealloc, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ (reprfunc)JField_repr, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ (reprfunc)JField_str, /* tp_str */ NULL, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "Java Field Wrapper", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ JField_members, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ NULL, /* tp_new */ };jpy-2.0.0/src/main/c/jpy_jbyte_buffer.h0000664000175000017500000000226415202503234020135 0ustar alastairalastair/* * Copyright 2023 JPY-CONSORTIUM Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_JBYTE_BUFFER_H #define JPY_JBYTE_BUFFER_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * The Java ByteBuffer representation in Python. * * IMPORTANT: JPy_JByteBufferObj must only differ from the JPy_JObj structure by the 'pyBuffer' member * since we use the same basic type, name JPy_JType for it. DON'T ever change member positions! * @see JPy_JObj */ typedef struct JPy_JByteBufferObj { PyObject_HEAD jobject objectRef; Py_buffer *pyBuffer; } JPy_JByteBufferObj; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JBYTE_BUFFER_H */ jpy-2.0.0/src/main/c/jpy_jbyte_buffer.c0000664000175000017500000000144615202503234020131 0ustar alastairalastair/* * Copyright 2023 JPY-CONSORTIUM Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "jpy_module.h" #include "jpy_diag.h" #include "jpy_jarray.h" #include "jpy_jbyte_buffer.h" /* * This file for now is just a place-holder for future JPy_JByteBufferObj specific functions. */jpy-2.0.0/src/main/c/jpy_jfield.h0000664000175000017500000000266015202503234016724 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_JFIELD_H #define JPY_JFIELD_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * Python object representing a Java method. It's type is 'JMethod'. */ typedef struct { PyObject_HEAD // The declaring class. JPy_JType* declaringClass; // Field name. PyObject* name; // Field type. JPy_JType* type; // Method is static? char isStatic; // Method is final? char isFinal; // Field ID retrieved from JNI. jfieldID fid; } JPy_JField; /** * The Python 'JMethod' type singleton. */ extern PyTypeObject JField_Type; JPy_JField* JField_New(JPy_JType* declaringType, PyObject* fieldKey, JPy_JType* fieldType, jboolean isStatic, jboolean isFinal, jfieldID fid); void JField_Del(JPy_JField* field); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JFIELD_H */jpy-2.0.0/src/main/c/jpy_diag.c0000664000175000017500000001272115202503234016365 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "structmember.h" #include "jpy_diag.h" #include "jpy_compat.h" int JPy_DiagFlags = JPy_DIAG_F_OFF; void JPy_DiagPrint(int diagFlags, const char * format, ...) { if ((JPy_DiagFlags & diagFlags) != 0) { va_list args; va_start(args, format); vfprintf(stdout, format, args); fflush(stdout); va_end(args); } } PyObject* Diag_New(void) { JPy_Diag* self; self = (JPy_Diag*) PyObject_New(PyObject, &Diag_Type); self->F_OFF = JPy_DIAG_F_OFF; self->F_TYPE = JPy_DIAG_F_TYPE; self->F_METH = JPy_DIAG_F_METH; self->F_EXEC = JPy_DIAG_F_EXEC; self->F_MEM = JPy_DIAG_F_MEM; self->F_JVM = JPy_DIAG_F_JVM; self->F_ERR = JPy_DIAG_F_ERR; self->F_ALL = JPy_DIAG_F_ALL; return (PyObject*) self; } PyObject* Diag_getattro(JPy_Diag* self, PyObject *attr_name) { //printf("Diag_getattro: attr_name=%s\n", JPy_AS_UTF8(attr_name)); if (strcmp(JPy_AS_UTF8(attr_name), "flags") == 0) { return JPy_FROM_CLONG(JPy_DiagFlags); } else { return PyObject_GenericGetAttr((PyObject*) self, attr_name); } } int Diag_setattro(JPy_Diag* self, PyObject *attr_name, PyObject *v) { //printf("Diag_setattro: attr_name=%s\n", JPy_AS_UTF8(attr_name)); if (strcmp(JPy_AS_UTF8(attr_name), "flags") == 0) { if (JPy_IS_CLONG(v)) { JPy_DiagFlags = self->flags = (int) JPy_AS_CLONG(v); } else { PyErr_SetString(PyExc_ValueError, "value for 'flags' must be an integer number"); return -1; } return 0; } else { return PyObject_GenericSetAttr((PyObject*) self, attr_name, v); } } static PyMemberDef Diag_members[] = { {"flags", T_INT, offsetof(JPy_Diag, flags), READONLY, "Combination of diagnostic flags (F_* constants). If != 0, diagnostic messages are printed out."}, {"F_OFF", T_INT, offsetof(JPy_Diag, F_OFF), READONLY, "Don't print any diagnostic messages"}, {"F_TYPE", T_INT, offsetof(JPy_Diag, F_TYPE), READONLY, "Type resolution: print diagnostic messages while generating Python classes from Java classes"}, {"F_METH", T_INT, offsetof(JPy_Diag, F_METH), READONLY, "Method resolution: print diagnostic messages while resolving Java overloaded methods"}, {"F_EXEC", T_INT, offsetof(JPy_Diag, F_EXEC), READONLY, "Execution: print diagnostic messages when Java code is executed"}, {"F_MEM", T_INT, offsetof(JPy_Diag, F_MEM), READONLY, "Memory: print diagnostic messages when wrapped Java objects are allocated/deallocated"}, {"F_JVM", T_INT, offsetof(JPy_Diag, F_JVM), READONLY, "JVM: print diagnostic information usage of the Java VM Invocation API"}, {"F_ERR", T_INT, offsetof(JPy_Diag, F_ERR), READONLY, "Errors: print diagnostic information when erroneous states are detected"}, {"F_ALL", T_INT, offsetof(JPy_Diag, F_ALL), READONLY, "Print any diagnostic messages"}, {NULL} /* Sentinel */ }; PyTypeObject Diag_Type = { PyVarObject_HEAD_INIT(NULL, 0) "jpy.Diag", /* tp_name */ sizeof (JPy_Diag), /* tp_basicsize */ 0, /* tp_itemsize */ NULL, /* tp_dealloc */ 0, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ NULL, /* tp_reserved */ NULL, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ NULL, /* tp_str */ (getattrofunc) Diag_getattro, /* tp_getattro */ (setattrofunc) Diag_setattro, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "Controls output of diagnostic information for debugging", /* tp_doc */ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ Diag_members, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) NULL, /* tp_init */ NULL, /* tp_alloc */ NULL, /* tp_new */ }; jpy-2.0.0/src/main/c/jpy_compat.c0000664000175000017500000000116715202503234016746 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "jpy_compat.h" jpy-2.0.0/src/main/c/jpy_diag.h0000664000175000017500000000263415202503234016374 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JPY_DIAG_H #define JPY_DIAG_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" typedef struct JPy_Diag { PyObject_HEAD int flags; int F_OFF; int F_TYPE; int F_METH; int F_EXEC; int F_MEM; int F_JVM; int F_ERR; int F_ALL; } JPy_Diag; #define JPy_DIAG_F_OFF 0x00 #define JPy_DIAG_F_TYPE 0x01 #define JPy_DIAG_F_METH 0x02 #define JPy_DIAG_F_EXEC 0x04 #define JPy_DIAG_F_MEM 0x08 #define JPy_DIAG_F_JVM 0x10 #define JPy_DIAG_F_ERR 0x20 #define JPy_DIAG_F_ALL 0xff extern PyTypeObject Diag_Type; extern int JPy_DiagFlags; PyObject* Diag_New(void); void JPy_DiagPrint(int diagFlags, const char * format, ...); #define JPy_DIAG_PRINT if (JPy_DiagFlags != 0) JPy_DiagPrint #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_DIAG_H */jpy-2.0.0/src/main/c/jpy_jobj.h0000664000175000017500000000240515202503234016410 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ #ifndef JPY_JOBJ_H #define JPY_JOBJ_H #ifdef __cplusplus extern "C" { #endif #include "jpy_compat.h" /** * The Java Object representation in Python. * @see JPy_JArray */ typedef struct JPy_JObj { PyObject_HEAD jobject objectRef; } JPy_JObj; int JObj_Check(PyObject* arg); int JByteBuffer_Check(JPy_JType* type); PyObject* JObj_New(JNIEnv* jenv, jobject objectRef); PyObject* JObj_FromType(JNIEnv* jenv, JPy_JType* type, jobject objectRef); int JObj_InitTypeSlots(PyTypeObject* type, const char* typeName, PyTypeObject* superType); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !JPY_JOBJ_H */jpy-2.0.0/src/main/java/0000775000175000017500000000000015202503234015127 5ustar alastairalastairjpy-2.0.0/src/main/java/org/0000775000175000017500000000000015202503234015716 5ustar alastairalastairjpy-2.0.0/src/main/java/org/jpy/0000775000175000017500000000000015202503234016520 5ustar alastairalastairjpy-2.0.0/src/main/java/org/jpy/PyLibInitializer.java0000664000175000017500000000571015202503234022611 0ustar alastairalastairpackage org.jpy; import java.util.Objects; /** * This helper class is to safely and programmatically configure jpy. * * Configuration of {@link PyLib} is done at class initialization time via system properties. * This is a potentially delicate time in the lifecycle of a program - and errors around order of * initialization can be hard to track down. To safely and programmatically configure jpy, we must * ensure that we are the ones causing the initialization of {@link PyLib} (and {@link DL}). */ public class PyLibInitializer { /** * Should be set to true during {@link PyLib}'s static initialization */ static boolean pyLibInitialized = false; /** * Should be set to true during {@link DL}'s static initialization */ static boolean dlInitialized = false; /** * Returns true iff the {@link PyLib} class has been initialized */ public static boolean isPyLibInitialized() { return pyLibInitialized; } /** * Returns true iff the {@link DL} class has been initialized */ public static boolean isDlInitialized() { return dlInitialized; } /** * This method should only be called once - it is dependent on {@link PyLib} and {@link DL} being * uninitialized. Any consumers who want to programmatically configure jpy should call this method * first. * * @param pyLib the python library * @param jpyLib the jpy library * @param jdlLib the jdl library */ public static void initPyLib(String pyLib, String jpyLib, String jdlLib) { synchronized (PyLibInitializer.class) { System.setProperty(PyLibConfig.PYTHON_LIB_KEY, pyLib); System.setProperty(PyLibConfig.JPY_LIB_KEY, jpyLib); System.setProperty(PyLibConfig.JDL_LIB_KEY, jdlLib); ensurePyLibInit(); // safety check to make sure that the PyLib initialization process didn't change any values ensurePropertySame(PyLibConfig.PYTHON_LIB_KEY, pyLib); ensurePropertySame(PyLibConfig.JPY_LIB_KEY, jpyLib); ensurePropertySame(PyLibConfig.JDL_LIB_KEY, jdlLib); } } private static void ensurePyLibInit() { if (pyLibInitialized) { throw new IllegalStateException("PyLib is already initialized"); } if (dlInitialized) { throw new IllegalStateException("DL is already initialized"); } PyLib.dummyMethodForInitialization(); if (!pyLibInitialized) { throw new IllegalStateException( "PyLib should have been initialized. This should not happen."); } // DL is not always initialized (platform dependent), so don't need to check it } private static void ensurePropertySame(String propertyName, String propertyValue) { final String currentValue = System.getProperty(propertyName); if (!Objects.equals(propertyValue, currentValue)) { throw new IllegalStateException(String.format( "PyLib initialization has changed the value of system property '%s': was '%s', is now '%s'", propertyName, propertyValue, currentValue)); } } } jpy-2.0.0/src/main/java/org/jpy/annotations/0000775000175000017500000000000015202503234021055 5ustar alastairalastairjpy-2.0.0/src/main/java/org/jpy/annotations/Return.java0000664000175000017500000000212615202503234023200 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to mark method parameters as return values, that is, an argument may be returned as-is by the method. *

* Note: this class is not used yet. * * @author Norman Fomferra */ @Target(value = ElementType.PARAMETER) @Retention(value = RetentionPolicy.RUNTIME) public @interface Return { } jpy-2.0.0/src/main/java/org/jpy/annotations/Output.java0000664000175000017500000000215515202503234023223 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 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. *

* Note: this class is not used yet. * * @author Norman Fomferra */ @Target(value = ElementType.PARAMETER) @Retention(value = RetentionPolicy.RUNTIME) public @interface Output { } jpy-2.0.0/src/main/java/org/jpy/annotations/Mutable.java0000664000175000017500000000213615202503234023313 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to mark method parameters as mutable, that is, an argument's state is expected to be modified by the method. *

* Note: this class is not used yet. * * @author Norman Fomferra */ @Target(value = ElementType.PARAMETER) @Retention(value = RetentionPolicy.RUNTIME) public @interface Mutable { } jpy-2.0.0/src/main/java/org/jpy/IdentityModule.java0000664000175000017500000000076715202503234022334 0ustar alastairalastairpackage org.jpy; public interface IdentityModule extends AutoCloseable { String IDENTITY_CODE = "def identity(x):\n return x"; static IdentityModule create(CreateModule createModule) { return create(createModule, IdentityModule.class); } static T create(CreateModule createModule, Class clazz) { return createModule.callAsFunctionModule("identity_module", IDENTITY_CODE, clazz); } PyObject identity(Object object); @Override void close(); } jpy-2.0.0/src/main/java/org/jpy/PyModule.java0000664000175000017500000001063415202503234021125 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import java.util.Objects; import static org.jpy.PyLib.assertPythonRuns; /** * Represents a Python module. * * @author Norman Fomferra * @since 0.7 */ public class PyModule extends PyObject { /** * The Python module's name. */ private final String name; PyModule(String name, long pointer) { super(pointer); this.name = name; } /** * @return The Python module's name. */ public String getName() { return name; } /** * Get the Python interpreter's main module and return its Java representation. * It can be used to access global scope variables and functions. For example: *

     *      PyLib.execScript("def incByOne(x): return x + 1");
     *      PyModule mainModule = PyModule.getMain();
     *      PyObject eleven = mainModule.call("incByOne", 10);
     * 
* * @return The Python main module's Java representation. * @since 0.8 */ public static PyModule getMain() { return importModule("__main__"); } /** * Get the Python interpreter's buildins module and returns its Java representation. * It can be used to call functions such as {@code len()}, {@code type()}, {@code list()}, etc. For example: *
     *      builtins = PyModule.getBuiltins();
     *      PyObject size = builtins.call("len", pyList);
     * 
* * @return Java representation of Python's builtin module. * @see org.jpy.PyObject#call(String, Object...) * @since 0.8 */ public static PyModule getBuiltins() { try { //Python 3.3+ return importModule("builtins"); } catch (Exception e) { //Python 2.7 return importModule("__builtin__"); } } /** * Import a Python module into the Python interpreter and return its Java representation. * * @param name The Python module's name. * @return The Python module's Java representation. */ public static PyModule importModule(String name) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); long pointer = PyLib.importModule(name); return pointer != 0 ? new PyModule(name, pointer) : null; } /** * Extends Python's 'sys.path' variable by the given module path. * * @param modulePath The new module path. Should be an absolute pathname. * @param prepend If true, the new path will be the new first element of 'sys.path', otherwise it will be the last. * @return The altered 'sys.path' list. * @since 0.8 */ public static PyObject extendSysPath(String modulePath, boolean prepend) { Objects.requireNonNull(modulePath, "path must not be null"); try (final PyModule sys = importModule("sys")) { final PyObject sysPath = sys.getAttribute("path"); if (prepend) { //noinspection EmptyTryBlock try (final PyObject pyObj = sysPath.call("insert", 0, modulePath)) { } } else { //noinspection EmptyTryBlock try (final PyObject pyObj = sysPath.call("append", modulePath)) { } } return sysPath; } } /** * Create a Java proxy instance of this Python module which contains compatible functions to the ones provided in the * interface given by the {@code type} parameter. * * @param type The interface's type. * @param The interface name. * @return A (proxy) instance implementing the given interface. */ @Override public T createProxy(Class type) { assertPythonRuns(); Objects.requireNonNull(type, "type must not be null"); return (T) createProxy(PyLib.CallableKind.FUNCTION, type); } } jpy-2.0.0/src/main/java/org/jpy/PyObjectState.java0000664000175000017500000000375115202503234022111 0ustar alastairalastairpackage org.jpy; import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** * An auxiliary structure that allows for proper cleanup of {@link PyObject} after they have been * GC'd. */ final class PyObjectState implements AutoCloseable { private static final AtomicLongFieldUpdater updater = AtomicLongFieldUpdater.newUpdater(PyObjectState.class, "pointer"); /** * The value of the Python/C API {@code PyObject*} which this class represents. */ private volatile long pointer; PyObjectState(long pointer) { if (pointer == 0) { throw new IllegalArgumentException("pointer == 0"); } this.pointer = pointer; } public final long borrowPointer() { final long localPointer = updater.get(this); // TODO: how does this differ from just a volatile read against pointer? if (localPointer == 0) { throw new IllegalStateException("PyObjectState has already been taken"); } return localPointer; } /** * Only a single caller can ever take the pointer. Once taken, they are responsible for closing * the pointer. * * @return the pointer, or 0 if it's already been taken */ final long takePointer() { final long localPointer = updater.get(this); // TODO: how does this differ from just a volatile read against pointer? if (localPointer == 0) { // It's already been taken return 0; } if (!updater.compareAndSet(this, localPointer, 0)) { // Another thread concurrently took the pointer return 0; } // the caller is responsible for closing it now! return localPointer; } @Override public void close() { final long pointerForClosure = takePointer(); if (pointerForClosure == 0) { // It's already been taken return; } // We are closing it PyLib.decRef(pointerForClosure); } } jpy-2.0.0/src/main/java/org/jpy/DL.java0000664000175000017500000000570315202503234017667 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; /** * A replacement for {@link System#load(String)} with support for POSIX {@code dlopen} flags. *

* Important note: This class is useful on POSIX (Unix/Linux) systems only. On Windows OSes, all methods * are no-ops. * * @author Norman Fomferra * @see dlopen(3) - Linux manual page * @since 0.7 */ public class DL { /** * Resolve undefined symbols as code from the dynamic library is executed. */ public static final int RTLD_LAZY = 0x0001; /** * Resolve all undefined symbols before {@link #dlopen} returns and fail if this cannot be done. */ public static final int RTLD_NOW = 0x0002; /** * This is the converse of RTLD_GLOBAL, and the default if neither flag is specified. */ public static final int RTLD_LOCAL = 0x0004; /** * External symbols defined in the library will be made available to subsequently loaded libraries. */ public static final int RTLD_GLOBAL = 0x0008; /** * loads the dynamic library file named by the null-terminated string filename and returns * an opaque "handle" for the dynamic library. If filename is {@code null}, then the returned handle * is for the main program. If filename contains a slash ("/"), then it is interpreted as a * (relative or absolute) pathname. * * @param filename dynamic library filename or {@code null} * @param flag combination of {@link #RTLD_GLOBAL} or {@link #RTLD_LOCAL} with {@link #RTLD_LAZY}, * {@link #RTLD_NOW}. * @return opaque "handle" for the dynamic library. */ public static native long dlopen(String filename, int flag); public static native int dlclose(long handle); public static native String dlerror(); static { // see documentation in PyLibInitializer for explanation PyLibInitializer.dlInitialized = true; try { System.loadLibrary("jdl"); } catch (Throwable t) { String jdlLibPath = System.getProperty(PyLibConfig.JDL_LIB_KEY); if (jdlLibPath != null) { System.load(jdlLibPath); } else { throw new RuntimeException("Failed to load 'jdl' shared library. You can use system property 'jpy.jdlLib' to specify it.", t); } } } } jpy-2.0.0/src/main/java/org/jpy/jsr223/0000775000175000017500000000000015202503234017545 5ustar alastairalastairjpy-2.0.0/src/main/java/org/jpy/jsr223/ScriptEngineFactoryImpl.java0000664000175000017500000002321115202503234025153 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.jsr223; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * jpy's ScriptEngineFactory implementation of JSR 223: Scripting for the Java Platform. *

* Note, jpy's JSR 223 support is not yet functional. This class is only used by unit-tests so far. * * @author Norman Fomferra */ public class ScriptEngineFactoryImpl implements ScriptEngineFactory { private final Map parameters; private ScriptEngineImpl scriptEngine; public ScriptEngineFactoryImpl() { parameters = new HashMap<>(); parameters.put(ScriptEngine.NAME, getNames().get(0)); parameters.put(ScriptEngine.ENGINE, getEngineName()); parameters.put(ScriptEngine.ENGINE_VERSION, getEngineVersion()); parameters.put(ScriptEngine.LANGUAGE, getLanguageName()); parameters.put(ScriptEngine.LANGUAGE_VERSION, getLanguageVersion()); } /** * Returns the full name of the ScriptEngine. For * instance an implementation based on the Mozilla Rhino Javascript engine * might return Rhino Mozilla Javascript Engine. * * @return The name of the engine implementation. */ @Override public String getEngineName() { return "jpy Python Engine"; } /** * Returns the version of the ScriptEngine. * * @return The ScriptEngine implementation version. */ @Override public String getEngineVersion() { return "0.1-alpha"; } /** * Returns an immutable list of filename extensions, which generally identify scripts * written in the language supported by this ScriptEngine. * The array is used by the ScriptEngineManager to implement its * getEngineByExtension method. * * @return The list of extensions. */ @Override public List getExtensions() { return Collections.unmodifiableList(Arrays.asList("py", "pyc")); } /** * Returns an immutable list of mimetypes, associated with scripts that * can be executed by the engine. The list is used by the * ScriptEngineManager class to implement its * getEngineByMimetype method. * * @return The list of mime types. */ @Override public List getMimeTypes() { return Collections.unmodifiableList(Arrays.asList("text/python", "application/python", "text/x-python", "application/x-python")); } /** * Returns an immutable list of short names for the ScriptEngine, which may be used to * identify the ScriptEngine by the ScriptEngineManager. * For instance, an implementation based on the Mozilla Rhino Javascript engine might * return list containing {"javascript", "rhino"}. * * @return an immutable list of short names */ @Override public List getNames() { return Collections.unmodifiableList(Arrays.asList("cpython", "python")); } /** * Returns the name of the scripting langauge supported by this * ScriptEngine. * * @return The name of the supported language. */ @Override public String getLanguageName() { return "python"; } /** * Returns the version of the scripting language supported by this * ScriptEngine. * * @return The version of the supported language. */ @Override public String getLanguageVersion() { // todo - return true Python version --> e.g. call python -V // using PyLibConfig.getProperties().getProperty("jpy.pythonExecutable") return "3.x"; } /** * Returns the value of an attribute whose meaning may be implementation-specific. * Keys for which the value is defined in all implementations are: *

    *
  • ScriptEngine.ENGINE
  • *
  • ScriptEngine.ENGINE_VERSION
  • *
  • ScriptEngine.NAME
  • *
  • ScriptEngine.LANGUAGE
  • *
  • ScriptEngine.LANGUAGE_VERSION
  • *
*

* The values for these keys are the Strings returned by getEngineName, * getEngineVersion, getName, getLanguageName and * getLanguageVersion respectively.

* A reserved key, THREADING, whose value describes the behavior of the engine * with respect to concurrent execution of scripts and maintenance of state is also defined. * These values for the THREADING key are:

*

    *
  • null - The engine implementation is not thread safe, and cannot * be used to execute scripts concurrently on multiple threads. *
  • "MULTITHREADED" - The engine implementation is internally * thread-safe and scripts may execute concurrently although effects of script execution * on one thread may be visible to scripts on other threads. *
  • "THREAD-ISOLATED" - The implementation satisfies the requirements * of "MULTITHREADED", and also, the engine maintains independent values * for symbols in scripts executing on different threads. *
  • "STATELESS" - The implementation satisfies the requirements of *
  • "THREAD-ISOLATED". In addition, script executions do not alter the * mappings in the Bindings which is the engine scope of the * ScriptEngine. In particular, the keys in the Bindings * and their associated values are the same before and after the execution of the script. *
*

* Implementations may define implementation-specific keys. * * @param key The name of the parameter * @return The value for the given parameter. Returns null if no * value is assigned to the key. */ @Override public Object getParameter(String key) { return parameters.get(key); } /** * Returns a String which can be used to invoke a method of a Java object using the syntax * of the supported scripting language. The method returns *
     *  {@code String.format("%s.%s(%s)", obj, m, String.join(", ", args)); }
     * 
* * @param obj The name representing the object whose method is to be invoked. The * name is the one used to create bindings using the put method of * ScriptEngine, the put method of an ENGINE_SCOPE * Bindings,or the setAttribute method * of ScriptContext. The identifier used in scripts may be a decorated form of the * specified one. * @param m The name of the method to invoke. * @param args names of the arguments in the method call. * @return The String used to invoke the method in the syntax of the scripting language. */ @Override public String getMethodCallSyntax(String obj, String m, String... args) { return String.format("%s.%s(%s)", obj, m, String.join(", ", args)); } /** * Returns a String that can be used as a statement to display the specified String using * the syntax of the supported scripting language. The method returns *
     *  {@code "print(" + toDisplay + ")";}
     * 
* * @param toDisplay The String to be displayed by the returned statement. * @return The string used to display the String in the syntax of the scripting language. */ @Override public String getOutputStatement(String toDisplay) { return "print(" + toDisplay + ")"; } /** * Returns a valid scripting language executable program with given statements. * The method returns *
     *  {@code String.join("\n", statements);}
     * 
* * @param statements The statements to be executed. May be return values of * calls to the getMethodCallSyntax and getOutputStatement methods. * @return The Program */ @Override public String getProgram(String... statements) { return String.join("\n", statements); } /** * Returns an instance of the ScriptEngine associated with this * ScriptEngineFactory. A new ScriptEngine is generally * returned, but implementations may pool, share or reuse engines. * * @return A new ScriptEngine instance. */ @Override public ScriptEngine getScriptEngine() { if (scriptEngine == null) { synchronized (this) { if (scriptEngine == null) { scriptEngine = new ScriptEngineImpl(this); } } } return scriptEngine; } } jpy-2.0.0/src/main/java/org/jpy/jsr223/ScriptEngineImpl.java0000664000175000017500000002246715202503234023637 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy.jsr223; import org.jpy.PyLib; import org.jpy.PyModule; import org.jpy.PyObject; import org.jpy.PyInputMode; import javax.script.AbstractScriptEngine; import javax.script.Bindings; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngineFactory; import javax.script.ScriptException; import javax.script.SimpleBindings; import java.io.BufferedReader; import java.io.File; import java.io.Reader; import java.util.stream.Collectors; /** * jpy's ScriptEngine implementation of JSR 223: Scripting for the Java Platform. * * @author Norman Fomferra * @since 0.8 */ class ScriptEngineImpl extends AbstractScriptEngine implements Invocable { public static final String EXTRA_PATHS_KEY = ScriptEngineImpl.class.getName() + ".extraPaths"; private final ScriptEngineFactoryImpl factory; ScriptEngineImpl(ScriptEngineFactoryImpl factory) { this.factory = factory; PyLib.startPython(System.getProperty(EXTRA_PATHS_KEY, "").split(File.pathSeparator)); } /** * Returns a ScriptEngineFactory for the class to which this ScriptEngine belongs. * * @return The ScriptEngineFactory */ @Override public ScriptEngineFactory getFactory() { return factory; } /** * Returns an uninitialized Bindings. * * @return A Bindings that can be used to replace the state of this ScriptEngine. **/ @Override public Bindings createBindings() { return new SimpleBindings(); } /** * Same as eval(String, ScriptContext) where the source of the script * is read from a Reader. * * @param reader The source of the script to be executed by the script engine. * @param context The ScriptContext passed to the script engine. * @return The value returned from the execution of the script. * @throws ScriptException if an error occurs in script. * @throws NullPointerException if either argument is null. */ @Override public Object eval(Reader reader, ScriptContext context) throws ScriptException { return eval(new BufferedReader(reader).lines().collect(Collectors.joining("\n")), context); } /** * Causes the immediate execution of the script whose source is the String * passed as the first argument. The script may be reparsed or recompiled before * execution. State left in the engine from previous executions, including * variable values and compiled procedures may be visible during this execution. * * @param script The script to be executed by the script engine. * @param context A ScriptContext exposing sets of attributes in * different scopes. The meanings of the scopes ScriptContext.GLOBAL_SCOPE, * and ScriptContext.ENGINE_SCOPE are defined in the specification. *

* The ENGINE_SCOPE Bindings of the ScriptContext contains the * bindings of scripting variables to application objects to be used during this * script execution. * @return The value returned from the execution of the script. * @throws ScriptException if an error occurs in script. ScriptEngines should create and throw * ScriptException wrappers for checked Exceptions thrown by underlying scripting * implementations. * @throws NullPointerException if either argument is null. */ @Override public Object eval(String script, ScriptContext context) throws ScriptException { return PyObject.executeCode(script, PyInputMode.SCRIPT, context.getBindings(ScriptContext.GLOBAL_SCOPE), context.getBindings(ScriptContext.ENGINE_SCOPE)); } /** * Calls a method on a script object compiled during a previous script execution, * which is retained in the state of the ScriptEngine. * * @param thiz If the procedure is a member of a class * defined in the script and thiz is an instance of that class * returned by a previous execution or invocation, the named method is * called through that instance. * @param name The name of the procedure to be called. * @param args Arguments to pass to the procedure. The rules for converting * the arguments to scripting variables are implementation-specific. * @return The value returned by the procedure. The rules for converting the scripting * variable returned by the script method to a Java Object are implementation-specific. * @throws ScriptException if an error occurs during invocation of the method. * @throws NoSuchMethodException if method with given name or matching argument types cannot be found. * @throws NullPointerException if the method name is null. * @throws IllegalArgumentException if the specified thiz is null or the specified Object is * does not represent a scripting object. */ @Override public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException { if (!(thiz instanceof PyObject)) { throw new IllegalArgumentException("'thiz' must be an instance of " + PyObject.class.getName()); } return ((PyObject) thiz).call(name, args); } /** * Used to call top-level procedures and functions defined in scripts. * * @param name of the procedure or function to call * @param args Arguments to pass to the procedure or function * @return The value returned by the procedure or function * @throws ScriptException if an error occurs during invocation of the method. * @throws NoSuchMethodException if method with given name or matching argument types cannot be found. * @throws NullPointerException if method name is null. */ @Override public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException { return PyModule.getMain().call(name, args); } /** * Returns an implementation of an interface using functions compiled in * the interpreter. The methods of the interface * may be implemented using the invokeFunction method. * * @param clasz The Class object of the interface to return. * @return An instance of requested interface - null if the requested interface is unavailable, * i. e. if compiled functions in the ScriptEngine cannot be found matching * the ones in the requested interface. * @throws IllegalArgumentException if the specified Class object * is null or is not an interface. */ @Override public T getInterface(Class clasz) { if (clasz == null || !clasz.isInterface()) { throw new IllegalArgumentException("'clasz' must be an interface"); } return PyModule.getMain().createProxy(clasz); } /** * Returns an implementation of an interface using member functions of * a scripting object compiled in the interpreter. The methods of the * interface may be implemented using the invokeMethod method. * * @param thiz The scripting object whose member functions are used to implement the methods of the interface. * @param clasz The Class object of the interface to return. * @return An instance of requested interface - null if the requested interface is unavailable, * i. e. if compiled methods in the ScriptEngine cannot be found matching * the ones in the requested interface. * @throws IllegalArgumentException if the specified Class object * is null or is not an interface, or if the specified Object is * null or does not represent a scripting object. */ @Override public T getInterface(Object thiz, Class clasz) { if (clasz == null || !clasz.isInterface()) { throw new IllegalArgumentException("'clasz' must be an interface"); } if (!(thiz instanceof PyObject)) { throw new IllegalArgumentException("'thiz' must be an instance of " + PyObject.class.getName()); } PyObject pyObject = (PyObject) thiz; return pyObject.createProxy(clasz); } } jpy-2.0.0/src/main/java/org/jpy/PyDictWrapper.java0000664000175000017500000001653315202503234022130 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; import java.lang.ref.Reference; import java.util.*; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.stream.Collectors; /** * A simple wrapper around PyObjects that are actually Python dictionaries, to present the most useful parts of a * Map interface. */ public class PyDictWrapper implements Map, AutoCloseable { // todo: consider using https://docs.python.org/3/c-api/mapping.html instead of // https://docs.python.org/3/c-api/dict.html? Or adding an additional layer? private final PyObject pyObject; PyDictWrapper(PyObject pyObject) { this.pyObject = pyObject; } @Override public void close() { pyObject.close(); } @Override public int size() { try (final PyObject pyObj = pyObject.callMethod("__len__")) { return pyObj.getIntValue(); } } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean containsKey(Object key) { try { return PyLib.pyDictContains(pyObject.getPointer(), key, null); } finally { Reference.reachabilityFence(this.pyObject); } } /** * An extension to the Map interface that allows the use of String keys without generating warnings. */ public boolean containsKey(String key) { try { return PyLib.pyDictContains(pyObject.getPointer(), key, String.class); } finally { Reference.reachabilityFence(this.pyObject); } } public boolean containsKey(PyObject key) { try { return PyLib.pyDictContains(pyObject.getPointer(), key, PyObject.class); } finally { Reference.reachabilityFence(this.pyObject); } } @Override public boolean containsValue(Object value) { return values().contains(value); } @Override public PyObject get(Object key) { try { return getItem(key); } catch (KeyError e) { return null; } } /** * An extension to the Map interface that allows the use of String keys without generating warnings. */ public PyObject get(String key) { return get((Object)key); } @Override public PyObject put(PyObject key, PyObject value) { PyObject previous; try { // note: this is a BORROWED REFERENCE - todo previous = getItem(key); } catch (KeyError e) { previous = null; } setItem(key, value); return previous; } public PyObject getItem(Object key) throws KeyError { // todo: eventually use api https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItem // note: this is a BORROWED REFERENCE - todo return pyObject.callMethod("__getitem__", key); } public void setItem(Object key, Object value) { // todo: eventually use api https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem //noinspection EmptyTryBlock try (final PyObject result = pyObject.callMethod("__setitem__", key, value)) { } } public void putObject(Object key, Object value) { setItem(key, value); } public void delItem(Object key) { // todo: eventually use api https://docs.python.org/3/c-api/dict.html#c.PyDict_DelItem //noinspection EmptyTryBlock try (final PyObject result = pyObject.callMethod("__delitem__", key)) { } } @Override public PyObject remove(Object key) { final PyObject existing = get(key); if (existing != null) { delItem(key); } return existing; } public PyObject remove(String key) { return remove((Object)key); } @Override public void putAll(Map m) { throw new UnsupportedOperationException(); } @Override public void clear() { // todo: https://docs.python.org/3/c-api/dict.html#c.PyDict_Clear //noinspection EmptyTryBlock try (PyObject pyObj = pyObject.callMethod("clear")) { } } /** * {@inheritDoc} * *
Note: we are returning a COPY not a VIEW of the keys */ @Override public Set keySet() { final PyObject pyObj; try { pyObj = PyLib.pyDictKeys(this.pyObject.getPointer()); } finally { Reference.reachabilityFence(this.pyObject); } try (pyObj) { return new LinkedHashSet<>(pyObj.asList()); } } /** * {@inheritDoc} * *
Note: we are returning a COPY not a VIEW of the values */ @Override public Collection values() { final PyObject pyObj; try { pyObj = PyLib.pyDictValues(this.pyObject.getPointer()); } finally { Reference.reachabilityFence(this.pyObject); } return pyObj.asList(); } /** * {@inheritDoc} * *
Note: we are returning a COPY not a VIEW of the entries */ @Override public Set> entrySet() { // todo: we'd prefer to use something like // https://docs.python.org/2.7/c-api/dict.html#c.PyDict_Next // https://docs.python.org/3/c-api/dict.html#c.PyDict_Next // but that method signature is a bit weird on the java <-> python jni layer with PyObject // reference return values... final PyObject pyObj; try { pyObj = PyLib.pyDictKeys(this.pyObject.getPointer()); } finally { Reference.reachabilityFence(this.pyObject); } try (pyObj) { return pyObj .asList() .stream() .map(p -> new SimpleImmutableEntry<>(p, get(p))) .collect(Collectors.toCollection(LinkedHashSet::new)); } } /** * Gets the underlying PyObject. * * @return the PyObject wrapped by this dictionary. */ public PyObject unwrap() { return pyObject; } /** * Gets the underlying pointer for this object. * * @return the pointer to the underlying Python object wrapped by this dictionary. */ long getPointer() { return pyObject.getPointer(); } /** * Copy this dictionary into a new dictionary. * * @return a wrapped copy of this Python dictionary. */ public PyDictWrapper copy() { final PyObject pyObj; try { pyObj = PyLib.copyDict(this.pyObject.getPointer()); } finally { Reference.reachabilityFence(this.pyObject); } return new PyDictWrapper(pyObj); } } jpy-2.0.0/src/main/java/org/jpy/PyInputMode.java0000664000175000017500000000360615202503234021605 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; /** * Source code input modes for compiling/executing Python source code. * * @author Norman Fomferra * @since 0.8 * @see PyObject#executeCode(String, PyInputMode) * @see PyObject#executeCode(String, PyInputMode, Object, Object) */ public enum PyInputMode { /** * Compile/execute single statement code. *

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

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

* The start symbol from the Python grammar for sequences of * statements as read from a file or other source ({@code Py_eval_input} in Python PyRun API). */ EXPRESSION(258); public int value() { return value; } private final int value; PyInputMode(int value) { this.value = value; } } jpy-2.0.0/src/main/java/org/jpy/CreateModule.java0000664000175000017500000000371015202503234021735 0ustar alastairalastairpackage org.jpy; import java.util.Objects; import org.jpy.PyLib.CallableKind; public class CreateModule implements AutoCloseable { // note: we are keeping the imports in the function to limit the scope needed private static final String PREFERRED = "def create_module(module_name, script):\n" + " from importlib import util\n" + " spec = util.spec_from_loader(module_name, loader=None)\n" + " module = util.module_from_spec(spec)\n" + " exec(script, module.__dict__)\n" + " return module"; private static final String DEPRECATED = "def create_module(module_name, script):\n" + " import imp\n" + " module = imp.new_module(module_name)\n" + " exec(script, module.__dict__)\n" + " return module"; public static CreateModule create() { final String code = PyLib.getPythonVersion().startsWith("3") ? PREFERRED : DEPRECATED; try ( final PyObject dict = PyObject.executeCode("dict()", PyInputMode.EXPRESSION); final PyObject exec = PyObject.executeCode(code, PyInputMode.SCRIPT, null, dict)) { return new CreateModule(dict.asDict().get("create_module")); } } private final PyObject function; private CreateModule(PyObject function) { this.function = Objects.requireNonNull(function, "function"); } public PyObject call(String moduleName, String moduleScript) { return function.call("__call__", moduleName, moduleScript); } public T callAsFunctionModule(String moduleName, String moduleScript, Class clazz) { //noinspection unchecked return (T)call(moduleName, moduleScript).createProxy(CallableKind.FUNCTION, clazz); } public T callAsMethodModule(String moduleName, String moduleScript, Class clazz) { return call(moduleName, moduleScript).createProxy(clazz); } @Override public void close() { function.close(); } } jpy-2.0.0/src/main/java/org/jpy/StopIteration.java0000664000175000017500000000161215202503234022167 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; /** * Translation of Python StopIteration so that they can be programmatically detected from Java. */ public class StopIteration extends RuntimeException { StopIteration(String message) { super(message); } } jpy-2.0.0/src/main/java/org/jpy/package-info.java0000664000175000017500000000404315202503234021710 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Provides the classes necessary to *

    *
  1. create an embedded Python VM,
  2. *
  3. to execute Python code,
  4. *
  5. to import Python modules,
  6. *
  7. to access Python variables, and finally
  8. *
  9. to call Python functions and class methods.
  10. *
* *

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

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

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

* Some {@link org.jpy.PyModule} methods return {@link org.jpy.PyObject} instances. These can be used to * create instances of Python classes, and to access Python class members, attributes and to invoke Python object methods.. * * @since 0.7 */ package org.jpy;jpy-2.0.0/src/main/java/org/jpy/PyObjectReferences.java0000664000175000017500000002210515202503234023104 0ustar alastairalastairpackage org.jpy; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Allows for proper cleanup of PyObjects outside of the {@link Object#finalize()} method. * *

Note: this setup could likely be better structured as a factory, but then the existing JNI * code would need to be aware of it. Instead, we'll ensure that new PyObjects register. */ class PyObjectReferences { private static final String WEAK = "weak"; private static final String REF_TYPE = System.getProperty("PyObject.reference_type", WEAK); /** * At the end of the day, this is the most important number in determining the relative value * that is being placed in cleaning up vs performing more python work first. The larger this * number is, the more emphasis that is placed on cleaning up. The smaller this number is, the * more emphasis that is placed on other python work. It's possible that out of memory issues * occur if too small a number is provided here - thus the guidance is to err on the larger * side. */ private static final int DEFAULT_BATCH_CLOSE_SIZE = Integer.parseInt(System.getProperty("PyObject.batch_close_size", "4096")); private static final long CLEANUP_THREAD_ACTIVE_SLEEP_MILLIS = Long.parseLong(System.getProperty("PyObject.active_sleep_millis", "0")); private static final long CLEANUP_THREAD_PASSIVE_SLEEP_MILLIS = Long.parseLong(System.getProperty("PyObject.passive_sleep_millis", "1000")); private final ReferenceQueue referenceQueue; private final Map, PyObjectState> references; private final long[] buffer; PyObjectReferences() { this(DEFAULT_BATCH_CLOSE_SIZE); } PyObjectReferences(int batchCloseSize) { if (batchCloseSize <= 0) { throw new IllegalArgumentException("batchCloseSize must be positive"); } referenceQueue = new ReferenceQueue<>(); references = new ConcurrentHashMap<>(); buffer = new long[batchCloseSize]; } void register(PyObject pyObject) { if (references.put(asRef(pyObject), pyObject.getState()) != null) { throw new IllegalStateException("Existing reference overwritten - this should not happen."); } } private Reference asRef(PyObject pyObject) { // The implementation details regarding best-practices of phantom vs weak references // notifications is a bit murky to me. While the most technically correct replacement for // finalization logic is a phantom reference, does a weak reference serve a similar purpose // if we can guarantee the object won't ever be re-animated after being marked as weak? If // so, does using weak references get us the notification a bit sooner? And - does using // weak references actually allow us to reclaim memory a bit faster, since phantom // references don't allow GC to actually proceed on the object until the phantom references // themselves are collected (see the phantom references javadoc)? // // I *think* the answer to the above questions are "yes". // // As such, using a weak reference here, while a bit unsafer, is the more performant choice. // We must guarantee that no other users will keep weak references to PyObjects and // re-animate them. return WEAK.equals(REF_TYPE) ? new WeakReference<>(pyObject, referenceQueue) : new PhantomReference<>(pyObject, referenceQueue); } /** * This should *only* be invoked through the proxy, or when we *know* we have the GIL. */ public synchronized int threadSafeCleanup() { return PyLib.ensureGil(() -> { int index = 0; while (index < buffer.length) { final Reference reference = referenceQueue.poll(); if (reference == null) { break; } index = appendIfNotClosed(buffer, index, reference); } if (index == 0) { return 0; } // We really really really want to make sure we *already* have the GIL lock at this point in // time. Otherwise, we block here until the GIL is available for us, and stall all cleanup // related to our PyObjects. if (index == 1) { PyLib.decRef(buffer[0]); return 1; } PyLib.decRefs(buffer, index); return index; }); } private int appendIfNotClosed(long[] buffer, int index, Reference reference) { reference.clear(); // helps GC proceed a bit faster for PhantomReference - guava Finalizer does this too final PyObjectState state = references.remove(reference); if (state == null) { throw new IllegalStateException("Reference from queue not in map - this should not happen."); } final long pointerForClosure = state.takePointer(); if (pointerForClosure == 0) { // it's already been closed return index; } buffer[index] = pointerForClosure; return index + 1; } PyObjectCleanup asProxy() { try ( final CreateModule createModule = CreateModule.create(); final IdentityModule identityModule = IdentityModule.create(createModule)) { return identityModule .identity(this) .createProxy(PyObjectCleanup.class); } } Thread createCleanupThread(String name) { return new Thread(this::cleanupThreadLogic, name); } private void cleanupThreadLogic() { // Note: this loop logic *could* be written completely in python (as seen below), as python // should release the gil during a `time.sleep(seconds)` operation. It would save us on the // number of java -> python JNI crossings (but not on the python -> java JNI crossings) - // but that isn't a big concern wrt cleanup logic. More important might be java thread // lifecycle interruption, which we get when we call Thread.sleep. /* def cleanup(references): import time while True: size = references.cleanup() sleep_time = 0.1 if size == 1024 else 1.0 time.sleep(sleep_time) */ // try-catch block to handle PyLib not initialized exception when a race condition occurs in the free-threaded // mode that the cleanup thread starts running after Python is already finalized. try { final PyObjectCleanup proxy = asProxy(); while (!Thread.currentThread().isInterrupted()) { // This blocks on the GIL, acquires the GIL, and then releases the GIL. // For linux, acquiring the GIL involves a pthread_mutex_lock, which does not provide // any fairness guarantees. As such, we need to be mindful of other python users/code, // and ensure we don't overly acquire the GIL causing starvation issues, especially when // there is no cleanup work to do. final int size = proxy.threadSafeCleanup(); // Although, it *does* make sense to potentially take the GIL in a tight loop when there // is a lot of real cleanup work to do. Sleeping for any amount of time may be // detrimental to the cleanup of resources. There is a balance that we want to try to // achieve between producers of PyObjects, and the cleanup of PyObjects (us). // It would be much nicer if ReferenceQueue exposed a method that blocked until the // queue was non-empty and *doesn't* remove any items. We can potentially implement this // by using reflection to access the internal lock of the ReferenceQueue in the future. if (size == buffer.length) { if (CLEANUP_THREAD_ACTIVE_SLEEP_MILLIS == 0) { Thread.yield(); } else { try { Thread.sleep(CLEANUP_THREAD_ACTIVE_SLEEP_MILLIS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } } else { try { Thread.sleep(CLEANUP_THREAD_PASSIVE_SLEEP_MILLIS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } } } catch (RuntimeException e) { String msg; if ((msg = e.getMessage()) != null && !(msg.contains("PyLib not initialized") || msg.contains("interpreter shutdown"))) { throw e; } } } } jpy-2.0.0/src/main/java/org/jpy/KeyError.java0000664000175000017500000000157515202503234021135 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; /** * Translation of Python KeyErrors so that they can be programmatically detected from Java. */ public class KeyError extends RuntimeException { KeyError(String message) { super(message); } }jpy-2.0.0/src/main/java/org/jpy/PyLibConfig.java0000664000175000017500000001112515202503234021530 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jpy; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.Properties; import java.util.Set; /** * Provides configuration for {@link org.jpy.PyLib}. * * @author Norman Fomferra * @since 0.7 */ class PyLibConfig { private static final boolean DEBUG = Boolean.getBoolean("jpy.debug"); public static final String PYTHON_LIB_KEY = "jpy.pythonLib"; public static final String JPY_LIB_KEY = "jpy.jpyLib"; public static final String JDL_LIB_KEY = "jpy.jdlLib"; public static final String JPY_CONFIG_KEY = "jpy.config"; public static final String JPY_CONFIG_RESOURCE = "jpyconfig.properties"; public enum OS { WINDOWS, UNIX, MAC_OS, SUNOS, } private static final Properties properties = new Properties(); static { if (DEBUG) System.out.println("org.jpy.PyLibConfig: entered static initializer"); InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(JPY_CONFIG_RESOURCE); if (stream != null) { loadConfig(stream, JPY_CONFIG_RESOURCE); } String path = System.getProperty(JPY_CONFIG_KEY); if (path != null) { File file = new File(path); if (file.isFile()) { loadConfig(file); } } File file = new File(JPY_CONFIG_RESOURCE).getAbsoluteFile(); if (file.isFile()) { loadConfig(file); } if (DEBUG) System.out.println("org.jpy.PyLibConfig: exited static initializer"); } private static void loadConfig(InputStream stream, String name) { try { if (DEBUG) System.out.printf(String.format("org.jpy.PyLibConfig: loading configuration resource %s\n", name)); try (Reader reader = new InputStreamReader(stream)) { loadConfig(reader); } } catch (IOException e) { if (DEBUG) e.printStackTrace(System.err); } } private static void loadConfig(File file) { try { if (DEBUG) System.out.printf(String.format("%s: loading configuration file %s\n", PyLibConfig.class.getName(), file)); try (Reader reader = new FileReader(file)) { loadConfig(reader); } } catch (IOException e) { System.err.printf("org.jpy.PyLibConfig: %s: %s\n", file, e.getMessage()); if (DEBUG) e.printStackTrace(System.err); } } private static void loadConfig(Reader reader) throws IOException { properties.load(reader); Set propertyNames = properties.stringPropertyNames(); for (String propertyName : propertyNames) { String propertyValue = properties.getProperty(propertyName); if (propertyValue != null) { System.setProperty(propertyName, propertyValue); } } } public static Properties getProperties() { return new Properties(properties); } public static String getProperty(String key, boolean mustHave) { // System properties overwrite .jpy properties String property = System.getProperty(key); if (property != null) { return property; } property = properties.getProperty(key); if (property == null && mustHave) { throw new RuntimeException("missing configuration property '" + key + "'"); } return property; } public static OS getOS() { String os = System.getProperty("os.name").toLowerCase(); if (os.contains("win")) { return OS.WINDOWS; } else if (os.contains("nix") || os.contains("nux") || os.contains("aix")) { return OS.UNIX; } else if (os.contains("mac")) { return OS.MAC_OS; } else if (os.contains("sunos")) { return OS.SUNOS; } return null; } } jpy-2.0.0/src/main/java/org/jpy/Assignment.java0000664000175000017500000000255315202503234021500 0ustar alastairalastairpackage org.jpy; import java.util.HashMap; import java.util.Map; import java.util.Optional; class Assignment { private static final Map, Class> boxedToPrimitive; static { boxedToPrimitive = new HashMap<>(); boxedToPrimitive.put(Boolean.class, boolean.class); boxedToPrimitive.put(Byte.class, byte.class); boxedToPrimitive.put(Character.class, char.class); boxedToPrimitive.put(Short.class, short.class); boxedToPrimitive.put(Integer.class, int.class); boxedToPrimitive.put(Long.class, long.class); boxedToPrimitive.put(Float.class, float.class); boxedToPrimitive.put(Double.class, double.class); } static Optional> getUnboxedType(Class clazz) { return Optional.ofNullable(boxedToPrimitive.get(clazz)); } static boolean isAssignableFrom(Class signatureType, Object instance) { return isAssignableFrom(signatureType, instance.getClass()); } static boolean isAssignableFrom(Class signatureType, Class instanceType) { if (signatureType.isAssignableFrom(instanceType)) { return true; } if (signatureType.isPrimitive()) { return getUnboxedType(instanceType) .filter(signatureType::isAssignableFrom) .isPresent(); } return false; } } jpy-2.0.0/src/main/java/org/jpy/PyLib.java0000664000175000017500000005661515202503234020417 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.function.Supplier; import static org.jpy.PyLibConfig.*; /** * Represents the library that provides the Python interpreter (CPython). *

* When the {@code PyLib} class is loaded, it reads its configuration from a Java properties file called {@code .jpy} * which must exist in the current user's home directory. The configuration file has been written to this location * by installing the Python jpy module using {@code python3 setup.py install --user} on Unix * and {@code python setup.py install}) on Windows. *

* Currently, the following properties are recognised in the {@code .jpy} file: *

    *
  • {@code python.lib} - the Python shared library (usually required on Unix only)
  • *
  • {@code jpy.lib} - the jpy shared library path for Python (Unix: {@code jpy*.so}, Windows: {@code jpy*.pyd})
  • *
*

* jpy API clients should first call {@link #isPythonRunning()} in order to check if a Python interpreter is already available. * If not, {@link #startPython(String...)} must be called before any other jpy API is used. *

* Important note for developers: If you change the signature of any of the native {@code PyLib} methods, * you must first run {@code javah} on the compiled class, and then adapt {@code src/main/c/jni/org_jpy_PyLib.c}. * * @author Norman Fomferra * @since 0.7 */ @SuppressWarnings("WeakerAccess") public class PyLib { private static final boolean DEBUG = Boolean.getBoolean("jpy.debug"); private static final boolean ON_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows"); private static final boolean STOP_IS_NO_OP = Boolean.getBoolean("jpy.stopIsNoOp") || ON_WINDOWS; private static String dllFilePath; private static Throwable dllProblem; private static boolean dllLoaded; /** * The kind of callable Python objects. */ public enum CallableKind { /** * Function call without the Python {@code self} as first argument. */ FUNCTION, /** * An instance method call with the Python {@code self} (= the instance) as first argument. */ METHOD, } /** * Controls output of diagnostic information for debugging. */ @SuppressWarnings("UnusedDeclaration") public static class Diag { static { PyLib.loadLib(); } /** * Print no diagnostic information at all. */ public static final int F_OFF = 0x00; /** * Print diagnostic information while Java types are resolved. */ public static final int F_TYPE = 0x01; /** * Print diagnostic information while Java methods overloads are selected. */ public static final int F_METH = 0x02; /** * Print diagnostic information when code execution flow is passed from Java to Python or the other way round. */ public static final int F_EXEC = 0x04; /** * Print diagnostic information about memory allocation/deallocation. */ public static final int F_MEM = 0x08; /** * Print diagnostic information usage of the Java VM Invocation API. */ public static final int F_JVM = 0x10; /** * Print diagnostic information if erroneous states are detected in the jpy Python module. */ public static final int F_ERR = 0x20; /** * Print any diagnostic information. */ public static final int F_ALL = 0xff; /** * @return the current diagnostic flags. */ public static native int getFlags(); /** * Sets the current diagnostic flags. * * @param flags the current diagnostic flags. */ public static native void setFlags(int flags); private Diag() { } } @SuppressWarnings("UnusedDeclaration") public static String getDllFilePath() { return dllFilePath; } /** * Throws a runtime exception if Python interpreter is not running. Possible reasons for this are *

    *
  • You have not called {@link #startPython(String...)} yet.
  • *
  • The Python shared library code for the Python interpreter could not be found or could not be be loaded.
  • *
  • The Python shared library code for the Python 'jpy' module could not be found or could not be be loaded.
  • *
  • The Python interpreter could not be initialised.
  • *
*/ public static void assertPythonRuns() { if (dllProblem != null) { throw new RuntimeException("PyLib not initialized", dllProblem); } if (!isPythonRunning()) { throw new RuntimeException("PyLib not initialized"); } } /** * @return {@code true} if the Python interpreter is running and the the 'jpy' module has been loaded. */ public static native boolean isPythonRunning(); /** * Delegates to {@link #startPython(int, String...)} with {@code flags = Diag.F_OFF}. */ public static void startPython(String... extraPaths) { startPython(Diag.F_OFF, extraPaths); } /** * Starts the Python interpreter. It does the following: *
    *
  1. Initializes the Python interpreter, if not already running.
  2. *
  3. Prepends any given extra paths to Python's 'sys.path' (e.g. so that 'jpy' can be loaded from isolated directories).
  4. *
  5. Imports the 'jpy' extension module, if not already done.
  6. *
* * @param flags If non-zero, is passed to {@link Diag#setFlags(int)} before python is started * @param extraPaths List of paths that will be prepended to Python's 'sys.path'. * @throws RuntimeException if Python could not be started or if the 'jpy' extension module could not be loaded. */ public static void startPython(int flags, String... extraPaths) { ArrayList dirList = new ArrayList<>(1 + extraPaths.length); File moduleDir = new File(dllFilePath).getParentFile(); if (moduleDir != null) { dirList.add(moduleDir.getAbsoluteFile()); } for (String extraPath : extraPaths) { File extraDir = new File(extraPath).getAbsoluteFile(); if (!dirList.contains(extraDir)) { dirList.add(extraDir); } } extraPaths = new String[dirList.size()]; for (int i = 0; i < dirList.size(); i++) { extraPaths[i] = dirList.get(i).getPath(); } if (DEBUG) { System.out.printf("org.jpy.PyLib: Starting Python with %d extra module path(s):%n", extraPaths.length); for (String path : extraPaths) { System.out.printf("org.jpy.PyLib: %s%n", path); } Diag.setFlags(Diag.F_EXEC | flags); } else if (flags != 0) { Diag.setFlags(flags); } startPython0(extraPaths); } static native boolean startPython0(String... paths); /** * Does the equivalent of setting the PYTHONHOME environment variable. If used, * this must be called prior to calling {@code startPython()}. * Supported for Python 2.7, and Python 3.5 or higher * @param pythonHome Path to Python Home (must be less than 256 characters!) * @return true if successful, false if it fails */ public static native boolean setPythonHome(String pythonHome); /** * Useful for virtual environments, helps in setting sys.prefix/exec_prefix. * If used, this must be called prior to calling {@code startPython()}. * @param programName Path to Python executable (must be less than 256 characters!) * @return true if successful, false if it fails * @see Py_SetProgramName (2) * @see Py_SetProgramName (3) */ public static native boolean setProgramName(String programName); /** * @return The Python interpreter version string. */ public static native String getPythonVersion(); /** * Stops the Python interpreter. * * Important note: Stopping the Python interpreter again after it has been restarted using * {@link #startPython} currently causes a fatal error in the the Java Runtime Environment originating from * the Python interpreter (function {@code Py_Finalize} in standard CPython implementation). * There is currently no workaround for that problem other than not restarting the Python interpreter from * your code. * For more information refer to https://github.com/bcdev/jpy/issues/70 */ public static void stopPython() { PyObject.cleanup(); if (!STOP_IS_NO_OP) { stopPython0(); } } static native void stopPython0(); @Deprecated public static native int execScript(String script); /** * Callers must close the returned reference with {@link #decRef(long)}. */ static native long executeCode(String code, int start, Object globals, Object locals); /** * Callers must close the returned reference with {@link #decRef(long)}. */ static native long executeScript (String file, int start, Object globals, Object locals) throws FileNotFoundException; public static native PyObject getMainGlobals(); /** * Return a dictionary of the global variables in the current execution frame, or NULL if no * frame is currently executing. * * @return the current globals, or null * @see PyEval_GetGlobals (2) * @see PyEval_GetGlobals (3) */ public static native PyObject getCurrentGlobals(); /** * Return a dictionary of the local variables in the current execution frame, or NULL if no * frame is currently executing. * * @return the current locals, or null * @see PyEval_GetLocals (2) * @see PyEval_GetLocals (3) */ public static native PyObject getCurrentLocals(); static native PyObject copyDict(long pyPointer); static native void incRef(long pointer); static native void decRef(long pointer); static native void decRefs(long[] pointers, int len); static native int getIntValue(long pointer); static native long getLongValue(long pointer); static native boolean getBooleanValue(long pointer); static native double getDoubleValue(long pointer); static native String getStringValue(long pointer); static native Object getObjectValue(long pointer); static native boolean isConvertible(long pointer); static native boolean pyNoneCheck(long pointer); static native boolean pyDictCheck(long pointer); static native boolean pyListCheck(long pointer); static native boolean pyBoolCheck(long pointer); static native boolean pyIntCheck(long pointer); static native boolean pyLongCheck(long pointer); static native boolean pyFloatCheck(long pointer); static native boolean pyStringCheck(long pointer); static native boolean pyCallableCheck(long pointer); static native boolean pyFunctionCheck(long pointer); static native boolean pyModuleCheck(long pointer); static native boolean pyTupleCheck(long pointer); static native long getType(long pointer); static native String str(long pointer); static native String repr(long pointer); static native long hash(long pointer); static native boolean eq(long pointer1, Object other); static native PyObject newDict(); /** * https://docs.python.org/2/c-api/dict.html#c.PyDict_Keys * https://docs.python.org/3/c-api/dict.html#c.PyDict_Keys * @return Return a PyListObject containing all the keys from the dictionary. */ static native PyObject pyDictKeys(long pointer); /** * https://docs.python.org/2/c-api/dict.html#c.PyDict_Values * https://docs.python.org/3/c-api/dict.html#c.PyDict_Values * @return Return a PyListObject containing all the values from the dictionary p. */ static native PyObject pyDictValues(long pointer); /** * Determine if dictionary dict contains key. * This is equivalent to the Python expression `key in dict` * * https://docs.python.org/2/c-api/dict.html#c.PyDict_Contains * https://docs.python.org/3/c-api/dict.html#c.PyDict_Contains * * @param dict the dictionary * @param key the key * @param keyClass Optional type for converting the key to a Python object * @return True iff key is in dict. */ static native boolean pyDictContains(long dict, T key, Class keyClass); static native T[] getObjectArrayValue(long pointer, Class itemType); /** * Callers must close the returned reference with {@link #decRef(long)}. */ static native long importModule(String name); /** * Gets the value of a given Python attribute as Python object pointer. *

* Callers must close the returned reference with {@link #decRef(long)}. * * @param pointer Identifies the Python object which contains the attribute {@code name}. * @param name The attribute name. * @return Pointer to a Python object that is the value of the attribute (always a new reference). */ static native long getAttributeObject(long pointer, String name); /** * Gets the value of a given Python attribute as Java value. * * @param pointer Identifies the Python object which contains the attribute {@code name}. * @param name The attribute name. * @param valueType The expected return type. * @return A value that represents the converted Python attribute value. */ static native T getAttributeValue(long pointer, String name, Class valueType); /** * Sets the Python attribute given by {@code name} of the Python object pointed to by {@code pointer}. *

* Before the Python attribute is set, the Java {@code value} is converted into a corresponding * Python object using the optional {@code valueType}. * The {@code value} may also be of type {@code PyObject}. * In this case, it will be directly translated into the corresponding Python object without conversion. * * @param pointer Identifies the Python object which contains the attribute {@code name}. * @param name The attribute name. * @param value The new attribute value. * @param valueType Optional type for converting the value to a Python object. */ static native void setAttributeValue(long pointer, String name, T value, Class valueType); /** * Deletes the Python attribute given by {@code name} of the Python object pointed to by {@code pointer}. *

* * @param pointer Identifies the Python object which contains the attribute {@code name}. * @param name The attribute name. */ static native void delAttribute(long pointer, String name); /** * Checks for the existence the Python attribute given by {@code name} of the Python object pointed to by {@code pointer}. *

* * @param pointer Identifies the Python object which contains the attribute {@code name}. * @param name The attribute name. * @return true if the Python object has an attribute named {@code name} */ static native boolean hasAttribute(long pointer, String name); public static native boolean hasGil(); public static native T ensureGil(Supplier runnable); /** * Calls a Python callable and returns the resulting Python object. *

* Before the Python callable is called, the {@code args} array of Java objects is converted into corresponding * Python objects. * The {@code args} array may also contain objects of type {@code PyObject}. * These will be directly translated into the corresponding Python objects without conversion. *

* Callers must close the returned reference with {@link #decRef(long)}. * * @param pointer Identifies the Python object which contains the callable {@code name}. * @param methodCall true, if this is a call of a method of the Python object pointed to by {@code pointer}. * @param name The name of the callable. * @param argCount The argument count (length of the following {@code args} array). * @param args The arguments. * @param paramTypes Optional array of parameter types for the conversion of the {@code args} into a Python tuple. * If not null, it must be an array of the same length as {@code args}. * @return The resulting Python object (always a new reference). */ static native long callAndReturnObject(long pointer, boolean methodCall, String name, int argCount, Object[] args, Class[] paramTypes); /** * Calls a Python callable and returns the a Java Object. *

* Before the Python callable is called, the {@code args} array of Java objects is converted into corresponding * Python objects. The return value of the Python call is converted to a Java object according the the given * return type. * The {@code args} array may also contain objects of type {@code PyObject}. * These will be directly translated into the corresponding Python objects without conversion. * * @param pointer Identifies the Python object which contains the callable {@code name}. * @param methodCall true, if this is a call of a method of the Python object pointed to by {@code pointer}. * @param name The name of the callable. * @param argCount The argument count (length of the following {@code args} array). * @param args The arguments. * @param paramTypes Optional array of parameter types for the conversion of the {@code args} into a Python tuple. * If not null, it must be an array of the same length as {@code args}. * @param returnType Optional return type. * @return The resulting Python object (always a new reference). */ static native T callAndReturnValue(long pointer, boolean methodCall, String name, int argCount, Object[] args, Class[] paramTypes, Class returnType); private static void loadLib() { if (dllLoaded || dllProblem != null) { return; } try { String pythonLibPath = getProperty(PYTHON_LIB_KEY, false); if (pythonLibPath != null && new File(pythonLibPath).isFile()) { preloadPythonLib(pythonLibPath); } // E.g. dllFilePath = "/usr/local/lib/python3.3/dist-packages/jpy.cpython-33m.so"; dllFilePath = getProperty(JPY_LIB_KEY, true); dllFilePath = new File(dllFilePath).getAbsolutePath(); if (DEBUG) System.out.printf("org.jpy.PyLib: System.load(\"%s\")%n", dllFilePath); //if (DEBUG) System.out.println("org.jpy.PyLib: context class loader: " + Thread.currentThread().getContextClassLoader()); //if (DEBUG) System.out.println("org.jpy.PyLib: class class loader: " + PyLib.class.getClassLoader()); System.load(dllFilePath); dllProblem = null; dllLoaded = true; } catch (Throwable t) { dllProblem = t; throw t; } } private static void preloadPythonLib(String pythonLibPath) { if (getOS() != OS.WINDOWS) { // For PyLib, we load the shared library that was generated for the Python extension module 'jpy'. // However, to use 'jpy' from Java we also need the Python shared library to be loaded as well. // On Windows, this is done auto-magically, on Linux and Darwin we have to either change 'setup.py' // to also include a dependency to the Python shared lib or, as done here, explicitly load it. // // If the Python shared lib is not found, we get error messages similar to the following: // java.lang.UnsatisfiedLinkError: /usr/local/lib/python3.3/dist-packages/jpy.cpython-33m.so: // /usr/local/lib/python3.3/dist-packages/jpy.cpython-33m.so: undefined symbol: PyFloat_Type if (DEBUG) System.out.printf("org.jpy.PyLib: DL.dlopen(\"%s\", DL.RTLD_GLOBAL + DL.RTLD_LAZY%n", pythonLibPath); long handle = DL.dlopen(pythonLibPath, DL.RTLD_GLOBAL + DL.RTLD_LAZY); if (handle == 0) { String message = "Failed to load Python shared library '" + pythonLibPath + "'"; String dlError = DL.dlerror(); if (dlError != null) { message += ": " + dlError; } throw new RuntimeException(message); } } else { // Fixes https://github.com/bcdev/jpy/issues/58 // Loading of jpy DLL fails for user-specific Python installations on Windows if (DEBUG) System.out.printf("org.jpy.PyLib: System.load(\"%s\")%n", pythonLibPath); try { System.load(pythonLibPath); } catch (Exception e) { String message = "Failed to load Python shared library '" + pythonLibPath + "': " + e.getMessage(); System.err.println(message); } } } private PyLib() { } /** * We call this method to ensure that this class gets loaded, and thus the static block gets run * * We don't technically need this method. Callers could instead rely on reflection, using * {@link Class#forName(String)}, but this method is better because it is much more explicit. */ static void dummyMethodForInitialization() { } static { // see documentation in PyLibInitializer for explanation PyLibInitializer.pyLibInitialized = true; if (DEBUG) System.out.println("org.jpy.PyLib: entered static initializer"); loadLib(); if (DEBUG) System.out.println("org.jpy.PyLib: exited static initializer"); } } jpy-2.0.0/src/main/java/org/jpy/PyObjectCleanup.java0000664000175000017500000000011515202503234022407 0ustar alastairalastairpackage org.jpy; interface PyObjectCleanup { int threadSafeCleanup(); } jpy-2.0.0/src/main/java/org/jpy/PyObject.java0000664000175000017500000006300515202503234021106 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; import static org.jpy.PyLib.assertPythonRuns; import java.io.FileNotFoundException; import java.lang.ref.Reference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** * Represents a Python object (of Python/C API type {@code PyObject*}) in the Python interpreter. * *

Reachability hazard (JLS §12.6.1): Instance methods that pass {@code getPointer()} * to a JNI call must use {@code Reference.reachabilityFence(this)} in a {@code finally} block. * Without the fence, the JIT may treat {@code this} as unreachable after {@code getPointer()} * has copied the native pointer into a {@code long}, allowing the GC to collect the wrapper * and the cleanup thread to {@code Py_DECREF} the underlying {@code PyObject*} while JNI is * still using it — resulting in a use-after-free crash (SIGSEGV). * * @author Norman Fomferra * @since 0.7 */ public class PyObject implements AutoCloseable { // Having this static is less than optimal (would be much better to have it attached to the // running python instance) - but that's a lot harder to do. private static final PyObjectReferences REFERENCES = new PyObjectReferences(); private static final AtomicReference CLEANUP_THREAD = new AtomicReference<>(); private static final boolean CLEANUP_ON_THREAD = Boolean.parseBoolean(System.getProperty("PyObject.cleanup_on_thread", "true")); private static void startCleanupThread() { if (CLEANUP_THREAD.get() == null) { final Thread thread = REFERENCES.createCleanupThread("PyObject-cleanup"); thread.setDaemon(true); if (!CLEANUP_THREAD.compareAndSet(null, thread)) { return; } thread.start(); } } public static int cleanup() { return REFERENCES.asProxy().threadSafeCleanup(); } private final PyObjectState state; PyObject(long pointer) { this(pointer, false); } @SuppressWarnings("WeakerAccess") PyObject(long pointer, boolean fromJNI) { state = new PyObjectState(pointer); if (fromJNI) { if (CLEANUP_ON_THREAD) { // ensures that we've only started after python has been started, and we know there is something to cleanup startCleanupThread(); } } registerSelfInto(REFERENCES); } final void registerSelfInto(PyObjectReferences references) { references.register(this); } final PyObjectState getState() { return state; } /** * Decreases the reference count of {@code this} on first invocation. */ @Override public final void close() { state.close(); } /** * Executes Python source code. * * @param code The Python source code. * @param mode The execution mode. * @return The result of executing the code as a Python object. */ public static PyObject executeCode(String code, PyInputMode mode) { return executeCode(code, mode, null, null); } /** * Executes Python source script. * * @param script The Python source script. * @param mode The execution mode. * @return The result of executing the script as a Python object. */ public static PyObject executeScript(String script, PyInputMode mode) throws FileNotFoundException { return executeScript(script, mode, null, null); } /** * Executes Python source code in the context specified by the {@code globals} and {@code locals} maps. *

* If a Java value in the {@code globals} and {@code locals} maps cannot be directly converted into a Python object, a Java wrapper will be created instead. * If a Java value is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param code The Python source code. * @param mode The execution mode. * @param globals The global variables to be set, or {@code null}. * @param locals The locals variables to be set, or {@code null}. * @return The result of executing the code as a Python object. */ public static PyObject executeCode(String code, PyInputMode mode, Object globals, Object locals) { Objects.requireNonNull(code, "code must not be null"); Objects.requireNonNull(mode, "mode must not be null"); return new PyObject(PyLib.executeCode(code, mode.value(), globals, locals)); } /** * Executes Python source script in the context specified by the {@code globals} and {@code locals} maps. *

* If a Java value in the {@code globals} and {@code locals} maps cannot be directly converted into a Python object, a Java wrapper will be created instead. * If a Java value is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param script The Python source script. * @param mode The execution mode. * @param globals The global variables to be set, or {@code null}. * @param locals The locals variables to be set, or {@code null}. * @return The result of executing the script as a Python object. * @throws FileNotFoundException if the script file is not found */ public static PyObject executeScript(String script, PyInputMode mode, Object globals, Object locals) throws FileNotFoundException { Objects.requireNonNull(script, "script must not be null"); Objects.requireNonNull(mode, "mode must not be null"); return new PyObject(PyLib.executeScript(script, mode.value(), globals, locals)); } /** * @return A unique pointer to the wrapped Python object. */ public final long getPointer() { return state.borrowPointer(); } /** * @return This Python object as a Java {@code int} value. */ public int getIntValue() { assertPythonRuns(); try { return PyLib.getIntValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * @return This Python object as a Java {@code long} value. */ public long getLongValue() { assertPythonRuns(); try { return PyLib.getLongValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * @return This Python object as a Java {@code boolean} value. */ public boolean getBooleanValue() { assertPythonRuns(); try { return PyLib.getBooleanValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * @return This Python object as a Java {@code double} value. */ public double getDoubleValue() { assertPythonRuns(); try { return PyLib.getDoubleValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * @return This Python object as a Java {@code String} value. */ public String getStringValue() { assertPythonRuns(); try { return PyLib.getStringValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * Gets this Python object as Java {@code Object} value. *

* If this Python object cannot be converted into a Java object, a Java wrapper of type {@link PyObject} will be returned. * If this Python object is a wrapped Java object, it will be unwrapped. * * @return This Python object as a Java {@code Object} value. */ public Object getObjectValue() { assertPythonRuns(); try { return PyLib.getObjectValue(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * Gets the Python type object for this wrapped object. * * @return This Python object's type as a {@code PyObject} wrapped value. */ public PyObject getType() { assertPythonRuns(); try { return new PyObject(PyLib.getType(getPointer())); } finally { Reference.reachabilityFence(this); } } public boolean isDict() { assertPythonRuns(); try { return PyLib.pyDictCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isList() { assertPythonRuns(); try { return PyLib.pyListCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isBoolean() { assertPythonRuns(); try { return PyLib.pyBoolCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isLong() { assertPythonRuns(); try { return PyLib.pyLongCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isInt() { assertPythonRuns(); try { return PyLib.pyIntCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isNone() { assertPythonRuns(); try { return PyLib.pyNoneCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isFloat() { assertPythonRuns(); try { return PyLib.pyFloatCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isCallable() { assertPythonRuns(); try { return PyLib.pyCallableCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isFunction() { assertPythonRuns(); try { return PyLib.pyFunctionCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isModule() { assertPythonRuns(); try { return PyLib.pyModuleCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isTuple() { assertPythonRuns(); try { return PyLib.pyTupleCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isString() { assertPythonRuns(); try { return PyLib.pyStringCheck(getPointer()); } finally { Reference.reachabilityFence(this); } } public boolean isConvertible() { assertPythonRuns(); try { return PyLib.isConvertible(getPointer()); } finally { Reference.reachabilityFence(this); } } public List asList() { if (!isList()) { throw new ClassCastException("Can not convert non-list type to a list!"); } return new PyListWrapper(this); } public PyDictWrapper asDict() { if (!isDict()) { throw new ClassCastException("Can not convert non-list type to a dictionary!"); } return new PyDictWrapper(this); } /** * Gets this Python object as Java {@code Object[]} value of the given item type. * Appropriate type conversions from Python to Java will be performed as needed. *

* If a Python item value cannot be converted into a Java item object, a Java wrapper of type {@link PyObject} will be returned. * If a Python item value is a wrapped Java object, it will be unwrapped. * If this Python object value is a wrapped Java array object of given type, it will be unwrapped. * * @param itemType The expected item type class. * @param The expected item type name. * @return This Python object as a Java {@code Object[]} value. */ public T[] getObjectArrayValue(Class itemType) { assertPythonRuns(); Objects.requireNonNull(itemType, "itemType must not be null"); try { return PyLib.getObjectArrayValue(getPointer(), itemType); } finally { Reference.reachabilityFence(this); } } /** * Gets the Python value of a Python attribute. *

* If the Python value cannot be converted into a Java object, a Java wrapper of type {@link PyObject} will be returned. * If the Python value is a wrapped Java object, it will be unwrapped. * * @param name A name of the Python attribute. * @return A wrapper for the returned Python attribute value. */ public PyObject getAttribute(String name) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); long pointer; try { pointer = PyLib.getAttributeObject(getPointer(), name); } finally { Reference.reachabilityFence(this); } return pointer != 0 ? new PyObject(pointer) : null; } /** * Gets the value of a Python attribute as Java object of a given type. * Appropriate type conversions from Python to Java will be performed using the given value type. *

* If the Python value cannot be converted into a Java object, a Java wrapper of type {@link PyObject} will be returned. * If the Python value is a wrapped Java object, it will be unwrapped. * * @param name A name of the Python attribute. * @param valueType The type of the value or {@code null}, if unknown. * @param The expected value type name. * @return The Python attribute value as Java object. */ public T getAttribute(String name, Class valueType) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); try { return PyLib.getAttributeValue(getPointer(), name, valueType); } finally { Reference.reachabilityFence(this); } } /** * Sets the value of a Python attribute from the given Java object. *

* If the Java {@code value} cannot be directly converted into a Python object, a Java wrapper will be created instead. * If the Java {@code value} is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param name A name of the Python attribute. * @param value The new attribute value as Java object. May be {@code null}. * @param The value type name. */ public void setAttribute(String name, T value) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); try { PyLib.setAttributeValue(getPointer(), name, value, value != null ? value.getClass() : null); } finally { Reference.reachabilityFence(this); } } /** * Deletes the value of a Python attribute. * * @param name the name of the Python attribute. */ public void delAttribute(String name) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); try { PyLib.delAttribute(getPointer(), name); } finally { Reference.reachabilityFence(this); } } /** * Checks for the existence of a Python attribute.. * * @param name the name of the Python attribute. * @return whether this attribute exists for this object */ public boolean hasAttribute(String name) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); try { return PyLib.hasAttribute(getPointer(), name); } finally { Reference.reachabilityFence(this); } } /** * Sets the value of a Python attribute from the given Java object and Java type (if given). * Appropriate type conversions from Java to Python will be performed using the given value type. *

* If the Java {@code value} cannot be directly converted into a Python object, a Java wrapper will be created instead. * If the Java {@code value} is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param name A name of the Python attribute. * @param value The new attribute value as Java object. * @param valueType The value type used for the conversion. May be {@code null}. * @param The value type name. */ public void setAttribute(String name, T value, Class valueType) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); try { PyLib.setAttributeValue(getPointer(), name, value, valueType); } finally { Reference.reachabilityFence(this); } } /** * Call the callable Python method with the given name and arguments. *

* If a Java value in {@code args} cannot be directly converted into a Python object, a Java wrapper will be created instead. * If the Java value in {@code args} is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param name A name of a Python attribute that evaluates to a callable object. * @param args The arguments for the method call. * @return A wrapper for the returned Python object. */ public PyObject callMethod(String name, Object... args) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); long pointer; try { pointer = PyLib.callAndReturnObject(getPointer(), true, name, args.length, args, null); } finally { Reference.reachabilityFence(this); } return pointer != 0 ? new PyObject(pointer) : null; } /** * Call the callable Python object with the given name and arguments. *

* If a Java value in {@code args} cannot be directly converted into a Python object, a Java wrapper will be created instead. * If the Java value in {@code args} is a wrapped Python object of type {@link PyObject}, it will be unwrapped. * * @param name A name of a Python attribute that evaluates to a callable object, * @param args The arguments for the call. * @return A wrapper for the returned Python object. */ public PyObject call(String name, Object... args) { assertPythonRuns(); Objects.requireNonNull(name, "name must not be null"); long pointer; try { pointer = PyLib.callAndReturnObject(getPointer(), false, name, args.length, args, null); } finally { Reference.reachabilityFence(this); } return pointer != 0 ? new PyObject(pointer) : null; } public T call(Class returnType, String name, Class[] paramTypes, Object[] args) { Objects.requireNonNull(returnType, "returnType must not be null"); Objects.requireNonNull(name, "name must not be null"); Objects.requireNonNull(paramTypes, "paramTypes must not be null"); Objects.requireNonNull(args, "args must not be null"); if (paramTypes.length != args.length) { throw new IllegalArgumentException("paramTypes and args must be of equal length"); } for (int i = 0; i < args.length; ++i) { // Let's be defensive here right now - we can loosen this in the future if necessary. Objects.requireNonNull(paramTypes[i], "paramTypes items must be non null"); Objects.requireNonNull(args[i], "args items must be non null"); if (!Assignment.isAssignableFrom(paramTypes[i], args[i])) { throw new IllegalArgumentException(String.format( "Argument %d of type '%s' is not assignable to type '%s'", i, args[i].getClass(), paramTypes[i])); } } assertPythonRuns(); try { return PyLib.callAndReturnValue(getPointer(), false, name, args.length, args, paramTypes, returnType); } finally { Reference.reachabilityFence(this); } } public T call(Class returnType, String name, Class clazz0, A0 arg0) { return call(returnType, name, new Class[] { clazz0 }, new Object[] { arg0 }); } public T call(Class returnType, String name, Class clazz0, A0 arg0, Class clazz1, A1 arg1) { return call(returnType, name, new Class[] { clazz0, clazz1 }, new Object[] { arg0, arg1 }); } public T call(Class returnType, String name, Class clazz0, A0 arg0, Class clazz1, A1 arg1, Class clazz2, A2 arg2) { return call(returnType, name, new Class[] { clazz0, clazz1, clazz2 }, new Object[] { arg0, arg1, arg2 }); } /** * Create a Java proxy instance of this Python object which contains compatible methods to the ones provided in the * interface given by the {@code type} parameter. * * @param type The interface class. * @param The interface name. * @return A (proxy) instance implementing the given interface. */ public T createProxy(Class type) { assertPythonRuns(); //noinspection unchecked Objects.requireNonNull(type, "type must not be null"); return (T) createProxy(PyLib.CallableKind.METHOD, type); } /** * Create a Java proxy instance of this Python object (or module) which contains compatible methods * (or functions) to the ones provided in the interfaces given by all the {@code type} parameters. * * @param callableKind The kind of calls to be made. * @param types The interface types. * @return A instance implementing the all the given interfaces which serves as a proxy for the given Python object (or module). */ public Object createProxy(PyLib.CallableKind callableKind, Class... types) { assertPythonRuns(); ClassLoader classLoader = types[0].getClassLoader(); InvocationHandler invocationHandler = new PyProxyHandler(this, callableKind); return Proxy.newProxyInstance(classLoader, types, invocationHandler); } /** * Unwraps the original Python object used to create {@code object}. The inverse of * {@link #createProxy(Class)}. * * @param object The object that may be a proxy. * @return The Python object, or null if the object is not a proxy. */ public static PyObject unwrapProxy(Object object) { if (!Proxy.isProxyClass(object.getClass())) { return null; } InvocationHandler handler = Proxy.getInvocationHandler(object); if (!(handler instanceof PyProxyHandler)) { return null; } return ((PyProxyHandler)handler).getPyObject(); } /** * Gets the python string representation of this object. * * @return A string representation of the object. * @see #getPointer() */ @Override public final String toString() { return str(); } /** * Gets a the python repr of this object * * @return A string representation of the object. * @see #getPointer() */ public final String repr() { try { return PyLib.repr(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * Runs the python str function on this object. * @return The String representation of this object. */ public final String str() { try { return PyLib.str(getPointer()); } finally { Reference.reachabilityFence(this); } } /** * Runs the python hash function on this object. * @return The hash. */ public final long hash() { try { return PyLib.hash(getPointer()); } finally { Reference.reachabilityFence(this); } } public final boolean eq(Object other) { try { return PyLib.eq(getPointer(), other); } finally { Reference.reachabilityFence(this); } } /** * Indicates whether some other object is "equal to" this one. * * @param o The other object. * @return {@code true} if the other object is an instance of {@link org.jpy.PyObject} and if their pointers are equal, {@code false} otherwise. * @see #getPointer() */ @Override public final boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof PyObject)) { return false; } PyObject pyObject = (PyObject) o; return getPointer() == pyObject.getPointer(); } /** * Computes a hash code from this object's pointer value. * * @return A hash code value for this object. * @see #getPointer() */ @Override public final int hashCode() { final long pointer = getPointer(); return (int) (pointer ^ (pointer >>> 32)); } } jpy-2.0.0/src/main/java/org/jpy/PyListWrapper.java0000664000175000017500000001467115202503234022161 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file was modified by Deephaven Data Labs. * */ package org.jpy; import java.util.*; /** * A simple wrapper around a Python List object that implements a java List of PyObjects. */ public class PyListWrapper implements List { // todo: https://docs.python.org/3/c-api/list.html vs https://docs.python.org/3/c-api/sequence.html private PyObject pyObject; PyListWrapper(PyObject pyObject) { this.pyObject = pyObject; } @Override public int size() { try (final PyObject len = this.pyObject.callMethod("__len__")) { return len.getIntValue(); } } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean contains(Object o) { for (PyObject obj : this) { if (obj.equals(o)) { return true; } } return false; } @Override public Iterator iterator() { return new Iterator() { int ii = 0; int size = size(); @Override public boolean hasNext() { return ii < size; } @Override public PyObject next() { return get(ii++); } }; } @Override public PyObject[] toArray() { int size = size(); PyObject [] result = new PyObject[size]; for (int ii = 0; ii < size; ++ii) { result[ii] = get(ii); } return result; } @Override public T[] toArray(T[] a) { int size = size(); if (a.length < size) { a = Arrays.copyOf(a, size); } for (int ii = 0; ii < size; ++ii) { //noinspection unchecked a[ii] = (T)get(ii); } if (a.length > size) { a[size] = null; } return a; } @Override public boolean add(PyObject pyObject) { //noinspection EmptyTryBlock try (final PyObject obj = pyObject.callMethod("append", pyObject)) { } return true; } @Override public boolean remove(Object o) { try { //noinspection EmptyTryBlock try (final PyObject obj = pyObject.callMethod("remove", o)) { } return true; } catch (Exception e) { return false; } } @Override public boolean containsAll(Collection c) { return c.stream().allMatch(this::contains); } @Override public boolean addAll(Collection c) { boolean result = false; for (PyObject po : c) { result |= add(po); } return result; } @Override public boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { //noinspection EmptyTryBlock try (final PyObject obj = pyObject.callMethod("clear")) { } } @Override public PyObject get(int index) { // todo https://docs.python.org/3/c-api/list.html#c.PyList_GetItem return pyObject.callMethod("__getitem__", index); } @Override public PyObject set(int index, PyObject element) { final PyObject existing = get(index); setItem(index, element); return existing; } public void setItem(int index, PyObject element) { //noinspection EmptyTryBlock try (final PyObject obj = pyObject.callMethod("__setitem__", index, element)) { } } @Override public void add(int index, PyObject element) { //noinspection EmptyTryBlock try (final PyObject obj = pyObject.callMethod("insert", index, element)) { } } @Override public PyObject remove(int index) { throw new UnsupportedOperationException(); } @Override public int indexOf(Object o) { int size = size(); for (int ii = 0; ii < size; ++ii) { PyObject pyObject = get(ii); if (pyObject == null ? o == null : pyObject.equals(o)) { return ii; } } return -1; } @Override public int lastIndexOf(Object o) { int size = size(); for (int ii = size - 1; ii >= 0; --ii) { PyObject pyObject = get(ii); if (pyObject == null ? o == null : pyObject.equals(o)) { return ii; } } return -1; } @Override public ListIterator listIterator() { throw new UnsupportedOperationException(); } @Override public ListIterator listIterator(int index) { throw new UnsupportedOperationException(); } @Override public List subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); } /** * Return a summarized preview of this list. * * @param prefixLength The maximum prefix of the array to convert * @return The String representation of array */ public String toString(final int prefixLength) { if (isEmpty()) { return "[]"; } final StringBuilder builder = new StringBuilder("["); final int displaySize = Math.min(size(), prefixLength); builder.append(get(0).str()); for (int ei = 1; ei < displaySize; ++ei) { builder.append(", ").append(get(ei).str()); } if (displaySize == size()) { builder.append(']'); } else { builder.append(", ...]"); } return builder.toString(); } } jpy-2.0.0/src/main/java/org/jpy/PyProxyHandler.java0000664000175000017500000001256215202503234022321 0ustar alastairalastair/* * Copyright 2015 Brockmann Consult GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.jpy; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.ref.Reference; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; import org.jpy.PyLib.CallableKind; import static org.jpy.PyLib.assertPythonRuns; /** * The {@code InvocationHandler} for used by the proxy instances created by the * {@link PyObject#createProxy(Class)} and {@link PyModule#createProxy(Class)} * methods. * * @author Norman Fomferra * @since 0.7 */ class PyProxyHandler implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode"); equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class }); toStringMethod = Object.class.getMethod("toString"); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private final PyObject pyObject; private final PyLib.CallableKind callableKind; public PyProxyHandler(PyObject pyObject, PyLib.CallableKind callableKind) { if (pyObject == null) { throw new NullPointerException("pyObject"); } this.pyObject = pyObject; this.callableKind = callableKind; } @Override public Object invoke(Object proxyObject, Method method, Object[] args) throws Throwable { //assertPythonRuns(); // todo: get rid of this check to remove a call down into JNI? if (method.isDefault()) { // This allows our proxy-able interfaces to define default methods. // Note: in this current implementation, defaults methods will always take precedence. final Class declaringClass = method.getDeclaringClass(); // https://blog.jooq.org/2018/03/28/correct-reflective-access-to-interface-default-methods-in-java-8-9-10/ // note: the following throws an IllegalAccessException of the form no private access for invokespecial //return MethodHandles.lookup() // .in(declaringClass) // .unreflectSpecial(method, declaringClass) // .bindTo(proxyObject) // .invokeWithArguments(args); // Unfortunately, the following doesn't work w/ Java 9+. There should be some new api // methods that work w/ Java 9+ though. (MethodHandles#privateLookupIn) final Constructor constructor = Lookup.class .getDeclaredConstructor(Class.class); constructor.setAccessible(true); return constructor .newInstance(declaringClass) .in(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxyObject) .invokeWithArguments(args); } final long pointer = this.pyObject.getPointer(); if ((PyLib.Diag.getFlags() & PyLib.Diag.F_METH) != 0) { System.out.printf("org.jpy.PyProxyHandler: invoke: %s(%s) on pyObject=%s in thread %s\n", method.getName(), Arrays.toString(args), Long.toHexString(pointer), Thread.currentThread()); } final String methodName = method.getName(); final Class returnType = method.getReturnType(); if (method.equals(hashCodeMethod)) { return callPythonHash(); } else if (method.equals(equalsMethod)) { return this.pyObject.eq(args[0]); } else if (method.equals(toStringMethod)) { return this.pyObject.str(); } else if ("close".equals(method.getName()) && method.getParameterCount() == 0 && void.class.equals(method.getReturnType()) && AutoCloseable.class.isAssignableFrom(method.getDeclaringClass())) { this.pyObject.close(); return null; } try { return PyLib.callAndReturnValue( pointer, callableKind == CallableKind.METHOD, methodName, args != null ? args.length : 0, args, method.getParameterTypes(), returnType); } finally { Reference.reachabilityFence(this.pyObject); } } PyObject getPyObject() { return pyObject; } /** * Calls the Python hash() function on the Python object, and downsamples to * 32 bits of it, since Python hash codes are 64 bits on 64 bit * machines. */ private int callPythonHash() { return Long.hashCode(this.pyObject.hash()); } } jpy-2.0.0/LICENSE.txt0000664000175000017500000002613515202503234014325 0ustar alastairalastair Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.jpy-2.0.0/RELEASE.md0000664000175000017500000000366515202503234014107 0ustar alastairalastair# Release Jpy is built and released across a matrix of operating systems, architectures, and Python versions. The jar artifacts are Java 8+ compatible and released to [org.jpyconsortium:jpy](https://repo1.maven.org/maven2/org/jpyconsortium/jpy) on Maven Central. The wheel artifacts are compatible with the table below, and released to the PyPi package [jpy](https://pypi.org/project/jpy/). | Version | OS | Arch | |---------|---------|--------| | 3.9 | Linux | x86_64 | | 3.10 | Linux | x86_64 | | 3.11 | Linux | x86_64 | | 3.12 | Linux | x86_64 | | 3.13 | Linux | x86_64 | | 3.13t | Linux | x86_64 | | 3.14 | Linux | x86_64 | | 3.14t | Linux | x86_64 | | 3.9 | Linux | arm64 | | 3.10 | Linux | arm64 | | 3.11 | Linux | arm64 | | 3.12 | Linux | arm64 | | 3.13 | Linux | arm64 | | 3.13t | Linux | arm64 | | 3.14 | Linux | arm64 | | 3.14t | Linux | arm64 | | 3.9 | MacOS | x86_64 | | 3.10 | MacOS | x86_64 | | 3.11 | MacOS | x86_64 | | 3.12 | MacOS | x86_64 | | 3.13 | MacOS | x86_64 | | 3.13t | MacOS | x86_64 | | 3.14 | MacOS | x86_64 | | 3.14t | MacOS | x86_64 | | 3.9 | MacOS | arm64 | | 3.10 | MacOS | arm64 | | 3.11 | MacOS | arm64 | | 3.12 | MacOS | arm64 | | 3.13 | MacOS | arm64 | | 3.13t | MacOS | arm64 | | 3.14 | MacOS | arm64 | | 3.14t | MacOS | arm64 | | 3.9 | Windows | x86_64 | | 3.10 | Windows | x86_64 | | 3.11 | Windows | x86_64 | | 3.12 | Windows | x86_64 | | 3.13 | Windows | x86_64 | | 3.13t | Windows | x86_64 | | 3.14 | Windows | x86_64 | | 3.14t | Windows | x86_64 | ## Process The [build.yml](.github/workflows/build.yml) workflow is the main process by which PRs and releases are built. 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). jpy-2.0.0/doc/0000775000175000017500000000000015202503234013240 5ustar alastairalastairjpy-2.0.0/doc/conf.py0000664000175000017500000001777015202503234014553 0ustar alastairalastair#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # jpy documentation build configuration file, created by # sphinx-quickstart on Tue Jan 21 08:21:22 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.ifconfig', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'jpy' copyright = '2015, Brockmann Consult GmbH' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.9' # The full version, including alpha/beta/rc tags. release = '0.9.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'jpydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'jpy.tex', 'jpy Documentation', 'Brockmann Consult GmbH', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'jpy', 'jpy Documentation', ['Brockmann Consult GmbH'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'jpy', 'jpy Documentation', 'Brockmann Consult GmbH', 'jpy', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False jpy-2.0.0/doc/_static/0000775000175000017500000000000015202503234014666 5ustar alastairalastairjpy-2.0.0/doc/_static/java-apidocs/0000775000175000017500000000000015202503234017227 5ustar alastairalastairjpy-2.0.0/doc/_static/java-apidocs/package-list0000664000175000017500000000001015202503234021505 0ustar alastairalastairorg.jpy jpy-2.0.0/doc/_static/java-apidocs/script.js0000664000175000017500000000147315202503234021076 0ustar alastairalastairfunction show(type) { count = 0; for (var key in methods) { var row = document.getElementById(key); if ((methods[key] & type) != 0) { row.style.display = ''; row.className = (count++ % 2) ? rowColor : altColor; } else row.style.display = 'none'; } updateTabs(type); } function updateTabs(type) { for (var value in tabs) { var sNode = document.getElementById(tabs[value][0]); var spanNode = sNode.firstChild; if (value == type) { sNode.className = activeTableTab; spanNode.innerHTML = tabs[value][1]; } else { sNode.className = tableTab; spanNode.innerHTML = "" + tabs[value][1] + ""; } } } jpy-2.0.0/doc/_static/java-apidocs/index.html0000664000175000017500000000544315202503234021232 0ustar alastairalastair Java-Python Bridge 0.10.0-SNAPSHOT Java API <noscript> <div>JavaScript is disabled on your browser.</div> </noscript> <h2>Frame Alert</h2> <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> jpy-2.0.0/doc/_static/java-apidocs/org/0000775000175000017500000000000015202503234020016 5ustar alastairalastairjpy-2.0.0/doc/_static/java-apidocs/org/jpy/0000775000175000017500000000000015202503234020620 5ustar alastairalastairjpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyInputMode.html0000664000175000017500000004422015202503234023725 0ustar alastairalastair PyInputMode (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

JavaScript is disabled on your browser.
org.jpy

Enum PyInputMode

    • Enum Constant Detail

      • STATEMENT

        public static final PyInputMode STATEMENT
        Compile/execute single statement code.

        The start symbol from the Python grammar for a single statement (Py_single_input in Python PyRun API). This is the symbol used for the interactive interpreter loop.

      • SCRIPT

        public static final PyInputMode SCRIPT
        Compile/execute multi-statement script code.

        The start symbol from the Python grammar for sequences of statements as read from a file or other source (Py_file_input in Python PyRun API). This is the symbol to use when compiling arbitrarily long Python source code.

      • EXPRESSION

        public static final PyInputMode EXPRESSION
        Compile/execute single expression.

        The start symbol from the Python grammar for sequences of statements as read from a file or other source (Py_eval_input in Python PyRun API).

    • Method Detail

      • values

        public static PyInputMode[] values()
        Returns an array containing the constants of this enum type, in the order they are declared. This method may be used to iterate over the constants as follows:
        for (PyInputMode c : PyInputMode.values())
            System.out.println(c);
        
        Returns:
        an array containing the constants of this enum type, in the order they are declared
      • valueOf

        public static PyInputMode valueOf(String name)
        Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
        Parameters:
        name - the name of the enum constant to be returned.
        Returns:
        the enum constant with the specified name
        Throws:
        IllegalArgumentException - if this enum type has no constant with the specified name
        NullPointerException - if the argument is null
      • value

        public int value()

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/package-use.html0000664000175000017500000001403215202503234023673 0ustar alastairalastair Uses of Package org.jpy (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Package
org.jpy

  • Classes in org.jpy used by org.jpy 
    Class and Description
    CreateModule 
    IdentityModule 
    KeyError
    Translation of Python KeyErrors so that they can be programmatically detected from Java.
    PyDictWrapper
    A simple wrapper around PyObjects that are actually Python dictionaries, to present the most useful parts of a Map interface.
    PyInputMode
    Source code input modes for compiling/executing Python source code.
    PyLib.CallableKind
    The kind of callable Python objects.
    PyModule
    Represents a Python module.
    PyObject
    Represents a Python object (of Python/C API type PyObject*) in the Python interpreter.

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/DL.html0000664000175000017500000004312315202503234022010 0ustar alastairalastair DL (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class DL



  • public class DL
    extends Object
    A replacement for System.load(String) with support for POSIX dlopen flags.

    Important note: This class is useful on POSIX (Unix/Linux) systems only. On Windows OSes, all methods are no-ops.

    Since:
    0.7
    See Also:
    dlopen(3) - Linux manual page
    • Field Summary

      Fields 
      Modifier and Type Field and Description
      static int RTLD_GLOBAL
      External symbols defined in the library will be made available to subsequently loaded libraries.
      static int RTLD_LAZY
      Resolve undefined symbols as code from the dynamic library is executed.
      static int RTLD_LOCAL
      This is the converse of RTLD_GLOBAL, and the default if neither flag is specified.
      static int RTLD_NOW
      Resolve all undefined symbols before dlopen(java.lang.String, int) returns and fail if this cannot be done.
    • Constructor Summary

      Constructors 
      Constructor and Description
      DL() 
    • Field Detail

      • RTLD_LAZY

        public static final int RTLD_LAZY
        Resolve undefined symbols as code from the dynamic library is executed.
        See Also:
        Constant Field Values
      • RTLD_LOCAL

        public static final int RTLD_LOCAL
        This is the converse of RTLD_GLOBAL, and the default if neither flag is specified.
        See Also:
        Constant Field Values
      • RTLD_GLOBAL

        public static final int RTLD_GLOBAL
        External symbols defined in the library will be made available to subsequently loaded libraries.
        See Also:
        Constant Field Values
    • Constructor Detail

      • DL

        public DL()
    • Method Detail

      • dlopen

        public static long dlopen(String filename,
                                  int flag)
        loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic library. If filename is null, then the returned handle is for the main program. If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname.
        Parameters:
        filename - dynamic library filename or null
        flag - combination of RTLD_GLOBAL or RTLD_LOCAL with RTLD_LAZY, RTLD_NOW.
        Returns:
        opaque "handle" for the dynamic library.
      • dlclose

        public static int dlclose(long handle)
      • dlerror

        public static String dlerror()

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/package-frame.html0000664000175000017500000000445215202503234024176 0ustar alastairalastair org.jpy (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

org.jpy

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/CreateModule.html0000664000175000017500000003634115202503234024066 0ustar alastairalastair CreateModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class CreateModule

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyLib.Diag.html0000664000175000017500000004147715202503234023405 0ustar alastairalastair PyLib.Diag (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyLib.Diag

  • Enclosing class:
    PyLib


    public static class PyLib.Diag
    extends Object
    Controls output of diagnostic information for debugging.
    • Field Summary

      Fields 
      Modifier and Type Field and Description
      static int F_ALL
      Print any diagnostic information.
      static int F_ERR
      Print diagnostic information if erroneous states are detected in the jpy Python module.
      static int F_EXEC
      Print diagnostic information when code execution flow is passed from Java to Python or the other way round.
      static int F_JVM
      Print diagnostic information usage of the Java VM Invocation API.
      static int F_MEM
      Print diagnostic information about memory allocation/deallocation.
      static int F_METH
      Print diagnostic information while Java methods overloads are selected.
      static int F_OFF
      Print no diagnostic information at all.
      static int F_TYPE
      Print diagnostic information while Java types are resolved.
    • Field Detail

      • F_OFF

        public static final int F_OFF
        Print no diagnostic information at all.
        See Also:
        Constant Field Values
      • F_TYPE

        public static final int F_TYPE
        Print diagnostic information while Java types are resolved.
        See Also:
        Constant Field Values
      • F_METH

        public static final int F_METH
        Print diagnostic information while Java methods overloads are selected.
        See Also:
        Constant Field Values
      • F_EXEC

        public static final int F_EXEC
        Print diagnostic information when code execution flow is passed from Java to Python or the other way round.
        See Also:
        Constant Field Values
      • F_MEM

        public static final int F_MEM
        Print diagnostic information about memory allocation/deallocation.
        See Also:
        Constant Field Values
      • F_JVM

        public static final int F_JVM
        Print diagnostic information usage of the Java VM Invocation API.
        See Also:
        Constant Field Values
      • F_ERR

        public static final int F_ERR
        Print diagnostic information if erroneous states are detected in the jpy Python module.
        See Also:
        Constant Field Values
    • Method Detail

      • getFlags

        public static int getFlags()
        Returns:
        the current diagnostic flags.
      • setFlags

        public static void setFlags(int flags)
        Sets the current diagnostic flags.
        Parameters:
        flags - the current diagnostic flags.

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/KeyError.html0000664000175000017500000002705015202503234023254 0ustar alastairalastair KeyError (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class KeyError

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/package-tree.html0000664000175000017500000002166315202503234024046 0ustar alastairalastair org.jpy Class Hierarchy (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Hierarchy For Package org.jpy

Class Hierarchy

Interface Hierarchy

Enum Hierarchy

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/StopIteration.html0000664000175000017500000002660515202503234024323 0ustar alastairalastair StopIteration (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class StopIteration

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyLibInitializer.html0000664000175000017500000003547715202503234024751 0ustar alastairalastair PyLibInitializer (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyLibInitializer



  • public class PyLibInitializer
    extends Object
    This helper class is to safely and programmatically configure jpy. Configuration of PyLib is done at class initialization time via system properties. This is a potentially delicate time in the lifecycle of a program - and errors around order of initialization can be hard to track down. To safely and programmatically configure jpy, we must ensure that we are the ones causing the initialization of PyLib (and DL).
    • Constructor Detail

      • PyLibInitializer

        public PyLibInitializer()
    • Method Detail

      • isPyLibInitialized

        public static boolean isPyLibInitialized()
        Returns true iff the PyLib class has been initialized
      • isDlInitialized

        public static boolean isDlInitialized()
        Returns true iff the DL class has been initialized
      • initPyLib

        public static void initPyLib(String pyLib,
                                     String jpyLib,
                                     String jdlLib)
        This method should only be called once - it is dependent on PyLib and DL being uninitialized. Any consumers who want to programmatically configure jpy should call this method first.
        Parameters:
        pyLib - the python library
        jpyLib - the jpy library
        jdlLib - the jdl library

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyModule.html0000664000175000017500000005251715202503234023256 0ustar alastairalastair PyModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyModule

  • All Implemented Interfaces:
    AutoCloseable


    public class PyModule
    extends PyObject
    Represents a Python module.
    Since:
    0.7
    • Method Detail

      • getName

        public String getName()
        Returns:
        The Python module's name.
      • getMain

        public static PyModule getMain()
        Get the Python interpreter's main module and return its Java representation. It can be used to access global scope variables and functions. For example:
              PyLib.execScript("def incByOne(x): return x + 1");
              PyModule mainModule = PyModule.getMain();
              PyObject eleven = mainModule.call("incByOne", 10);
         
        Returns:
        The Python main module's Java representation.
        Since:
        0.8
      • getBuiltins

        public static PyModule getBuiltins()
        Get the Python interpreter's buildins module and returns its Java representation. It can be used to call functions such as len(), type(), list(), etc. For example:
              builtins = PyModule.getBuiltins();
              PyObject size = builtins.call("len", pyList);
         
        Returns:
        Java representation of Python's builtin module.
        Since:
        0.8
        See Also:
        PyObject.call(String, Object...)
      • importModule

        public static PyModule importModule(String name)
        Import a Python module into the Python interpreter and return its Java representation.
        Parameters:
        name - The Python module's name.
        Returns:
        The Python module's Java representation.
      • extendSysPath

        public static PyObject extendSysPath(String modulePath,
                                             boolean prepend)
        Extends Python's 'sys.path' variable by the given module path.
        Parameters:
        modulePath - The new module path. Should be an absolute pathname.
        prepend - If true, the new path will be the new first element of 'sys.path', otherwise it will be the last.
        Returns:
        The altered 'sys.path' list.
        Since:
        0.8
      • createProxy

        public <T> T createProxy(Class<T> type)
        Create a Java proxy instance of this Python module which contains compatible functions to the ones provided in the interface given by the type parameter.
        Overrides:
        createProxy in class PyObject
        Type Parameters:
        T - The interface name.
        Parameters:
        type - The interface's type.
        Returns:
        A (proxy) instance implementing the given interface.

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyDictWrapper.html0000664000175000017500000011763315202503234024256 0ustar alastairalastair PyDictWrapper (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyDictWrapper

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/IdentityModule.html0000664000175000017500000003102715202503234024450 0ustar alastairalastair IdentityModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Interface IdentityModule

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyLib.CallableKind.html0000664000175000017500000004104015202503234025030 0ustar alastairalastair PyLib.CallableKind (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Enum PyLib.CallableKind

    • Enum Constant Detail

      • FUNCTION

        public static final PyLib.CallableKind FUNCTION
        Function call without the Python self as first argument.
      • METHOD

        public static final PyLib.CallableKind METHOD
        An instance method call with the Python self (= the instance) as first argument.
    • Method Detail

      • values

        public static PyLib.CallableKind[] values()
        Returns an array containing the constants of this enum type, in the order they are declared. This method may be used to iterate over the constants as follows:
        for (PyLib.CallableKind c : PyLib.CallableKind.values())
            System.out.println(c);
        
        Returns:
        an array containing the constants of this enum type, in the order they are declared
      • valueOf

        public static PyLib.CallableKind valueOf(String name)
        Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
        Parameters:
        name - the name of the enum constant to be returned.
        Returns:
        the enum constant with the specified name
        Throws:
        IllegalArgumentException - if this enum type has no constant with the specified name
        NullPointerException - if the argument is null

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyLib.html0000664000175000017500000006643215202503234022540 0ustar alastairalastair PyLib (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyLib



  • public class PyLib
    extends Object
    Represents the library that provides the Python interpreter (CPython).

    When the PyLib class is loaded, it reads its configuration from a Java properties file called .jpy which must exist in the current user's home directory. The configuration file has been written to this location by installing the Python jpy module using python3 setup.py install --user on Unix and python setup.py install) on Windows.

    Currently, the following properties are recognised in the .jpy file:

    • python.lib - the Python shared library (usually required on Unix only)
    • jpy.lib - the jpy shared library path for Python (Unix: jpy*.so, Windows: jpy*.pyd)

    jpy API clients should first call isPythonRunning() in order to check if a Python interpreter is already available. If not, startPython(String...) must be called before any other jpy API is used.

    Important note for developers: If you change the signature of any of the native PyLib methods, you must first run javah on the compiled class, and then adapt src/main/c/jni/org_jpy_PyLib.c.

    Since:
    0.7
    • Method Detail

      • getDllFilePath

        public static String getDllFilePath()
      • assertPythonRuns

        public static void assertPythonRuns()
        Throws a runtime exception if Python interpreter is not running. Possible reasons for this are
        • You have not called startPython(String...) yet.
        • The Python shared library code for the Python interpreter could not be found or could not be be loaded.
        • The Python shared library code for the Python 'jpy' module could not be found or could not be be loaded.
        • The Python interpreter could not be initialised.
      • isPythonRunning

        public static boolean isPythonRunning()
        Returns:
        true if the Python interpreter is running and the the 'jpy' module has been loaded.
      • startPython

        public static void startPython(int flags,
                                       String... extraPaths)
        Starts the Python interpreter. It does the following:
        1. Initializes the Python interpreter, if not already running.
        2. Prepends any given extra paths to Python's 'sys.path' (e.g. so that 'jpy' can be loaded from isolated directories).
        3. Imports the 'jpy' extension module, if not already done.
        Parameters:
        flags - If non-zero, is passed to PyLib.Diag.setFlags(int) before python is started
        extraPaths - List of paths that will be prepended to Python's 'sys.path'.
        Throws:
        RuntimeException - if Python could not be started or if the 'jpy' extension module could not be loaded.
      • setPythonHome

        public static boolean setPythonHome(String pythonHome)
        Does the equivalent of setting the PYTHONHOME environment variable. If used, this must be called prior to calling startPython(). Supported for Python 2.7, and Python 3.5 or higher
        Parameters:
        pythonHome - Path to Python Home (must be less than 256 characters!)
        Returns:
        true if successful, false if it fails
      • setProgramName

        public static boolean setProgramName(String programName)
        Useful for virtual environments, helps in setting sys.prefix/exec_prefix. If used, this must be called prior to calling startPython().
        Parameters:
        programName - Path to Python executable (must be less than 256 characters!)
        Returns:
        true if successful, false if it fails
        See Also:
        Py_SetProgramName (2), Py_SetProgramName (3)
      • getPythonVersion

        public static String getPythonVersion()
        Returns:
        The Python interpreter version string.
      • stopPython

        public static void stopPython()
        Stops the Python interpreter. Important note: Stopping the Python interpreter again after it has been restarted using startPython(java.lang.String...) currently causes a fatal error in the the Java Runtime Environment originating from the Python interpreter (function Py_Finalize in standard CPython implementation). There is currently no workaround for that problem other than not restarting the Python interpreter from your code. For more information refer to https://github.com/bcdev/jpy/issues/70
      • execScript

        @Deprecated
        public static int execScript(String script)
        Deprecated. 
      • getMainGlobals

        public static PyObject getMainGlobals()
      • getCurrentGlobals

        public static PyObject getCurrentGlobals()
        Return a dictionary of the global variables in the current execution frame, or NULL if no frame is currently executing.
        Returns:
        the current globals, or null
        See Also:
        PyEval_GetGlobals (2), PyEval_GetGlobals (3)
      • getCurrentLocals

        public static PyObject getCurrentLocals()
        Return a dictionary of the local variables in the current execution frame, or NULL if no frame is currently executing.
        Returns:
        the current locals, or null
        See Also:
        PyEval_GetLocals (2), PyEval_GetLocals (3)
      • hasGil

        public static boolean hasGil()

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/package-summary.html0000664000175000017500000002673015202503234024604 0ustar alastairalastair org.jpy (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Package org.jpy

Provides the classes necessary to create an embedded Python VM, to execute Python code, to import Python modules, to access Python variables, and finally to call Python functions and class methods.

See: Description

  • Interface Summary 
    Interface Description
    IdentityModule  
  • Class Summary 
    Class Description
    CreateModule  
    DL
    A replacement for System.load(String) with support for POSIX dlopen flags.
    PyDictWrapper
    A simple wrapper around PyObjects that are actually Python dictionaries, to present the most useful parts of a Map interface.
    PyLib
    Represents the library that provides the Python interpreter (CPython).
    PyLib.Diag
    Controls output of diagnostic information for debugging.
    PyLibInitializer
    This helper class is to safely and programmatically configure jpy.
    PyListWrapper
    A simple wrapper around a Python List object that implements a java List of PyObjects.
    PyModule
    Represents a Python module.
    PyObject
    Represents a Python object (of Python/C API type PyObject*) in the Python interpreter.
  • Enum Summary 
    Enum Description
    PyInputMode
    Source code input modes for compiling/executing Python source code.
    PyLib.CallableKind
    The kind of callable Python objects.
  • Exception Summary 
    Exception Description
    KeyError
    Translation of Python KeyErrors so that they can be programmatically detected from Java.
    StopIteration
    Translation of Python StopIteration so that they can be programmatically detected from Java.

Package org.jpy Description

Provides the classes necessary to
  1. create an embedded Python VM,
  2. to execute Python code,
  3. to import Python modules,
  4. to access Python variables, and finally
  5. to call Python functions and class methods.

The entry point to the jpy Java API is the PyLib class which is used to either detect an already running Python interpreter or to start a new one:

     if (!PyLib.isPythonRunning()) {
         PyLib.startPython(opt1, opt2, ...);
     }
 
jpy API clients should first call PyLib.isPythonRunning() in order to check if a Python interpreter is already available. If not, PyLib.startPython(String...) must be called before any other jpy API is used.

Once the Python interpreter in running clients can either execute Python code directly using the PyLib.execScript(String) method or load a Python module using PyModule.importModule(String). The returned PyModule object then is the entry point to access Python variables and invoke functions.

Some PyModule methods return PyObject instances. These can be used to create instances of Python classes, and to access Python class members, attributes and to invoke Python object methods..

Since:
0.7

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyObject.html0000664000175000017500000021137415202503234023235 0ustar alastairalastair PyObject (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyObject

  • All Implemented Interfaces:
    AutoCloseable
    Direct Known Subclasses:
    PyModule


    public class PyObject
    extends Object
    implements AutoCloseable
    Represents a Python object (of Python/C API type PyObject*) in the Python interpreter.
    Since:
    0.7
    • Method Detail

      • cleanup

        public static int cleanup()
      • close

        public final void close()
        Decreases the reference count of this on first invocation.
        Specified by:
        close in interface AutoCloseable
      • executeCode

        public static PyObject executeCode(String code,
                                           PyInputMode mode)
        Executes Python source code.
        Parameters:
        code - The Python source code.
        mode - The execution mode.
        Returns:
        The result of executing the code as a Python object.
      • executeCode

        public static PyObject executeCode(String code,
                                           PyInputMode mode,
                                           Object globals,
                                           Object locals)
        Executes Python source code in the context specified by the globals and locals maps.

        If a Java value in the globals and locals maps cannot be directly converted into a Python object, a Java wrapper will be created instead. If a Java value is a wrapped Python object of type PyObject, it will be unwrapped.

        Parameters:
        code - The Python source code.
        mode - The execution mode.
        globals - The global variables to be set, or null.
        locals - The locals variables to be set, or null.
        Returns:
        The result of executing the code as a Python object.
      • executeScript

        public static PyObject executeScript(String script,
                                             PyInputMode mode,
                                             Object globals,
                                             Object locals)
                                      throws FileNotFoundException
        Executes Python source script in the context specified by the globals and locals maps.

        If a Java value in the globals and locals maps cannot be directly converted into a Python object, a Java wrapper will be created instead. If a Java value is a wrapped Python object of type PyObject, it will be unwrapped.

        Parameters:
        script - The Python source script.
        mode - The execution mode.
        globals - The global variables to be set, or null.
        locals - The locals variables to be set, or null.
        Returns:
        The result of executing the script as a Python object.
        Throws:
        FileNotFoundException - if the script file is not found
      • getPointer

        public final long getPointer()
        Returns:
        A unique pointer to the wrapped Python object.
      • getIntValue

        public int getIntValue()
        Returns:
        This Python object as a Java int value.
      • getLongValue

        public long getLongValue()
        Returns:
        This Python object as a Java long value.
      • getBooleanValue

        public boolean getBooleanValue()
        Returns:
        This Python object as a Java boolean value.
      • getDoubleValue

        public double getDoubleValue()
        Returns:
        This Python object as a Java double value.
      • getStringValue

        public String getStringValue()
        Returns:
        This Python object as a Java String value.
      • getObjectValue

        public Object getObjectValue()
        Gets this Python object as Java Object value.

        If this Python object cannot be converted into a Java object, a Java wrapper of type PyObject will be returned. If this Python object is a wrapped Java object, it will be unwrapped.

        Returns:
        This Python object as a Java Object value.
      • getType

        public PyObject getType()
        Gets the Python type object for this wrapped object.
        Returns:
        This Python object's type as a PyObject wrapped value.
      • isDict

        public boolean isDict()
      • isList

        public boolean isList()
      • isBoolean

        public boolean isBoolean()
      • isLong

        public boolean isLong()
      • isInt

        public boolean isInt()
      • isNone

        public boolean isNone()
      • isFloat

        public boolean isFloat()
      • isCallable

        public boolean isCallable()
      • isFunction

        public boolean isFunction()
      • isModule

        public boolean isModule()
      • isTuple

        public boolean isTuple()
      • isString

        public boolean isString()
      • isConvertible

        public boolean isConvertible()
      • getObjectArrayValue

        public <T> T[] getObjectArrayValue(Class<? extends T> itemType)
        Gets this Python object as Java Object[] value of the given item type. Appropriate type conversions from Python to Java will be performed as needed.

        If a Python item value cannot be converted into a Java item object, a Java wrapper of type PyObject will be returned. If a Python item value is a wrapped Java object, it will be unwrapped. If this Python object value is a wrapped Java array object of given type, it will be unwrapped.

        Type Parameters:
        T - The expected item type name.
        Parameters:
        itemType - The expected item type class.
        Returns:
        This Python object as a Java Object[] value.
      • getAttribute

        public PyObject getAttribute(String name)
        Gets the Python value of a Python attribute.

        If the Python value cannot be converted into a Java object, a Java wrapper of type PyObject will be returned. If the Python value is a wrapped Java object, it will be unwrapped.

        Parameters:
        name - A name of the Python attribute.
        Returns:
        A wrapper for the returned Python attribute value.
      • getAttribute

        public <T> T getAttribute(String name,
                                  Class<? extends T> valueType)
        Gets the value of a Python attribute as Java object of a given type. Appropriate type conversions from Python to Java will be performed using the given value type.

        If the Python value cannot be converted into a Java object, a Java wrapper of type PyObject will be returned. If the Python value is a wrapped Java object, it will be unwrapped.

        Type Parameters:
        T - The expected value type name.
        Parameters:
        name - A name of the Python attribute.
        valueType - The type of the value or null, if unknown.
        Returns:
        The Python attribute value as Java object.
      • setAttribute

        public <T> void setAttribute(String name,
                                     T value)
        Sets the value of a Python attribute from the given Java object.

        If the Java value cannot be directly converted into a Python object, a Java wrapper will be created instead. If the Java value is a wrapped Python object of type PyObject, it will be unwrapped.

        Type Parameters:
        T - The value type name.
        Parameters:
        name - A name of the Python attribute.
        value - The new attribute value as Java object. May be null.
      • delAttribute

        public void delAttribute(String name)
        Deletes the value of a Python attribute.
        Parameters:
        name - the name of the Python attribute.
      • hasAttribute

        public boolean hasAttribute(String name)
        Checks for the existence of a Python attribute..
        Parameters:
        name - the name of the Python attribute.
        Returns:
        whether this attribute exists for this object
      • setAttribute

        public <T> void setAttribute(String name,
                                     T value,
                                     Class<? extends T> valueType)
        Sets the value of a Python attribute from the given Java object and Java type (if given). Appropriate type conversions from Java to Python will be performed using the given value type.

        If the Java value cannot be directly converted into a Python object, a Java wrapper will be created instead. If the Java value is a wrapped Python object of type PyObject, it will be unwrapped.

        Type Parameters:
        T - The value type name.
        Parameters:
        name - A name of the Python attribute.
        value - The new attribute value as Java object.
        valueType - The value type used for the conversion. May be null.
      • callMethod

        public PyObject callMethod(String name,
                                   Object... args)
        Call the callable Python method with the given name and arguments.

        If a Java value in args cannot be directly converted into a Python object, a Java wrapper will be created instead. If the Java value in args is a wrapped Python object of type PyObject, it will be unwrapped.

        Parameters:
        name - A name of a Python attribute that evaluates to a callable object.
        args - The arguments for the method call.
        Returns:
        A wrapper for the returned Python object.
      • call

        public PyObject call(String name,
                             Object... args)
        Call the callable Python object with the given name and arguments.

        If a Java value in args cannot be directly converted into a Python object, a Java wrapper will be created instead. If the Java value in args is a wrapped Python object of type PyObject, it will be unwrapped.

        Parameters:
        name - A name of a Python attribute that evaluates to a callable object,
        args - The arguments for the call.
        Returns:
        A wrapper for the returned Python object.
      • call

        public <T,A0> T call(Class<T> returnType,
                             String name,
                             Class<A0> clazz0,
                             A0 arg0)
      • call

        public <T,A0,A1> T call(Class<T> returnType,
                                String name,
                                Class<A0> clazz0,
                                A0 arg0,
                                Class<A1> clazz1,
                                A1 arg1)
      • call

        public <T,A0,A1,A2> T call(Class<T> returnType,
                                   String name,
                                   Class<A0> clazz0,
                                   A0 arg0,
                                   Class<A1> clazz1,
                                   A1 arg1,
                                   Class<A2> clazz2,
                                   A2 arg2)
      • createProxy

        public <T> T createProxy(Class<T> type)
        Create a Java proxy instance of this Python object which contains compatible methods to the ones provided in the interface given by the type parameter.
        Type Parameters:
        T - The interface name.
        Parameters:
        type - The interface class.
        Returns:
        A (proxy) instance implementing the given interface.
      • createProxy

        public Object createProxy(PyLib.CallableKind callableKind,
                                  Class<?>... types)
        Create a Java proxy instance of this Python object (or module) which contains compatible methods (or functions) to the ones provided in the interfaces given by all the type parameters.
        Parameters:
        callableKind - The kind of calls to be made.
        types - The interface types.
        Returns:
        A instance implementing the all the given interfaces which serves as a proxy for the given Python object (or module).
      • unwrapProxy

        public static PyObject unwrapProxy(Object object)
        Unwraps the original Python object used to create object. The inverse of createProxy(Class).
        Parameters:
        object - The object that may be a proxy.
        Returns:
        The Python object, or null if the object is not a proxy.
      • toString

        public final String toString()
        Gets the python string representation of this object.
        Overrides:
        toString in class Object
        Returns:
        A string representation of the object.
        See Also:
        getPointer()
      • repr

        public final String repr()
        Gets a the python repr of this object
        Returns:
        A string representation of the object.
        See Also:
        getPointer()
      • str

        public final String str()
        Runs the python str function on this object.
        Returns:
        The String representation of this object.
      • hash

        public final long hash()
        Runs the python hash function on this object.
        Returns:
        The hash.
      • eq

        public final boolean eq(Object other)
      • equals

        public final boolean equals(Object o)
        Indicates whether some other object is "equal to" this one.
        Overrides:
        equals in class Object
        Parameters:
        o - The other object.
        Returns:
        true if the other object is an instance of PyObject and if their pointers are equal, false otherwise.
        See Also:
        getPointer()
      • hashCode

        public final int hashCode()
        Computes a hash code from this object's pointer value.
        Overrides:
        hashCode in class Object
        Returns:
        A hash code value for this object.
        See Also:
        getPointer()

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyListWrapper.html0000664000175000017500000014135415202503234024303 0ustar alastairalastair PyListWrapper (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
org.jpy

Class PyListWrapper

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/0000775000175000017500000000000015202503234022517 5ustar alastairalastairjpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyInputMode.html0000664000175000017500000002420415202503234025624 0ustar alastairalastair Uses of Class org.jpy.PyInputMode (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyInputMode

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/DL.html0000664000175000017500000001015215202503234023703 0ustar alastairalastair Uses of Class org.jpy.DL (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.DL

No usage of org.jpy.DL

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/CreateModule.html0000664000175000017500000001565615202503234025773 0ustar alastairalastair Uses of Class org.jpy.CreateModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.CreateModule

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyLib.Diag.html0000664000175000017500000001030215202503234025263 0ustar alastairalastair Uses of Class org.jpy.PyLib.Diag (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyLib.Diag

No usage of org.jpy.PyLib.Diag

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/KeyError.html0000664000175000017500000001267715202503234025164 0ustar alastairalastair Uses of Class org.jpy.KeyError (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.KeyError

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/StopIteration.html0000664000175000017500000001034315202503234026212 0ustar alastairalastair Uses of Class org.jpy.StopIteration (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.StopIteration

No usage of org.jpy.StopIteration

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyLibInitializer.html0000664000175000017500000001040415202503234026627 0ustar alastairalastair Uses of Class org.jpy.PyLibInitializer (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyLibInitializer

No usage of org.jpy.PyLibInitializer

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyModule.html0000664000175000017500000001465615202503234025157 0ustar alastairalastair Uses of Class org.jpy.PyModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyModule

    • Uses of PyModule in org.jpy

      Methods in org.jpy that return PyModule 
      Modifier and Type Method and Description
      static PyModule PyModule.getBuiltins()
      Get the Python interpreter's buildins module and returns its Java representation.
      static PyModule PyModule.getMain()
      Get the Python interpreter's main module and return its Java representation.
      static PyModule PyModule.importModule(String name)
      Import a Python module into the Python interpreter and return its Java representation.

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyDictWrapper.html0000664000175000017500000001337415202503234026152 0ustar alastairalastair Uses of Class org.jpy.PyDictWrapper (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyDictWrapper

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/IdentityModule.html0000664000175000017500000001304215202503234026344 0ustar alastairalastair Uses of Interface org.jpy.IdentityModule (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Interface
org.jpy.IdentityModule

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyLib.CallableKind.html0000664000175000017500000001721015202503234026731 0ustar alastairalastair Uses of Class org.jpy.PyLib.CallableKind (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyLib.CallableKind

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyLib.html0000664000175000017500000001021315202503234024421 0ustar alastairalastair Uses of Class org.jpy.PyLib (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyLib

No usage of org.jpy.PyLib

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyObject.html0000664000175000017500000010006715202503234025130 0ustar alastairalastair Uses of Class org.jpy.PyObject (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyObject

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

jpy-2.0.0/doc/_static/java-apidocs/org/jpy/class-use/PyListWrapper.html0000664000175000017500000001034315202503234026173 0ustar alastairalastair Uses of Class org.jpy.PyListWrapper (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Uses of Class
org.jpy.PyListWrapper

No usage of org.jpy.PyListWrapper

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

jpy-2.0.0/doc/_static/java-apidocs/deprecated-list.html0000664000175000017500000001064415202503234023173 0ustar alastairalastair Deprecated List (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Deprecated API

Contents

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

jpy-2.0.0/doc/_static/java-apidocs/allclasses-frame.html0000664000175000017500000000414515202503234023337 0ustar alastairalastair All Classes (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

All Classes

jpy-2.0.0/doc/_static/java-apidocs/stylesheet.css0000664000175000017500000003105215202503234022133 0ustar alastairalastair/* Javadoc style sheet */ /* Overall document style */ @import url('resources/fonts/dejavu.css'); body { background-color:#ffffff; color:#353833; font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; font-size:14px; margin:0; } a:link, a:visited { text-decoration:none; color:#4A6782; } a:hover, a:focus { text-decoration:none; color:#bb7a2a; } a:active { text-decoration:none; color:#4A6782; } a[name] { color:#353833; } a[name]:hover { text-decoration:none; color:#353833; } pre { font-family:'DejaVu Sans Mono', monospace; font-size:14px; } h1 { font-size:20px; } h2 { font-size:18px; } h3 { font-size:16px; font-style:italic; } h4 { font-size:13px; } h5 { font-size:12px; } h6 { font-size:11px; } ul { list-style-type:disc; } code, tt { font-family:'DejaVu Sans Mono', monospace; font-size:14px; padding-top:4px; margin-top:8px; line-height:1.4em; } dt code { font-family:'DejaVu Sans Mono', monospace; font-size:14px; padding-top:4px; } table tr td dt code { font-family:'DejaVu Sans Mono', monospace; font-size:14px; vertical-align:top; padding-top:4px; } sup { font-size:8px; } /* Document title and Copyright styles */ .clear { clear:both; height:0px; overflow:hidden; } .aboutLanguage { float:right; padding:0px 21px; font-size:11px; z-index:200; margin-top:-9px; } .legalCopy { margin-left:.5em; } .bar a, .bar a:link, .bar a:visited, .bar a:active { color:#FFFFFF; text-decoration:none; } .bar a:hover, .bar a:focus { color:#bb7a2a; } .tab { background-color:#0066FF; color:#ffffff; padding:8px; width:5em; font-weight:bold; } /* Navigation bar styles */ .bar { background-color:#4D7A97; color:#FFFFFF; padding:.8em .5em .4em .8em; height:auto;/*height:1.8em;*/ font-size:11px; margin:0; } .topNav { background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; width:100%; clear:right; height:2.8em; padding-top:10px; overflow:hidden; font-size:12px; } .bottomNav { margin-top:10px; background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; width:100%; clear:right; height:2.8em; padding-top:10px; overflow:hidden; font-size:12px; } .subNav { background-color:#dee3e9; float:left; width:100%; overflow:hidden; font-size:12px; } .subNav div { clear:left; float:left; padding:0 0 5px 6px; text-transform:uppercase; } ul.navList, ul.subNavList { float:left; margin:0 25px 0 0; padding:0; } ul.navList li{ list-style:none; float:left; padding: 5px 6px; text-transform:uppercase; } ul.subNavList li{ list-style:none; float:left; } .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { color:#FFFFFF; text-decoration:none; text-transform:uppercase; } .topNav a:hover, .bottomNav a:hover { text-decoration:none; color:#bb7a2a; text-transform:uppercase; } .navBarCell1Rev { background-color:#F8981D; color:#253441; margin: auto 5px; } .skipNav { position:absolute; top:auto; left:-9999px; overflow:hidden; } /* Page header and footer styles */ .header, .footer { clear:both; margin:0 20px; padding:5px 0 0 0; } .indexHeader { margin:10px; position:relative; } .indexHeader span{ margin-right:15px; } .indexHeader h1 { font-size:13px; } .title { color:#2c4557; margin:10px 0; } .subTitle { margin:5px 0 0 0; } .header ul { margin:0 0 15px 0; padding:0; } .footer ul { margin:20px 0 5px 0; } .header ul li, .footer ul li { list-style:none; font-size:13px; } /* Heading styles */ div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { background-color:#dee3e9; border:1px solid #d0d9e0; margin:0 0 6px -8px; padding:7px 5px; } ul.blockList ul.blockList ul.blockList li.blockList h3 { background-color:#dee3e9; border:1px solid #d0d9e0; margin:0 0 6px -8px; padding:7px 5px; } ul.blockList ul.blockList li.blockList h3 { padding:0; margin:15px 0; } ul.blockList li.blockList h2 { padding:0px 0 20px 0; } /* Page layout container styles */ .contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { clear:both; padding:10px 20px; position:relative; } .indexContainer { margin:10px; position:relative; font-size:12px; } .indexContainer h2 { font-size:13px; padding:0 0 3px 0; } .indexContainer ul { margin:0; padding:0; } .indexContainer ul li { list-style:none; padding-top:2px; } .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { font-size:12px; font-weight:bold; margin:10px 0 0 0; color:#4E4E4E; } .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { margin:5px 0 10px 0px; font-size:14px; font-family:'DejaVu Sans Mono',monospace; } .serializedFormContainer dl.nameValue dt { margin-left:1px; font-size:1.1em; display:inline; font-weight:bold; } .serializedFormContainer dl.nameValue dd { margin:0 0 0 1px; font-size:1.1em; display:inline; } /* List styles */ ul.horizontal li { display:inline; font-size:0.9em; } ul.inheritance { margin:0; padding:0; } ul.inheritance li { display:inline; list-style:none; } ul.inheritance li ul.inheritance { margin-left:15px; padding-left:15px; padding-top:1px; } ul.blockList, ul.blockListLast { margin:10px 0 10px 0; padding:0; } ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; margin-bottom:15px; line-height:1.4; } ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { padding:0px 20px 5px 10px; border:1px solid #ededed; background-color:#f8f8f8; } ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { padding:0 0 5px 8px; background-color:#ffffff; border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { margin-left:0; padding-left:0; padding-bottom:15px; border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { list-style:none; border-bottom:none; padding-bottom:0; } table tr td dl, table tr td dl dt, table tr td dl dd { margin-top:0; margin-bottom:1px; } /* Table styles */ .overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { width:100%; border-left:1px solid #EEE; border-right:1px solid #EEE; border-bottom:1px solid #EEE; } .overviewSummary, .memberSummary { padding:0px; } .overviewSummary caption, .memberSummary caption, .typeSummary caption, .useSummary caption, .constantsSummary caption, .deprecatedSummary caption { position:relative; text-align:left; background-repeat:no-repeat; color:#253441; font-weight:bold; clear:none; overflow:hidden; padding:0px; padding-top:10px; padding-left:1px; margin:0px; white-space:pre; } .overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, .useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, .overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, .useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, .overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, .useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, .overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, .useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { color:#FFFFFF; } .overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, .useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; padding-bottom:7px; display:inline-block; float:left; background-color:#F8981D; border: none; height:16px; } .memberSummary caption span.activeTableTab span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; margin-right:3px; display:inline-block; float:left; background-color:#F8981D; height:16px; } .memberSummary caption span.tableTab span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; margin-right:3px; display:inline-block; float:left; background-color:#4D7A97; height:16px; } .memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { padding-top:0px; padding-left:0px; padding-right:0px; background-image:none; float:none; display:inline; } .overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, .useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { display:none; width:5px; position:relative; float:left; background-color:#F8981D; } .memberSummary .activeTableTab .tabEnd { display:none; width:5px; margin-right:3px; position:relative; float:left; background-color:#F8981D; } .memberSummary .tableTab .tabEnd { display:none; width:5px; margin-right:3px; position:relative; background-color:#4D7A97; float:left; } .overviewSummary td, .memberSummary td, .typeSummary td, .useSummary td, .constantsSummary td, .deprecatedSummary td { text-align:left; padding:0px 0px 12px 10px; } th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ vertical-align:top; padding-right:0px; padding-top:8px; padding-bottom:3px; } th.colFirst, th.colLast, th.colOne, .constantsSummary th { background:#dee3e9; text-align:left; padding:8px 3px 3px 7px; } td.colFirst, th.colFirst { white-space:nowrap; font-size:13px; } td.colLast, th.colLast { font-size:13px; } td.colOne, th.colOne { font-size:13px; } .overviewSummary td.colFirst, .overviewSummary th.colFirst, .useSummary td.colFirst, .useSummary th.colFirst, .overviewSummary td.colOne, .overviewSummary th.colOne, .memberSummary td.colFirst, .memberSummary th.colFirst, .memberSummary td.colOne, .memberSummary th.colOne, .typeSummary td.colFirst{ width:25%; vertical-align:top; } td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { font-weight:bold; } .tableSubHeadingColor { background-color:#EEEEFF; } .altColor { background-color:#FFFFFF; } .rowColor { background-color:#EEEEEF; } /* Content styles */ .description pre { margin-top:0; } .deprecatedContent { margin:0; padding:10px 0; } .docSummary { padding:0; } ul.blockList ul.blockList ul.blockList li.blockList h3 { font-style:normal; } div.block { font-size:14px; font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } td.colLast div { padding-top:0px; } td.colLast a { padding-bottom:3px; } /* Formatting effect styles */ .sourceLineNo { color:green; padding:0 30px 0 0; } h1.hidden { visibility:hidden; overflow:hidden; font-size:10px; } .block { display:block; margin:3px 10px 2px 0px; color:#474747; } .deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, .overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { font-weight:bold; } .deprecationComment, .emphasizedPhrase, .interfaceName { font-style:italic; } div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, div.block div.block span.interfaceName { font-style:normal; } div.contentContainer ul.blockList li.blockList h2{ padding-bottom:0px; } jpy-2.0.0/doc/_static/java-apidocs/constant-values.html0000664000175000017500000002207615202503234023252 0ustar alastairalastair Constant Field Values (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Constant Field Values

Contents

org.jpy.*

  • org.jpy.DL 
    Modifier and Type Constant Field Value
    public static final int RTLD_GLOBAL 8
    public static final int RTLD_LAZY 1
    public static final int RTLD_LOCAL 4
    public static final int RTLD_NOW 2
  • org.jpy.IdentityModule 
    Modifier and Type Constant Field Value
    public static final String IDENTITY_CODE "def identity(x):\n return x"
  • org.jpy.PyLib.Diag 
    Modifier and Type Constant Field Value
    public static final int F_ALL 255
    public static final int F_ERR 32
    public static final int F_EXEC 4
    public static final int F_JVM 16
    public static final int F_MEM 8
    public static final int F_METH 2
    public static final int F_OFF 0
    public static final int F_TYPE 1

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

jpy-2.0.0/doc/_static/java-apidocs/overview-tree.html0000664000175000017500000002160315202503234022722 0ustar alastairalastair Class Hierarchy (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Hierarchy For All Packages

Package Hierarchies:

Class Hierarchy

Interface Hierarchy

Enum Hierarchy

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

jpy-2.0.0/doc/_static/java-apidocs/serialized-form.html0000664000175000017500000001120215202503234023205 0ustar alastairalastair Serialized Form (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

Serialized Form

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

jpy-2.0.0/doc/_static/java-apidocs/index-all.html0000664000175000017500000015410315202503234021776 0ustar alastairalastair Index (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
A C D E F G H I K L O P R S T U V 

A

add(PyObject) - Method in class org.jpy.PyListWrapper
 
add(int, PyObject) - Method in class org.jpy.PyListWrapper
 
addAll(Collection<? extends PyObject>) - Method in class org.jpy.PyListWrapper
 
addAll(int, Collection<? extends PyObject>) - Method in class org.jpy.PyListWrapper
 
asDict() - Method in class org.jpy.PyObject
 
asList() - Method in class org.jpy.PyObject
 
assertPythonRuns() - Static method in class org.jpy.PyLib
Throws a runtime exception if Python interpreter is not running.

C

call(String, String) - Method in class org.jpy.CreateModule
 
call(String, Object...) - Method in class org.jpy.PyObject
Call the callable Python object with the given name and arguments.
call(Class<T>, String, Class<?>[], Object[]) - Method in class org.jpy.PyObject
 
call(Class<T>, String, Class<A0>, A0) - Method in class org.jpy.PyObject
 
call(Class<T>, String, Class<A0>, A0, Class<A1>, A1) - Method in class org.jpy.PyObject
 
call(Class<T>, String, Class<A0>, A0, Class<A1>, A1, Class<A2>, A2) - Method in class org.jpy.PyObject
 
callAsFunctionModule(String, String, Class<T>) - Method in class org.jpy.CreateModule
 
callAsMethodModule(String, String, Class<T>) - Method in class org.jpy.CreateModule
 
callMethod(String, Object...) - Method in class org.jpy.PyObject
Call the callable Python method with the given name and arguments.
cleanup() - Static method in class org.jpy.PyObject
 
clear() - Method in class org.jpy.PyDictWrapper
 
clear() - Method in class org.jpy.PyListWrapper
 
close() - Method in class org.jpy.CreateModule
 
close() - Method in interface org.jpy.IdentityModule
 
close() - Method in class org.jpy.PyDictWrapper
 
close() - Method in class org.jpy.PyObject
Decreases the reference count of this on first invocation.
contains(Object) - Method in class org.jpy.PyListWrapper
 
containsAll(Collection<?>) - Method in class org.jpy.PyListWrapper
 
containsKey(Object) - Method in class org.jpy.PyDictWrapper
 
containsKey(String) - Method in class org.jpy.PyDictWrapper
An extension to the Map interface that allows the use of String keys without generating warnings.
containsKey(PyObject) - Method in class org.jpy.PyDictWrapper
 
containsValue(Object) - Method in class org.jpy.PyDictWrapper
 
copy() - Method in class org.jpy.PyDictWrapper
Copy this dictionary into a new dictionary.
create() - Static method in class org.jpy.CreateModule
 
create(CreateModule) - Static method in interface org.jpy.IdentityModule
 
create(CreateModule, Class<T>) - Static method in interface org.jpy.IdentityModule
 
CreateModule - Class in org.jpy
 
createProxy(Class<T>) - Method in class org.jpy.PyModule
Create a Java proxy instance of this Python module which contains compatible functions to the ones provided in the interface given by the type parameter.
createProxy(Class<T>) - Method in class org.jpy.PyObject
Create a Java proxy instance of this Python object which contains compatible methods to the ones provided in the interface given by the type parameter.
createProxy(PyLib.CallableKind, Class<?>...) - Method in class org.jpy.PyObject
Create a Java proxy instance of this Python object (or module) which contains compatible methods (or functions) to the ones provided in the interfaces given by all the type parameters.

D

delAttribute(String) - Method in class org.jpy.PyObject
Deletes the value of a Python attribute.
delItem(Object) - Method in class org.jpy.PyDictWrapper
 
DL - Class in org.jpy
A replacement for System.load(String) with support for POSIX dlopen flags.
DL() - Constructor for class org.jpy.DL
 
dlclose(long) - Static method in class org.jpy.DL
 
dlerror() - Static method in class org.jpy.DL
 
dlopen(String, int) - Static method in class org.jpy.DL
loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic library.

E

ensureGil(Supplier<T>) - Static method in class org.jpy.PyLib
 
entrySet() - Method in class org.jpy.PyDictWrapper

Note: we are returning a COPY not a VIEW of the entries
eq(Object) - Method in class org.jpy.PyObject
 
equals(Object) - Method in class org.jpy.PyObject
Indicates whether some other object is "equal to" this one.
execScript(String) - Static method in class org.jpy.PyLib
Deprecated.
executeCode(String, PyInputMode) - Static method in class org.jpy.PyObject
Executes Python source code.
executeCode(String, PyInputMode, Object, Object) - Static method in class org.jpy.PyObject
Executes Python source code in the context specified by the globals and locals maps.
executeScript(String, PyInputMode) - Static method in class org.jpy.PyObject
Executes Python source script.
executeScript(String, PyInputMode, Object, Object) - Static method in class org.jpy.PyObject
Executes Python source script in the context specified by the globals and locals maps.
extendSysPath(String, boolean) - Static method in class org.jpy.PyModule
Extends Python's 'sys.path' variable by the given module path.

F

F_ALL - Static variable in class org.jpy.PyLib.Diag
Print any diagnostic information.
F_ERR - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information if erroneous states are detected in the jpy Python module.
F_EXEC - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information when code execution flow is passed from Java to Python or the other way round.
F_JVM - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information usage of the Java VM Invocation API.
F_MEM - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information about memory allocation/deallocation.
F_METH - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information while Java methods overloads are selected.
F_OFF - Static variable in class org.jpy.PyLib.Diag
Print no diagnostic information at all.
F_TYPE - Static variable in class org.jpy.PyLib.Diag
Print diagnostic information while Java types are resolved.

G

get(Object) - Method in class org.jpy.PyDictWrapper
 
get(String) - Method in class org.jpy.PyDictWrapper
An extension to the Map interface that allows the use of String keys without generating warnings.
get(int) - Method in class org.jpy.PyListWrapper
 
getAttribute(String) - Method in class org.jpy.PyObject
Gets the Python value of a Python attribute.
getAttribute(String, Class<? extends T>) - Method in class org.jpy.PyObject
Gets the value of a Python attribute as Java object of a given type.
getBooleanValue() - Method in class org.jpy.PyObject
 
getBuiltins() - Static method in class org.jpy.PyModule
Get the Python interpreter's buildins module and returns its Java representation.
getCurrentGlobals() - Static method in class org.jpy.PyLib
Return a dictionary of the global variables in the current execution frame, or NULL if no frame is currently executing.
getCurrentLocals() - Static method in class org.jpy.PyLib
Return a dictionary of the local variables in the current execution frame, or NULL if no frame is currently executing.
getDllFilePath() - Static method in class org.jpy.PyLib
 
getDoubleValue() - Method in class org.jpy.PyObject
 
getFlags() - Static method in class org.jpy.PyLib.Diag
 
getIntValue() - Method in class org.jpy.PyObject
 
getItem(Object) - Method in class org.jpy.PyDictWrapper
 
getLongValue() - Method in class org.jpy.PyObject
 
getMain() - Static method in class org.jpy.PyModule
Get the Python interpreter's main module and return its Java representation.
getMainGlobals() - Static method in class org.jpy.PyLib
 
getName() - Method in class org.jpy.PyModule
 
getObjectArrayValue(Class<? extends T>) - Method in class org.jpy.PyObject
Gets this Python object as Java Object[] value of the given item type.
getObjectValue() - Method in class org.jpy.PyObject
Gets this Python object as Java Object value.
getPointer() - Method in class org.jpy.PyObject
 
getPythonVersion() - Static method in class org.jpy.PyLib
 
getStringValue() - Method in class org.jpy.PyObject
 
getType() - Method in class org.jpy.PyObject
Gets the Python type object for this wrapped object.

H

hasAttribute(String) - Method in class org.jpy.PyObject
Checks for the existence of a Python attribute..
hasGil() - Static method in class org.jpy.PyLib
 
hash() - Method in class org.jpy.PyObject
Runs the python hash function on this object.
hashCode() - Method in class org.jpy.PyObject
Computes a hash code from this object's pointer value.

I

identity(Object) - Method in interface org.jpy.IdentityModule
 
IDENTITY_CODE - Static variable in interface org.jpy.IdentityModule
 
IdentityModule - Interface in org.jpy
 
importModule(String) - Static method in class org.jpy.PyModule
Import a Python module into the Python interpreter and return its Java representation.
indexOf(Object) - Method in class org.jpy.PyListWrapper
 
initPyLib(String, String, String) - Static method in class org.jpy.PyLibInitializer
This method should only be called once - it is dependent on PyLib and DL being uninitialized.
isBoolean() - Method in class org.jpy.PyObject
 
isCallable() - Method in class org.jpy.PyObject
 
isConvertible() - Method in class org.jpy.PyObject
 
isDict() - Method in class org.jpy.PyObject
 
isDlInitialized() - Static method in class org.jpy.PyLibInitializer
Returns true iff the DL class has been initialized
isEmpty() - Method in class org.jpy.PyDictWrapper
 
isEmpty() - Method in class org.jpy.PyListWrapper
 
isFloat() - Method in class org.jpy.PyObject
 
isFunction() - Method in class org.jpy.PyObject
 
isInt() - Method in class org.jpy.PyObject
 
isList() - Method in class org.jpy.PyObject
 
isLong() - Method in class org.jpy.PyObject
 
isModule() - Method in class org.jpy.PyObject
 
isNone() - Method in class org.jpy.PyObject
 
isPyLibInitialized() - Static method in class org.jpy.PyLibInitializer
Returns true iff the PyLib class has been initialized
isPythonRunning() - Static method in class org.jpy.PyLib
 
isString() - Method in class org.jpy.PyObject
 
isTuple() - Method in class org.jpy.PyObject
 
iterator() - Method in class org.jpy.PyListWrapper
 

K

KeyError - Exception in org.jpy
Translation of Python KeyErrors so that they can be programmatically detected from Java.
keySet() - Method in class org.jpy.PyDictWrapper

Note: we are returning a COPY not a VIEW of the keys

L

lastIndexOf(Object) - Method in class org.jpy.PyListWrapper
 
listIterator() - Method in class org.jpy.PyListWrapper
 
listIterator(int) - Method in class org.jpy.PyListWrapper
 

O

org.jpy - package org.jpy
Provides the classes necessary to create an embedded Python VM, to execute Python code, to import Python modules, to access Python variables, and finally to call Python functions and class methods.

P

put(PyObject, PyObject) - Method in class org.jpy.PyDictWrapper
 
putAll(Map<? extends PyObject, ? extends PyObject>) - Method in class org.jpy.PyDictWrapper
 
putObject(Object, Object) - Method in class org.jpy.PyDictWrapper
 
PyDictWrapper - Class in org.jpy
A simple wrapper around PyObjects that are actually Python dictionaries, to present the most useful parts of a Map interface.
PyInputMode - Enum in org.jpy
Source code input modes for compiling/executing Python source code.
PyLib - Class in org.jpy
Represents the library that provides the Python interpreter (CPython).
PyLib.CallableKind - Enum in org.jpy
The kind of callable Python objects.
PyLib.Diag - Class in org.jpy
Controls output of diagnostic information for debugging.
PyLibInitializer - Class in org.jpy
This helper class is to safely and programmatically configure jpy.
PyLibInitializer() - Constructor for class org.jpy.PyLibInitializer
 
PyListWrapper - Class in org.jpy
A simple wrapper around a Python List object that implements a java List of PyObjects.
PyModule - Class in org.jpy
Represents a Python module.
PyObject - Class in org.jpy
Represents a Python object (of Python/C API type PyObject*) in the Python interpreter.

R

remove(Object) - Method in class org.jpy.PyDictWrapper
 
remove(String) - Method in class org.jpy.PyDictWrapper
 
remove(Object) - Method in class org.jpy.PyListWrapper
 
remove(int) - Method in class org.jpy.PyListWrapper
 
removeAll(Collection<?>) - Method in class org.jpy.PyListWrapper
 
repr() - Method in class org.jpy.PyObject
Gets a the python repr of this object
retainAll(Collection<?>) - Method in class org.jpy.PyListWrapper
 
RTLD_GLOBAL - Static variable in class org.jpy.DL
External symbols defined in the library will be made available to subsequently loaded libraries.
RTLD_LAZY - Static variable in class org.jpy.DL
Resolve undefined symbols as code from the dynamic library is executed.
RTLD_LOCAL - Static variable in class org.jpy.DL
This is the converse of RTLD_GLOBAL, and the default if neither flag is specified.
RTLD_NOW - Static variable in class org.jpy.DL
Resolve all undefined symbols before DL.dlopen(java.lang.String, int) returns and fail if this cannot be done.

S

set(int, PyObject) - Method in class org.jpy.PyListWrapper
 
setAttribute(String, T) - Method in class org.jpy.PyObject
Sets the value of a Python attribute from the given Java object.
setAttribute(String, T, Class<? extends T>) - Method in class org.jpy.PyObject
Sets the value of a Python attribute from the given Java object and Java type (if given).
setFlags(int) - Static method in class org.jpy.PyLib.Diag
Sets the current diagnostic flags.
setItem(Object, Object) - Method in class org.jpy.PyDictWrapper
 
setItem(int, PyObject) - Method in class org.jpy.PyListWrapper
 
setProgramName(String) - Static method in class org.jpy.PyLib
Useful for virtual environments, helps in setting sys.prefix/exec_prefix.
setPythonHome(String) - Static method in class org.jpy.PyLib
Does the equivalent of setting the PYTHONHOME environment variable.
size() - Method in class org.jpy.PyDictWrapper
 
size() - Method in class org.jpy.PyListWrapper
 
startPython(String...) - Static method in class org.jpy.PyLib
Delegates to PyLib.startPython(int, String...) with flags = Diag.F_OFF.
startPython(int, String...) - Static method in class org.jpy.PyLib
Starts the Python interpreter.
StopIteration - Exception in org.jpy
Translation of Python StopIteration so that they can be programmatically detected from Java.
stopPython() - Static method in class org.jpy.PyLib
Stops the Python interpreter.
str() - Method in class org.jpy.PyObject
Runs the python str function on this object.
subList(int, int) - Method in class org.jpy.PyListWrapper
 

T

toArray() - Method in class org.jpy.PyListWrapper
 
toArray(T[]) - Method in class org.jpy.PyListWrapper
 
toString(int) - Method in class org.jpy.PyListWrapper
Return a summarized preview of this list.
toString() - Method in class org.jpy.PyObject
Gets the python string representation of this object.

U

unwrap() - Method in class org.jpy.PyDictWrapper
Gets the underlying PyObject.
unwrapProxy(Object) - Static method in class org.jpy.PyObject
Unwraps the original Python object used to create object.

V

value() - Method in enum org.jpy.PyInputMode
 
valueOf(String) - Static method in enum org.jpy.PyInputMode
Returns the enum constant of this type with the specified name.
valueOf(String) - Static method in enum org.jpy.PyLib.CallableKind
Returns the enum constant of this type with the specified name.
values() - Method in class org.jpy.PyDictWrapper

Note: we are returning a COPY not a VIEW of the values
values() - Static method in enum org.jpy.PyInputMode
Returns an array containing the constants of this enum type, in the order they are declared.
values() - Static method in enum org.jpy.PyLib.CallableKind
Returns an array containing the constants of this enum type, in the order they are declared.
A C D E F G H I K L O P R S T U V 

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

jpy-2.0.0/doc/_static/java-apidocs/help-doc.html0000664000175000017500000002115515202503234021614 0ustar alastairalastair API Help (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

How This API Document Is Organized

This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
  • Package

    Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:

    • Interfaces (italic)
    • Classes
    • Enums
    • Exceptions
    • Errors
    • Annotation Types
  • Class/Interface

    Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:

    • Class inheritance diagram
    • Direct Subclasses
    • All Known Subinterfaces
    • All Known Implementing Classes
    • Class/interface declaration
    • Class/interface description
    • Nested Class Summary
    • Field Summary
    • Constructor Summary
    • Method Summary
    • Field Detail
    • Constructor Detail
    • Method Detail

    Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.

  • Annotation Type

    Each annotation type has its own separate page with the following sections:

    • Annotation Type declaration
    • Annotation Type description
    • Required Element Summary
    • Optional Element Summary
    • Element Detail
  • Enum

    Each enum has its own separate page with the following sections:

    • Enum declaration
    • Enum description
    • Enum Constant Summary
    • Enum Constant Detail
  • Use

    Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.

  • Tree (Class Hierarchy)

    There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.

    • When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.
    • When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.
  • Deprecated API

    The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.

  • Index

    The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.

  • Prev/Next

    These links take you to the next or previous class, interface, package, or related page.

  • Frames/No Frames

    These links show and hide the HTML frames. All pages are available with or without frames.

  • All Classes

    The All Classes link shows all classes and interfaces except non-static nested types.

  • Serialized Form

    Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.

  • Constant Field Values

    The Constant Field Values page lists the static final fields and their values.

This help file applies to API documentation generated using the standard doclet.

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

jpy-2.0.0/doc/_static/java-apidocs/allclasses-noframe.html0000664000175000017500000000351515202503234023674 0ustar alastairalastair All Classes (Java-Python Bridge 0.10.0-SNAPSHOT Java API)

All Classes

jpy-2.0.0/doc/_static/figures/0000775000175000017500000000000015202503234016332 5ustar alastairalastairjpy-2.0.0/doc/_static/figures/deployment.png0000664000175000017500000002635315202503234021231 0ustar alastairalastair‰PNG  IHDRñd¿û•Ú)tEXtcopyleftGenerated by http://plantuml.com09jzTXtplantumlxœm‘ÑNÂ0†ï›ìÎ%\ЬCÙ…A˜ â.MÙX²µMÛaööv€âåþ}=ÿwÚuܸª,L•ZI”&ËÙåç¢vŸJ6ˆ‡Å†gvyA­º t}츹N*' ªë€lDM)¹[ªÒè9´·¿ê¦ø4 €Ò‡s×ux2úKþÓp€N*±CÛqá«®ë輇¿©3vØ6 ”ùáFÉ¢àҽϦ°Gc…?Íh²eQk…9"S.·ßzåÉPy]S'0“oªB̲É\†…+IDATxÚí |åÝøc„p„C!@­Šh)¢x ‡G r(m ŠTÀ¿Tü›¾IjB¢‚EŽ$ZŽp Ð KL(ä$°@ D%VÑ1H% g HßvÞu³»äÜ™ý~?Ï'ŸÉìì쳿gžïüž¹Öë_`¼Nœ8p:NœüÌÉìí”ꊳ4 N‡z"÷ê¯r*§€FÀéÐ §‰\t2;Þ½åHÄ"œ€Ó¡œ.J½rå{ˉ¬xœ€ÓÁPNß±Êl6–”””––VVVÒF8bsR´8òªI‹#‰RÕòCùw9=1|±ÉdÊÌÌÌÏÏ­—••Ñd8®‘˜;/nIÛ§¯~7...%%E´.Ùº¤ê4N‡k8ÝÑIQåD¥¾.h^LLŒh]²u³Ùl±Xh2œ×pº#k+bu£Ó‰Óccc%U/))¡Ép:àtÀéžsì%â''EÕâöc/8§C®Ùs¤8§C¨®8{*§@-ÅQ¯^˵ÐúL©¯eÄé8š¶kçž#œ€Ó§Nœ8Ý“îååUY™wç·\¸°Ï楪ªýwÜñó²²ì{ïýELÌÛ6¯ÆÆ¾sß}½p:Nm9]þΞ=iÅŠ›—Ö¬ ÿãŸS–yà_~ÿ}ŽúREEÞÝw÷TÞ‹Óp:hË饥;î¹çŽ,PçË´¤ç_}•ª,óÖ[¯.Y¤¾ºjÕœ—q:N-:]ÊäÉOoÝ¥ÎOMý`üø‘ê2ÅÅ[úõë­¾êïßïðá$œ€ÓA£N/*JS«ó‡ y°°0Þz™ûïïõ駉2qìXzŸ>=­ß‹Óp:hËéRFztÿþõ2‘Ÿ¿á‰'²YfþüÀ3&ÈÄ»ïþñ­·^Åé84íôœœÕcÇ— ù›‘±Âf‹å£.]:VW¸ë®Û‹‹·àtœšpúÏîwút®Lœ;gn×Î×ú¥‡ê»oߺï¶ë}™/©ºz #NÀéà~§tϼy••yþóÿŒ1Èú¥ÄÄݺݔðž]§GDÌ”éùóq:N­8½°0þþû{µlé3`@Ÿ#GR¬_º|ùàã’¿vþí·»þzoåGœ€ÓANçÙ84Juùñ¸ZŠ#¯š´8ò'?‘Á³vp:è)1ç71§ÇéG"Yÿ†Zøí:œús:¿1 8§ãtœ<öñ““¢jáØ Ný9s¤€Ó@uÅÙS9j)ŽÚxõZƨ…ÖgJ¹–§ƒŽÓvî9Àé€Óq:Ní9ÝÑ)SWåô,NÀéÐP§k§àtœõÇú”éöˆU‰á‹×¿».hž¸µnÅrßb·¬]‡Óp:4f³Ùd2ÅÅÅÅÔI±c©ƒÔDêc±Xhœõ¡°°PRã””Qjl§Ç6òéR©‰Ô§´´”vÀéPJJJòóóE¦’#'Öqzb#!Ÿ.ušH}ÊÊÊhœõA’bѨdÇf³9³ŽˆÓ3 ùt©ƒÔDêSYYI»àt¨"PɋŤ‹¥¤ŽˆÓK ùt©ƒÔDêSUUE»àtp5ât‚€ÓÁ …¯"8p:àtÀé8p:àtp\Ë€ÓÁ8pÏNœ8p:àtÀé€Ó§àtÀé€ÓAp-#Nœ8p:Nœ8Ü ×2àt0Üs€Ó§Nœ8p:àtÀé8p:àtÐ#\Ë€Ó§Nœ€Ó§NwµŒ8Œ÷àtÀé€Ó§Nœ8p:NCsÎò'N·yp:hšÏ殩üÜ¢þ»ÒÛêtYÞÇG£7"œºáìÇ·ø´Öº*t™_]~†àtÐÙC_Kéü¸µÖEèI­ÍŸò.ÁÀé 3Ndˆ÷꿵듊ÖEèÉm‡Ä_7@Rx‚€ÓAlí>jsûÇDë–˜Ô”N#’Z>²sÀK„§ƒ.ùrõÖ„f™ºJnãŸÒaxBó‡$[',8tÉ•K—“}Ç{õWŠéçOœ:æ“ॠÞDè ÍYÖ§œ:¦ºüL‚ÏÃâôMmý%m' 8ôÍž±§…­$8šòòòì¦'cCrBóAYÛÒ\ðY4+N÷PD‚^.¡¯W{×|PNNÍêFªËÏœÌ.Ð{©®8KSât;=2rfvö*½—ˆˆ™8ÝíˆÕËœô[NåД8]ÇN!^¹rHï%+k%N׈ÓD.:™¯Çr$bNÇé8]CNˆˆ0›Í………%%%¥¥¥•••´²ë.rÔéVt"+§ãtœÞøåôé\q´u1™¢–/³¼|—s§‡‡‡›L¦ÌÌÌüü|ÑzYY­ŒÓëêôí«È p:Noü¤»&㜿%888...%%E´.}R:$­Ü¤Øœ-޼êÄâÈE"GµüP¾G_NO _Lf€Óqzã;=2r¦š§GD¼®h]f:yKPPPLLŒh]ú¤¤Z‹…VvAbî¼è(mWœ¾>ø]2œŽÓßéò×fÎàÁ8Òº²@XX˜8=66611Q:¤$Y´² œî褨rÊQwN_4̧ãtW8=#cŘ1Ãìj§»Ñ鎬­(RwNO[ÄV„Óqº+œ.U­®>`Wë8§ãtœŽÓuãt“)J=¾sg´ræµ×Æ©WÂàtw{‰øÉIQµèôØ NÇé8½É®ž#µ!:úMœîv§ì)NÇé8½É~ìXzTÔ¬ÔÔÔ‡HÎ.ËlÛö>Nw#ÕgO娥8jãÕk£ZŸ)ÕݵŒ8§ãô&wú5—ÁéÚIÛõ~ÏNÇé8§NÇé€Óq:N¯E)(ˆÃé8p:½Q£NWOq_wÝuíÚù¾øâ¨S§²œ,ߢ…õ{q:NœÚrº:]Q‘2vìðZ.Óq:4Ôé ïEu]CÍç½Ô,Êu/8]ïN—rá¾6mZ>œÔ§OOëùçÏïíÞ½³õ¥«Ê{eð‹_Üêííݼy³Í›+ _¾|04tJ×®ZµjñØcZ,©Ÿ%Ëßzk7›åq:N'Owÿsk‚Ó àô3gvûùu”‰ÁƒHO_ªÎ_µjÎĉOÕÌÓ‡ y°¤d«L''/M+ó.œ¾xñëçΙ¥,X0mòä§Õåýýû9’òãÖËãtœNž~ubåÊÐÎ;tèÐ> `ÜÅ‹ûåVߟcw ÅÅ[ž~z°,àãÓü—¿¼mË–HuÜÜ»té(½ÚîóÓY§ëÚé••y¢`å)Ê ï >P}é¾ûz™Í±5.[QÍUÝu×íêAù'2n¹¥«º€l¥µ2âtœîqNïÝ»GQQòÙ³{dœ8ÞInew ÒK—, ’·WTäÍûšŒ”Õ&LxRæ;–ÞÔ§ëÅé ÞÞÞ²#Ÿ2åYIÕe~uõÙßòI¢LïÚµæž{î°{<ýÒ¥ƒ57­-|äíÊ´L´lé£.PUµ§ãtœnßéiiK”éo¾I—ÌÈInuÍãé’æ_wÝuêÊhº‡kpº½X—°°W&MºzØä¹ç_º4øšçHÕÛµóU.—½…óåq:NÇéW'ÔcÒmÚ¶õu’[9êH»w¯•ìØá=zt·^³Ú!qº';½´t‡lWGnëС½ æjïôûïïe}ìE½ü§ãtœîÌéÊYɲ[·né$·²»†U«æôìyóŠ!{÷®³X>²^s£œVÅézwºÙß6`Ê”gÕ9’ƒ%+ÊväèåËß´>Gj÷¸ NÇé8ÝÖéyyk”鯿NëÖí&'¹•Ý5téÒñÛoÿ¦L>œ„ÓqzÍ’“³Z8xðÿî ŸÚ¦Mk_ßVN}ùòÁY³&ÞpCÛol7n܈/¿Ü†Óq:N¿¶Óû÷ïóÕW©’­¿d}ŸHÍÜÊî$I}çÂ…}Òcûö½§{¦ÓÙÀzôèÎó^p:¸Âéë×Ïõóë(%0p¼zÆnneóFe"7÷ûuë–"tYNÇé6¥²2oúôÂÂ^Áé8\áôúåVMz?7Nw#ÕågÄãj)޼êÄâÈŸüìQžŸÞ¦Mkÿ~çÏïÅé8ßé.ìÛ´i±zë#5;É­lÖ€Ó ™˜ó;G8ôáôÐÐ)>>ÍEÊÎî$·²YN7¤ÓD.²þa#µð{¤8´xìE§»Ñ鎬­(§ãtÀé8§ãtœ8§»åØKÄONŠª…c/8p:NןÓ9GŠÓ§ãt#P]qöTNZŠ£6^½–1j¡õ™Ò:]ˈÓq:àtœ®­´]G‰9NÇé8§NÇé€ÓqºÇ8ÝÑ)Síå¤.NÇéúvzDÄÌZþPœ–‹| œ®§ë½àtœ®o§ œî^¬O™f/]·åÝ¿l ]°.h^íKÜ/Ÿùë#/Öé-?y{ßßýuà„z¿])±ËWÊVg2™Ìf³Åb¡eqº>¨¨¨Èù/áááÁÁÁ F¾ùۗÕ%þ/„56Œxqáøÿo3síÚµ8]#J¤¤¤ˆckÇú9 6¶÷]½&¶¾¬û`¹¬aÝ‚b†ÔYj.õ—oQZZJkâtý!ùˆicbü€â~þdìÜÅ1ŠŒgc›u¡ïmì4tÃÉ1Ñ«l^"ÃÒ²CÍÏÏ-J[$ÖŽø^Ï$¾–Ø0^˜™0pBW"u–šKýå[”••Ñš8]ìY±1é¦áÉ¿ýc\ìúØÆFœÛ¬X÷èÄ]†K~G†¥5$ø"DiÙ¹fÖ‚´·ÿ’Üó·™;vf6ŒŒô¿%ù=¾=buCV"u–šKýå[TVVÒš8]O\¹tùã™ïoö{"wéú:eUµGœžØt¼>/þ†! ÏÍHÜO†¥D…q¢Œ–J®ÉçGRnyª 6¥¤1صnë/'ë¬÷¤ÎRs©¿|‹ªª*Z§ë*ŸJÝ÷ÛY_þ¼NYU§g6%;â7oøâöe±dXzÝ|nÙ?éíF\áÞ açŸ$°8¬ªvYU§—¸ 2,œM‹8 àt0;¬œW¹tö§!Ñò×¾ìùã'?žùþå‹?Ðî8Ý€|àïi½ÆJÊì!N¯.?c~îÍô>Ï—Óú8Ý8\¹tù³¹kR:8žœåißýXüùâGm °%h0ÏpÁ§|»%—PãtãpÎòŒG¦d ðØK»$YþS¥pq›¦(MÝ-£(ìkeû·Ä¤pœnÄæ’¥2R‘ ìÆö Ië5ö»í{]3Øâ7ÒC9âtè<´ÆÑèÍ’m¸ìãöOzû“ ¥„§CãP¾Š €JuùÓÏF¹òÜõÅïþ™ÒiÄÙ/Ž|œ÷5Ÿ†D»þ–…Ïæ®Ù3&ˆàãt=!‰6¯lѬÓK–$U~naËq%—/þ°Åo¤ëÏWËçn¿w‚$ì4N×GmÑå±ø8½ö|¹z«MÌÎöãb½œ‘”G¹VïœåÚ¬¡–½œýâøÎ/í5ƒ §»]ÜS£ñãéºOC¢·øäæœîNÊò>ÖŽïº8GúϽ‡S{>Ë¥8ÝÍ9¦ö+ɵŒ8ŒƒÖ΋V>J£àt¨§ÐM?¥çíÈè6µç³'2òiœîNJ–$ñÌ #âñO §(|•Ö¶s<9Ë5OÃé`ß,YþS3™Â“]Låç–í÷NØ3&¨ª¬œhÔÍÞ—/}êhôf§»åjÅ¿Ï%§p×qƒg¾oúÙ(†êõ#ÿåy@ V¬âðÑ-~#«ËÏÐF8ÝE(¿Ô“Ök,¿ÔãvNfˆÖM‹àȺt$#Ѭ7L¯Íý N7&y¿U°Ð1ƵŒ"¦=c‚Dël™µgרÅQñš­^UY9©:Nw†9Øb¤ç2r¬NÈSã㺜ítœ8§àtOâüñ“Ú|ô9N¿&_oHgü8þõÙŠ8]˜:Û­¿)Ëû˜-pº§S]~fï„0Ã_­høc/¥©»·øü4$š3¨º†;Ëpzƒ8™] ù1®VtŽ'<—Qt°kÔŒý&òcx²oûnû^ÝU[öǦŸ¢ùpz=‘MG„®ÇMœP²$I¼àÉ×;ŸýâxJ§:Mx¥ùrŸd3Æéõ„k†ÄÃo`Ù3&èïócuZyIÕÓz%ÓÂép•²¼eô©ëc‰"tÑ:çEp:ükçÀ—¾Þ®÷o‘ûD`É’$Z§;ãÛ-¹—Î^P®Vä|ºÇr2»Àž–|"#ÿŸ{ã»ðSã8Ý>ÊáÅ‚€…~:”ߘVÕDrvúàtý!Ù'AK·ø,MÝM4xދ‘Ed“0ÀgÀéÇþIoï5ƒÛÒpº å…Åé}ž×ò#Åpº8#ŠÓqùâúÝ<8sˆÓpº¡Æ§NÇéÁæÑïüÝØgŒd\’Ök¬Í¸Ê¯Šñ,§_:{_”Çé ädv.ê™å?ÕúߌG¦XbRÝ4 ¥ØÌÁéúæÊ¥ËŽöÌ’§Èn|ÿ¤·±’¸–ñšÉàÎ/e 8ü¤Ý4rsƒ¤«›nªþ{<9kû½ ß:ò­S:P› ª¬\þÅéúæ³¹kj^V,¢—q¨´®Ÿ¨Þ8¿ ›k´íæ ²™9Úœ$‹×Ââ5r]üîŸJ…S{>ë!#Ô#‹6¨ÏkürõV ‚§+6”Ó¯>;´ãˆšV’Q§ŒC%VŽèÉQjY–÷ñÑèÍ„Èù°Ïæ&µ«¹aÇn¿ZFv-¢3å±…ÅQñ~,­Mü•˜òÅ3õŠÁÓòC9]FÄÛnýõÉSŒýSs³#¼éñšZ?•s(¥óã\èéÜ&¦ÎƒØòïŸ0ðEëÇ7eKj"_sǃÅãYþS7µ²µû¯=íN`ƒ8=ÿåyL‘æ,ù §Ôƒ~ñ;Eë_}h’þŸpýÀ~¬¾”,IÚÒíIøvßEDÙäA¥lî0<¡Ù Äëf}Íðϧ»pü¤é–ßÈ·–}ª|ë«_¿ã°O‚–ât!£Ë­]ŸTvÑe{‹pJ=øzCºtþ«ðxUî톸÷Ю9¶q‡Œ%Œ¢uwuÒï~^qº"¸Ï¬÷àËØhß„°„oÆW› å#žvù²—š0­×ØÌGÿðŸ&lý«ô>Ïï,qåg_á—Ú‡qóCU ¤ßõ1©+ßýmoáÌ÷e¼˜Üfpüu™f<4Å-•Ié4âß{è›; ;}ðsÜ­&µxXÙ˜E8]O|6wÍŽ~UIQwÑWóÍöeùOýûüØòÂbiä‰Òæöý{´>ÌÓN+Õ›óÇO–,IÊÔúÑÿlu×õ·Þeøè–ËÕ› J¸~`ÖW=ö÷.?™ÚãiŽÔÛŸõh§———g뇌 É >I2’Ðâá¤.#6õ·õ©? _a‰I=™]Pù¹…KñjÄJF9¢!IñøÝÞºRUV.yCiêîO#6˜ž#eÀ‹IÝG&ø>* {‚ﯲ¶¥¹¸k$4ôÑ+á _OEEES‡® “‘¹õñWÛ Ö¾Ê1ζN—µ{é_¯ë¥ØÌÌÉÉÑñVèV¶>ý?²ƒüèÕ·tWómÛ¶EGGK»¸]î5{P[¯f-kl¥MÍ­^­e=.èMMíœÞ^í´¯²FŒ³}§GFÎÌÎ^¥»1Ó[¡»ðójëõ€¯ËÔXhÄé:íAnìMˆ˜FâlßéòIW®Ò]ÉÊZéâ­P>Ñ`eû{sôX툈ץEF¥§ë´¹±7 b‰³a6› KJJJKK+++›h+”óÌ­P³M/Ç»WësºËz“‡;½ã¬]§Ÿ>k“‹™LQË—¿Y^¾ËytÂÃÃM&Sfff~~¾¨¬¬ §{ŽÓeZ´>cÆ ¥é«ªªpº.z“1œ®…8k×éÊWµwÌtœó·ÇÅÅ¥¤¤H€d¿';=œîQNW´>mÚ4·h]›†Ò~o2†Óµg­;Ýú˜µrÌT™éä-AAA111 ÙïÉpÆb±àtOsºµ®e§k¹7Ééî³ÖnÝK•9ƒ?à(@ÊaaaØØØÄÄDÙéI¯Æéètwi]ËN×ro2’ÓÝgý9=#cŘ1Ãì§ãt›ù®×º¾œ®Þdl§»2ÎúsºÔ­ºú€Ýátœ^ó%k]_N×No2¶Ó]g=9ÝdŠRUíÜ­ g^{mœzN§{¸ÓÝ10zôÐ1cÆN÷ðRóºàšw™†††šÍæ¦Öº^œ®µÞdT§»>Îzrú5—Áé'ÛIHHHZZZ~~¾ÅbiŠû!õåt­õ&£:ÝõqÆé8ÝSœ¬lEEEMq?$NÇé8§S~R âšn[š={ö† L&“¤êMq?$NÇé8ÝàN—eܽM›7oÞ¬_¿Þî­F]K‹>M=ÕéëÖ­Û´iSnnnS܉Óq:N'Ooª"Bß¼yñåËõÕ%šh÷£Þ~ݤ[N×KÄšh8¨c§;êxêüFé™Î¯8VŠrYûyºL¬\Ú¹s‡ÚŒ»xqßáÃI}úô´^øüù½Ý»wþþûyõ÷¿ÿµ¯o+Y ?ƒòªØ94tJ×®ZµjñØcZ,©kŽŽ¹õÖnÞÞÞŠÇ•™*6ÍöJÇŽío¾¹ËÒ¥Áv›©fUÕù¹¹véÒqðàœ×ÇîÛ•îÔé?¿Ž³gOª®>PsµÎ«]ó½ÊX¤Y³ëûö½SV¢#§»~ä¤ýÞäʈÙÆZ™§7Etjƒ.œÞ»w¢¢ä³g÷ˆ ÇËL±XzúRuáU«æLœø”LÌ™ó‡µkߪªÚŸðžêý… §/^üú¹sf) L›<ùiuÍþþýŽIùñÇ‚ääE⸚M N¯^öòËÏœ<™Y\¼E<èÈé5«ªÌŸ0áI™yìXºóúØ}{lì;Ë–ýIö[§NeI5dÚîjíVÛÑ{åËŠÖ/]:˜˜¸ gÏ›ÉÓuÝ›\1G[¾1âÜ <]2¦©SÇHö$ æºus;ÝnVå(Ý»æÇÖE–Ô¾ÓÓÒ–(Óß|“~×]·Ë„({øðêÂ÷Ý×ËlŽ• qbͧ-Ë[ÄhÊô‰·ÜÒU]³¤üÎw«êô€}¾øÂ¤Lgf®p´e׬ª2¿¤dkmêc÷íòÑÖ_JÝWÙ¬ÖQµí¾W2wÙùéñØKýpÆîMM1›-\ÿÙÖrØê¼´ç9=*jVxøTI²lò>»Ñ±›U9J÷²“Ô¦ÓÕ&”¼m[_e(›Â'Ÿ$Êô®]kî¹çe–-}$J5G‹ê1™eÔ5Ë kéôÖ­[ªC**òmÄ5«ªÌW+à¼>vß~à m­û’õòÖ«µ[mGï5™Þ—ï¾»§ŒljFL/N¯ýÎØ½©)"f×éŽ6³Ú[kÓ@nŒsƒœÞ¯_ïo¿ý›2½sg´óèØÍª¥{Æsú™3»•i±ª¸U=L+Ÿ;sæ‹8Ý톪ýÎØ½©)"¦Ü±áíí}ç·¤¤D¨+±;t”¼×©Œàt v]-8=4tŠOsµ³9Š˜lXþþýΟßëšødf®èׯw«V-nºéFÙ@­oòÄéî2”ÖpÚwº+#æ‚s³ÓmÒOœÎ“uôd%-8]›8-;ݽsÁǹÙé6é'NG—8½!=H#8-;ݽsÁÇyÐsq:…ç2âtÙ8q:N§àtœŽÓq:NÇé8§ãt¶BœŽÓq:NÇé8‚Óq NÇé8‚Óq:NÇé8‚Óq:NÇéõÜ ÿê E;EùHœŽÓ=×麴•TÛ•[!è W:]§=ȽÉÓHœ½Œg+l…åååÙÿeÞ¼y!!!A y–-[g2™Ìf³ÅbiR§2$ÝÅÙÖé9ÿ%"""<<<88XúC˜~X»vmSo…ÖÈG¤¤¤ˆ,bAÛHIKI{–––6Ñö`€äâÞäöˆ-ñÁHqöròš¤3’ÔHOˆÑ!.ÈÈòóó¥ ä³AÛHIKI{I‡)++ûWÓ£ëä–Þä–ˆÅ{õ7Rœ9]Ò&¡®ÉÈDò)Ò ™ m¤¤¥¤½d“¨¬¬tÓõÛƒÜÕ›Ü1qº‘âìåÜV:MB]™‘Éú¥d¿ZÚFÚHZJÚK„^UUå§ë·¹«7¹%bât#ÅÙ™Ó¥è4 u}F`¤ä®Þä–ˆ‰ÓggN—õê4 u}F`¤ä®Þä–ˆ‰Óg/:x2ât#}œMQø*œ8p:àtœ8p:€ÛàZFãÀ=G8§àtœ€Óq:NÀé8@p-#àtÀé€Óp:àtÀéî„kŒ÷àtœ€Óq:NÇé8§ãt=µŒ€Ó§NÀé€Ó§¸®e0Üs€Óq:NÇé8§àtCsÎò'N·y§hšÏ殩üÜ¢þk}-cYÞÇG£7ãtÝpö‹ã[üFZk]ºÌ¯.?ƒÓôDÖWS:?n­uzRëG÷¿ôŽÞ¿NãDÆx¯þ[»>©h]„žÜvHüu$…Çéúck·_onÿ˜hÝ“šÒiDRËGvôŸl€ï…ÓÀùreJÂõƒLÝG%·ñOé0<¡ù S9‡p:€.¹rér’ï¯â½ú+ÅtóSÆø^8<”g}à=@„žÐlÐWnÅé:¦ºüLBó‡Äé›ÚøKÚŽÓô͞ѳÅéŸþi™a¾N—R^^ž­26$Ç7”µ-ÍeŸ¸mÛ¶èèh N# ^óÒ}½Ú»þCp:Çé‘‘3³²Vz`‰ˆx]¾þèÑC£¢¢p:Äéb·+Wy`‘/®|ýÀÀñM¡uœ8Ý N—iÑúŒ3JJJÊÊʪªªp:àt;]Ñú´iÓQë8pºÛœÞèZÇé€ÓÝéôÆÕ:Nœîf§7¢Öq:àt÷;½±´ŽÓ§»ÚéŽ.Ï=zè˜1c `ÜÅ‹ûNêÓ§§õÂçÏïíÞ½ó÷ßçÈ«¿ÿý¯}}[Éùù”WÅΡ¡SºvíÔªU‹Ç{ÐbùH]sttÈ­·vóööV<®ÌT±ÉÓÃÂ^騱ýÍ7wYº4Ønþ^§ªâtðP§÷îÝ£¨(ùìÙ=¢æÀÀñ2sðàÒÓ—ª ¯Z5gâħdbΜ?¬]ûVUÕþ„„÷T™.\8}ñâ×Ï3KY°`ÚäÉO«kö÷ïwäHÊ?$'/­×<ì£N¯^öòËÏœ<™Y\¼¥oß;9½öUÅéà¡NOK[¢LóMú]wÝ.¢ìáê ßw_/³9V&D©åå»lV%o9u*K™>q"ã–[ºªk–<Úî'Öœ9`@Ÿ/¾0)Ó™™+9½öUÅéà¡N?}:W™–¼m[_™¨®>н{çO>I”é]»ÖÜsÏÊ-[ú\ºd{¼E Y^™– YF]³¬°–NoݺåÅ‹û”銊_}•*²~iìØáê22=lØ€)SžUç¼õÖ«‰‰ .\Ø·qãü»ïþÏ9ÒåËß´>Gªý¨“ÓׯŸ;}ú R1»ÔÇ‘Ók_Uœêtñ©Ÿ_G)ã­ÓðœœÕòêÁƒqê±öèÑC[¶ô¹ï¾^‡mT¯eœ5kâ 7´½ñÆvãÆPÓí:9]JHÈË’hßv[÷•+C}|š7°ª8<Ô鎖‘Œ¸G-è»ïvªÏÔòÞ×ÚW§€¡œ~áÂ>åî|碬¬Ì›>ý…°°W\ãñnÝn’\êvút®|îÌ™/ÖÞéuª*NC9=4tŠOsѺsQ¶iÓÚß¿ßùó{]ãôÌÌýúõnÕªÅM7Ý8^½¦6N¯SUq:ðØ‹‡?—§NÇé8p:NÀé8§ãtÀé8§NÇé8§ÔÚé‘‘3Å_›"aÁé ?§ƒp:è†òòòìÿ, +–-[†Ó@˜L¦¸¸¸¨„E‚c6›- N} yhJJŠø+¬€HX$8………¥¥¥8ôA~~¾˜KÒD°B"a‘à””””••átÐâ,IEÍfs&X!‘°Hp$I¯¬¬Äé $ mY,–°B"a‘àˆÐ«ªªp:€‡‚Óp:àth:þ Ù6Mmã´IEND®B`‚jpy-2.0.0/doc/_static/figures/deployment.puml0000664000175000017500000000050515202503234021411 0ustar alastairalastair@startuml component JVM component Python artifact jdl.so artifact jpy.so artifact jpy.jar artifact jpyutil.py file jpyconfig.properties file jpyconfig.py jpy.jar ..> jpy.so jpy.jar ..> jdl.so jpy.jar ..> jpyconfig.properties jpyutil.py ..> jpyconfig.py Python ..> jpy.so Python ..> jpyutil.py JVM ..> jpy.jar @endumljpy-2.0.0/doc/reference.rst0000664000175000017500000005674515202503234015751 0ustar alastairalastair######### Reference ######### ********** Python API ********** This reference addresses the jpy Python module. jpy Functions ============= .. py:function:: create_jvm(options) :module: jpy Create the Java VM using the given *options* sequence of strings. Possible option strings are of the form: +-------------------------------+------------------------------------------------------------------------------------------------------------------------+ | Option | Meaning | +===============================+========================================================================================================================+ | ``-D=`` | Set a `Java system property `_. | | | The most important system property is ``java.class.path`` to include your Java libraries. You may also consider | | | ``java.library.path`` if your Java code uses native code provided in shared libraries. | +-------------------------------+------------------------------------------------------------------------------------------------------------------------+ | ``-verbose[:class|gc|jni]`` | Enable verbose output. The options can be followed by a comma-separated list | | | of names indicating what kind of messages will be printed by the JVM. | | | For example, ``-verbose:gc,class`` instructs the JVM to print GC and class | | | loading related messages. Standard names include: gc, class, and jni. | +-------------------------------+------------------------------------------------------------------------------------------------------------------------+ | ``-X`` | Set a non-standard JVM option which usually begins with ``-X`` or an underscore. For example, | | | the Oracle JDK/JRE supports ``-Xms`` and ``-Xmx`` options to allow programmers specify the initial | | | and maximum heap size. Please refer to the documentation of the used Java Runtime Environment (JRE). | +-------------------------------+------------------------------------------------------------------------------------------------------------------------+ The function throws a runtime error on failure. It has no return value. Usage example:: jpy.create_jvm(['-Xmx512M', '-Djava.class.path=/usr/home/norman/jpy-test/classes']) .. py:function:: destroy_jvm() :module: jpy Destroy the Java Virtual Machine. The function has no effect if the JVM is has not yet been created or has already been destroyed. No return value. .. py:function:: get_type(name, resolve=False) :module: jpy Return a type object for the given, fully qualified Java type *name* which is the name of a Java primitive type, a Java class name, or a Java array type name. Java class names must be fully qualified, e.g. ``'java.awt.Point'``. For inner classes a dollar sign is used to separate it from its containing class, e.g. ``'java.awt.geom.Ellipse2D$Float'``. Java array type names have a trailing opening bracket, followed by either a Java class name and a trailing semicolon or followed by one of the primitive type indicators: * ``'Z'``, the Java ``boolean`` type (an 8-bit Boolean value) * ``'C'``, Java ``char`` type (a 16-bit unicode character) * ``'B'``, Java ``byte`` type (an 8-bit signed integer number) * ``'S'``, Java ``short`` type (a 16-bit signed integer number) * ``'I'``, Java ``int`` type (a 32-bit signed integer number) * ``'J'``, Java ``long`` type (a 64-bit signed integer number) * ``'F'``, Java ``float`` type (a 32-bit floating point number) * ``'D'``, Java ``double`` type (a 64-bit floating point number) Examples: ``'[java.awt.Point;'`` (1d object array), ``'[[[F'`` (3d float array). If the returned Java type has public constructors it can be used to create Java object instances in the same way Python objects are created from their types, e.g.:: String = jpy.get_type('java.lang.String') s = String(‘Hello jpy!’) s = s.substring(0, 5) The returned Java types are also used to access the type's static fields and methods:: Runtime = jpy.get_type('java.lang.Runtime') rt = Runtime.getRuntime() tm = rt.totalMemory() The returned Java types have a `jclass` attribute which returns the actual Java object. This allows for using the Java types where a Java method would expect a parameter of type `java.lang.Class`. They also have a `jclassname` attribute, which returns the Java type associated with the 'obj' reference. The `jclassname` is the reference's declared type (which may be a supertype), rather than the object's runtime type (as returned by `obj.getClass().getName()`). To instantiate Java array objects, the :py:func:`jpy.array()` function is used. Implementation note: All types loaded so far from the Java VM are stored in the global :py:data:`jpy.types` variable. If the requested type does not already exists in :py:data:`jpy.types`, the class is newly loaded from the Java VM. The root class of all Java types retrieved that way is :py:class:`jpy.JType`. Make sure that :py:func:`jpy.create_jvm()` has already been called. Otherwise the function fails with a runtime exception. .. py:function:: array(item_type, init) :module: jpy Create a Java array object for the given *item_type* and of the given initializer *init*. *item_type* may be a *type* object as returned by the :py:func:`jpy.get_type()` function or a type *name* as it is used for the :py:func:`jpy.get_type()` function. In addition, the name of a Java primitive type can be used: * ``'boolean'`` (an 8-bit Boolean value) * ``'char'`` (a 16-bit unicode character) * ``'byte'`` (an 8-bit signed integer number) * ``'short'`` (a 16-bit signed integer number) * ``'int'`` (a 32-bit signed integer number) * ``'long'`` (a 64-bit signed integer number) * ``'float'`` (a 32-bit floating point number) * ``'double'`` (a 64-bit floating point number) The value for the *init* parameter may bei either an array length in the range ``0`` to ``2**31-1`` or a sequence of objects which all must be convertible to the given *item_type*. Make sure that :py:func:`jpy.create_jvm()` has already been called. Otherwise the function fails with a runtime exception. Examples::: a = jpy.array('java.lang.String', ['A', 'B', 'C']) a = jpy.array('int', [1, 2, 3]) a = jpy.array('float', 512) .. py:function:: cast(jobj, type) :module: jpy Convert a Java object to a Java object with the given Java *type* (type object or type name, see :py:func:`jpy.get_type()`). If *jobj* is already of *type*, *jobj* is returned. If *jobj* is an instance of *type*, a new wrapper object will be created for this type, otherwise ``None`` is returned. This function is useful if you need to convert the `java.util.Object` values returned e.g. by Java collections (implementations of the `java.util.Set`, `java.util.Map`, `java.util.List` & Co.) to specific types. For example:: ArrayList = jpy.get_type('java.util.ArrayList') File = jpy.get_type('java.io.File') al = ArrayList() al.add(File('/home/bibo/.jpy')) item = al.get(0) # item has type java.util.Object, but actually is a java.io.File print(type(item)) item = jpy.cast(item, File) # item has now type java.io.File print(type(item)) Make sure that :py:func:`jpy.create_jvm()` has already been called. Otherwise the function fails with a runtime exception. Variables ========= .. py:data:: types :module: jpy A dictionary that maps Java class names to the respective Python type objects (wrapped Java classes). You should never modify the value of this variable nor directly modify the dictionary's contents. .. py:data:: type_callbacks :module: jpy Contains callbacks which are called before jpy translates Java methods to Python methods while Java classes are being loaded. These callbacks can be used to annotate Java methods so that jpy can better translate them to Python. This is a powerful but advanced jpy feature that you usually don't have to use. Consider a Java method:: double[] readData(long offset, int length, double[] data); of some Java class ``Reader``. From the method's documentation we know that if we pass ``null`` for *data*, it will create a new array of the given length, read data into it and the return that instance. If we pass an existing array it will be reused instead. From plain Java class introspection, jpy can neither detect if a primitive array parameter is modified by a method and/or whether it shall serve as the method's return value. To overcome the problem of such semantics inherent to a Java method implementation, jpy uses a dictionary ``type_callbacks`` in which you can register a Java class name with a callable of following signature: :: callback(type, method) This can be used to equip specific Java methods of a class with additional information while the Java class is being loaded from the Java VM. *type* is the Java class and *method* is the current class method being loaded. *method* is of type :py:class:`jpy.JMethod`. The callback should return either ``True`` or ``False``. If it returns ``False``, jpy will not add the given method to the Python version of the Java class. Here is an example: :: def annotate_Reader_readData_methods(type, method): if method.name == 'readData' and method.param_count == 3: param_type_str = str(method.get_param_type(1)) if param_type_str == "" || param_type_str == "": method.set_param_mutable(2, True) method.set_param_return(2, True) return True class_name = 'com.acme.Reader' jpy.type_callbacks[class_name] = annotate_Reader_readData_methods # This will invoke the callback above Reader = jpy.get_type(class_name) Once a method parameter is annotated that way, jpy can transfer the semantics of a Java method to Python. For example:: import numpy as np r = Reader('test.tif') a = np.array(1024, np.dtype=np.float64) a = r.read(0, len(a), a) r.close() Here a call to the ``read`` method will modify the numpy array's content as desired and return the same array instance as indicated by the Java method's specification. .. py:data:: type_translations :module: jpy Contains callbacks which are called when instantiating a Python object from a Java object. After the standard wrapping of the Java object as a Python object, the Java type name is looked up in this dictionary. If the returned item is a callable, the callable is called with the JPy object as an argument, and the callable's result is returned to the user. .. py:data:: VerboseExceptions.enabled :module: jpy If set to true, then jpy will produce more verbose exception messages; which include the full Java stack trace. If set to false, then jpy produces exceptions using only the underlying Java exception's toString method. .. py:data:: diag :module: jpy An object used to control output of diagnostic information for debugging. This variable is only useful for jpy modification and further development. .. py:data:: diag.flags :module: jpy Integer bit-combination of diagnostic flags (see following F_* constants). If this value is not zero, diagnostic messages are printed to the standard output stream for any subsequent jpy library calls. Its default value is ``jpy.diag.F_OFF`` which is zero. For example:: jpy.diag.flags = jpy.diag.F_EXEC + jpy.diag.F_JVM The following flags are defined: * ``F_OFF`` - Don't print any diagnostic messages * ``F_ERR`` - Errors: print diagnostic information when erroneous states are detected * ``F_TYPE`` - Type resolution: print diagnostic messages while generating Python classes from Java classes * ``F_METH`` - Method resolution: print diagnostic messages while resolving Java overloaded methods * ``F_EXEC`` - Execution: print diagnostic messages when Java code is executed * ``F_MEM`` - Memory: print diagnostic messages when wrapped Java objects are allocated/deallocated * ``F_JVM`` - JVM: print diagnostic information usage of the Java VM Invocation API * ``F_ALL`` - Print all possible diagnostic messages Types ===== You will never have to use the following type directly. But it may be of use to know where they come from when they are referred to, e.g. in error messages. .. py:class:: JType :module: jpy This type is the base class for all type representing Java classes. It is actually a meta-type used to dynamically create Python type instances from loaded Java classes. Such derived types are returned by :py:func:`jpy.get_type` instead or can be directly looked up in :py:data:`jpy.types`. .. py:class:: JOverloadedMethod :module: jpy This type represents an overloaded Java method. It is composed of one or more :py:class:`jpy.JMethod` objects. .. py:class:: JMethod :module: jpy This type represents a Java method. It is part of a :py:class:`jpy.JOverloadedMethod`. .. py:attribute:: name The method's name. Read-only attribute. .. py:attribute:: return_type The method's return type. Read-only attribute. .. py:attribute:: param_count The method's parameter count. Read-only attribute. .. py:method:: JMethod.get_param_type(i) -> type Get the type of the *i*-th Java method parameter. .. py:method:: JMethod.is_param_return(i) -> bool Return ``True`` if arguments passed to the *i*-th Java method parameter will be the return value of the method, ``False`` otherwise. .. py:method:: JMethod.set_param_return(i, value) Set if arguments passed to the *i*-th Java method parameter will be the return value of the method, with *value* being a Boolean. .. py:method:: JMethod.is_param_output(i) -> bool Return ``True`` if the arguments passed to the *i*-th Java method parameter is a mere output (and not read from), ``False`` otherwise. .. py:method:: JMethod.set_param_output(i, value) Set if arguments passed to the *i*-th Java method parameter is a mere output (and not read from), with *value* being a Boolean. Used to optimise Python buffer to Java array parameter passing. .. py:method:: JMethod.is_param_mutable(i) -> bool Return ``True`` if the arguments passed to the *i*-th Java method parameter is mutable, ``False`` otherwise. .. py:method:: JMethod.set_param_mutable(i, value) Set if arguments passed to the *i*-th Java method parameter is mutable, with *value* being a Boolean. .. py:class:: JField :module: jpy This type represents is used to represent Java class fields. Type Conversions ================ This section describes the type possible type conversions made by jpy when Python values are passed as arguments to Java typed parameters. In the tables given below are the generated match values ranging from (types never match) to 100 (full match) when comparing a given Java parameter type (rows) with a provided Python value (columns). These match values are also used for finding the best matching Java method overload for a given Python argument tuple. Java primitive types -------------------- +--------------+--------------+----------+---------+------------+--------+ | | ``NoneType`` | ``bool`` | ``int`` | ``float`` | number | +==============+==============+==========+=========+============+========+ | ``boolean`` | 1 | 100 | 10 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``char`` | 0 | 10 | 100 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``byte`` | 0 | 10 | 100 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``short`` | 0 | 10 | 100 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``int`` | 0 | 10 | 100 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``long`` | 0 | 10 | 100 | 0 | 0 | +--------------+--------------+----------+---------+------------+--------+ | ``float`` | 0 | 1 | 10 | 90 | 50 | +--------------+--------------+----------+---------+------------+--------+ | ``double`` | 0 | 1 | 10 | 100 | 50 | +--------------+--------------+----------+---------+------------+--------+ Java object types ----------------- +-------------------------+--------------+----------+---------+------------+---------+ | | ``NoneType`` | ``bool`` | ``int`` | ``float`` | ``str`` | +=========================+==============+==========+=========+============+=========+ | ``java.lang.Boolean`` | 1 | 100 | 10 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Character`` | 1 | 10 | 100 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Byte`` | 1 | 10 | 100 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Short`` | 1 | 10 | 100 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Integer`` | 1 | 10 | 100 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Long`` | 1 | 10 | 100 | 0 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Float`` | 1 | 1 | 10 | 90 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Double`` | 1 | 1 | 10 | 100 | 0 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.String`` | 1 | 0 | 0 | 0 | 100 | +-------------------------+--------------+----------+---------+------------+---------+ | ``java.lang.Object`` | 1 | 10 | 10 | 10 | 10 | +-------------------------+--------------+----------+---------+------------+---------+jpy Java primitive array types -------------------------- +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | | ``NoneType`` | seq | buf('b') | buf('B') | buf('u') | buf('h') | buf('H') | buf('i') | buf('I') | buf('l') | buf('L') | buf('q') | buf('Q') | buf('f') | buf('d') | +================+==============+=====+==========+==========+==========+==========+==========+==========+==========+==========+==========+==========+==========+==========+==========+ | ``boolean[]`` | 1 | 10 | 100 | 100 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``char[]`` | 1 | 10 | 0 | 0 | 100 | 80 | 90 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``byte[]`` | 1 | 10 | 100 | 90 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``short[]`` | 1 | 10 | 0 | 0 | 0 | 100 | 90 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``int[]`` | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 100 | 90 | 100 | 90 | 0 | 0 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``long[]`` | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100 | 90 | 0 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``float[]`` | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100 | 0 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ | ``double[]`` | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100 | +----------------+--------------+-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ If a python buffer is passed as argument to a primitive array parameter, but it doesn't match the buffer types given above, the a match value of 10 applies, as long as the item size of a buffer matches the Java array item size. Java object array types ----------------------- For String arrays, if a sequence is matched with a value of 80 if all the elements in the sequence are Python strings. todo ******** Java API ******** jpy's Java API documentation has been generated from Java source code using the javadoc tool. It can be found `here <_static/java-apidocs/index.html>`_. jpy-2.0.0/doc/install.rst0000664000175000017500000002601315202503234015442 0ustar alastairalastair############ Installation ############ jpy's installation is currently the full build process from sources. We will try to ease the installation process in the future. After successful installation you will be able * to use Java from Python by importing the jpy module ``import jpy`` and * to use Python from Java by importing the jpy Java API classes ``import org.jpy.*;`` from ``jpy.jar`` on your Java classpath. ******************* Getting the Sources ******************* The first step is to clone the jpy repository or download the sources from the `jpy Project page `_. We recommend you clone the repository using the `git `_ tool:: git clone https://github.com/jpy-consortium/jpy.git If you don't want to use git, you can also download stable source releases from the `jpy releases page `_ on GitHub. In the following it is assumed that the jpy sources are either checked out or unpacked into a directory named ``jpy``. .. _build: ****************** Build from Sources ****************** Change into the checkout directory (``cd jpy``) and follow the build steps below. After successful build, the ``build`` directory will contain the platform-dependent jpy versions: build/ lib-*os-platform*-*python-version*/ jpy.so (Unixes only) jdl.so (Unixes only) jpy.pyd (Windows only) jdl.pyd (Windows only) jpyutil.py jpyconfig.py jpyconfig.properties ************************ Running Java from Python ************************ In order to use jpy from Python you will have to add the respective directory ``lib-``*os-platform*``-``*python-version* for your platform and Python version to your Python path. This can be done either programmatically in Python, e.g. import sys sys.path.append('build/lib-*os-platform*-*python-version*') You could also alter the ``PYTHONPATH`` environment variable, export PYTHONPATH=$PYTHONPATH:build/lib-*os-platform*-*python-version* Finally you could copy the contained files into your Python installation's ``site-packages`` directory to make jpy permanently available. ************************ Running Python from Java ************************ To use the jpy Java API, put ``lib/jpy.jar`` on your classpath. Or if you use Maven add the following dependency to your project: org.jpy jpy 0.8 The jpy Java API requires a maximum of two configuration parameters: * ``jpy.jpyLib`` - path to the 'jpy' Python module, namely the ``jpy.so`` (Unix) or ``jpy.pyd`` (Windows) file * ``jpy.jdlLib`` - path to the 'jdl' Python module, namely the ``jpy.so`` (Unix) file. Not used on Windows. Another optional parameter * ``jpy.debug`` - which is either ``true`` or ``false`` can be used to output extra debugging information. All the parameters can be passed directly to the JVM either as Java system properties or by using the single system property * ``jpy.config`` - which is a path to a Java properties files containing the definitions of the two parameters named above. Such property file is also written for each build and is found in ``build/lib--/jpyconfig.properties``. Setting PYTHONHOME ------------------ If the environment variable ``PYTHONHOME`` is not set when you call Python from Java, you may get an error about file system encodings not being found. It is possible to set the location of Python from your Java program. Use ``PyLib.setPythonHome(pathToPythonHome)`` to do that, where ``pathToPythonHome`` is a ``String`` that contains the location of the Python installation. ======================== Build for Linux / Darwin ======================== You will need * `Python 2.7 or 3.3 `_ or higher * `Oracle JDK 7 `_ or higher * `Maven 3 `_ or higher * For Linux: ``gcc`` * For Darwin: `Xcode `_ To build and test the jpy Python module use the following commands:: export JDK_HOME= export JAVA_HOME=$JDK_HOME python setup.py --maven build where ``JAVA_HOME`` is used by Maven and ``JDK_HOME`` by ``setup.py``. On Darwin, you may find the current JDK/Java home using the following expression:: export JDK_HOME=$(/usr/libexec/java_home) If you encounter linkage errors during setup saying that something like a ``libjvm.so`` (Linux) or ``libjvm.dylib`` (Darwin) cannot be found, then you can try adding its containing directory to the ``LD_LIBRARY_PATH`` environment variable, e.g.:: export LD_LIBRARY_PATH=$JDK_HOME/jre/lib/server:$LD_LIBRARY_PATH =========================== Build for Microsoft Windows =========================== Python 2.7 ---------- You will need * `Python 2.7 `_ or higher (2.6 may work as well but is not tested) * `Oracle JDK 7 `_ or higher (JDK 6 may work as well) * `Maven 3 `_ or higher * `Microsoft Visual C++ 10 `_ or higher Note that if you build for a 32-bit Python, make sure to also install a 32-bit JDK. Accordingly, for a 64-bit Python, you will need a 64-bit JDK. If you use the free Microsoft Visual C++ Express edition, then you only can build for a 32-bit Python. Open the command-line and execute:: SET VS90COMNTOOLS=%VS100COMNTOOLS% SET JDK_HOME= SET JAVA_HOME=%JDK_HOME% SET PATH=%JDK_HOME%\jre\bin\server;%PATH% Then, to actually build and test the jpy Python module use the following command:: python setup.py --maven build Python 3.3 and higher --------------------- You will need * `Python 3.3 `_ or higher (3.2 may work as well but is not tested) * `Oracle JDK 7 `_ or higher (JDK 6 may work as well) * `Maven 3 `_ or higher * `Microsoft Windows SDK 7.1 `_ or higher If you build for a 32-bit Python, make sure to also install a 32-bit JDK. Accordingly, for a 64-bit Python, you will need a 64-bit JDK. The Python setup tools (``distutils``) can make use of the command-line C/C++ compilers of the free Microsoft Windows SDK. These will by used by ``distutils`` if the ``DISTUTILS_USE_SDK`` environment variable is set. The compilers are made accessible via the command-line by using the ``setenv`` tool of the Windows SDK. In order to install the Windows SDK do the following 1. If you already use Microsoft Visual C++ 2010, make sure to uninstall the x86 and amd64 compiler redistributables first. Otherwise the installation of the Windows SDK will definitely fail. This may also apply to higher versions of Visual C++. 2. Download and install `Windows SDK 7.1 `_. (This step failed for me the first time. A second 'repair' install was successful.) 3. Download and install `Windows SDK 7.1 SP1 `_. Open the command-line and execute:: "C:\Program Files\Microsoft SDKs\Windows\v7.1\bin\setenv" /x64 /release to prepare a build of the 64-bit version of jpy. Use:: "C:\Program Files\Microsoft SDKs\Windows\v7.1\bin\setenv" /x86 /release to prepare a build of the 32-bit version of jpy. Now set other environment variables:: SET DISTUTILS_USE_SDK=1 SET JDK_HOME= SET JAVA_HOME=%JDK_HOME% SET PATH=%JDK_HOME%\jre\bin\server;%PATH% Then, to actually build and test the jpy Python module use the following command:: python setup.py --maven build ********************** Typical Build Problems ********************** ===================== Environment variables ===================== Make sure that ``JAVA_HOME`` and ``JDK_HOME`` are always set, not only when installing, but also when using jpy. Additionally make sure that your ``PATH`` environment variable contains the ``JAVA_HOME``. Set environment variables on `Windows `_ Set environment variables on `Linux `_ ============================================== Binary incompatibility between Python and Java ============================================== When used from Python, jpy must be able to find an installed Java Virtual Machine (JVM) on your computer. This is usually the one that has been linked to the Python module during the build process. If the JVM cannot be found, you will have to adapt the ``LD_LIBRARY_PATH`` (Unix) or ``PATH`` (Windows) environment variables to contain the path to the JVM shared libraries. That is ``libjvm.dylib`` (Darwin), ``libjvm.so`` (Linux) and ``jvm.dll`` (Windows). Make sure to use matching platform architectures, e.g. only use a 64-bit JVM for a 64-bit Python. Otherwise the JVM may be found but you will get error similar to the following one (Windows in this case):: >>> import jpy Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Python33-amd64\Lib\site-packages\jpy.pyd: Can't load AMD 64-bit .dll on a IA 32-bit platform ====================================== Unable to find vcvarsall.bat (Windows) ====================================== If you build for Python 2.7, ``setup.py`` may fail with the following message:: C:\Users\Norman\JavaProjects\jpy>c:\Python27-amd64\python.exe setup.py install Building a 64-bit library for a Windows system running install running build running build_ext building 'jpy' extension error: Unable to find vcvarsall.bat This happens, because ``distutils`` uses an environment variable of an older Microsoft Visual C++ version, namely ``VS90COMNTOOLS``. Make sure to it to the value of your current version. For example:: SET VS90COMNTOOLS=%VS100COMNTOOLS% ========================= DLL load failed (Windows) ========================= ``setup.py`` may fail with the following message:: C:\Users\Norman\JavaProjects\jpy>c:\Python27\python.exe setup.py install Building a 32-bit library for a Windows system running install running build running build_ext ... running install_lib running install_egg_info Removing c:\Python27\Lib\site-packages\jpy-0.7.2-py2.7.egg-info Writing c:\Python27\Lib\site-packages\jpy-0.7.2-py2.7.egg-info Importing module 'jpy' in order to retrieve its shared library location... Traceback (most recent call last): File "setup.py", line 133, in import jpy ImportError: DLL load failed: %1 is not a valid Win32 application Fix this by adding the path to the Java VM shared library (``jvm.dll``) to the ``PATH`` environment variable:: SET PATH=%JDK_HOME%\jre\bin\server;%PATH% jpy-2.0.0/doc/index.rst0000664000175000017500000000110215202503234015073 0ustar alastairalastair.. jpy documentation master file, created by sphinx-quickstart on Mon Jan 20 21:26:19 2014. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to jpy's documentation! =============================== jpy is a *bi-directional* Java-Python bridge allowing you to call Java from Python and Python from Java. Contents: .. toctree:: :maxdepth: 2 intro install tutorial reference modify Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` jpy-2.0.0/doc/Makefile0000664000175000017500000001513615202503234014706 0ustar alastairalastair# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/jpy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/jpy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/jpy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/jpy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." jpy-2.0.0/doc/intro.rst0000664000175000017500000001175615202503234015137 0ustar alastairalastair############ Introduction ############ jpy is a **bi-directional** Python-Java bridge which you can use to embed Java code in Python programs or the other way round. It has been designed particularly with regard to maximum data transfer speed between the two languages. It comes with a number of outstanding features: * Fully translates Java class hierarchies to Python * Transparently handles Java method overloading * Support of Java multi-threading * Fast and memory-efficient support of primitive Java array parameters via `Python buffers `_ (e.g. `numpy arrays `_) * Support of Java methods that modify primitive Java array parameters (mutable parameters) * Java arrays translate into Python sequence objects * Java API for accessing Python objects (``jpy.jar``) jpy has been tested with Python 2.7, 3.3, 3.4 and Oracle Java 7 and 8 JDKs. It will presumably also work with Python 2.6 or 3.2 and a Java 6 JDK. The initial development of jpy has been driven by the need to write Python extensions to an established scientific imaging application programmed in Java, namely the `BEAM `_ toolbox funded by the European Space Agency (ESA). Writing such Python plug-ins for a Java application usually requires a bi-directional communication between Python and Java since the Python extension code must be able to call back into the Java APIs. ************ How it works ************ The jpy Python module is entirely written in the C programming language. The same resulting shared library is used as a Python jpy module and also as native library for the Java library (``jpy.jar``). Python programs that import the ``jpy`` module can load Java classes, access Java class fields, and call class constructors and methods. Java programs with ``jpy.jar`` on the classpath can import Python modules, access module attributes such as class types and variables, and call any callable objects such as module-level functions, class constructors, as well as static and instance class methods. Calling Java from Python ======================== Instantiate Python objects from Java classes and call their public methods and fields:: import jpy File = jpy.get_type('java.io.File') file = File('test/it') name = file.getName() Calling Python from Java ======================== Access Python attributes and call Python functions from Java:: PyModule sys = PyModule.importModule("sys"); PyObject path = sys.getAttribute("path"); path.call("append", "/usr/home/norman/"); String value = path.getStringValue(); Implementing Java interfaces using Python ========================================= With jpy you can implement Java interfaces using Python. We instantiating Java (proxy) objects from Python modules or classes. If you call methods of the resulting Java object, jpy will delegate the calls to the matching Python module functions or class methods. Here is how this works. Assuming we have a Java interface ``PlugIn.java`` :: public interface PlugIn { String[] process(String arg); } and a Python implementation ``bibo_plugin.py`` :: class BiboPlugIn: def process(self, arg): return arg.split(); then we can call the Python code from Java as follows :: // Import the Python module PyModule plugInModule = PyLib.importModule("bibo_plugin"); // Call the Python class to instantiate an object PyObject plugInObj = plugInModule.call("BiboPlugin"); // Create a Java proxy object for the Python object PlugIn plugIn = plugInObj.createProxy(PlugIn.class); String[] result = plugIn.process('Abcdefghi jkl mnopqr stuv wxy z'); ******************* Current limitations ******************* * Java non-final, static class fields are currently not supported: The reason is that Java classes are represented in jpy's Python API as dynamically allocated, built-in extension types. Built-in extension types cannot have (as of Python 3.3) static, computed attributes which we would need for getting/setting Java static class fields. * Public final static fields are represented as normal (non-computed) type attributes: Their values are Python representations of the final Java values. The limitation here is, that they can be overwritten from Python, because Python does not know final/constant attributes. This could only be achieved with computed attributes, but as said before, they are not supported for built-in extension types. * It is currently not possible to shutdown the Java VM from Python and then restart it. ******************************** Other projects with similar aims ******************************** * `JPype `_ - allow python programs full access to java class libraries * `Jython `_ - Python for the Java Platform * `JyNI `_ - Jython Native Interface * `Jynx `_ - improve integration of Java with Python jpy-2.0.0/doc/tutorial.rst0000664000175000017500000000206115202503234015634 0ustar alastairalastair######## Tutorial ######## Sorry, the jpy tutorial is not yet written. Meanwhile please refer to jpy's Python and Java unit-level tests in order to learn how to use jpy. They are located at * `src/test/python `_ * `src/test/java `_ ********************* Using jpy with Python ********************* Using the Java Standard Library =============================== Calling your Java Classes from Python ===================================== Primitive array parameters that are mutable ------------------------------------------- Primitive array parameters that are return value ------------------------------------------------ ******************* Using jpy with Java ******************* Getting Started =============== Using the Python Standard Library ================================= Calling your Python functions from Java ======================================= Extending Java with Python ========================== jpy-2.0.0/doc/.gitignore0000664000175000017500000000002215202503234015222 0ustar alastairalastair_build _templates jpy-2.0.0/doc/modify.rst0000664000175000017500000000641415202503234015266 0ustar alastairalastair############# How to Modify ############# =============== Rebuild Process =============== jpy's source distribution directory layout uses the `Maven common directory structure `_. * ``setup.py`` - Python build/installation script, will compile Python and Java sources, install the libraries and run all unit-level tests. * ``pom.xml`` - Maven project file to build the Java sources. Called by ``setup.py``. * ``src/main/c`` - C source files for the jpy Python API * ``src/test/python`` - Python API test cases * ``src/main/java`` - Java source files for the jpy Java API * ``src/test/java`` - Java API test cases After changing any source code just run setup again as indicated in the :ref:`build` process. After changing signatures of native methods in ``src/main/java/org/jpy/PyLib.java``, you need to compile the Java classes and regenerate the C headers for the ``PyLib`` class using Maven:: mvn compile javah -d src/main/c/jni -v -classpath target/classes org.jpy.PyLib Then always adapt changes ``org_jpy_PyLib.c`` according to newly generated ``org_jpy_PyLib.h`` and ``org_jpy_PyLib_Diag.h``. Files are found in ``src/main/c/jni/``. Then run setup again as indicated above. ======================= C Programming Guideline ======================= * Follow style used in Python itself * Python type global variable names: `J_Type` * Python type instance structs: `JPy_J` * Python function decl for a type: `J_(JNIEnv* jenv, JPy_J* , ...)` * The pointer is always the first parameter, only type slots obtain their `jenv` from `JPy_GetJEnv()` * Python slots function for a type: `J_(JNIEnv* jenv, JPy_J* self, ...)` * Usually functions shall indicate errors by returning NULL or -1 on error. Callers can expect that the PyErr_SetError has been set correctly and thus simply return NULL or -1 again. Exception: very simple functions, e.g. `JObj_Check()`, can go without error status indication. * Naming conventions: * jpy_jtype.h/c - The Java Meta-Type * JPy_JType type * JType_xxx() functions * jpy_jobj.h/c - The Java Object Wrapper * JPy_JObj type * JObj_xxx() functions * jpy_jmethod.h/c - The Java Method Wrapper * JPy_JMethod type * JPy_JOverloadedMethod type * JMethod_xxx() functions * JOverloadedMethod_xxx() functions * jpy_jfield.h/c - The Java Field Wrapper * JPy_JField type * JField_xxx() functions * jpy_conv.h/c - Conversion of Python objects from/to Java values * JPy_From functions / JPy_FROM_ macros create Python objects (new references!) from Java types * JPy_As functions / JPy_AS_ macros convert from Python objects to Java types * jpy_diag.h/c - Control of outputting diagnostic info * JPy_Diag type * JPy_DIAG_F_ macros * JPy_DIAG_PRINT(flags, format, ...) macros * jpy_module.h/c - The 'jpy' module definition * JPy_xxx() functions * jni/org_jpy_PyLib.h - generated by javah from PyLib.java * jni/org_jpy_PyLib_Diag.h - generated by javah from PyLib.java * jni/org_jpy_PyLib.c - native implementations from PyLib.java jpy-2.0.0/doc/make.bat0000664000175000017500000001446515202503234014657 0ustar alastairalastair@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\jpy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\jpy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end jpy-2.0.0/jpyutil.py0000664000175000017500000007011115202503234014545 0ustar alastairalastair""" The main usage of this module is to configure jpy with respect to a given Java version and Python version. The module can also be used as tool. For usage, type: python jpyutil.py --help The function being invoked here is `write_config_files()` which may also be directly used from your Python code. It will create a file 'jpyconfig.py' and/or a 'jpyconfig.properties' in the given output directory which is usually the one in which the jpy module is installed. To programmatically configure the Java Virtual machine, the `init_jvm()` function can be used: import jpyutil jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['target/test-classes']) # Without the former call, the following jpy import would create a JVM with default settings import jpy The `init_jvm()` can also be called with 'config_file' or 'config' arguments instead. If they are omitted, default configuration values will inferred by first trying to import the module 'jpyconfig.py' (which must therefore be detectable by the Python sys.path). Secondly, the environment variable 'JPY_PY_CONFIG' may point to such a Python configuration file. """ import sys import sysconfig import os import os.path import platform import ctypes import ctypes.util import logging import subprocess __author__ = "Norman Fomferra (Brockmann Consult GmbH) and contributors" __copyright__ = "Copyright 2015-2018 Brockmann Consult GmbH and contributors" __license__ = "Apache 2.0" __version__ = "2.0.0" # Setup a dedicated logger for jpyutil. # This way importing jpyutil does not interfere with logging in other modules logger = logging.getLogger('jpyutil') # Get log level from environment variable JPY_LOG_LEVEL. Default to INFO log_level = os.getenv('JPY_LOG_LEVEL', 'INFO') try: logger.setLevel(getattr(logging, log_level)) except AttributeError as ex: print('JPY_LOG_LEVEL must be DEBUG, INFO, WARNING, ERROR or CRITICAL') raise ex ch = logging.StreamHandler() ch.setLevel(getattr(logging, log_level)) formatter = logging.Formatter('%(name)s - %(levelname)s: %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) JDK_HOME_VARS = ('JPY_JAVA_HOME', 'JPY_JDK_HOME', 'JAVA_HOME', 'JDK_HOME',) JRE_HOME_VARS = ('JPY_JAVA_HOME', 'JPY_JDK_HOME', 'JPY_JRE_HOME', 'JAVA_HOME', 'JDK_HOME', 'JRE_HOME', 'JAVA_JRE') JVM_LIB_NAME = 'jvm' def _get_python_lib_name(): try: abiflags = sys.abiflags except AttributeError: abiflags = '' version = sysconfig.get_config_var('VERSION') if not version: version = '' return 'python' + version + abiflags PYTHON_64BIT = sys.maxsize > 2 ** 32 PYTHON_LIB_NAME = _get_python_lib_name() PYTHON_LIB_DIR_CONFIG_VAR_NAMES = ('LDLIBRARYDIR', 'srcdir', 'BINDIR', 'DESTLIB', 'DESTSHARED', 'BINLIBDEST', 'LIBDEST', 'LIBDIR',) def _get_unique_config_values(names): values = [] for name in names: value = sysconfig.get_config_var(name) if value and value not in values: values.append(value) return values def _add_paths_if_exists(path_list, *paths): for path in paths: if os.path.exists(path) and path not in path_list: path_list.append(path) return path_list def _find_loadable_spec_origin(name): import importlib.util spec = importlib.util.find_spec(name) return spec.origin if spec and spec.has_location else None def _get_module_path(name, fail=False, install_path=None): """ Find the path to the jpy jni modules. """ spec_origin = _find_loadable_spec_origin(name) if not spec_origin: if fail: raise ImportError("module '{}' not found, or is not loadable".format(name)) return None return spec_origin if not install_path else os.path.join(install_path, os.path.split(spec_origin)[1]) def _find_file(search_dirs, *filenames): for filename in filenames: for dir_ in search_dirs: path = os.path.normpath(os.path.join(dir_, filename)) path_exists = os.path.exists(path) logger.debug("Exists '%s'? %s" % (path, "yes" if path_exists else "no")) if path_exists: return path return None def _get_java_api_properties(fail=False, path=None): jpy_config = Properties() jpy_config.set_property('jpy.jpyLib', _get_module_path('jpy', fail=fail, install_path=path)) jpy_config.set_property('jpy.jdlLib', _get_module_path('jdl', fail=fail, install_path=path)) jpy_config.set_property('jpy.pythonLib', _find_python_dll_file(fail=fail)) jpy_config.set_property('jpy.pythonPrefix', sys.prefix) jpy_config.set_property('jpy.pythonExecutable', sys.executable) return jpy_config def find_jdk_home_dir(): """ Try to detect the JDK home directory from Maven, if available, or use dedicated environment variables. :return: pathname if found, else None """ def is_jdk_dir(path): rst = os.path.exists(os.path.join(path, 'include')) and os.path.exists(os.path.join(path, 'lib')) logger.debug(f'Checking {path} for JDK...: {"yes" if rst else "no"}') return rst def walk_to_jdk(path): if is_jdk_dir(path): return path for root, dir_names, file_names in os.walk(path): for d in dir_names: p = os.path.join(root, d) if p and is_jdk_dir(p): return p return None for name in JDK_HOME_VARS: jdk_home_dir = os.environ.get(name, None) if jdk_home_dir: logger.debug(f'JAVA_HOME set by environment variable to {jdk_home_dir}') if is_jdk_dir(jdk_home_dir): return jdk_home_dir jdk_dir = walk_to_jdk(jdk_home_dir) if jdk_dir: logger.error(f'JAVA_HOME set by environment variable to {jdk_home_dir} but no no "include" or "lib" directory found. Possibly you meant {jdk_dir}?') else: logger.error(f'JAVA_HOME set by environment variable to {jdk_home_dir} but no no "include" or "lib" directory found. Does not appear to be a JDK directory.') exit(1) logger.debug('Checking Maven for JAVA_HOME...') try: output = subprocess.check_output(['mvn', '-v']) if isinstance(output, bytes) and not isinstance(output, str): # In Python 3, byte arrays must be explicitly converted into strings. output = output.decode('utf-8') for part in output.split('\n'): if part.startswith('Java home:'): path = part.split(':')[1].strip() if path.endswith('jre'): java_home = path[0:-3] logger.debug(f'JAVA_HOME set by maven to {java_home}') return java_home except Exception: # maven probably isn't installed or not on PATH logger.debug('Maven not found on PATH. No JAVA_HOME found.') return None def _java_process_java_home(): logger.debug('Checking Java for JAVA_HOME...') try: from java_utilities import lookup_property return lookup_property('java.home', use_env=False) except ImportError: logger.debug("java_utilities not found, skipping java process check") except Exception as e: logger.debug(e, exc_info=True) return None def find_jvm_dll_file(java_home_dir=None, fail=False): """ Try to detect the JVM's shared library file. :param java_home_dir: The Java JRE or JDK installation directory to be used for searching. :param fail: Controls behaviour when library file cannot be found. If False, use a fallback. If True, raise a runtime error. :return: pathname if found, else None """ logger.debug("Searching for JVM shared library file") if java_home_dir: jvm_dll_path = _find_jvm_dll_file(java_home_dir) if jvm_dll_path: return jvm_dll_path jvm_dll_path = os.environ.get('JPY_JVM_DLL', None) if jvm_dll_path: return jvm_dll_path for name in JRE_HOME_VARS: java_home_dir = os.environ.get(name, None) if java_home_dir: jvm_dll_path = _find_jvm_dll_file(java_home_dir) if jvm_dll_path: return jvm_dll_path java_home_dir = _java_process_java_home() if java_home_dir: jvm_dll_path = _find_jvm_dll_file(java_home_dir) if jvm_dll_path: return jvm_dll_path jvm_dll_path = ctypes.util.find_library(JVM_LIB_NAME) if jvm_dll_path: logger.debug("No JVM shared library file found in all search paths. Using fallback %s" % repr(jvm_dll_path)) elif fail: raise RuntimeError("can't find any JVM shared library") return jvm_dll_path def _get_jvm_lib_dirs(java_home_dir): machine = platform.machine() arch_dir_name = 'aarch64' if machine == 'aarch64' else 'amd64' if PYTHON_64BIT else 'i386' return (os.path.join(java_home_dir, 'bin'), os.path.join(java_home_dir, 'bin', 'server'), os.path.join(java_home_dir, 'bin', 'client'), os.path.join(java_home_dir, 'bin', 'zero'), os.path.join(java_home_dir, 'bin', arch_dir_name), os.path.join(java_home_dir, 'bin', arch_dir_name, 'server'), os.path.join(java_home_dir, 'bin', arch_dir_name, 'client'), os.path.join(java_home_dir, 'bin', arch_dir_name, 'zero'), os.path.join(java_home_dir, 'lib'), os.path.join(java_home_dir, 'lib', 'server'), os.path.join(java_home_dir, 'lib', 'client'), os.path.join(java_home_dir, 'lib', 'zero'), os.path.join(java_home_dir, 'lib', arch_dir_name), os.path.join(java_home_dir, 'lib', arch_dir_name, 'server'), os.path.join(java_home_dir, 'lib', arch_dir_name, 'client'), os.path.join(java_home_dir, 'lib', arch_dir_name, 'zero'), ) def _get_existing_subdirs(dirs, subdirname): new_dirs = [] for dir_ in dirs: new_dir = os.path.join(dir_, subdirname) if os.path.isdir(new_dir): new_dirs.append(new_dir) return new_dirs def _find_jvm_dll_file(java_home_dir): logger.debug("Searching for JVM shared library file in %s" % repr(java_home_dir)) if not os.path.exists(java_home_dir): return None search_dirs = [] jre_home_dir = os.path.join(java_home_dir, 'jre') if os.path.exists(jre_home_dir): search_dirs += _get_jvm_lib_dirs(jre_home_dir) search_dirs += _get_jvm_lib_dirs(java_home_dir) search_dirs = _add_paths_if_exists([], *search_dirs) if platform.system() == 'Windows': return _find_file(search_dirs, 'jvm.dll') elif platform.system() == 'Darwin': return _find_file(search_dirs, 'libjvm.dylib') # 'Window' and 'Darwin' did not succeed, try 'libjvm.so' on remaining platforms return _find_file(search_dirs, 'libjvm.so') def _find_python_dll_file(fail=False): logger.debug("Searching for Python shared library file") # Prepare list of search directories search_dirs = [sys.prefix] # installed_base/lib needs to be added to the search path for Python 3.13t files installed_base = sysconfig.get_config_var('installed_base') if installed_base: search_dirs.append(os.path.join(installed_base, "lib")) extra_search_dirs = [sysconfig.get_config_var(name) for name in PYTHON_LIB_DIR_CONFIG_VAR_NAMES] for extra_dir in extra_search_dirs: if extra_dir and extra_dir not in search_dirs and os.path.exists(extra_dir): search_dirs.append(extra_dir) if platform.system() == 'Windows': extra_search_dirs = _get_existing_subdirs(search_dirs, "DLLs") search_dirs = extra_search_dirs + search_dirs multi_arch_sub_dir = sysconfig.get_config_var('multiarchsubdir') if multi_arch_sub_dir: while multi_arch_sub_dir.startswith('/'): multi_arch_sub_dir = multi_arch_sub_dir[1:] extra_search_dirs = _get_existing_subdirs(search_dirs, multi_arch_sub_dir) search_dirs = extra_search_dirs + search_dirs logger.debug("Potential Python shared library search dirs: %s" % repr(search_dirs)) # Prepare list of possible library file names # Prefer "Install .so name" as first candidate for file_names instsoname = sysconfig.get_config_var("INSTSONAME") file_names = [ instsoname ] if instsoname else [] # account for Python debug builds debug_build = sysconfig.get_config_var('Py_DEBUG') # account for Python 3.13+ with GIL disabled dll_suffix = '' if sys.version_info >= (3, 13): if not sys._is_gil_enabled(): dll_suffix = 't' dll_suffix += 'd' if debug_build else '' vmaj = str(sys.version_info.major) vmin = str(sys.version_info.minor) if platform.system() == 'Windows': versions = (vmaj + vmin, vmaj, vmaj + vmin + dll_suffix, '') file_names += ['python' + v + '.dll' for v in versions] elif platform.system() == 'Darwin': versions = (vmaj + "." + vmin, vmaj, vmaj + "." + vmin + dll_suffix, '') file_names += ['libpython' + v + '.dylib' for v in versions] file_names += ['libpython' + v + '.so' for v in versions] else: versions = (vmaj + "." + vmin, vmaj, vmaj + "." + vmin + dll_suffix, '') file_names += ['libpython' + v + '.so' for v in versions] logger.debug("Potential Python shared library file names: %s" % repr(file_names)) python_dll_path = _find_file(search_dirs, *file_names) if python_dll_path: return python_dll_path python_dll_path = ctypes.util.find_library(PYTHON_LIB_NAME) if python_dll_path: logger.debug( "No Python shared library file found in all search paths. Using fallback %s" % repr(python_dll_path)) elif fail: raise RuntimeError("can't find any Python shared library") return python_dll_path def _read_config(config_file): config = Config() config.load(config_file) return config def _get_python_api_config(config_file=None): if config_file: # 1. Try argument, if any return _read_config(config_file) try: # 2. Try Python import machinery import jpyconfig return jpyconfig except ImportError: # 3. Try 'JPY_PY_CONFIG' environment variable, if any config_file = os.environ.get('JPY_PY_CONFIG', None) if config_file: return _read_config(config_file) return None def preload_jvm_dll(jvm_dll_file=None, java_home_dir=None, config_file=None, config=None, fail=True): # if jvm_dll_file is unknown, try getting it from config if not jvm_dll_file: if not config: config = _get_python_api_config(config_file=config_file) if config: jvm_dll_file = getattr(config, 'jvm_dll', None) if not java_home_dir: java_home_dir = getattr(config, 'java_home', None) # if jvm_dll_file is still unknown, try searching it using java_home_dir if not jvm_dll_file: jvm_dll_file = find_jvm_dll_file(java_home_dir=java_home_dir, fail=fail) if jvm_dll_file: logger.debug('Preloading JVM shared library %s' % repr(jvm_dll_file)) return ctypes.CDLL(jvm_dll_file, mode=ctypes.RTLD_GLOBAL) else: logger.warning('Failed to preload JVM shared library. No shared library found.') return None def get_jvm_options(jvm_maxmem=None, jvm_classpath=None, jvm_properties=None, jvm_options=None, config=None): if config: if not jvm_maxmem: jvm_maxmem = getattr(config, 'jvm_maxmem', None) if not jvm_classpath: jvm_classpath = getattr(config, 'jvm_classpath', None) if not jvm_properties: jvm_properties = getattr(config, 'jvm_properties', None) if not jvm_options: jvm_options = getattr(config, 'jvm_options', None) jvm_cp = None if jvm_classpath and len(jvm_classpath) > 0: jvm_cp = os.pathsep.join(jvm_classpath) if not jvm_cp: jvm_cp = os.environ.get('JPY_JVM_CLASSPATH', None) if not jvm_maxmem: jvm_maxmem = os.environ.get('JPY_JVM_MAXMEM', None) java_api_properties = _get_java_api_properties().values if jvm_properties: # Overwrite jpy_config jvm_properties = dict(list(java_api_properties.items()) + list(jvm_properties.items())) else: jvm_properties = java_api_properties options = [] if jvm_maxmem: options.append('-Xmx' + jvm_maxmem) if jvm_cp: options.append('-Djava.class.path=' + jvm_cp) if jvm_properties: for key in jvm_properties: value = jvm_properties[key] options.append('-D' + key + '=' + value) if jvm_options: options += jvm_options return options def init_jvm(java_home=None, jvm_dll=None, jvm_maxmem=None, jvm_classpath=None, jvm_properties=None, jvm_options=None, config_file=None, config=None): """ Creates a configured Java virtual machine which will be used by jpy. :param java_home: The Java JRE or JDK home directory used to search JVM shared library, if 'jvm_dll' is omitted. :param jvm_dll: The JVM shared library file. My be inferred from 'java_home'. :param jvm_maxmem: The JVM maximum heap space, e.g. '400M', '8G'. Refer to the java executable '-Xmx' option. :param jvm_classpath: The JVM search paths for Java class files. Separated by colons (Unix) or semicolons (Windows). Refer to the java executable '-cp' option. :param jvm_properties: A dictionary of key -> value pairs passed to the JVM as Java system properties. Refer to the java executable '-D' option. :param jvm_options: A list of extra options for the JVM. Refer to the java executable options. :param config_file: Extra configuration file (e.g. 'jpyconfig.py') to be loaded if 'config' parameter is omitted. :param config: An optional default configuration object providing default attributes for the 'jvm_maxmem', 'jvm_classpath', 'jvm_properties', 'jvm_options' parameters. :return: a tuple (cdll, actual_jvm_options) on success, None otherwise. """ if not config: config = _get_python_api_config(config_file=config_file) cdll = preload_jvm_dll(jvm_dll_file=jvm_dll, java_home_dir=java_home, config_file=config_file, config=config, fail=False) import jpy if not jpy.has_jvm(): jvm_options = get_jvm_options(jvm_maxmem=jvm_maxmem, jvm_classpath=jvm_classpath, jvm_properties=jvm_properties, jvm_options=jvm_options, config=config) logger.debug('Creating JVM with options %s' % repr(jvm_options)) jpy.create_jvm(options=jvm_options) try: py_lib_initializer = jpy.get_type('org.jpy.PyLibInitializer') py_lib_initializer.initPyLib( _find_python_dll_file(fail=True), _get_module_path('jpy', fail=True), _get_module_path('jdl', fail=True) ) except ValueError: # It's valid to not have jpy.jar on the classpath if you don't expect java to call into python logger.debug("Unable to find org.jpy.PyLibInitializer on classpath") pass else: jvm_options = None # print('jvm_dll =', jvm_dll) # print('jvm_options =', jvm_options) return cdll, jvm_options class Config: def load(self, path): """ Read Python file from 'path', execute it and return object that stores all variables of the Python code as attributes. :param path: :return: """ with open(path) as f: code = f.read() exec(code, {}, self.__dict__) class Properties: def __init__(self, values=None): if values: self.keys = values.keys() self.values = values.copy() else: self.keys = [] self.values = {} def set_property(self, key, value): if value: if key not in self.keys: self.keys.append(key) self.values[key] = value else: if key in self.keys: self.keys.remove(key) self.values.pop(key) def get_property(self, key, default_value=None): return self.values[key] if key in self.values else default_value def store(self, path, comments=()): with open(path, 'w') as f: for comment in comments: f.write('# ' + str(comment).replace('\\', '\\\\') + '\n') for key in self.keys: value = self.get_property(key) if value: f.write(str(key) + ' = ' + str(value).replace('\\', '\\\\') + '\n') else: f.write(str(key) + ' =\n') def load(self, path): self.__init__() with open(path) as f: lines = f.readlines() for line in lines: if line and len(line) > 0 and not line.startswith('#'): tokens = line.split('=') if len(tokens) == 2: self.set_property(tokens[0].strip(), tokens[1].strip().replace('\\\\', '\\')) else: raise ValueError('illegal Java properties format ' + line) def _execute_python_scripts(scripts, **kwargs): import subprocess failures = 0 for script in scripts: exit_code = subprocess.call([sys.executable, script], **kwargs) if exit_code: failures += 1 return failures def write_config_files(out_dir='.', java_home_dir=None, jvm_dll_file=None, install_dir=None, req_java_api_conf=True, req_py_api_conf=True, jvm_properties=None, jvm_options=None): """ Writes the jpy configuration files for Java and/or Python. :param out_dir: output directory, must exist :param java_home_dir: optional home directory of the Java JRE or JDK installation :param jvm_dll_file: optional file to JVM shared library file :param install_dir: optional path to where to searfh for modules :param req_java_api_conf: whether to write the jpy configuration file 'jpyconfig.properties' for Java :param req_py_api_conf: whether to write the jpy configuration file 'jpyconfig.py' for Python :return: zero on success, otherwise an error code """ import datetime retcode = 0 tool_name = os.path.basename(__file__) py_api_config_basename = 'jpyconfig.py' java_api_config_basename = 'jpyconfig.properties' if not jvm_dll_file: jvm_dll_file = find_jvm_dll_file(java_home_dir=java_home_dir) if jvm_dll_file: py_api_config_file = os.path.join(out_dir, py_api_config_basename) try: with open(py_api_config_file, 'w') as f: f.write("# Created by '%s' tool on %s\n" % (tool_name, str(datetime.datetime.now()))) f.write( "# This file is read by the 'jpyutil' module in order to load and configure the JVM from Python\n") if java_home_dir: f.write('java_home = %s\n' % repr(java_home_dir)) f.write('jvm_dll = %s\n' % repr(jvm_dll_file)) f.write('jvm_maxmem = None\n') f.write('jvm_classpath = []\n') if jvm_properties is None: f.write('jvm_properties = {}\n') else: f.write('jvm_properties = ' + str(jvm_properties) + '\n') if jvm_options is None: f.write('jvm_options = []\n') else: f.write('jvm_options = [\'' + jvm_options + '\']\n') logger.info("jpy Python API configuration written to '%s'" % py_api_config_file) except Exception: logger.exception("Error while writing Python API configuration") if req_py_api_conf: retcode = 1 else: logger.error("Can't determine any JVM shared library") if req_py_api_conf: retcode = 2 try: java_api_config_file = os.path.join(out_dir, java_api_config_basename) java_api_properties = _get_java_api_properties(fail=req_java_api_conf, path=install_dir) java_api_properties.store(java_api_config_file, comments=[ "Created by '%s' tool on %s" % (tool_name, str(datetime.datetime.now())), "This file is read by the jpy Java API (org.jpy.PyLib class) in order to find shared libraries"]) logger.info("jpy Java API configuration written to '%s'" % java_api_config_file) except Exception: logger.exception("Error while writing Java API configuration") if req_java_api_conf: retcode = 3 return retcode def _main(): import argparse out_default = os.path.dirname(os.path.abspath(__file__)) parser = argparse.ArgumentParser(description='Generate configuration files for the jpy Python API (jpyconfig.py)\n' 'and the jpy Java API (jpyconfig.properties).') parser.add_argument("-o", "--out", action='store', default=out_default, help="Output directory for the generated configuration files. Defaults to " + out_default) parser.add_argument("--java_home", action='store', default=None, help="Java JDK or JRE home directory. Can also be " "set using one of the environment variables " + " | ".join(JRE_HOME_VARS) + ".") parser.add_argument("--jvm_dll", action='store', default=None, help="Java JVM shared library file. Usually inferred" " from java_home option. Can also be set " "using environment variable JPY_JVM_DLL.") parser.add_argument("--log_file", action='store', default=None, help="Optional log file.") parser.add_argument("--log_level", action='store', default='INFO', help="Possible values: DEBUG, INFO, WARNING, ERROR. Default is INFO.") parser.add_argument("-j", "--req_java", action='store_true', default=False, help="Require that Java API configuration succeeds.") parser.add_argument("-p", "--req_py", action='store_true', default=False, help="Require that Python API configuration succeeds.") parser.add_argument("--install_dir", action='store', default=None, help="Optional. Used during pip install of JPY") args = parser.parse_args() # Using _ suffix to avoid shadowing global log_level variable log_level_ = getattr(logging, args.log_level.upper(), None) if not isinstance(log_level_, int): raise ValueError('Invalid log level: %s' % log_level_) log_format = '%(levelname)s: %(message)s' log_file = args.log_file if log_file: logging.basicConfig(format=log_format, level=log_level_, filename=log_file, filemode='w') else: logging.basicConfig(format=log_format, level=log_level_) try: retcode = write_config_files(out_dir=args.out, java_home_dir=args.java_home, jvm_dll_file=args.jvm_dll, req_java_api_conf=args.req_java, req_py_api_conf=args.req_py, install_dir=args.install_dir) except: logger.exception("Configuration failed") retcode = 100 if retcode == 0: logger.info("Configuration completed successfully") exit(retcode) if __name__ == '__main__': _main() jpy-2.0.0/README.md0000664000175000017500000001117715202503234013761 0ustar alastairalastair![Build Status](https://github.com/jpy-consortium/jpy/actions/workflows/build.yml/badge.svg?branch=master) jpy - a Python-Java Bridge ========================== jpy is a **bi-directional** Python-Java bridge which you can use to embed Java code in Python programs or the other way round. It has been designed particularly with regard to maximum data transfer speed between the two languages. It comes with a number of outstanding features: * Fully translates Java class hierarchies to Python * Transparently handles Java method overloading * Support of Java multi-threading * Fast and memory-efficient support of primitive Java array parameters via [Python buffers](http://docs.python.org/3.3/c-api/buffer.html) (e.g. [Numpy arrays](http://docs.scipy.org/doc/numpy/reference/arrays.html)) * Support of Java methods that modify primitive Java array parameters (mutable parameters) * Java arrays translate into Python sequence objects * Java API for accessing Python objects (`jpy.jar`) jpy has been tested with Python 3.9–3.14 and OpenJDK 11+ on Linux, Windows, and macOS. The initial development of jpy was driven by the need to write Python extensions to an established scientific imaging application programmed in Java, namely the [SNAP](http://step.esa.int/) toolbox, the SeNtinel Application Platform project, funded by the [European Space Agency](http://www.esa.int/ESA) (ESA). (jpy is bundled with the SNAP distribution.) Current development and maintenance is funded by [Deephaven](https://deephaven.io). Writing such Python plug-ins for a Java application usually requires a bi-directional communication between Python and Java since the Python extension code must be able to call back into the Java APIs. For more information please have a look into jpy's * [documentation](http://jpy.readthedocs.org/en/latest/) * [source repository](https://github.com/jpy-consortium/jpy) * [issue tracker](https://github.com/jpy-consortium/jpy/issues?state=open) How to build wheels for Linux and Mac ------------------------------------- Install a JDK 11. Set `JDK_HOME` or `JPY_JDK_HOME` to point to your JDK installation and run the build script: $ export JDK_HOME= $ export JAVA_HOME=$JDK_HOME $ pip install setuptools wheel $ python setup.py build maven bdist_wheel On success, the wheel is found in the `dist` directory. To deploy the `jpy.jar` (if you don't know why you need this step, this is not for you):: $ mvn clean deploy -DskipTests=true How to build a wheel for Windows -------------------------------- Set `JDK_HOME` or `JPY_JDK_HOME` to point to your JDK installation. You'll need Windows SDK 7.1 or Visual Studio C++ to build the sources. With Windows SDK 7.1:: > SET VS90COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\ > SET DISTUTILS_USE_SDK=1 > C:\Program Files\Microsoft SDKs\Windows\v7.1\bin\setenv /x64 /release > SET JDK_HOME= > pip install setuptools wheel > python setup.py build maven bdist_wheel With Visual Studio 14 and higher it is much easier:: > SET VS100COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\ > SET JDK_HOME= > pip install setuptools wheel > python setup.py build maven bdist_wheel On success, the wheel can be found in the `dist` directory. How to install from sources --------------------------- TBD Releasing jpy ------------- The target reader of this section is a jpy developer wishing to release a new jpy version. Note: You need to have Sphinx installed to update the documentation. 1. Make sure all Java *and* Python units tests run green 2. Remove the `-SNAPSHOT` qualifier from versions names in both the Maven `pom.xml` and `setup.py` files, and update the version numbers and copyright years in `jpyutil.py` and `doc/conf.py`. 3. Generate Java API doc by running `mvn javadoc:javadoc` which will update directory `doc/_static` 4. Update documentation, `cd doc` and run `make html` 5. http://peterdowns.com/posts/first-time-with-pypi.html Running Tests ---------------- Run: `python setup.py build test` Code Of Conduct --------------- This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). For more information see the [Code of Conduct](CODE_OF_CONDUCT.md) or contact [opencode@deephaven.io](mailto:opencode@deephaven.io) with any additional questions or comments. Contributing ------------ For instructions on contributing, see [CONTRIBUTING.md](CONTRIBUTING.md). Notes ----- Some of the details on this README are out of date. Efforts to improve them will be made in the future. jpy-2.0.0/ez_setup.py0000664000175000017500000002625215202503234014712 0ustar alastairalastair#!/usr/bin/env python """ Setuptools bootstrapping installer. Run this script to install or upgrade setuptools. """ import os import shutil import sys import tempfile import zipfile import optparse import subprocess import platform import textwrap import contextlib import warnings from distutils import log try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen try: from site import USER_SITE except ImportError: USER_SITE = None DEFAULT_VERSION = "18.3.1" DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" DEFAULT_SAVE_DIR = os.curdir def _python_cmd(*args): """ Execute a command. Return True if the command succeeded. """ args = (sys.executable,) + args return subprocess.call(args) == 0 def _install(archive_filename, install_args=()): """Install Setuptools.""" with archive_context(archive_filename): # installing log.warn('Installing Setuptools') if not _python_cmd('setup.py', 'install', *install_args): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') # exitcode will be 2 return 2 def _build_egg(egg, archive_filename, to_dir): """Build Setuptools egg.""" with archive_context(archive_filename): # building an egg log.warn('Building a Setuptools egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') class ContextualZipFile(zipfile.ZipFile): """Supplement ZipFile class to support context manager for Python 2.6.""" def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() def __new__(cls, *args, **kwargs): """Construct a ZipFile or ContextualZipFile as appropriate.""" if hasattr(zipfile.ZipFile, '__exit__'): return zipfile.ZipFile(*args, **kwargs) return super(ContextualZipFile, cls).__new__(cls) @contextlib.contextmanager def archive_context(filename): """ Unzip filename to a temporary directory, set to the cwd. The unzipped target is cleaned up after. """ tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) with ContextualZipFile(filename) as archive: archive.extractall() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) yield finally: os.chdir(old_wd) shutil.rmtree(tmpdir) def _do_download(version, download_base, to_dir, download_delay): """Download Setuptools.""" egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): archive = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, archive, to_dir) sys.path.insert(0, egg) # Remove previously-imported pkg_resources if present (see # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). if 'pkg_resources' in sys.modules: del sys.modules['pkg_resources'] import setuptools setuptools.bootstrap_install_from = egg def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=DEFAULT_SAVE_DIR, download_delay=15): """ Ensure that a setuptools version is installed. Return None. Raise SystemExit if the requested version or later cannot be installed. """ to_dir = os.path.abspath(to_dir) # prior to importing, capture the module state for # representative modules. rep_modules = 'pkg_resources', 'setuptools' imported = set(sys.modules).intersection(rep_modules) try: import pkg_resources pkg_resources.require("setuptools>=" + version) # a suitable version is already installed return except ImportError: # pkg_resources not available; setuptools is not installed; download pass except pkg_resources.DistributionNotFound: # no version of setuptools was found; allow download pass except pkg_resources.VersionConflict as VC_err: if imported: _conflict_bail(VC_err, version) # otherwise, unload pkg_resources to allow the downloaded version to # take precedence. del pkg_resources _unload_pkg_resources() return _do_download(version, download_base, to_dir, download_delay) def _conflict_bail(VC_err, version): """ Setuptools was imported prior to invocation, so it is unsafe to unload it. Bail out. """ conflict_tmpl = textwrap.dedent(""" The required version of setuptools (>={version}) is not available, and can't be installed while this script is running. Please install a more recent version first, using 'easy_install -U setuptools'. (Currently using {VC_err.args[0]!r}) """) msg = conflict_tmpl.format(**locals()) sys.stderr.write(msg) sys.exit(2) def _unload_pkg_resources(): del_modules = [ name for name in sys.modules if name.startswith('pkg_resources') ] for mod_name in del_modules: del sys.modules[mod_name] def _clean_check(cmd, target): """ Run the command to download target. If the command fails, clean up before re-raising the error. """ try: subprocess.check_call(cmd) except subprocess.CalledProcessError: if os.access(target, os.F_OK): os.unlink(target) raise def download_file_powershell(url, target): """ Download the file at url to target using Powershell. Powershell will validate trust. Raise an exception if the command cannot complete. """ target = os.path.abspath(target) ps_cmd = ( "[System.Net.WebRequest]::DefaultWebProxy.Credentials = " "[System.Net.CredentialCache]::DefaultCredentials; " "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars() ) cmd = [ 'powershell', '-Command', ps_cmd, ] _clean_check(cmd, target) def has_powershell(): """Determine if Powershell is available.""" if platform.system() != 'Windows': return False cmd = ['powershell', '-Command', 'echo test'] with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) except Exception: return False return True download_file_powershell.viable = has_powershell def download_file_curl(url, target): cmd = ['curl', url, '--silent', '--output', target] _clean_check(cmd, target) def has_curl(): cmd = ['curl', '--version'] with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) except Exception: return False return True download_file_curl.viable = has_curl def download_file_wget(url, target): cmd = ['wget', url, '--quiet', '--output-document', target] _clean_check(cmd, target) def has_wget(): cmd = ['wget', '--version'] with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) except Exception: return False return True download_file_wget.viable = has_wget def download_file_insecure(url, target): """Use Python to download the file, without connection authentication.""" src = urlopen(url) try: # Read all the data in one block. data = src.read() finally: src.close() # Write all the data in one block to avoid creating a partial file. with open(target, "wb") as dst: dst.write(data) download_file_insecure.viable = lambda: True def get_best_downloader(): downloaders = ( download_file_powershell, download_file_curl, download_file_wget, download_file_insecure, ) viable_downloaders = (dl for dl in downloaders if dl.viable()) return next(viable_downloaders, None) def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=DEFAULT_SAVE_DIR, delay=15, downloader_factory=get_best_downloader): """ Download setuptools from a specified location and return its filename. `version` should be a valid setuptools version number that is available as an sdist for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. ``downloader_factory`` should be a function taking no arguments and returning a function for downloading a URL to a target. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) zip_name = "setuptools-%s.zip" % version url = download_base + zip_name saveto = os.path.join(to_dir, zip_name) if not os.path.exists(saveto): # Avoid repeated downloads log.warn("Downloading %s", url) downloader = downloader_factory() downloader(url, saveto) return os.path.realpath(saveto) def _build_install_args(options): """ Build the arguments to 'python setup.py install' on the setuptools package. Returns list of command line arguments. """ return ['--user'] if options.user_install else [] def _parse_args(): """Parse the command line for options.""" parser = optparse.OptionParser() parser.add_option( '--user', dest='user_install', action='store_true', default=False, help='install in user site package (requires Python 2.6 or later)') parser.add_option( '--download-base', dest='download_base', metavar="URL", default=DEFAULT_URL, help='alternative URL from where to download the setuptools package') parser.add_option( '--insecure', dest='downloader_factory', action='store_const', const=lambda: download_file_insecure, default=get_best_downloader, help='Use internal, non-validating downloader' ) parser.add_option( '--version', help="Specify which version to download", default=DEFAULT_VERSION, ) parser.add_option( '--to-dir', help="Directory to save (and re-use) package", default=DEFAULT_SAVE_DIR, ) options, args = parser.parse_args() # positional arguments are ignored return options def _download_args(options): """Return args for download_setuptools function from cmdline args.""" return dict( version=options.version, download_base=options.download_base, downloader_factory=options.downloader_factory, to_dir=options.to_dir, ) def main(): """Install or upgrade setuptools and EasyInstall.""" options = _parse_args() archive = download_setuptools(**_download_args(options)) return _install(archive, _build_install_args(options)) if __name__ == '__main__': sys.exit(main()) jpy-2.0.0/setup.py0000664000175000017500000003355315202503234014216 0ustar alastairalastair# !/usr/bin/env python3 # Copyright 2014-2020 Brockmann Consult GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import glob import os import os.path import platform import shutil import subprocess import sys import unittest from distutils import log from distutils.cmd import Command from distutils.util import get_platform from setuptools import setup from setuptools.command.install import install from setuptools.command.install_lib import install_lib from setuptools.command.test import test from setuptools.extension import Extension import jpyutil __author__ = jpyutil.__author__ __copyright__ = jpyutil.__copyright__ __license__ = jpyutil.__license__ __version__ = jpyutil.__version__ base_dir = os.path.dirname(os.path.relpath(__file__)) src_main_c_dir = os.path.join(base_dir, 'src', 'main', 'c') src_test_py_dir = os.path.join(base_dir, 'src', 'test', 'python') is_ci = os.environ.get('CI') == 'true' sources = [ os.path.join(src_main_c_dir, 'jpy_module.c'), os.path.join(src_main_c_dir, 'jpy_diag.c'), os.path.join(src_main_c_dir, 'jpy_verboseexcept.c'), os.path.join(src_main_c_dir, 'jpy_conv.c'), os.path.join(src_main_c_dir, 'jpy_compat.c'), os.path.join(src_main_c_dir, 'jpy_jtype.c'), os.path.join(src_main_c_dir, 'jpy_jarray.c'), os.path.join(src_main_c_dir, 'jpy_jobj.c'), os.path.join(src_main_c_dir, 'jpy_jmethod.c'), os.path.join(src_main_c_dir, 'jpy_jfield.c'), os.path.join(src_main_c_dir, 'jni/org_jpy_PyLib.c'), ] headers = [ os.path.join(src_main_c_dir, 'jpy_module.h'), os.path.join(src_main_c_dir, 'jpy_diag.h'), os.path.join(src_main_c_dir, 'jpy_conv.h'), os.path.join(src_main_c_dir, 'jpy_compat.h'), os.path.join(src_main_c_dir, 'jpy_jtype.h'), os.path.join(src_main_c_dir, 'jpy_jarray.h'), os.path.join(src_main_c_dir, 'jpy_jobj.h'), os.path.join(src_main_c_dir, 'jpy_jmethod.h'), os.path.join(src_main_c_dir, 'jpy_jfield.h'), os.path.join(src_main_c_dir, 'jni/org_jpy_PyLib.h'), ] # Python unit tests that just use Java runtime classes (rt.jar) python_java_rt_tests = [ os.path.join(src_test_py_dir, 'jpy_rt_test.py'), os.path.join(src_test_py_dir, 'jpy_mt_test.py'), os.path.join(src_test_py_dir, 'jpy_diag_test.py'), # os.path.join(src_test_py_dir, 'jpy_perf_test.py'), ] # Python unit tests that require target/test-classes or target/classes # available on the classpath python_java_jpy_tests = [ os.path.join(src_test_py_dir, 'jpy_array_test.py'), os.path.join(src_test_py_dir, 'jpy_field_test.py'), os.path.join(src_test_py_dir, 'jpy_retval_test.py'), os.path.join(src_test_py_dir, 'jpy_exception_test.py'), os.path.join(src_test_py_dir, 'jpy_py_exception_chain_test.py'), os.path.join(src_test_py_dir, 'jpy_overload_test.py'), os.path.join(src_test_py_dir, 'jpy_typeconv_test.py'), os.path.join(src_test_py_dir, 'jpy_typeconv_test_pyobj.py'), os.path.join(src_test_py_dir, 'jpy_typeres_test.py'), os.path.join(src_test_py_dir, 'jpy_modretparam_test.py'), os.path.join(src_test_py_dir, 'jpy_translation_test.py'), os.path.join(src_test_py_dir, 'jpy_gettype_test.py'), os.path.join(src_test_py_dir, 'jpy_reentrant_test.py'), os.path.join(src_test_py_dir, 'jpy_java_embeddable_test.py'), os.path.join(src_test_py_dir, 'jpy_obj_test.py'), os.path.join(src_test_py_dir, 'jpy_eval_exec_test.py'), os.path.join(src_test_py_dir, 'jpy_mt_eval_exec_test.py'), os.path.join(src_test_py_dir, 'jpy_reachability_fence_test.py'), ] # e.g. jdk_home_dir = '/home/marta/jdk1.7.0_15' jdk_home_dir = jpyutil.find_jdk_home_dir() if jdk_home_dir is None: log.error('Error: environment variable "JAVA_HOME" must be set to a JDK (>= v1.7) installation directory') exit(1) log.info('Building a %s-bit library for a %s system with JDK at %s' % ( '64' if jpyutil.PYTHON_64BIT else '32', platform.system(), jdk_home_dir)) jvm_dll_file = jpyutil.find_jvm_dll_file(jdk_home_dir) if not jvm_dll_file: log.error('Error: Cannot find any JVM shared library') exit(1) lib_dir = os.path.join(base_dir, 'lib') jpy_jar_file = os.path.join(lib_dir, 'jpy.jar') jvm_dll_dir = os.path.dirname(jvm_dll_file) include_dirs = [src_main_c_dir, os.path.join(jdk_home_dir, 'include')] library_dirs = [jvm_dll_dir] libraries = [jpyutil.JVM_LIB_NAME] define_macros = [] extra_link_args = [] extra_compile_args = [] if platform.system() == 'Windows': define_macros += [('WIN32', '1')] include_dirs += [os.path.join(jdk_home_dir, 'include', 'win32')] library_dirs += [os.path.join(jdk_home_dir, 'lib')] elif platform.system() == 'Linux': include_dirs += [os.path.join(jdk_home_dir, 'include', 'linux')] libraries += ['dl'] extra_link_args += ['-Xlinker', '-rpath', jvm_dll_dir] elif platform.system() == 'Darwin': include_dirs += [os.path.join(jdk_home_dir, 'include', 'darwin')] if is_ci: # This has the effect of removing the non-portable LC_LOAD_DYLIB and LC_RPATH directives for libjvm.dylib from the .so files. # See https://github.com/jpy-consortium/jpy/issues/79 for details. libraries = None library_dirs = None else: # Remove local build workaround for non-portable macOS wheels # TODO: https://github.com/jpy-consortium/jpy/issues/80 library_dirs += [os.path.join(sys.exec_prefix, 'lib')] extra_link_args += ['-Xlinker', '-rpath', jvm_dll_dir] # ----------- Functions ------------- def _build_dir(): # this is hacky, but use distutils logic to get build dir. see: distutils.command.build plat = '.%s-%d.%d' % (get_platform(), sys.version_info.major, sys.version_info.minor) log.info('Platform specifier: "%s"' % plat) path = os.path.join('build', 'lib' + plat) log.info('Build directory path: "%s"' % path) return path def package_maven(): """ Run maven package lifecycle """ if not os.getenv('JAVA_HOME'): # make sure Maven uses the same JDK which we have used to compile # and link the C-code os.environ['JAVA_HOME'] = jdk_home_dir mvn_goal = 'package' log.info("Executing Maven goal '" + mvn_goal + "'") code = subprocess.call(['mvn', 'clean', mvn_goal, '-DskipTests', '-B'], shell=platform.system() == 'Windows') if code: exit(code) # Copy JAR results to lib/*.jar if not os.path.exists(lib_dir): os.mkdir(lib_dir) target_dir = os.path.join(base_dir, 'target') jar_files = glob.glob(os.path.join(target_dir, '*.jar')) jar_files = [f for f in jar_files if not (f.endswith('-sources.jar') or f.endswith('-javadoc.jar'))] if not jar_files: log.error('Maven did not generate any JAR artifacts') exit(1) for jar_file in jar_files: build_dir = _build_dir() if os.path.exists(build_dir): log.info('Build directory "%s" exists.' % build_dir) else: log.info('Creating missing build directory "%s".' % build_dir) os.makedirs(build_dir) log.info("Copying " + jar_file + " -> " + build_dir + "") shutil.copy(jar_file, build_dir) def _read(filename): """ Helper function for reading in project files """ with open(filename, encoding='UTF-8') as file: return file.read() def test_python_java_rt(): """ Run Python test cases against Java runtime classes. """ sub_env = {'PYTHONPATH': _build_dir()} import os sub_env.update(dict(os.environ)) log.info('Executing Python unit tests (against Java runtime classes)...') return jpyutil._execute_python_scripts(python_java_rt_tests, env=sub_env) def test_python_java_classes(): """ Run Python tests against JPY test classes """ sub_env = {'PYTHONPATH': _build_dir()} import os sub_env.update(dict(os.environ)) log.info('Executing Python unit tests (against JPY test classes)...') return jpyutil._execute_python_scripts(python_java_jpy_tests, env=sub_env) def test_maven(): jpy_config = os.path.join(_build_dir(), 'jpyconfig.properties') mvn_args = '-DargLine=-Xmx512m -Djpy.config=' + jpy_config + ' -Djpy.debug=true' log.info("Executing Maven goal 'test' with arg line " + repr(mvn_args)) code = subprocess.call(['mvn', 'test', mvn_args], shell=platform.system() == 'Windows') return code == 0 def _write_jpy_config(target_dir=None, install_dir=None): """ Write out a well-formed jpyconfig.properties file for easier Java integration in a given location. """ if is_ci: # We don't want to publish the properties for the CI build system. return None if not target_dir: target_dir = _build_dir() args = [sys.executable, os.path.join(target_dir, 'jpyutil.py'), '--jvm_dll', jvm_dll_file, '--java_home', jdk_home_dir, '--log_level', 'DEBUG', '--req_java', '--req_py'] if install_dir: args.append('--install_dir') args.append(install_dir) log.info('Writing jpy configuration to %s using install_dir %s' % (target_dir, install_dir)) return subprocess.call(args) def _copy_jpyutil(): src = os.path.relpath(jpyutil.__file__) dest = _build_dir() log.info('Copying %s to %s' % (src, dest)) shutil.copy(src, dest) def _build_jpy(): package_maven() _copy_jpyutil() _write_jpy_config() def test_suite(): suite = unittest.TestSuite() def test_python_with_java_runtime(self): assert 0 == test_python_java_rt() def test_python_with_java_classes(self): assert 0 == test_python_java_classes() def test_java(self): assert test_maven() suite.addTest(test_python_with_java_runtime) suite.addTest(test_python_with_java_classes) # comment out because the asynchronous nature of the PyObject GC in Java makes stopPython/startPython flakey. # suite.addTest(test_java) return suite class MavenBuildCommand(Command): """ Custom JPY Maven builder command """ description = 'run Maven to generate JPY jar' user_options = [] # do not remove, needs to be stubbed out! def initialize_options(self): pass def finalize_options(self): pass def run(self): self.announce('Building JPY') _build_jpy() class JpyBuildBeforeTest(test): """ Customization of SetupTools Install command for JPY """ def run(self): self.run_command('build') self.run_command('maven') test.run(self) class JpyInstallLib(install_lib): """ Custom install_lib command for getting install_dir """ def run(self): _write_jpy_config(install_dir=self.install_dir) install_lib.run(self) class JpyInstall(install): """ Custom install command to trigger Maven steps """ def run(self): self.run_command('build') self.run_command('maven') install.run(self) setup(name='jpy', description='Bi-directional Python-Java bridge', long_description=_read('README.md') + '\n\n' + _read('CHANGES.md'), version=__version__, long_description_content_type='text/markdown', platforms='Windows, Linux, Darwin', author=__author__, author_email='norman.fomferra@brockmann-consult.de', maintainer='Brockmann Consult GmbH', maintainer_email='norman.fomferra@brockmann-consult.de', license=__license__, url='https://github.com/jpy-consortium/jpy', download_url='https://pypi.python.org/pypi/jpy/' + __version__, py_modules=['jpyutil'], ext_modules=[Extension('jpy', sources=sources, depends=headers, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros), Extension('jdl', sources=[os.path.join(src_main_c_dir, 'jni/org_jpy_DL.c')], depends=[os.path.join(src_main_c_dir, 'jni/org_jpy_DL.h')], include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, extra_link_args=extra_link_args, extra_compile_args=extra_compile_args, define_macros=define_macros), ], test_suite='setup.test_suite', cmdclass={ 'maven': MavenBuildCommand, 'test': JpyBuildBeforeTest, 'install': JpyInstall, 'install_lib': JpyInstallLib }, python_requires='>=3.9', classifiers=['Development Status :: 5 - Production/Stable', # Indicate who your project is intended for 'Intended Audience :: Developers', # Pick your license as you wish (should match "license" above) 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', ]) jpy-2.0.0/.dockerignore0000664000175000017500000000002615202503234015145 0ustar alastairalastair.idea .git __pycache__jpy-2.0.0/CONTRIBUTING.md0000664000175000017500000000531315202503234014726 0ustar alastairalastair# Contributing This guide will serve as a reference for contributing to Jpy. ## Getting the source 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. To learn more see: * [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow) * [Forking Projects](https://guides.github.com/activities/forking/) * [Fork A Repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) * [Working With Forks](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks) To get started quickly: 1) Navigate to [https://github.com/jpy-consortium/jpy](https://github.com/jpy-consortium/jpy). 2) Click `Fork` in the top right corner. 3) `git clone git@github.com:/jpy.git` 4) Commit changes to your own branches in your forked repository. 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. 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`). Over time, forks will get out of sync with the upstream repository. To stay up to date, either: * Navigate to `https://github.com//jpy` and click on `Fetch upstream`, or * Follow these directions on [Syncing A Fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork). ## Creating a Pull Request Pull requests can be created through the GitHub website or through the GitHub CLI. ### GitHub Web 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). ### GitHub CLI 1) [Install GitHub command line tool](https://github.com/cli/cli). 2) On the command line, cd into your checked-out fork/branch. 3) `gh pr create -f -w` * Use `jpy-consortium/jpy` as the base repository. * Use `/jpy` as the repository to push to. 4) Your changes should automatically get pushed, and then a new pull request with your changes should open up in your browser. 5) Complete the information in the pull request and click `Create pull request`. For more information, see: * [gh pr create](https://cli.github.com/manual/gh_pr_create) * [CLI In Use](https://cli.github.com/manual/examples.html) jpy-2.0.0/appveyor.yml0000664000175000017500000001473615202503234015076 0ustar alastairalastairversion: '{build}' image: - macos - Visual Studio 2015 - Ubuntu2004 configuration: Release platform: - x64 environment: matrix: - JPY_PYTHON_HOME_WINDOWS: C:\Python34-x64 JPY_PYTHON_HOME_UBUNTU: /home/appveyor/venv3.4 JPY_MAC_PYTHON_VERSION: 3.4.10 # Python 3.4 is incompatible with OpenSSL 1.1, so we have to force the # installation of OpenSSL 1.0 (see https://bugs.python.org/issue31838 , # https://gist.github.com/souzagab/0ae60e61939d51385de87904b65b2da2 , # and more detailed comments below). JPY_MAC_OPENSSL_PACKAGE: openssl.rb - JPY_PYTHON_HOME_WINDOWS: C:\Python35-x64 JPY_PYTHON_HOME_UBUNTU: /home/appveyor/venv3.5 JPY_MAC_PYTHON_VERSION: 3.5.9 JPY_MAC_OPENSSL_PACKAGE: openssl@1.1 - JPY_PYTHON_HOME_WINDOWS: C:\Python36-x64 JPY_PYTHON_HOME_UBUNTU: /home/appveyor/venv3.6 JPY_MAC_PYTHON_VERSION: 3.6.11 JPY_MAC_OPENSSL_PACKAGE: openssl@1.1 - JPY_PYTHON_HOME_WINDOWS: C:\Python37-x64 JPY_PYTHON_HOME_UBUNTU: /home/appveyor/venv3.7 JPY_MAC_PYTHON_VERSION: 3.7.8 JPY_MAC_OPENSSL_PACKAGE: openssl@1.1 - JPY_PYTHON_HOME_WINDOWS: C:\Python38-x64 JPY_PYTHON_HOME_UBUNTU: /home/appveyor/venv3.8 JPY_MAC_PYTHON_VERSION: 3.8.5 JPY_MAC_OPENSSL_PACKAGE: openssl@1.1 for: - matrix: only: - image: Visual Studio 2015 environment: JDK11_X64: C:\Program Files\Java\jdk-11 JPY_JDK_HOME: '%JDK11_X64%' install: - set JDK_HOME=%JPY_JDK_HOME% # JAVA_HOME is used by Maven - set JAVA_HOME=%JPY_JDK_HOME% - set - call "%JPY_JDK_HOME%\bin\java.exe" -XshowSettings:properties -version - call "%JPY_PYTHON_HOME_WINDOWS%\Scripts\pip.exe" install --upgrade setuptools - call "%JPY_PYTHON_HOME_WINDOWS%\Scripts\pip.exe" install wheel - call "%JPY_PYTHON_HOME_WINDOWS%\Scripts\pip.exe" install psutil - call "%JPY_PYTHON_HOME_WINDOWS%\python.exe" ci\appveyor\dump-dlls.py build_script: - set PATH=%JPY_PYTHON_HOME_WINDOWS%;%JPY_PYTHON_HOME_WINDOWS%\DLLs;%JPY_JDK_HOME%;%JPY_JDK_HOME%\bin;%PATH% - call "%JPY_PYTHON_HOME_WINDOWS%\python.exe" setup.py maven bdist_wheel - move dist win artifacts: - path: 'win\*.whl' name: wheel cache: # see https://www.appveyor.com/docs/build-cache/ - '%MAVEN_HOME% -> pom.xml' - '%UserProfile%\.m2 -> pom.xml' - matrix: only: - image: Ubuntu2004 environment: JDK11_X64: /usr/lib/jvm/java-11-openjdk-amd64 JPY_JDK_HOME: $JDK11_X64 install: - set JDK_HOME=$JPY_JDK_HOME # JAVA_HOME is used by Maven - set JAVA_HOME=$JPY_JDK_HOME - $JPY_JDK_HOME/bin/java -XshowSettings:properties -version build_script: - set PATH=$JPY_PYTHON_HOME_UBUNTU/bin:$JPY_JDK_HOME/bin:$PATH - $JPY_PYTHON_HOME_UBUNTU/bin/python setup.py maven bdist_wheel - mv dist linux artifacts: - path: 'linux/*.whl' name: wheel - matrix: only: - image: macos environment: JPY_PYTHON_HOME_MAC: /Users/appveyor/.pyenv/versions/$JPY_MAC_PYTHON_VERSION JDK11_X64: /Library/Java/JavaVirtualMachines/temurin-11.jdk/Contents/Home/ JPY_JDK_HOME: $JDK11_X64 # See note on deployment targets below. MACOSX_DEPLOYMENT_TARGET: 10.9 install: - set JDK_HOME=$JPY_JDK_HOME # JAVA_HOME is used by Maven - set JAVA_HOME=$JPY_JDK_HOME # In contrast to Ubuntu and Windows, the AppVeyor macOS image does not # have Maven preinstalled. - brew install maven - $JPY_JDK_HOME/bin/java -XshowSettings:properties -version # We want to produce a macOS 10.9 wheel that will be compatible with # versions 10.9 and up -- see # https://github.com/MacPython/wiki/wiki/Spinning-wheels . We can't use the # preinstalled Python for this, since we need to set # MACOSX_DEPLOYMENT_TARGET before installing Python. We set # MACOSX_DEPLOYMENT_TARGET in the "environment" section. Then here we # use brew to install pyenv and pyenv to install a 10.9-targeted Python # environment. # - brew update # The Python 3.4 Mac build is particularly challenging, because Python 3.4 # is incompatible with OpenSSL 1.1, and OpenSSL 1.0 is no longer supported # by homebrew. We install OpenSSL 1.0 by explicitly fetching the 1.0 # formula from an old revision in the homebrew repository and installing # from the downloaded file. Then we set a lot of environment variables to # make sure that pyenv can find the newly installed openssl and uninstall # version 1.1. (For the Python >3.4 builds, OpenSSL 1.1 is then # automatically reinstalled by pyenv.) Most of this configuration can be # removed once we drop Python 3.4 support. # # As of October 2020, "brew install" forbids installation of openssl from # a GitHub commit URL, so we fetch it with curl and install from the file. - curl https://raw.githubusercontent.com/Homebrew/homebrew-core/64555220bfbf4a25598523c2e4d3a232560eaad7/Formula/openssl.rb > openssl.rb # For Python 3.4, $JPY_MAC_OPENSSL_PACKAGE should contain the name of the # downloaded file; for newer Pythons, "openssl@1.1". - brew install -f $JPY_MAC_OPENSSL_PACKAGE - brew install pyenv - export ssldir=/usr/local/opt/openssl - export PKG_CONFIG_PATH="${ssldir}/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" - export LDFLAGS="-L${ssldir}/lib" - export LD_LIBRARY_PATH="${ssldir}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" - export CFLAGS="-I${ssldir}/include" - export CPPFLAGS="-I${ssldir}/include" - brew uninstall --ignore-dependencies openssl@1.1 - export PYTHON_CONFIGURE_OPTS="--enable-shared" - pyenv install $JPY_MAC_PYTHON_VERSION # Set pyenv's python as the global Python environment - pyenv global $JPY_MAC_PYTHON_VERSION - /Users/appveyor/.pyenv/versions/$JPY_MAC_PYTHON_VERSION/bin/pip install wheel - set JPY_PYTHON_HOME_MAC=/Users/appveyor/.pyenv/versions/$JPY_MAC_PYTHON_VERSION - ls $JPY_PYTHON_HOME_MAC build_script: - set PATH=$JPY_PYTHON_HOME_MAC/bin:$JPY_JDK_HOME/bin:$PATH - $JPY_PYTHON_HOME_MAC/bin/python setup.py maven bdist_wheel - mv dist osx artifacts: # Apple call their OS "macOS" these days, but this name has to match the # directory name on the FTP server. - path: 'osx/*.whl' name: wheel deploy: - provider: FTP protocol: ftp host: ftp.brockmann-consult.de username: jpy password: # See https://www.appveyor.com/docs/build-configuration/#secure-variables secure: AMte8IErI/LRGmLGq4Y5YQ== folder: software debug: true jpy-2.0.0/pysobug/0000775000175000017500000000000015202503234014163 5ustar alastairalastairjpy-2.0.0/pysobug/make.sh0000664000175000017500000000076015202503234015437 0ustar alastairalastair#!/usr/bin/env bash set -x #echo on # These are more or less the compiler and linker flags used by Python's distutils.core.setup() CC_FLAGS="-pthread -g -fwrapv -O2 -Wall -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2" LD_FLAGS="-shared -fPIC -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-O1" gcc $CC_FLAGS mypy.c -ldl -o mypy gcc $CC_FLAGS mypydl.c -I/usr/include/python3.4m -lpython3.4m $LD_FLAGS -o mypydl.so python3 setup.py install --user jpy-2.0.0/pysobug/README.txt0000664000175000017500000000403215202503234015660 0ustar alastairalastair ############################# Python shared library problem ############################# Problem: Native Python C-extensions can't find Python symbols if Python interpreter is embedded into a shared library. This problem does not occur on Windows systems. * mypydl.c - Source for a shared libary (mypydl.so) that embeds a Python interpreter * mypy.c - Source for a main program (mypy) that dynamically loads the shared library mypydl.so * mypymod.c - Native Python extension module (mypymod) that uses the Python C-API make.sh - builds mypydl.so, mypy and the mypymod Python extension module. The main program mypy interprets all given arguments as Python code and executes it:: $ bash ./make.sh $ ./mypy "print('Hello')" mypy: executing [print('Hello')] Hello mypy: status 0 Here is how to reproduce the problem:: $ ./mypy "import mypymod" mypy: executing [import mypymod] Traceback (most recent call last): File "", line 1, in ImportError: /home/norman/.local/lib/python3.4/site-packages/mypymod.cpython-34m.so: undefined symbol: Py_BuildValue ... mypy: status -1 Or with numpy:: $ ./mypy "import numpy" mypy: executing [import numpy] ... ImportError: /home/norman/.local/lib/python3.4/site-packages/numpy/core/multiarray.cpython-34m.so: undefined symbol: PyExc_SystemError mypy: status -1 If the Python shared library is explicitly pre-loaded, then these errors don't occur:: $ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.4m.so ./mypy "import mypymod" mypy: executing [import mypymod] PyInit_mypymod: enter PyInit_mypymod: exit: module=0x7f72d34a4188 mypy: status 0 The error also doesn't occur, if I link my Python extension module explicitly against the Python shared library:: extension = Extension('mypymod', sources=['mypymod.c'], libraries=['python3.4m']) setup(name='mypymod', ext_modules=[extension]) Understandably but unfortunately, most Python extensions don't declare the dependency to a specific Python version explicitly.jpy-2.0.0/pysobug/mypymod.c0000664000175000017500000000164015202503234016026 0ustar alastairalastair#include PyObject* id(PyObject* self, PyObject* args); static PyMethodDef functions[] = { {"id", id, METH_VARARGS, "id()"}, {NULL, NULL, 0, NULL} /*Sentinel*/ }; static struct PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "mypymod", /* Name */ "My Python Module", /* Module documentation */ -1, /* Size of per-interpreter state */ functions, NULL, // m_reload NULL, // m_traverse NULL, // m_clear NULL // m_free }; PyMODINIT_FUNC PyInit_mypymod(void) { PyObject* module = NULL; printf("PyInit_mypymod: enter\n"); module = PyModule_Create(&module_def); if (module == NULL) { return NULL; } printf("PyInit_mypymod: exit: module=%p\n", module); return module; } PyObject* id(PyObject* self, PyObject* args) { static int idval = 0; idval++; return Py_BuildValue("i", idval); } jpy-2.0.0/pysobug/mypydl.c0000664000175000017500000000051115202503234015642 0ustar alastairalastair#include int mypy_run(int argc, const char** argv) { const char* script; int i, status; Py_Initialize(); for (i = 0; i < argc; i++) { script = argv[i]; printf("mypy: executing [%s]\n", script); status = PyRun_SimpleString(script); printf("mypy: status %d\n", status); } Py_Finalize(); return 0; } jpy-2.0.0/pysobug/setup.py0000664000175000017500000000047615202503234015704 0ustar alastairalastair#!/usr/bin/env python3 from distutils.core import setup from distutils.extension import Extension # This one works #extension = Extension('mypymod', sources=['mypymod.c'], libraries=['python3.4m']) # This one not extension = Extension('mypymod', sources=['mypymod.c']) setup(name='mypymod', ext_modules=[extension]) jpy-2.0.0/pysobug/.gitignore0000664000175000017500000000002615202503234016151 0ustar alastairalastairbuild *.o *.so mypy jpy-2.0.0/pysobug/mypy.c0000664000175000017500000000120015202503234015316 0ustar alastairalastair#include #include #include typedef int (*mypy_run_fn)(int argc, const char** argv); int main(int argc, const char** argv) { void* handle; mypy_run_fn mypy_run; // This one works handle = dlopen("./mypydl.so", RTLD_LAZY | RTLD_GLOBAL); // This one not //handle = dlopen("./mypydl.so", RTLD_LAZY); if (handle == NULL) { fprintf(stderr, "mypy: error: %s\n", dlerror()); return 1; } mypy_run = (mypy_run_fn) dlsym(handle, "mypy_run"); if (mypy_run == NULL) { fprintf(stderr, "mypy: error: %s\n", dlerror()); return 2; } mypy_run(argc - 1, argv + 1); dlclose(handle); return 0; } jpy-2.0.0/CHANGES.md0000664000175000017500000002700715202503234014073 0ustar alastairalastair# jpy Changelog ## Version 2.0.0 This version bumps the minimum Java requirements from 8 to 11. See [#226](https://github.com/jpy-consortium/jpy/pull/226) for more details. * [#226](https://github.com/jpy-consortium/jpy/pull/226) fix: Fix a race caused by premature-finalization when calling a method on transient PyObjects * [#228](https://github.com/jpy-consortium/jpy/pull/228) Replace Java 23 with 25 in Check CI due to EOL * [#229](https://github.com/jpy-consortium/jpy/pull/229) Update top level documentation * [#231](https://github.com/jpy-consortium/jpy/pull/231) Fix the empty jar issue ## Version 1.4.0 * [#220](https://github.com/jpy-consortium/jpy/pull/220) Use Python built-in exception formating * [#215](https://github.com/jpy-consortium/jpy/pull/215) Bump actions/checkout from 5 to 6 * [#207](https://github.com/jpy-consortium/jpy/pull/207) Bump actions/setup-python from 5 to 6 * [#223](https://github.com/jpy-consortium/jpy/pull/223) Bump docker/setup-buildx-action from 3 to 4 * [#224](https://github.com/jpy-consortium/jpy/pull/224) Bump docker/bake-action from 6.9.0 to 7.1.0 * [#221](https://github.com/jpy-consortium/jpy/pull/221) Bump actions/download-artifact from 4 to 8 * [#222](https://github.com/jpy-consortium/jpy/pull/222) Bump actions/upload-artifact from 4 to 7 * [#225](https://github.com/jpy-consortium/jpy/pull/225) Bump docker/setup-qemu-action from 3 to 4 ## Version 1.3.0 * [#212](https://github.com/jpy-consortium/jpy/pull/212) feat: Add 3.14 builds * [#214](https://github.com/jpy-consortium/jpy/pull/214) Remove 3.6, 3.7, 3.8 from project's metadata and related dead code in the code base ## Version 1.2.0 * [#199](https://github.com/jpy-consortium/jpy/pull/199) Call PyObject_IsTrue() to test truthiness ## Version 1.1.0 * [#179](https://github.com/jpy-consortium/jpy/pull/179) Improve libpython search with python's sysconfig INSTSONAME ## Version 1.0.0 * [#176](https://github.com/jpy-consortium/jpy/pull/176) fix: make PyObject cleanup thread-safe in free-threaded Python and reduce contention * [#175](https://github.com/jpy-consortium/jpy/pull/175) Update project's Development Status classifier ## Version 0.19.0 * [#165](https://github.com/jpy-consortium/jpy/pull/165) feat: free-threaded Python (3.13.0+) support * [#168](https://github.com/jpy-consortium/jpy/pull/168) Add Python 3.13 builds * [#164](https://github.com/jpy-consortium/jpy/pull/164) fix: Make org.jpy.PyLib.getCurrentLocals/Globals work for Python 3.13 * [#162](https://github.com/jpy-consortium/jpy/pull/162) Find Zero-Assembler OpenJDK 21 ## Version 0.18.0 * [#158](https://github.com/jpy-consortium/jpy/pull/158) Get the correct computed tb lineno * [#153](https://github.com/jpy-consortium/jpy/pull/153) Remove pip upgrade from CI * [#150](https://github.com/jpy-consortium/jpy/pull/150) Bump docker/bake-action from 4.5.0 to 4.6.0 ## Version 0.17.0 * [#146](https://github.com/jpy-consortium/jpy/pull/146) Delay resolving super classes until referenced * [#145](https://github.com/jpy-consortium/jpy/pull/145) Use Py_ssize_t when calculate buffer len ## Version 0.16.0 * [#128](https://github.com/jpy-consortium/jpy/pull/128) Function for converting Python values to an explicit Java type * [#132](https://github.com/jpy-consortium/jpy/pull/132) Update auditwheel command to use --exclude * Various CI-related version bumps ## Version 0.15.0 * [#112](https://github.com/jpy-consortium/jpy/pull/112) Add jpy.byte_buffer() function * [#119](https://github.com/jpy-consortium/jpy/pull/119) Fix Mac OSX + OpenJDK builds where `JAVA_HOME` contains `libexec` but not `lib` * [#121](https://github.com/jpy-consortium/jpy/pull/121) Python 3.12 build * [#109](https://github.com/jpy-consortium/jpy/pull/109) Add aarch64 Linux wheels to build / release workflow * [#113](https://github.com/jpy-consortium/jpy/pull/113) Update build.yml actions ## Version 0.14.0 * [#99](https://github.com/jpy-consortium/jpy/pull/99) Check for exception in getInt/Long/DoubleValue() * [#104](https://github.com/jpy-consortium/jpy/pull/104) PyDictWrapper.values() incorrectly close the underlying PyObject while it is still referenced ## Version 0.13.0 * [#96](https://github.com/jpy-consortium/jpy/pull/96) Python 3.11 compatibility ## Version 0.12.0 * [#88](https://github.com/jpy-consortium/jpy/pull/88) Use valueOf() to box primitive values instead of creating new objects every time * [#89](https://github.com/jpy-consortium/jpy/pull/89) Add Java process lookup for 'java.home' in find_jvm_dll_file() * [#85](https://github.com/jpy-consortium/jpy/pull/85) Support creation of zero-sized primitive Java arrays ## Version 0.11.1 * [#79](https://github.com/jpy-consortium/jpy/issues/79) Produce usable / distributable macosx wheels ## Version 0.11.0 * Publish artifacts to [PyPi](https://pypi.org/project/jpy/). Source tarball and binary wheels for Python 3.6 - 3.10 for Linux, Mac, and Windows (x86_64). * Publish release to Maven Central with group id `org.jpyconsortium` and artifact id `jpy`. Java-8 compatible jars. * Many more changes. ## Version 0.10 * Add the ability to pass properties and options to write_config_files. These values get passed to the jvm when it is initialized. [#180](https://github.com/bcdev/jpy/pull/180#issue-513362903) Contribution by davidlehrian. * Make jpy work with Anaconda by setting environment variable `PYTHONHOME` from Java [#143](https://github.com/bcdev/jpy/issues/143). Contribution by Dr-Irv. * Fixed: Constants are not properly passed from Java to Python when using interfaces [#140](https://github.com/bcdev/jpy/issues/140). Contribution by Dr-Irv. * Fixed: Cannot iterate through a dict in Python 3.x [#136](https://github.com/bcdev/jpy/issues/136). Contribution by Dr-Irv. * Automatically build 64-bit Python wheels for all Python versions from 3.4 to 3.8 on Linux, Windows, and Mac (fixes [#174](https://github.com/bcdev/jpy/issues/174)). ## Version 0.9 This version includes a number of contributions from supportive GitHub users. Thanks to all of you! ### Fixes * Corrected Java reference count of complex PyObject passed back and forth to methods (issue #120). Fix by sbarnoud. * Fixed problem where default methods on Java 8 Interfaces were not found (issue #102). Fix by Charles P. Wright. * Fixed error caused by missing `sys.argv` in Python when called from Java (issue #81). Fix by Dave Voutila. * Fixed problem where calling jpy.get_type() too many times causes a memory access error (issue #74). Fix by Dave Voutila. * Fixed a corruption when retrieving long values (#72). Fix by chipkent. * Fixed fatal error when stopping python session (issue #70, #77). Fix by Dave Voutila. * Explicit null checks for avoiding JVM crash (issue #126). Fix by Geomatys. ### Improvements * Can now use pip to install Python `jpy` package directly from GitHub (#83). This works for Linux and OS X where C compilers are available by default and should work on Windows with Visual Studio 15 installed. Contribution by Dave Voutila. * Java `PyObject` is now serializable. Contribution by Mario Briggs. * Improved Varargs method matching. You may pass in either an array (as in the past) or individual Python arguments, the match for a varargs method call is the minimum match for each of the arguments. Zero length arrays (i.e. no arguments) are also permitted with a match value of 10. * `jpy.type_translations` dictionary for callbacks when instantiating Python objects. * `jpy.VerboseExceptions` enables full Java stack traces. * More Python exceptions are translated to the corresponding Java type. * Globals and locals are converted when executing code with PyLib, to allow variables to be used across statement invocation; and interrogated from Java. * PyObject wrappers for dictionary, list, and introspection functions to tell you whether or not you can convert the object. * Support for isAssignable checks when dealing with Python Strings and primitives, to allow matches for argument types such as `java.lang.Comparable` or `java.lang.Number`. ## Version 0.8 ### Fixes * Java interface types don't include methods of extended interfaces (issue #64) * Loading of jpy DLL fails for user-specific Python installations on Windows (issue #58) * Java interface types didn't expose java.lang.Object methods (issue #57) * Java 1-arg static method was confused with a zero-arg non-static method (issue #54) * Python interpreter crash occurred when executing del statement on Java arrays (issue #52) * Python extensions loaded from Java couldn't see Python symbols (Linux) (issue #38) ### Improvements * It is now possible to use jpy Java API to work with multiple Python installations (issue #35). A tool called 'jpyutil.py' can be used to write configuration files that determine the required shared libraries for a given Python versions. A new Java system property 'jpy.config' is used to point to a desired configuration file. * Simplified jpy installation (issue #15): - removed need to add JVM path to PATH (Windows) / LD_LIBRARY_PATH (Unix) environment variable - removed need to compile Java module using Maven - removed need to specify JDK_HOME environment variable, if JAVA_HOME already points to a JDK * Added 'jclass' attribute to Python type that wraps a Java class (issue #63) . * Java API extensions - new `jpy.org.PyObject.executeCode()` methods - new `jpy.org.PyModule.getBuiltins()` method - new `jpy.org.PyModule.getMain()` method - new `jpy.org.PyModule.extendSysPath()` method * Java API configuration changes: - System property `jpy.jpyLib`: - System property `jpy.jdlLib`: - System property `jpy.pythonLib`: - System property `jpy.config`: - Loaded from - File `./jpyconfig.properties` - Resource `/jpyconfig.properties` - File `${jpy.config}` * Python API configuration changes: - Loaded from - File `./jpyconfig.py` - Resource `${jpy-module}/jpyconfig.py` - Attribute java_home - Attribute jvm_dll * Python API extensions - new `jpyutil` module - `jpyutil.init_jvm(...)` - `jpyutil.preload_jvm_lib(...)` - new `jpyutil` tool - usage: `jpyutil.py [-h] [--out OUT] [--java_home JAVA_HOME] [--jvm_dll JVM_DLL]` * Added basic support for Java Scripting Engine API (issue #53) ### Other changes * Switched to Apache 2.0 license from version 0.8 and later (issue #60) ## Version 0.7.5 * Fixed bad pointer in C-code which caused unpredictable crashes (issue #43) ## Version 0.7.4 * Fixed a problem where jpy crashes with unicode arguments (issue #42) * Fixed segmentation fault occurring occasionally during installation of jpy (issue #40) * Improved Java exception messages on Python errors (issue #39) ## Version 0.7.3 * Fixed problem where a Java primitive array argument has occasionally not been initialised by a related Python buffer argument (issue #37) ## Version 0.7.2 * Added backward compatibility with Python 2.7 (issue #34). * Added Java parameter annotation 'output' (issue #36). This is used to optimise passing Python buffer arguments where Java primitive arrays are expected. * Removed debugging prints of the form "JNI_OnLoad: ..." * Corrected documentation of jpy.array(type, init) function, which was said to be jpy.array(type, length) * Removed console dumps that occurred when calling from Java proxies into Python * Updated Java API documentation and added it to Sphinx doc folder (doc/_static/java-apidoc) * Added new diagnostic F_ERR flag to Java class PyLib.Diag * Java class PyLib is no longer instantiable ## Version 0.7.1 * Updated README and added MANIFEST.in after recognising that the jpy-0.7.zip distribution misses most of the required source files and learning what to do on this case. ## Version 0.7 * Initial version. jpy-2.0.0/.gitignore0000664000175000017500000000027115202503234014463 0ustar alastairalastairvenv target build dist MANIFEST .idea *.iml *.jar *.war *.ear __pycache__/ hs_err_pid* winbuild.log local.dat setup.out *.pyc jpy.egg-info lib/ .vagrant Vagrantfile *.so *.dll .vscode/jpy-2.0.0/ci/0000775000175000017500000000000015202503234013066 5ustar alastairalastairjpy-2.0.0/ci/travis/0000775000175000017500000000000015202503234014376 5ustar alastairalastairjpy-2.0.0/ci/travis/upload-to-ftp.sh0000775000175000017500000000040115202503234017423 0ustar alastairalastair#!/usr/bin/env bash function upload_ftp { echo Uploading ${1}: ${2} curl --ftp-create-dirs -T $2 -u "$FTP_USER:$FTP_PASSWORD" "ftp://$FTP_HOST/software/$TRAVIS_OS_NAME/" } echo "success pushing artifacts to FTP..." upload_ftp "wheel" "dist/*.whl" jpy-2.0.0/ci/travis/before_script.sh0000664000175000017500000000220715202503234017561 0ustar alastairalastair#!/usr/bin/env bash if [[ $TRAVIS_OS_NAME == 'osx' ]]; then # Install some custom requirements on OS X # e.g. brew install pyenv-virtualenv # See https://gist.github.com/Bouke/11261620 # and https://github.com/bincrafters/conan-bazel_installer brew update || brew update brew outdated pyenv || brew upgrade pyenv brew install pyenv-virtualenv eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)" pyenv install --list pyenv install --skip-existing $PYTHON_VERSION pyenv virtualenv $PYTHON_VERSION jpy-venv else # Install pyenv # See https://github.com/pyenv/pyenv git clone https://github.com/pyenv/pyenv.git $HOME/.pyenv export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # Install pyenv virtualenv plugin # See https://github.com/pyenv/pyenv-virtualenv git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv eval "$(pyenv virtualenv-init -)" # Create virtualenv from current Python pyenv virtualenv jpy-venv fi pyenv rehash pyenv activate jpy-venv pip install wheel jpy-2.0.0/ci/travis/run.sh0000664000175000017500000000103115202503234015531 0ustar alastairalastair#!/usr/bin/env bash if [[ $TRAVIS_OS_NAME != 'osx' ]]; then export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" fi eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)" pyenv activate jpy-venv python --version # oracle-java8-set-default seems to modify PATH but not JAVA_HOME :( java -version if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export JAVA_HOME=$(/usr/libexec/java_home -v 1.8); else export JAVA_HOME=/usr/lib/jvm/java-8-oracle fi echo $JAVA_HOME python setup.py --maven bdist_wheel jpy-2.0.0/ci/appveyor/0000775000175000017500000000000015202503234014733 5ustar alastairalastairjpy-2.0.0/ci/appveyor/dump-dlls.py0000664000175000017500000000014415202503234017205 0ustar alastairalastairimport psutil, os p = psutil.Process(os.getpid()) for dll in p.memory_maps(): print(dll.path) jpy-2.0.0/MANIFEST.in0000664000175000017500000000052415202503234014232 0ustar alastairalastairinclude *.txt include *.xml include *.md include doc/make.bat include doc/Makefile include lib/*.jar recursive-include doc *.py recursive-include doc *.rst recursive-include src/main/c *.h recursive-include src/main/c *.c recursive-include src/main/java *.java recursive-include src/test/java *.java recursive-include src/test/python *.py jpy-2.0.0/.github/0000775000175000017500000000000015202503234014033 5ustar alastairalastairjpy-2.0.0/.github/workflows/0000775000175000017500000000000015202503234016070 5ustar alastairalastairjpy-2.0.0/.github/workflows/check.yml0000664000175000017500000000364015202503234017673 0ustar alastairalastairname: Check on: pull_request: branches: [ 'master', 'release/v*' ] push: branches: [ 'master', 'release/v*' ] jobs: test: runs-on: ubuntu-22.04 strategy: matrix: python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] java: ['11', '17', '21', '25'] steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python }} - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: ${{ matrix.java }} - run: pip install "setuptools < 72" numpy - name: Run Test run: python setup.py test - name: Upload JVM Error Logs uses: actions/upload-artifact@v7 if: failure() with: name: check-ci-jvm-err path: | **/*_pid*.log **/core.* if-no-files-found: ignore test-free-threaded: runs-on: ubuntu-22.04 strategy: matrix: python: ['3.13t', '3.14t'] java: ['11', '17', '21', '25'] steps: - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: ${{ matrix.java }} - uses: astral-sh/setup-uv@v7 - run: | uv python install ${{ matrix.python }} uv venv --python ${{ matrix.python }} source .venv/bin/activate uv pip install pip echo $JAVA_HOME echo PATH=$PATH >> $GITHUB_PATH - run: pip install "setuptools < 72" numpy - name: Run Free-threaded Test run: python setup.py test - name: Upload JVM Error Logs uses: actions/upload-artifact@v7 if: failure() with: name: check-ci-jvm-err path: | **/*_pid*.log **/core.* if-no-files-found: ignore jpy-2.0.0/.github/workflows/build.yml0000664000175000017500000002222215202503234017712 0ustar alastairalastairname: Build JPY distributions on: pull_request: branches: [ 'master', 'release/v*' ] push: branches: [ 'master', 'release/v*' ] jobs: jars: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '11' - run: mvn --batch-mode clean package javadoc:jar source:jar-no-fork -DskipTests - uses: actions/upload-artifact@v7 with: name: jars path: target sdist: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 # TODO: sdist shouldn't need java - uses: actions/setup-java@v5 id: setup-java with: distribution: 'temurin' java-version: '11' - run: python setup.py sdist - uses: actions/upload-artifact@v7 with: name: sdist path: dist bdist-wheel: runs-on: ${{ matrix.info.machine }} strategy: fail-fast: false matrix: info: # Note: the free-threaded Linux wheels are not currently buildable via the docker bake workflow due to the # limitations of the current image (docker.io/library/python:X doesn't provide free-threaded versions). Using # the oldest available GitHub Linux runner image (ubuntu-22.04 / ubuntu-22.04-arm), we are able to use get # the free-threaded builds via actions/setup-python. This produces a manylinux_2_34 compatible wheel (as # opposed to manylinux_2_17 produced via the docker bake process). This is a compromise that seems reasonable # given the more experimental nature of the free-threaded builds, coupled with the fact that python 3.13t is # less likely to be used on older Linux distributions. - { machine: 'ubuntu-22.04', python: '3.13t', java: '11', cmd: '.github/env/Linux/bdist-wheel.sh' } - { machine: 'ubuntu-22.04-arm', python: '3.13t', java: '11', cmd: '.github/env/Linux/bdist-wheel.sh' } - { machine: 'ubuntu-22.04', python: '3.14t', java: '11',cmd: '.github/env/Linux/bdist-wheel.sh' } - { machine: 'ubuntu-22.04-arm', python: '3.14t', java: '11', cmd: '.github/env/Linux/bdist-wheel.sh' } - { machine: 'windows-2022', python: '3.9', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.10', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.11', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.12', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.13', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.13t', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.14', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'windows-2022', python: '3.14t', java: '11', cmd: '.\.github\env\Windows\bdist-wheel.ps1' } - { machine: 'macos-14', python: '3.11', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.12', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.13', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.13t', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.14', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.14t', java: '11', cmd: '.github/env/macOS/bdist-wheel.sh' } steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: ${{ matrix.info.python }} - uses: actions/setup-java@v5 id: setup-java with: distribution: 'temurin' java-version: ${{ matrix.info.java }} - run: ${{ matrix.info.cmd }} - uses: actions/upload-artifact@v7 with: name: build-${{ matrix.info.python }}-${{ matrix.info.machine }} path: dist/*.whl retention-days: 1 # Python 3.6, 3.7 doesn't support M1 support at all. # Python 3.11, 3.12 builds have universal2 defaults: # https://github.com/actions/runner-images/issues/4133 # Python 3.9 builds _had_ universal2 defaults for a short period of time before it was reverted: # https://github.com/actions/python-versions/pull/114, https://github.com/actions/python-versions/pull/175 # Regardless, we can explicitly instruct python at compile time to create universal2 packages: # https://github.com/pypa/cibuildwheel/blob/v2.16.2/cibuildwheel/macos.py#L247-L260 # Note: using macosx-11.0-universal2 instead of macosx-10.9-universal2, as the build warns otherwise that the version # was bumped to 11.0 because the underlying python build has a minimum of 11.0. # There might be a better solution for this in the future: # https://github.com/actions/setup-python/issues/547 bdist-wheel-universal2-hack: runs-on: ${{ matrix.info.machine }} env: _PYTHON_HOST_PLATFORM: macosx-11.0-universal2 ARCHFLAGS: -arch arm64 -arch x86_64 strategy: fail-fast: false matrix: info: - { machine: 'macos-14', python: '3.9', cmd: '.github/env/macOS/bdist-wheel.sh' } - { machine: 'macos-14', python: '3.10', cmd: '.github/env/macOS/bdist-wheel.sh' } steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: ${{ matrix.info.python }} - uses: actions/setup-java@v5 id: setup-java with: distribution: 'temurin' java-version: '11' - run: pip install --upgrade pip - run: pip install --upgrade setuptools - run: ${{ matrix.info.cmd }} - uses: actions/upload-artifact@v7 with: name: build-${{ matrix.info.python }}-${{ matrix.info.machine }} path: dist/*.whl retention-days: 1 bdist-wheel-linux-arm64: runs-on: 'ubuntu-24.04-arm' steps: - uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - run: mkdir /tmp/dist - name: Bake uses: docker/bake-action@v7.1.0 env: DOCKER_BUILD_RECORD_UPLOAD: false with: files: .github/docker/docker-bake.hcl pull: true set: | *.output=type=local,dest=/tmp/dist - uses: actions/upload-artifact@v7 with: name: bdist-wheel-linux-arm64 path: /tmp/dist/*.whl retention-days: 1 bdist-wheel-linux-amd64: runs-on: 'ubuntu-24.04' steps: - uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - run: mkdir /tmp/dist - name: Bake uses: docker/bake-action@v7.1.0 env: DOCKER_BUILD_RECORD_UPLOAD: false with: files: .github/docker/docker-bake.hcl pull: true set: | *.output=type=local,dest=/tmp/dist - uses: actions/upload-artifact@v7 with: name: bdist-wheel-linux-amd64 path: /tmp/dist/*.whl retention-days: 1 collect-artifacts: runs-on: ubuntu-24.04 needs: ['sdist', 'bdist-wheel', 'bdist-wheel-universal2-hack', 'bdist-wheel-linux-amd64', 'bdist-wheel-linux-arm64'] steps: - uses: actions/checkout@v6 - uses: actions/download-artifact@v8 with: path: download-artifacts - name: collect-artifacts.sh run: .github/scripts/collect-artifacts.sh - uses: actions/upload-artifact@v7 with: name: jpy path: collect-artifacts release-artifacts: if: ${{ startsWith(github.ref, 'refs/heads/release/v') }} runs-on: ubuntu-24.04 needs: ['collect-artifacts'] steps: - uses: actions/download-artifact@v8 with: name: jpy path: dist - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} publish-jar: if: ${{ startsWith(github.ref, 'refs/heads/release/v') }} runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '11' server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg-passphrase: GPG_PASSPHRASE - name: Publish package run: mvn --batch-mode deploy -Pjpy-maven-deploy -DskipTests env: MAVEN_USERNAME: ${{ secrets.CENTRAL_USERNAME }} MAVEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}jpy-2.0.0/.github/requirements.txt0000664000175000017500000000000615202503234017313 0ustar alastairalastairwheel jpy-2.0.0/.github/env/0000775000175000017500000000000015202503234014623 5ustar alastairalastairjpy-2.0.0/.github/env/Linux/0000775000175000017500000000000015202503234015722 5ustar alastairalastairjpy-2.0.0/.github/env/Linux/requirements.txt0000664000175000017500000000023515202503234021206 0ustar alastairalastairsetuptools wheel auditwheel>=6.4.2; python_version >= '3.14' auditwheel>=6.1.0; python_version >= '3.13' auditwheel>=6.0.0; python_version >= '3.9' patchelf jpy-2.0.0/.github/env/Linux/bdist-wheel.sh0000775000175000017500000000071315202503234020471 0ustar alastairalastair#!/usr/bin/env bash set -o errexit set -o pipefail set -o nounset __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" python -m pip install -r "${__dir}/requirements.txt" # Can add --build-number if necessary python setup.py bdist_wheel --dist-dir dist.linux # Note: auditwheel only works with a single file argument - we are relying on finding exactly one wheel auditwheel \ repair \ --exclude libjvm.so \ --wheel-dir dist/ \ dist.linux/* jpy-2.0.0/.github/env/macOS/0000775000175000017500000000000015202503234015625 5ustar alastairalastairjpy-2.0.0/.github/env/macOS/requirements.txt0000664000175000017500000000002115202503234021102 0ustar alastairalastairsetuptools wheel jpy-2.0.0/.github/env/macOS/bdist-wheel.sh0000775000175000017500000000023415202503234020372 0ustar alastairalastair#!/usr/bin/env bash set -o errexit set -o pipefail set -o nounset python -m pip install -r .github/env/macOS/requirements.txt python setup.py bdist_wheel jpy-2.0.0/.github/env/Windows/0000775000175000017500000000000015202503234016255 5ustar alastairalastairjpy-2.0.0/.github/env/Windows/requirements.txt0000664000175000017500000000002115202503234021532 0ustar alastairalastairsetuptools wheel jpy-2.0.0/.github/env/Windows/bdist-wheel.ps10000775000175000017500000000013215202503234021110 0ustar alastairalastairpython -m pip install -r .github/env/Windows/requirements.txt python setup.py bdist_wheel jpy-2.0.0/.github/scripts/0000775000175000017500000000000015202503234015522 5ustar alastairalastairjpy-2.0.0/.github/scripts/collect-artifacts.sh0000775000175000017500000000034215202503234021463 0ustar alastairalastair#!/usr/bin/env bash set -o errexit set -o pipefail set -o nounset mkdir collect-artifacts find download-artifacts -type f mv download-artifacts/*/*.whl collect-artifacts/ mv download-artifacts/*/*.tar.gz collect-artifacts/ jpy-2.0.0/.github/docker/0000775000175000017500000000000015202503234015302 5ustar alastairalastairjpy-2.0.0/.github/docker/docker-bake.hcl0000664000175000017500000000301215202503234020135 0ustar alastairalastairgroup "default" { targets = [ "python-39-linux", "python-310-linux", "python-311-linux", "python-312-linux", "python-313-linux", "python-314-linux" ] } variable "DEBIAN_BASE" { # bullseye: manylinux2014 / manylinux_2_17 # bookworm: manylinux_2_34 # trixie: Unable to build on Debian trixie, https://github.com/jpy-consortium/jpy/issues/202 default = "bullseye" } variable "GITHUB_ACTIONS" { default = false } target "shared" { dockerfile = ".github/docker/Dockerfile" cache-from = [ GITHUB_ACTIONS ? "type=gha,scope=jpy-build" : "" ] cache-to = [ GITHUB_ACTIONS ? "type=gha,mode=max,scope=jpy-build" : "" ] } target "python-39-linux" { inherits = [ "shared" ] args = { PYTHON_TAG = "3.9-${DEBIAN_BASE}" } } target "python-310-linux" { inherits = [ "shared" ] args = { PYTHON_TAG = "3.10-${DEBIAN_BASE}" } } target "python-311-linux" { inherits = [ "shared" ] args = { PYTHON_TAG = "3.11-${DEBIAN_BASE}" } } target "python-312-linux" { inherits = [ "shared" ] args = { PYTHON_TAG = "3.12-${DEBIAN_BASE}" } } target "python-313-linux" { inherits = [ "shared" ] args = { PYTHON_TAG = "3.13-${DEBIAN_BASE}" } } target "python-314-linux" { inherits = [ "shared" ] args = { DEBIAN_BASE = "bookworm" # For Python 3.14, only Debian Bookworm and above is supported PYTHON_TAG = "3.14-bookworm" } } jpy-2.0.0/.github/docker/README.md0000664000175000017500000000110615202503234016557 0ustar alastairalastair# docker buildx bake This is an alternative way to build the jpy Linux wheels. 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. That said, it can be useful locally too; either to produce native or arm64 Linux wheels. ```bash docker buildx bake \ --file .github/docker/docker-bake.hcl \ --set "*.output=type=local,dest=/tmp/dist" ``` ```bash docker buildx bake \ --file .github/docker/docker-bake.hcl \ --set "*.output=type=local,dest=/tmp/dist" \ --set "*.platform=linux/arm64/v8" ``` jpy-2.0.0/.github/docker/Dockerfile0000664000175000017500000000277415202503234017306 0ustar alastairalastair# syntax=docker/dockerfile:1.4 ARG PYTHON_TAG FROM python:${PYTHON_TAG} as build ARG DEBIAN_FRONTEND="noninteractive" ARG TARGETARCH ARG PYTHON_TAG RUN \ --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-${TARGETARCH}-${PYTHON_TAG} \ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=apt-${TARGETARCH}-${PYTHON_TAG} \ set -eux; \ rm -f /etc/apt/apt.conf.d/docker-clean; \ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache; \ wget --no-hsts -O /usr/share/keyrings/adoptium.asc https://packages.adoptium.net/artifactory/api/gpg/key/public; \ 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; \ apt-get -qq update; \ apt-get -qq -y --no-install-recommends install temurin-11-jdk maven RUN \ --mount=type=cache,target=/root/.cache/pip,sharing=locked \ set -eux; \ python -m venv /jpy-build-venv; \ /jpy-build-venv/bin/pip install --upgrade pip setuptools; \ /jpy-build-venv/bin/pip install --only-binary=:all: wheel patchelf ENV JAVA_HOME=/usr/lib/jvm/temurin-11-jdk-${TARGETARCH} \ VIRTUAL_ENV='/jpy-build-venv' \ PATH="/jpy-build-venv/bin:${PATH}" \ CI=true COPY --link . /jpy RUN \ --mount=type=cache,target=/root/.m2,sharing=locked \ cd /jpy; \ .github/env/Linux/bdist-wheel.sh FROM scratch COPY --link --from=build /jpy/dist/* . jpy-2.0.0/.github/dependabot.yml0000664000175000017500000000016615202503234016666 0ustar alastairalastairversion: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"