Skip to content

Commit

Permalink
Merge pull request #1214 from astrelsky/jarray
Browse files Browse the repository at this point in the history
"Generic" JArray support
  • Loading branch information
marscher authored Oct 2, 2024
2 parents 3bb9473 + 4e3b6d8 commit a566bae
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 2 deletions.
4 changes: 4 additions & 0 deletions doc/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o
Latest Changes:

- **1.5.1_dev0 - 2023-12-15**

- Added support for typing ``JArray`` (Java type only), e.g. ``JArray[java.lang.Object]`` ``"JArray[java.lang.Object]"``

- Fixed uncaught exception while setting traceback causing issues in Python 3.11/3.12.

- Use PEP-518 and PEP-660 configuration for the package, allowing editable and
configurable builds using modern Python packaging tooling.
Where before ``python setup.py --enable-tracing develop``, now can be done with
Expand Down
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
12 changes: 12 additions & 0 deletions jpype/_jarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,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, str, etc is not allowed
key = _jpype._java_lang_Class
if isinstance(key, _jpype._java_lang_Class):
key = _jpype.JClass(key)
if isinstance(key, _jpype.JClass):
return type(key[0])
raise TypeError("%s is not a Java class" % key)


class _JArrayProto(object):

Expand Down
60 changes: 60 additions & 0 deletions jpype/_jarray.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# *****************************************************************************
#
# 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.
#
# See NOTICE file for details.
#
# *****************************************************************************
import typing


__all__ = ['JArray']


T = typing.TypeVar('T')
U = typing.TypeVar('U')


class JArray(typing.Generic[T]):

def __new__(cls, tp: typing.Type[T], dims=1) -> JArray[T]:
...

@classmethod
def of(cls, array, dtype: typing.Optional[typing.Type[U]] = None) -> JArray[U]:
...

def __len__(self) -> int:
...

def __iter__(self) -> typing.Iterator[T]:
...

def __reversed__(self) -> typing.Iterator[T]:
...

@typing.overload
def __getitem__(self, key: int) -> T:
...

@typing.overload
def __getitem__(self, key: slice) -> JArray[T]:
...

@typing.overload
def __setitem__(self, index: int, value: T):
...

@typing.overload
def __setitem__(self, index: slice, value: typing.Sequence[T]):
...
4 changes: 4 additions & 0 deletions jpype/_jclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def __new__(cls, jc, loader=None, initialize=True):

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

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
11 changes: 9 additions & 2 deletions native/python/pyjp_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,8 +747,15 @@ static PyObject *PyJPClass_array(PyJPClass *self, PyObject *item)

if (self->m_Class == NULL)
{
PyErr_Format(PyExc_TypeError, "Cannot instantiate unspecified array type");
return NULL;
// This is only reachable through an internal Jpype type that doesn't have a
// Java class equivalent such as JArray.
// __getitem__ takes precedence over __class_getitem__ which forces us through
// this path.
// If __class_getitem__ is not implemented in this class, the raised AttributeError
// will make its way back to Python.
PyObject *res = PyObject_CallMethod((PyObject *)self, "__class_getitem__", "O", item);
Py_DECREF(item);
return res;
}

if (PyIndex_Check(item))
Expand Down
18 changes: 18 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,20 @@ def testShortcut(self):
def testJArrayIndex(self):
with self.assertRaises(TypeError):
jpype.JArray[10]

def testJArrayGeneric(self):
self.assertEqual(type(JObject[1]), 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]

def testJArrayGenericJClass(self):
self.assertEqual(type(JClass[0]), JArray[JClass])

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

0 comments on commit a566bae

Please sign in to comment.