jpy-2.0.0/ 0000775 0001750 0001750 00000000000 15202503243 012473 5 ustar alastair alastair jpy-2.0.0/setup.cfg 0000664 0001750 0001750 00000000156 15202503234 014316 0 ustar alastair alastair [global]
verbose = 1
[build]
force = 1
[install]
record = setup.out
[metadata]
description-file = README.md jpy-2.0.0/pom.xml 0000664 0001750 0001750 00000016424 15202503234 014017 0 ustar alastair alastair
4.0.0UTF-8org.jpyconsortiumjpy2.0.0jarJava-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.0http://www.apache.org/licenses/LICENSE-2.0.txthttps://github.com/jpy-consortium/jpy2014Brockmann Consult GmbHhttp://www.brockmann-consult.deNorman Fomferranorman.fomferra@brockmann-consult.dehttps://github.com/formanBrockmann Consult GmbHhttp://www.brockmann-consult.deDave Voutilahttps://github.com/voutiladMario Briggshttps://github.com/mariobriggschipkenthttps://github.com/chipkentscm:git:git@github.com:jpy-consortium/jpy.gitscm:git:git@github.com:jpy-consortium/jpy.gitgit@github.com:jpy-consortium/jpy.gitGitHubhttps://github.com/jpy-consortium/jpy/issuesjunitjunit4.13.2testmaven-compiler-plugin3.10.11111truefalseUTF-8maven-javadoc-plugin3.4.0${project.basedir}/doc/_staticjava-apidocs${project.name} ${project.version} Java API${project.name} ${project.version} Java APIorg.jpy.annotationsUTF-8256mfalsefalsetrueorg.jpy.jsr223:org.jpy.annotations
https://docs.oracle.com/en/java/javase/11/docs/api/
org.apache.maven.wagonwagon-ssh1.0-beta-7jpy-maven-deploymaven-source-plugin3.2.1attach-sourcesjarmaven-javadoc-plugin3.4.0attach-javadocsjarorg.apache.maven.pluginsmaven-gpg-plugin3.0.1sign-artifactsverifysignorg.sonatype.centralcentral-publishing-maven-plugin0.8.0truecentral
jpy-2.0.0/CODE_OF_CONDUCT.md 0000664 0001750 0001750 00000012622 15202503234 015275 0 ustar alastair alastair
# 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/ 0000775 0001750 0001750 00000000000 15202503234 013262 5 ustar alastair alastair jpy-2.0.0/src/test/ 0000775 0001750 0001750 00000000000 15202503234 014241 5 ustar alastair alastair jpy-2.0.0/src/test/resources/ 0000775 0001750 0001750 00000000000 15202503234 016253 5 ustar alastair alastair jpy-2.0.0/src/test/resources/META-INF/ 0000775 0001750 0001750 00000000000 15202503234 017413 5 ustar alastair alastair jpy-2.0.0/src/test/resources/META-INF/services/ 0000775 0001750 0001750 00000000000 15202503234 021236 5 ustar alastair alastair jpy-2.0.0/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory 0000664 0001750 0001750 00000000046 15202503234 027516 0 ustar alastair alastair org.jpy.jsr223.ScriptEngineFactoryImpl jpy-2.0.0/src/test/resources/pymodules/ 0000775 0001750 0001750 00000000000 15202503234 020274 5 ustar alastair alastair jpy-2.0.0/src/test/resources/pymodules/mod_1.py 0000664 0001750 0001750 00000000013 15202503234 021637 0 ustar alastair alastair answer = 42 jpy-2.0.0/src/test/python/ 0000775 0001750 0001750 00000000000 15202503234 015562 5 ustar alastair alastair jpy-2.0.0/src/test/python/jpy_typeres_test.py 0000664 0001750 0001750 00000004500 15202503234 021547 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000001416 15202503234 023124 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000002441 15202503234 021012 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000023115 15202503234 021672 0 ustar alastair alastair # 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.py 0000664 0001750 0001750 00000002376 15202503234 020771 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000010634 15202503234 021144 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000014613 15202503234 020507 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000002366 15202503234 022017 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000004350 15202503234 023131 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000046425 15202503234 021737 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000002016 15202503234 020474 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000002340 15202503234 022412 0 ustar alastair alastair # 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.py 0000664 0001750 0001750 00000021671 15202503234 022377 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000021562 15202503234 023663 0 ustar alastair alastair """
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.py 0000664 0001750 0001750 00000007651 15202503234 021547 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000007762 15202503234 021366 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000014460 15202503234 022060 0 ustar alastair alastair # 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/ 0000775 0001750 0001750 00000000000 15202503234 017433 5 ustar alastair alastair jpy-2.0.0/src/test/python/fixtures/proxy_test_classes.py 0000664 0001750 0001750 00000002220 15202503234 023736 0 ustar alastair alastair class 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 0 jpy-2.0.0/src/test/python/fixtures/proc_class.py 0000664 0001750 0001750 00000001077 15202503234 022142 0 ustar alastair alastair class 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.py 0000664 0001750 0001750 00000001254 15202503234 022011 0 ustar alastair alastair class 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.py 0000664 0001750 0001750 00000002034 15202503234 023147 0 ustar alastair alastair class 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 -1 jpy-2.0.0/src/test/python/fixtures/raise_errors.py 0000664 0001750 0001750 00000000124 15202503234 022501 0 ustar alastair alastair def raise_if_zero(arg):
if not arg:
raise IndexError("arg wasn't there") jpy-2.0.0/src/test/python/fixtures/proc_module.py 0000664 0001750 0001750 00000000765 15202503234 022325 0 ustar alastair alastair
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.py 0000664 0001750 0001750 00000007351 15202503234 022516 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000022606 15202503234 021201 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000001270 15202503234 020627 0 ustar alastair alastair import 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.py 0000664 0001750 0001750 00000001503 15202503234 022056 0 ustar alastair alastair import 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/ 0000775 0001750 0001750 00000000000 15202503234 016347 5 ustar alastair alastair jpy-2.0.0/src/test/python/imp/import_ex1.py 0000664 0001750 0001750 00000007702 15202503234 021016 0 ustar alastair alastair """
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.txt 0000664 0001750 0001750 00000010024 15202503234 021527 0 ustar alastair alastair java.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.txt 0000664 0001750 0001750 00000010212 15202503234 021527 0 ustar alastair alastair java.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.py 0000664 0001750 0001750 00000004502 15202503234 021012 0 ustar alastair alastair """
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.py 0000664 0001750 0001750 00000010641 15202503234 023727 0 ustar alastair alastair import 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/ 0000775 0001750 0001750 00000000000 15202503234 015162 5 ustar alastair alastair jpy-2.0.0/src/test/java/org/ 0000775 0001750 0001750 00000000000 15202503234 015751 5 ustar alastair alastair jpy-2.0.0/src/test/java/org/jpy/ 0000775 0001750 0001750 00000000000 15202503234 016553 5 ustar alastair alastair jpy-2.0.0/src/test/java/org/jpy/EmbeddableTest.java 0000664 0001750 0001750 00000005646 15202503234 022275 0 ustar alastair alastair package 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/ 0000775 0001750 0001750 00000000000 15202503234 017600 5 ustar alastair alastair jpy-2.0.0/src/test/java/org/jpy/jsr223/Jsr223Test.java 0000664 0001750 0001750 00000004371 15202503234 022275 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002260 15202503234 022702 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000010225 15202503234 021131 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002267 15202503234 022124 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000052643 15202503234 022007 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001401 15202503234 023270 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000012207 15202503234 023154 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000016727 15202503234 021725 0 ustar alastair alastair package 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/ 0000775 0001750 0001750 00000000000 15202503234 020424 5 ustar alastair alastair jpy-2.0.0/src/test/java/org/jpy/fixtures/CyclicReferenceChild2.java 0000664 0001750 0001750 00000000647 15202503234 025351 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000016565 15202503234 027054 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000000435 15202503234 026172 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000007104 15202503234 027115 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002250 15202503234 027040 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000001734 15202503234 026710 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002603 15202503234 022341 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000007402 15202503234 025046 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001644 15202503234 023253 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002564 15202503234 026474 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000010123 15202503234 026407 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000004054 15202503234 025437 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001121 15202503234 026435 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000000221 15202503234 025351 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000001122 15202503234 024361 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000002301 15202503234 027114 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000000677 15202503234 025344 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000002072 15202503234 026637 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001016 15202503234 025122 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000001147 15202503234 025471 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000002304 15202503234 030267 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000004336 15202503234 030602 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002161 15202503234 026503 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000003656 15202503234 024533 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001723 15202503234 027530 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000003021 15202503234 027513 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000001513 15202503234 025341 0 ustar alastair alastair //
// 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.java 0000664 0001750 0001750 00000004655 15202503234 023457 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000006374 15202503234 022026 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000023520 15202503234 021277 0 ustar alastair alastair /*
* 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/ 0000775 0001750 0001750 00000000000 15202503234 014206 5 ustar alastair alastair jpy-2.0.0/src/main/c/ 0000775 0001750 0001750 00000000000 15202503234 014430 5 ustar alastair alastair jpy-2.0.0/src/main/c/jpy_jarray.h 0000664 0001750 0001750 00000003235 15202503234 016756 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000074353 15202503234 016416 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000151553 15202503234 016755 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000015155 15202503234 016432 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000007022 15202503234 020335 0 ustar alastair alastair /*
* 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/ 0000775 0001750 0001750 00000000000 15202503234 015210 5 ustar alastair alastair jpy-2.0.0/src/main/c/jni/org_jpy_PyLib.c 0000664 0001750 0001750 00000231537 15202503234 020137 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000005630 15202503234 017410 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000001711 15202503234 017411 0 ustar alastair alastair /* 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.h 0000664 0001750 0001750 00000002201 15202503234 021050 0 ustar alastair alastair /* 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.h 0000664 0001750 0001750 00000000436 15202503234 022521 0 ustar alastair alastair /* 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.h 0000664 0001750 0001750 00000024525 15202503234 020141 0 ustar alastair alastair /* 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.h 0000664 0001750 0001750 00000001716 15202503234 020346 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000317647 15202503234 016633 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000115457 15202503234 017125 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000025026 15202503234 016753 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000003100 15202503234 016740 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000011321 15202503234 016614 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000006465 15202503234 017130 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000020414 15202503234 016751 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000007056 15202503234 016440 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000010657 15202503234 016724 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000002264 15202503234 020135 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000001446 15202503234 020131 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000002660 15202503234 016724 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000012721 15202503234 016365 0 ustar alastair alastair /*
* 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.c 0000664 0001750 0001750 00000001167 15202503234 016746 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000002634 15202503234 016374 0 ustar alastair alastair /*
* 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.h 0000664 0001750 0001750 00000002405 15202503234 016410 0 ustar alastair alastair /*
* 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/ 0000775 0001750 0001750 00000000000 15202503234 015127 5 ustar alastair alastair jpy-2.0.0/src/main/java/org/ 0000775 0001750 0001750 00000000000 15202503234 015716 5 ustar alastair alastair jpy-2.0.0/src/main/java/org/jpy/ 0000775 0001750 0001750 00000000000 15202503234 016520 5 ustar alastair alastair jpy-2.0.0/src/main/java/org/jpy/PyLibInitializer.java 0000664 0001750 0001750 00000005710 15202503234 022611 0 ustar alastair alastair package 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/ 0000775 0001750 0001750 00000000000 15202503234 021055 5 ustar alastair alastair jpy-2.0.0/src/main/java/org/jpy/annotations/Return.java 0000664 0001750 0001750 00000002126 15202503234 023200 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002155 15202503234 023223 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002136 15202503234 023313 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000000767 15202503234 022334 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000010634 15202503234 021125 0 ustar alastair alastair /*
* 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:
*
*
* @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:
*
*
* @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.java 0000664 0001750 0001750 00000003751 15202503234 022111 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000005703 15202503234 017667 0 ustar alastair alastair /*
* 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/ 0000775 0001750 0001750 00000000000 15202503234 017545 5 ustar alastair alastair jpy-2.0.0/src/main/java/org/jpy/jsr223/ScriptEngineFactoryImpl.java 0000664 0001750 0001750 00000023211 15202503234 025153 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000022467 15202503234 023637 0 ustar alastair alastair /*
* 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_SCOPEBindings 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.java 0000664 0001750 0001750 00000016533 15202503234 022130 0 ustar alastair alastair /*
* 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 extends PyObject, ? extends PyObject> 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.java 0000664 0001750 0001750 00000003606 15202503234 021605 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000003710 15202503234 021735 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000001612 15202503234 022167 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000004043 15202503234 021710 0 ustar alastair alastair /*
* 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
*
*
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.
*
*
*
* 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:
*
*
*
* 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.java 0000664 0001750 0001750 00000022105 15202503234 023104 0 ustar alastair alastair package 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 extends PyObject> 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 extends PyObject> 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.java 0000664 0001750 0001750 00000001575 15202503234 021135 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000011125 15202503234 021530 0 ustar alastair alastair /*
* 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.java 0000664 0001750 0001750 00000002553 15202503234 021500 0 ustar alastair alastair package 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.java 0000664 0001750 0001750 00000056615 15202503234 020417 0 ustar alastair alastair /*
* 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:
*
*
Initializes the Python interpreter, if not already running.
*
Prepends any given extra paths to Python's 'sys.path' (e.g. so that 'jpy' can be loaded from isolated directories).
*
Imports the 'jpy' extension module, if not already done.
*
*
* @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 extends T> keyClass);
static native T[] getObjectArrayValue(long pointer, Class extends T> 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 extends T> 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 extends T> 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.java 0000664 0001750 0001750 00000000115 15202503234 022407 0 ustar alastair alastair package org.jpy;
interface PyObjectCleanup {
int threadSafeCleanup();
}
jpy-2.0.0/src/main/java/org/jpy/PyObject.java 0000664 0001750 0001750 00000063005 15202503234 021106 0 ustar alastair alastair /*
* 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 extends T> 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 extends T> 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 extends T> 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.java 0000664 0001750 0001750 00000014671 15202503234 022161 0 ustar alastair alastair /*
* 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 extends PyObject> c) {
boolean result = false;
for (PyObject po : c) {
result |= add(po);
}
return result;
}
@Override
public boolean addAll(int index, Collection extends PyObject> 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.java 0000664 0001750 0001750 00000012562 15202503234 022321 0 ustar alastair alastair /*
* 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.txt 0000664 0001750 0001750 00000026135 15202503234 014325 0 ustar alastair alastair
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.md 0000664 0001750 0001750 00000003665 15202503234 014107 0 ustar alastair alastair # 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/ 0000775 0001750 0001750 00000000000 15202503234 013240 5 ustar alastair alastair jpy-2.0.0/doc/conf.py 0000664 0001750 0001750 00000017770 15202503234 014553 0 ustar alastair alastair #!/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/ 0000775 0001750 0001750 00000000000 15202503234 014666 5 ustar alastair alastair jpy-2.0.0/doc/_static/java-apidocs/ 0000775 0001750 0001750 00000000000 15202503234 017227 5 ustar alastair alastair jpy-2.0.0/doc/_static/java-apidocs/package-list 0000664 0001750 0001750 00000000010 15202503234 021505 0 ustar alastair alastair org.jpy
jpy-2.0.0/doc/_static/java-apidocs/script.js 0000664 0001750 0001750 00000001473 15202503234 021076 0 ustar alastair alastair function 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.html 0000664 0001750 0001750 00000005443 15202503234 021232 0 ustar alastair alastair
Java-Python Bridge 0.10.0-SNAPSHOT Java API
jpy-2.0.0/doc/_static/java-apidocs/org/ 0000775 0001750 0001750 00000000000 15202503234 020016 5 ustar alastair alastair jpy-2.0.0/doc/_static/java-apidocs/org/jpy/ 0000775 0001750 0001750 00000000000 15202503234 020620 5 ustar alastair alastair jpy-2.0.0/doc/_static/java-apidocs/org/jpy/PyInputMode.html 0000664 0001750 0001750 00000044220 15202503234 023725 0 ustar alastair alastair
PyInputMode (Java-Python Bridge 0.10.0-SNAPSHOT Java API)
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.
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.
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
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.
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.
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).
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.
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.
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:
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:
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.
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
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.
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.
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!)
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
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.
Translation of Python StopIteration so that they can be programmatically detected from Java.
Package org.jpy Description
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.
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..
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.
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.
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.
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.
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.
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.
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.
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.
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.
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> T call(Class<T> returnType,
String name,
Class<?>[] paramTypes,
Object[] args)
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)
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.
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).
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.
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.
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.
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.
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.
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.