From 18313e340141f15b5b7da0bd038fd0d7dc571f25 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Sun, 1 Sep 2024 13:32:41 -0400 Subject: [PATCH 1/9] "Generic" JArray support --- jpype/_core.py | 1 + jpype/_jarray.py | 16 ++++++++++++++ jpype/_jarray.pyi | 43 ++++++++++++++++++++++++++++++++++++ jpype/_jclass.py | 5 +++++ native/python/pyjp_class.cpp | 5 +++-- test/jpypetest/test_array.py | 12 ++++++++++ 6 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 jpype/_jarray.pyi diff --git a/jpype/_core.py b/jpype/_core.py index ca1191d01..1bb940289 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -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( diff --git a/jpype/_jarray.py b/jpype/_jarray.py index 34ce2bd86..9510c74cb 100644 --- a/jpype/_jarray.py +++ b/jpype/_jarray.py @@ -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, 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): @@ -104,6 +116,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. diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi new file mode 100644 index 000000000..10b8de728 --- /dev/null +++ b/jpype/_jarray.pyi @@ -0,0 +1,43 @@ +# ***************************************************************************** +# +# 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 abc +import collections.abc +import typing + + +__all__ = ['JArray'] + + +T = typing.TypeVar('T') + + +class _JArrayGeneric(collections.abc.Sequence[T]): + + @abc.abstractmethod + def __setitem__(self, index, value): + ... + + +class JArray(_JArrayGeneric[T], metaclass=abc.ABCMeta): + + def __new__(cls, tp, dims=1): + ... + + @classmethod + def of(cls, array, dtype=None): + ... diff --git a/jpype/_jclass.py b/jpype/_jclass.py index faa905f24..3a93c8a93 100644 --- a/jpype/_jclass.py +++ b/jpype/_jclass.py @@ -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] diff --git a/native/python/pyjp_class.cpp b/native/python/pyjp_class.cpp index fc633e11c..9fb4071a0 100644 --- a/native/python/pyjp_class.cpp +++ b/native/python/pyjp_class.cpp @@ -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)) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 6f0c60855..011b56bf6 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -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()) @@ -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] From c7241233d499fdba5fb5be5cb3784d122ab9ef92 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:46:46 -0400 Subject: [PATCH 2/9] removed _JArrayGeneric in stub and improved test coverage --- jpype/_jarray.py | 6 +----- jpype/_jarray.pyi | 27 +++++++++++++++++++-------- jpype/_jclass.py | 1 - native/python/pyjp_class.cpp | 6 ++++++ test/jpypetest/test_array.py | 14 ++++++++++++-- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/jpype/_jarray.py b/jpype/_jarray.py index 9510c74cb..26c0a1578 100644 --- a/jpype/_jarray.py +++ b/jpype/_jarray.py @@ -102,7 +102,7 @@ def __class_getitem__(cls, key): key = _jpype.JClass(key) if isinstance(key, _jpype.JClass): return type(key[0]) - raise TypeError("Cannot instantiate unspecified array type") + raise TypeError("%s is not a Java class" % key) class _JArrayProto(object): @@ -116,10 +116,6 @@ 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. diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi index 10b8de728..dd79b023f 100644 --- a/jpype/_jarray.pyi +++ b/jpype/_jarray.pyi @@ -15,7 +15,6 @@ # See NOTICE file for details. # # ***************************************************************************** -import abc import collections.abc import typing @@ -26,18 +25,30 @@ __all__ = ['JArray'] T = typing.TypeVar('T') -class _JArrayGeneric(collections.abc.Sequence[T]): +class JArray(collections.abc.Collection[T], collections.abc.Reversible[T]): - @abc.abstractmethod - def __setitem__(self, index, value): + def __new__(cls, tp, dims=1): ... + @classmethod + def of(cls, array, dtype=None): + ... -class JArray(_JArrayGeneric[T], metaclass=abc.ABCMeta): + def __len__(self): + ... - def __new__(cls, tp, dims=1): + def __iter__(self): ... - @classmethod - def of(cls, array, dtype=None): + def __contains__(self, item): + # this is implicitly implemented + ... + + def __reversed__(self): + ... + + def __getitem__(self, key): + ... + + def __setitem__(self, index, value): ... diff --git a/jpype/_jclass.py b/jpype/_jclass.py index 3a93c8a93..e971bb907 100644 --- a/jpype/_jclass.py +++ b/jpype/_jclass.py @@ -98,7 +98,6 @@ 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] diff --git a/native/python/pyjp_class.cpp b/native/python/pyjp_class.cpp index 9fb4071a0..59a96c347 100644 --- a/native/python/pyjp_class.cpp +++ b/native/python/pyjp_class.cpp @@ -728,6 +728,12 @@ static PyObject *PyJPClass_array(PyJPClass *self, PyObject *item) if (self->m_Class == 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; diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 011b56bf6..d84b9c0e6 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -604,12 +604,22 @@ def testJArrayIndex(self): jpype.JArray[10] def testJArrayGeneric(self): - self.assertEqual(type(JObject[0]), JArray(JObject)) + 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))) + 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 testJArrayGenericString(self): + self.assertEqual(type(JClass[0]), JArray["java.lang.Class"]) + + def testJArrayGenericStringInvalid(self): + with self.assertRaises(TypeError): + JArray["foo.bar"] # type: ignore From 3b97cbb6b7c25f0bafe922b3be001e11417b45d2 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:15:43 -0400 Subject: [PATCH 3/9] added changelog entry --- doc/CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 217dd8320..aa78b901a 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -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``. + - 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 From f798c534922a533a4d11091d150b3170b54421a3 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:40:25 -0400 Subject: [PATCH 4/9] update changelog and add type hints to _jarray.pyi --- doc/CHANGELOG.rst | 70 +++++++++++++++++++++++------------------------ jpype/_jarray.pyi | 30 ++++++++++++++------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index aa78b901a..402e1e445 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -7,7 +7,7 @@ Latest Changes: - **1.5.1_dev0 - 2023-12-15** - - Added support for typing ``JArray``. + - Added support for typing ``JArray``. ``JArray[java.lang.Object]`` ``JArray["java.lang.Object"]`` - Fixed uncaught exception while setting traceback causing issues in Python 3.11/3.12. @@ -53,10 +53,10 @@ Latest Changes: - Add additional matching level for derived types to resolve ambiguities when a derived type is used in place of base class when determining the method - overload. This will resolve some previous ambiguities between methods. + overload. This will resolve some previous ambiguities between methods. - **1.4.1 - 2022-10-26** - + - Fixed issue with startJVM changing locale settings. - Changes to support Python 3.11 @@ -76,7 +76,7 @@ Latest Changes: - Support for byte order channels on buffer transfers. - Byte size for buffers now fixed to Java definitions. - + - When directly accessing Java arrays using memory view, Python requires a cast from buffers. Required because Python does not support memory view alterations on non-native sizes. @@ -91,8 +91,8 @@ Latest Changes: - dbapi2 handles drivers that don't support autocommit. - - Fixed issue when Java classes with dunder methods such as ``__del__`` - caused conflicts in Python type system. Java method which match dunder + - Fixed issue when Java classes with dunder methods such as ``__del__`` + caused conflicts in Python type system. Java method which match dunder patterns are longer translated to Python. - Fix issue with numpy arrays with no dimensions resulting in crash. @@ -110,7 +110,7 @@ Latest Changes: - Fixed issue with classes with unsatified dependencies leading to a crash on windows. - + - Fixed a bug with arrays created using the short cut. The wrong type was being returned. @@ -154,10 +154,10 @@ Latest Changes: - **1.1.1 - 2020-10-21** - - Fixed packaging problem on linux. + - Fixed packaging problem on linux. - **1.1.0 - 2020-10-13** - + - Correct bug resulting in reporting ambiguous overloads when resolving methods with variadic arguments. @@ -169,7 +169,7 @@ Latest Changes: - Fixed crash with Ctrl+C when multiple exceptions were generated. - - Removed extraneous exception when calling Ctrl+C before Java code is + - Removed extraneous exception when calling Ctrl+C before Java code is executed for methods and fields. - Fixed memory leak with string cache. @@ -203,9 +203,9 @@ Latest Changes: - Improved speed on transfer of lists, tuples, buffers to arrays of Java primitives by a factor of 4 to 100 depending on the data type. The - conversion uses optimized path for memory buffers, rather than the + conversion uses optimized path for memory buffers, rather than the Sequence API. When a Python buffer is encountered only the - first element is checked for conversion as Python buffers are homogeneous. + first element is checked for conversion as Python buffers are homogeneous. - Corrected symbol problem with Python 3.5.3. PySlice_Unpack was introduced in a later patch release and should not have been used. @@ -225,7 +225,7 @@ Latest Changes: - A bug was reported with numpy.linalg.inv resulting in crashes. This was traced to an interaction with threading between the JVM and some compilations - of numpy. The workaround appears to be calling numpy.linalg.inv prior to + of numpy. The workaround appears to be calling numpy.linalg.inv prior to starting the JVM. - **1.0.1 - 2020-07-16** @@ -233,7 +233,7 @@ Latest Changes: - Workarounds for Python 3.8.4 release. Python altered logic regarding the use of ``__setattr__`` for object and type, preventing it from being used to alter derived classes. Also the checking for errors was delegated from - the ``__setattr__`` method so exception types on some sanity checks + the ``__setattr__`` method so exception types on some sanity checks needed to be updated accordingly. - **1.0.0 - 2020-07-12** @@ -248,27 +248,27 @@ Latest Changes: - Introduced Python operator for Java casting. In Java to cast to a type you would use ``(Type) obj``, but Python does not support - anything similar. Therefore, we are enlisting the rarely used + anything similar. Therefore, we are enlisting the rarely used ``matmul`` operator as to allow an easy way to cast an object to a Java type. When a cast to a Java type is required, use - ``Type@obj`` or ``(Type)@obj``. + ``Type@obj`` or ``(Type)@obj``. - Introduced array notation to create Java arrays. In earlier versions, JArray factory was required to make a new array type. But this is tedious to read. In Java the notation would be ``Type[]`` to declare - a type or ``new Type[sz]`` to make a new array. Python does not - directly support this notation, but it does allow for unspecifed + a type or ``new Type[sz]`` to make a new array. Python does not + directly support this notation, but it does allow for unspecifed array sizes using a slice. All Java class types support - ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to + ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to create an array type which can be intiated later. This call be applied to multiple dimensions to create fixed sized arrays ``Type[s1][s2][s3]`` - to declare multidimension array types ``Type[:][:][:]`` or to + to declare multidimension array types ``Type[:][:][:]`` or to create a new multi dimensional array with unspecified dimensions ``Type[sz][:][:]``. Applying a slice with limits to a class is unsupported. - - Java classes annotated with ``@FunctionalInterface`` can be - converted from any Python object that implements ``__call__``. + - Java classes annotated with ``@FunctionalInterface`` can be + converted from any Python object that implements ``__call__``. This allows functions, lambdas, and class constructors to be used whereever Java accepts a lambda. @@ -287,7 +287,7 @@ Latest Changes: float and integer types will produce a ``TypeError``. - Use of ``JException`` is discouraged. To catch all exceptions - or test if an object is a Java exception type, + or test if an object is a Java exception type, use ``java.lang.Throwable``. - Chained Java exception causes are now reflected in the Python stackframes. @@ -301,7 +301,7 @@ Latest Changes: and ``collections.abc.MutableSequence``. - ``java.util.Collection`` completes the contract for ``collections.abc.Collection``. - + - Java classes are closed and will raise ``TypeError`` if extended in Python. - Handles Control-C gracefully. Previous versions crash whenever @@ -320,7 +320,7 @@ Latest Changes: - **0.7.4 - 4-28-2020** - Corrected a resource leak in arrays that affects array initialization, and variable - argument methods. + argument methods. - Upgraded diagnostic tracing and JNI checks to prevent future resource leaks. @@ -330,13 +330,13 @@ Latest Changes: classes is now completely in Java to allow enhancements for buffer support and revised type conversion system. - - Python module ``jpype.reflect`` will be removed in the next release. - + - Python module ``jpype.reflect`` will be removed in the next release. + - ``jpype.startJVM`` option ``convertStrings`` default will become False in the next release. - - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` - is deprecated to support casting to Python wrapper types in Java in a + - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` + is deprecated to support casting to Python wrapper types in Java in a future release. - Dropped support for Cygwin platform. @@ -370,7 +370,7 @@ Latest Changes: are direct. They can be converted to NumPy arrays with ``numpy.asarray(memoryview(obj))``. - - Proxies created with ``@JImplements`` properly implement ``toString``, + - Proxies created with ``@JImplements`` properly implement ``toString``, ``hashCode``, and ``equals``. - Proxies pass Python exceptions properly rather converting to @@ -388,16 +388,16 @@ Latest Changes: as Java holds on to it. - jpype.imports and JPackage verify existance of packages and classes. - Imports from Java packages support wildcards. + Imports from Java packages support wildcards. - Bug with JPackage that imported private and protected classes inappropriately has been corrected. Protected classes can still be imported using JClass. - - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` - is deprecated to support casting to Python wrapper types in Java in a + - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` + is deprecated to support casting to Python wrapper types in Java in a - - ``@JImplements`` with keyword argument ``deferred`` can be started + - ``@JImplements`` with keyword argument ``deferred`` can be started prior to starting the JVM. Methods are checked at first object creation. @@ -428,7 +428,7 @@ Latest Changes: Converter function must produce a Java class instance. - ``pathlib.Path`` can be implicitly converted into ``java.lang.File`` - and ``java.lang.Path``. + and ``java.lang.Path``. - ``datetime.datatime`` can implicitly convert to ``java.time.Instant``. diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi index dd79b023f..ed8ca7fd1 100644 --- a/jpype/_jarray.pyi +++ b/jpype/_jarray.pyi @@ -15,7 +15,6 @@ # See NOTICE file for details. # # ***************************************************************************** -import collections.abc import typing @@ -25,30 +24,43 @@ __all__ = ['JArray'] T = typing.TypeVar('T') -class JArray(collections.abc.Collection[T], collections.abc.Reversible[T]): +class JArray(typing.Collection[T], typing.Reversible[T]): - def __new__(cls, tp, dims=1): + def __new__(cls, tp: typing.Type[T], dims=1) -> JArray[T]: ... @classmethod def of(cls, array, dtype=None): ... - def __len__(self): + def __len__(self) -> int: ... - def __iter__(self): + def __iter__(self) -> typing.Iterator[T]: ... - def __contains__(self, item): + def __contains__(self, item: T) -> bool: # this is implicitly implemented ... - def __reversed__(self): + def __reversed__(self) -> typing.Iterator[T]: ... - def __getitem__(self, key): + @typing.overload + def __getitem__(self, key: int) -> T: ... - def __setitem__(self, index, value): + @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]): + ... + + def __class_getitem__(cls, key: T) -> typing.Type[JArray[T]]: ... From a7a7a70a18b0e71862bced023d0a471daf026cf1 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:53:35 -0400 Subject: [PATCH 5/9] explicitly subclass typing.Generic instead Unfortunately isinstance will fail on some things such as typing.Collection. Using typing.Generic will prevent confusion. --- jpype/_jarray.pyi | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi index ed8ca7fd1..eef3ad683 100644 --- a/jpype/_jarray.pyi +++ b/jpype/_jarray.pyi @@ -24,7 +24,7 @@ __all__ = ['JArray'] T = typing.TypeVar('T') -class JArray(typing.Collection[T], typing.Reversible[T]): +class JArray(typing.Generic[T]): def __new__(cls, tp: typing.Type[T], dims=1) -> JArray[T]: ... @@ -39,10 +39,6 @@ class JArray(typing.Collection[T], typing.Reversible[T]): def __iter__(self) -> typing.Iterator[T]: ... - def __contains__(self, item: T) -> bool: - # this is implicitly implemented - ... - def __reversed__(self) -> typing.Iterator[T]: ... @@ -61,6 +57,3 @@ class JArray(typing.Collection[T], typing.Reversible[T]): @typing.overload def __setitem__(self, index: slice, value: typing.Sequence[T]): ... - - def __class_getitem__(cls, key: T) -> typing.Type[JArray[T]]: - ... From dddaae815ef2608fc460e7c9474f8e7fb9ead5dc Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:40:44 -0400 Subject: [PATCH 6/9] remove str support from JArray __class_getitem__ --- doc/CHANGELOG.rst | 70 ++++++++++++++++++------------------ jpype/_jarray.py | 4 +-- test/jpypetest/test_array.py | 9 +---- 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 402e1e445..6a09d7346 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -7,7 +7,7 @@ Latest Changes: - **1.5.1_dev0 - 2023-12-15** - - Added support for typing ``JArray``. ``JArray[java.lang.Object]`` ``JArray["java.lang.Object"]`` + - 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. @@ -53,10 +53,10 @@ Latest Changes: - Add additional matching level for derived types to resolve ambiguities when a derived type is used in place of base class when determining the method - overload. This will resolve some previous ambiguities between methods. + overload. This will resolve some previous ambiguities between methods. - **1.4.1 - 2022-10-26** - + - Fixed issue with startJVM changing locale settings. - Changes to support Python 3.11 @@ -76,7 +76,7 @@ Latest Changes: - Support for byte order channels on buffer transfers. - Byte size for buffers now fixed to Java definitions. - + - When directly accessing Java arrays using memory view, Python requires a cast from buffers. Required because Python does not support memory view alterations on non-native sizes. @@ -91,8 +91,8 @@ Latest Changes: - dbapi2 handles drivers that don't support autocommit. - - Fixed issue when Java classes with dunder methods such as ``__del__`` - caused conflicts in Python type system. Java method which match dunder + - Fixed issue when Java classes with dunder methods such as ``__del__`` + caused conflicts in Python type system. Java method which match dunder patterns are longer translated to Python. - Fix issue with numpy arrays with no dimensions resulting in crash. @@ -110,7 +110,7 @@ Latest Changes: - Fixed issue with classes with unsatified dependencies leading to a crash on windows. - + - Fixed a bug with arrays created using the short cut. The wrong type was being returned. @@ -154,10 +154,10 @@ Latest Changes: - **1.1.1 - 2020-10-21** - - Fixed packaging problem on linux. + - Fixed packaging problem on linux. - **1.1.0 - 2020-10-13** - + - Correct bug resulting in reporting ambiguous overloads when resolving methods with variadic arguments. @@ -169,7 +169,7 @@ Latest Changes: - Fixed crash with Ctrl+C when multiple exceptions were generated. - - Removed extraneous exception when calling Ctrl+C before Java code is + - Removed extraneous exception when calling Ctrl+C before Java code is executed for methods and fields. - Fixed memory leak with string cache. @@ -203,9 +203,9 @@ Latest Changes: - Improved speed on transfer of lists, tuples, buffers to arrays of Java primitives by a factor of 4 to 100 depending on the data type. The - conversion uses optimized path for memory buffers, rather than the + conversion uses optimized path for memory buffers, rather than the Sequence API. When a Python buffer is encountered only the - first element is checked for conversion as Python buffers are homogeneous. + first element is checked for conversion as Python buffers are homogeneous. - Corrected symbol problem with Python 3.5.3. PySlice_Unpack was introduced in a later patch release and should not have been used. @@ -225,7 +225,7 @@ Latest Changes: - A bug was reported with numpy.linalg.inv resulting in crashes. This was traced to an interaction with threading between the JVM and some compilations - of numpy. The workaround appears to be calling numpy.linalg.inv prior to + of numpy. The workaround appears to be calling numpy.linalg.inv prior to starting the JVM. - **1.0.1 - 2020-07-16** @@ -233,7 +233,7 @@ Latest Changes: - Workarounds for Python 3.8.4 release. Python altered logic regarding the use of ``__setattr__`` for object and type, preventing it from being used to alter derived classes. Also the checking for errors was delegated from - the ``__setattr__`` method so exception types on some sanity checks + the ``__setattr__`` method so exception types on some sanity checks needed to be updated accordingly. - **1.0.0 - 2020-07-12** @@ -248,27 +248,27 @@ Latest Changes: - Introduced Python operator for Java casting. In Java to cast to a type you would use ``(Type) obj``, but Python does not support - anything similar. Therefore, we are enlisting the rarely used + anything similar. Therefore, we are enlisting the rarely used ``matmul`` operator as to allow an easy way to cast an object to a Java type. When a cast to a Java type is required, use - ``Type@obj`` or ``(Type)@obj``. + ``Type@obj`` or ``(Type)@obj``. - Introduced array notation to create Java arrays. In earlier versions, JArray factory was required to make a new array type. But this is tedious to read. In Java the notation would be ``Type[]`` to declare - a type or ``new Type[sz]`` to make a new array. Python does not - directly support this notation, but it does allow for unspecifed + a type or ``new Type[sz]`` to make a new array. Python does not + directly support this notation, but it does allow for unspecifed array sizes using a slice. All Java class types support - ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to + ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to create an array type which can be intiated later. This call be applied to multiple dimensions to create fixed sized arrays ``Type[s1][s2][s3]`` - to declare multidimension array types ``Type[:][:][:]`` or to + to declare multidimension array types ``Type[:][:][:]`` or to create a new multi dimensional array with unspecified dimensions ``Type[sz][:][:]``. Applying a slice with limits to a class is unsupported. - - Java classes annotated with ``@FunctionalInterface`` can be - converted from any Python object that implements ``__call__``. + - Java classes annotated with ``@FunctionalInterface`` can be + converted from any Python object that implements ``__call__``. This allows functions, lambdas, and class constructors to be used whereever Java accepts a lambda. @@ -287,7 +287,7 @@ Latest Changes: float and integer types will produce a ``TypeError``. - Use of ``JException`` is discouraged. To catch all exceptions - or test if an object is a Java exception type, + or test if an object is a Java exception type, use ``java.lang.Throwable``. - Chained Java exception causes are now reflected in the Python stackframes. @@ -301,7 +301,7 @@ Latest Changes: and ``collections.abc.MutableSequence``. - ``java.util.Collection`` completes the contract for ``collections.abc.Collection``. - + - Java classes are closed and will raise ``TypeError`` if extended in Python. - Handles Control-C gracefully. Previous versions crash whenever @@ -320,7 +320,7 @@ Latest Changes: - **0.7.4 - 4-28-2020** - Corrected a resource leak in arrays that affects array initialization, and variable - argument methods. + argument methods. - Upgraded diagnostic tracing and JNI checks to prevent future resource leaks. @@ -330,13 +330,13 @@ Latest Changes: classes is now completely in Java to allow enhancements for buffer support and revised type conversion system. - - Python module ``jpype.reflect`` will be removed in the next release. - + - Python module ``jpype.reflect`` will be removed in the next release. + - ``jpype.startJVM`` option ``convertStrings`` default will become False in the next release. - - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` - is deprecated to support casting to Python wrapper types in Java in a + - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` + is deprecated to support casting to Python wrapper types in Java in a future release. - Dropped support for Cygwin platform. @@ -370,7 +370,7 @@ Latest Changes: are direct. They can be converted to NumPy arrays with ``numpy.asarray(memoryview(obj))``. - - Proxies created with ``@JImplements`` properly implement ``toString``, + - Proxies created with ``@JImplements`` properly implement ``toString``, ``hashCode``, and ``equals``. - Proxies pass Python exceptions properly rather converting to @@ -388,16 +388,16 @@ Latest Changes: as Java holds on to it. - jpype.imports and JPackage verify existance of packages and classes. - Imports from Java packages support wildcards. + Imports from Java packages support wildcards. - Bug with JPackage that imported private and protected classes inappropriately has been corrected. Protected classes can still be imported using JClass. - - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` - is deprecated to support casting to Python wrapper types in Java in a + - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` + is deprecated to support casting to Python wrapper types in Java in a - - ``@JImplements`` with keyword argument ``deferred`` can be started + - ``@JImplements`` with keyword argument ``deferred`` can be started prior to starting the JVM. Methods are checked at first object creation. @@ -428,7 +428,7 @@ Latest Changes: Converter function must produce a Java class instance. - ``pathlib.Path`` can be implicitly converted into ``java.lang.File`` - and ``java.lang.Path``. + and ``java.lang.Path``. - ``datetime.datatime`` can implicitly convert to ``java.time.Instant``. diff --git a/jpype/_jarray.py b/jpype/_jarray.py index 26c0a1578..bc083f5ff 100644 --- a/jpype/_jarray.py +++ b/jpype/_jarray.py @@ -96,9 +96,9 @@ 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 + # passing int, float, str, etc is not allowed key = _jpype._java_lang_Class - if isinstance(key, (str, _jpype._java_lang_Class)): + if isinstance(key, _jpype._java_lang_Class): key = _jpype.JClass(key) if isinstance(key, _jpype.JClass): return type(key[0]) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index d84b9c0e6..8fe672a7a 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -613,13 +613,6 @@ def testJArrayGeneric_Init(self): def testJArrayInvalidGeneric(self): with self.assertRaises(TypeError): jpype.JArray[object] - + def testJArrayGenericJClass(self): self.assertEqual(type(JClass[0]), JArray[JClass]) - - def testJArrayGenericString(self): - self.assertEqual(type(JClass[0]), JArray["java.lang.Class"]) - - def testJArrayGenericStringInvalid(self): - with self.assertRaises(TypeError): - JArray["foo.bar"] # type: ignore From f7523a6e0a78a4053e26debc500fc4fbd4811f17 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:11:17 -0400 Subject: [PATCH 7/9] fix test coverage --- test/jpypetest/test_array.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 8fe672a7a..ab8c44730 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -616,3 +616,6 @@ def testJArrayInvalidGeneric(self): def testJArrayGenericJClass(self): self.assertEqual(type(JClass[0]), JArray[JClass]) + + def testJArrayJavaClass(self): + self.assertEqual(type(JObject[0]), JArray[JObject.class_]) From ab1c8331b1305802f96add8ffd9fd96b1cc14c3e Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:53:29 -0400 Subject: [PATCH 8/9] add return type hint to JArray.of --- jpype/_jarray.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi index eef3ad683..332e4c12d 100644 --- a/jpype/_jarray.pyi +++ b/jpype/_jarray.pyi @@ -30,7 +30,7 @@ class JArray(typing.Generic[T]): ... @classmethod - def of(cls, array, dtype=None): + def of(cls, array, dtype=None) -> JArray[T]: ... def __len__(self) -> int: From 4e3b6d8b1ae2acb2d477737c7a5bf9c23b263b35 Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <46897303+astrelsky@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:37:04 -0400 Subject: [PATCH 9/9] use a different TypeVar for JArray.of --- jpype/_jarray.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jpype/_jarray.pyi b/jpype/_jarray.pyi index 332e4c12d..1fd2c164c 100644 --- a/jpype/_jarray.pyi +++ b/jpype/_jarray.pyi @@ -22,6 +22,7 @@ __all__ = ['JArray'] T = typing.TypeVar('T') +U = typing.TypeVar('U') class JArray(typing.Generic[T]): @@ -30,7 +31,7 @@ class JArray(typing.Generic[T]): ... @classmethod - def of(cls, array, dtype=None) -> JArray[T]: + def of(cls, array, dtype: typing.Optional[typing.Type[U]] = None) -> JArray[U]: ... def __len__(self) -> int: