Skip to content

Commit

Permalink
"Generic" JArray support
Browse files Browse the repository at this point in the history
  • Loading branch information
astrelsky committed Sep 1, 2024
1 parent 66f8c6c commit f1486ad
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 3 deletions.
1 change: 1 addition & 0 deletions jpype/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ def initializeResources():
_jpype._type_classes[object] = _jpype._java_lang_Object
_jpype._type_classes[_jpype.JString] = _jpype._java_lang_String
_jpype._type_classes[_jpype.JObject] = _jpype._java_lang_Object
_jpype._type_classes[_jpype.JClass] = _jpype._java_lang_Class
_jinit.runJVMInitializers()

_jpype.JClass('org.jpype.JPypeKeywords').setKeywords(
Expand Down
38 changes: 37 additions & 1 deletion jpype/_jarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,34 @@
# See NOTICE file for details.
#
# *****************************************************************************
import abc
import collections.abc
import _jpype
import typing
from . import _jcustomizer


__all__ = ['JArray']


class JArray(_jpype._JObject, internal=True): # type: ignore[call-arg]
T = typing.TypeVar('T')


class _JArrayGeneric(collections.abc.Sequence[T]):

@abc.abstractmethod
def __setitem__(self, index, value):
...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.


if typing.TYPE_CHECKING:
_array_base = _JArrayGeneric
else:
# NOTE: the subscript operator needs to be used in the class definition for typing
_array_base = {T : _jpype._JObject}


class JArray(_array_base[T], internal=True): # type: ignore[call-arg]
""" Creates a Java array class for a Java type of a given dimension.
This serves as a base type and factory for all Java array classes.
Expand Down Expand Up @@ -92,6 +112,18 @@ def __new__(cls, tp, dims=1):
def of(cls, array, dtype=None):
return _jpype.arrayFromBuffer(array, dtype)

def __class_getitem__(cls, key):
if key is _jpype.JClass:
# explicit check for JClass
# _toJavaClass cannot be used
# passing int, float, etc is not allowed
key = _jpype._java_lang_Class
if isinstance(key, (str, _jpype._java_lang_Class)):
key = _jpype.JClass(key)
if isinstance(key, _jpype.JClass):
return type(key[0])
raise TypeError("Cannot instantiate unspecified array type")


class _JArrayProto(object):

Expand All @@ -104,6 +136,10 @@ def __iter__(self):
def __reversed__(self):
for elem in self[::-1]:
yield elem

def __contains__(self, item):
# "in" works without this but this should be more efficient
return _jpype.JClass("java.util.Arrays").asList(self).contains(item)

def clone(self):
""" Clone the Java array.
Expand Down
5 changes: 5 additions & 0 deletions jpype/_jclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ def __new__(cls, jc, loader=None, initialize=True):

# Pass to class factory to create the type
return _jpype._getClass(jc)

@classmethod
def __class_getitem__(cls, index):
# enables JClass[1] to get a Class[]
return JClass("java.lang.Class")[index]


class JInterface(_jpype._JObject, internal=True): # type: ignore[call-arg]
Expand Down
5 changes: 3 additions & 2 deletions native/python/pyjp_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,9 @@ static PyObject *PyJPClass_array(PyJPClass *self, PyObject *item)

if (self->m_Class == NULL)
{
PyErr_Format(PyExc_TypeError, "Cannot instantiate unspecified array type");
return NULL;
PyObject *res = PyObject_CallMethod((PyObject *)self, "__class_getitem__", "O", item);
Py_DECREF(item);
return res;
}

if (PyIndex_Check(item))
Expand Down
12 changes: 12 additions & 0 deletions test/jpypetest/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ def testShortcut(self):
# Check Objects
self.assertEqual(JString[5].getClass(), JArray(JString)(5).getClass())
self.assertEqual(JObject[5].getClass(), JArray(JObject)(5).getClass())
self.assertEqual(JClass[5].getClass(), JArray(JClass)(5).getClass())

# Test multidimensional
self.assertEqual(JDouble[5, 5].getClass(), JArray(JDouble, 2)(5).getClass())
Expand All @@ -601,3 +602,14 @@ def testShortcut(self):
def testJArrayIndex(self):
with self.assertRaises(TypeError):
jpype.JArray[10]

def testJArrayGeneric(self):
self.assertEqual(type(JObject[0]), JArray(JObject))

def testJArrayGeneric_Init(self):
Arrays = JClass("java.util.Arrays")
self.assertTrue(Arrays.equals(JObject[0], JArray(JObject)(0)))

def testJArrayInvalidGeneric(self):
with self.assertRaises(TypeError):
jpype.JArray[object]

0 comments on commit f1486ad

Please sign in to comment.