From 05a62efb8ac729f7f79b17d7fcfb329751de9d31 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 10 May 2021 07:51:22 -0700 Subject: [PATCH 001/110] Remove line in terminate. --- jpype/_core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jpype/_core.py b/jpype/_core.py index 4e12a6a0b..a71935535 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -347,8 +347,6 @@ def _JTerminate(): try: import jpype.config # We are exiting anyway so no need to free resources - if _jpype.isStarted(): - _jpype.JPypeContext.freeResources = False if jpype.config.onexit: _jpype.shutdown(jpype.config.destroy_jvm, False) except RuntimeError: From c619d60112c5405174a64e3cb2ad88212ec39f6f Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 4 Jun 2021 09:10:02 -0700 Subject: [PATCH 002/110] Documentation of shutdown issue. --- doc/dbapi2.rst | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/dbapi2.rst b/doc/dbapi2.rst index fdf9ef521..e068e4ae2 100644 --- a/doc/dbapi2.rst +++ b/doc/dbapi2.rst @@ -376,7 +376,40 @@ so that the custom return type accurately reflects the column type. # Custom return converter here return rc JSON = JSONType("JSON") - + + +Interactions with prepared statements +------------------------------------- + +Certain calls can be problematic for dbapi2 depending on driver. In +particular, SQL calls which invalidate the state of the connection will issue +an exception when the connection is used. In HSQLDB the statement +``cur.execute('shutdown')`` invalidates the connection which when the statement +is then automatically closed will then produce an exception. + +This exception is due to a conflict between dbapi2, Java, and HSQLDB +specifications. Dbapi2 requires that statements be executed as prepared +statements, Java requires that closing a statement yields no action if the +connect is already closed, and HSQLBD sets the ``isValid`` to false but not +``isClosed``. Thus executing a shutdown through dbapi2 would be expected to +close the prepared statement on an invalid connection resulting in an error. + +We can address these sort of driver specific behaviors by applying a customizer +to the Java class to add additional behaviors. + +.. code-block:: python + @jpype.JImplementationFor("java.sql.PreparedStatement") + class MyStatement(object): + @jpype.JOverride(sticky=True, rename='_close') + def close(self): + if not self.getConnection().isValid(100): + return + return self._close() + +Alternatively we can access the ``java.sql.Connection`` directly and call the +shutdown routine using an unprepared statement. Though that would require +accessing private fields. + Conclusion ========== From 53c0586909f084e8fe114ff8dd7fe35de99356f6 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 5 Jun 2021 08:52:10 -0700 Subject: [PATCH 003/110] =?UTF-8?q?Bump=20version:=201.3.0=20=E2=86=92=201?= =?UTF-8?q?.3.1=5Fdev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- doc/CHANGELOG.rst | 1 + jpype/__init__.py | 2 +- native/java/org/jpype/JPypeContext.java | 2 +- native/python/pyjp_module.cpp | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index aa96d7c33..8b3bad655 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.3.0 +current_version = 1.3.1_dev0 commit = True tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))? diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 20fa2cd4c..ad1039609 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: +- **1.3.1_dev0 - 2021-06-05** - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. diff --git a/jpype/__init__.py b/jpype/__init__.py index 692b88263..e4c7aeb6d 100644 --- a/jpype/__init__.py +++ b/jpype/__init__.py @@ -51,7 +51,7 @@ __all__.extend(_jcustomizer.__all__) __all__.extend(_gui.__all__) -__version__ = "1.3.0" +__version__ = "1.3.1_dev0" __version_info__ = __version__.split('.') diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index d8886c3db..425e0ae44 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -73,7 +73,7 @@ public class JPypeContext { - public final String VERSION = "1.3.0"; + public final String VERSION = "1.3.1_dev0"; private static JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index d0684e323..6466af8a9 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -727,7 +727,7 @@ PyMODINIT_FUNC PyInit__jpype() // PyJPModule = module; Py_INCREF(module); PyJPModule = module; - PyModule_AddStringConstant(module, "__version__", "1.3.0"); + PyModule_AddStringConstant(module, "__version__", "1.3.1_dev0"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. diff --git a/setup.py b/setup.py index a80c5bf91..f9d8a1cda 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ setup( name='JPype1', - version='1.3.0', + version='1.3.1_dev0', description='A Python to Java bridge.', long_description=open('README.rst').read(), license='License :: OSI Approved :: Apache Software License', From 10dcdfa865904251fa04b18cfb163862df24285b Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 5 Jun 2021 08:53:57 -0700 Subject: [PATCH 004/110] Repair whitespace --- native/python/pyjp_method.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/python/pyjp_method.cpp b/native/python/pyjp_method.cpp index 0d20ee0a8..cfa7640e0 100644 --- a/native/python/pyjp_method.cpp +++ b/native/python/pyjp_method.cpp @@ -400,7 +400,8 @@ void PyJPMethod_initType(PyObject* module) unsigned long flags = PyFunction_Type.tp_flags; PyFunction_Type.tp_flags |= Py_TPFLAGS_BASETYPE; PyJPMethod_Type = (PyTypeObject*) PyType_FromSpecWithBases(&methodSpec, tuple.get()); - PyFunction_Type.tp_flags = flags; JP_PY_CHECK(); + PyFunction_Type.tp_flags = flags; + JP_PY_CHECK(); PyModule_AddObject(module, "_JMethod", (PyObject*) PyJPMethod_Type); JP_PY_CHECK(); From b24be1d489fbbc97bde8ce4362549669c687d7d3 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 7 Jun 2021 09:09:56 -0700 Subject: [PATCH 005/110] Fix typo --- doc/dbapi2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/dbapi2.rst b/doc/dbapi2.rst index e068e4ae2..fd63b4d67 100644 --- a/doc/dbapi2.rst +++ b/doc/dbapi2.rst @@ -390,7 +390,7 @@ is then automatically closed will then produce an exception. This exception is due to a conflict between dbapi2, Java, and HSQLDB specifications. Dbapi2 requires that statements be executed as prepared statements, Java requires that closing a statement yields no action if the -connect is already closed, and HSQLBD sets the ``isValid`` to false but not +connection is already closed, and HSQLBD sets the ``isValid`` to false but not ``isClosed``. Thus executing a shutdown through dbapi2 would be expected to close the prepared statement on an invalid connection resulting in an error. From fc6fc527c572d208ddc348dfcaf62ec08b227500 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Tue, 24 Aug 2021 10:58:35 -0700 Subject: [PATCH 006/110] Updates to documentation --- doc/quickguide.py | 12 ++------- doc/quickguide.rst | 6 ++--- doc/userguide.rst | 67 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/doc/quickguide.py b/doc/quickguide.py index 7384e3711..ce27fbbdb 100644 --- a/doc/quickguide.py +++ b/doc/quickguide.py @@ -43,14 +43,6 @@ def python(s): """ % s -def generic(s): - return """ -.. code-block:: - - %s -""" % s - - def entry(Desc=None, Java=None, Python=None, Notes=None): global footnotes, footnotecounter if not Java: @@ -296,7 +288,7 @@ def entry(Desc=None, Java=None, Python=None, Notes=None): # Casting entry("Casting to a specific type", java("BaseClass b = (BaseClass)myObject;"), - generic("b = (BaseClass) @ myObject"), + python("b = (BaseClass) @ myObject"), "Matmul(@) is used as the casting operator.") endSection() @@ -703,7 +695,7 @@ def call(self): entry("Lambdas", java('DoubleUnaryOperator u = (p->p*2);'), - generic('u=DoubleUnaryOperator@(lambda x: x*2)'), + python('u=DoubleUnaryOperator@(lambda x: x*2)'), 'Any Java functional interface can take a lambda or callable.') endSection() diff --git a/doc/quickguide.rst b/doc/quickguide.rst index c748eacaf..8f6a04521 100644 --- a/doc/quickguide.rst +++ b/doc/quickguide.rst @@ -27,7 +27,7 @@ to be dangerous. package org.pkg; - public class BaseClass + publc class BaseClass { public void callMember(int i) {} @@ -248,7 +248,7 @@ module, loaded using ``JPackage`` or loaded with the ``JClass`` factory. | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | -| Casting to a specific | .. code-block:: java | .. code-block:: | +| Casting to a specific | .. code-block:: java | .. code-block:: python | | type [11]_ | | | | | BaseClass b = (BaseClass)myObject; | b = (BaseClass) @ myObject | | | | | @@ -706,7 +706,7 @@ with an interface for each methods that are to be accessed from Python. | Extending classes [23]_ | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | -| Lambdas [24]_ | .. code-block:: java | .. code-block:: | +| Lambdas [24]_ | .. code-block:: java | .. code-block:: python | | | | | | | DoubleUnaryOperator u = (p->p*2); | u=DoubleUnaryOperator@(lambda x: x*2) | | | | | diff --git a/doc/userguide.rst b/doc/userguide.rst index 31fdd38b5..c35c734a8 100644 --- a/doc/userguide.rst +++ b/doc/userguide.rst @@ -742,8 +742,9 @@ Details on the standard conversions provided by JPype are given in the section Java casting ------------ -To access a casting operation we use the casting ``JObject`` wrapper. -JObject accepts two arguments. The first argument is the object to convert and +To access a casting operation we use the casting ``JObject`` wrapper. +For example, ``JObject(object, Type)`` would produce a copy with specificed type. +The first argument is the object to convert and the second is the type to cast to. The second argument should always be a Java type specified using a class wrapper, a Java class instance, or a string. Casting will also add a hidden class argument to the resulting object such that @@ -758,6 +759,24 @@ general JPype constructors only provide access the Java constructor methods that are defined in the Java documentation. Casting on the other hand is entirely the domain of whatever JPype has defined including user defined casts. +As ``JObject`` syntax is long and does not look much like Java syntax, the +Python matmul operator is overloaded on JPype types such that one can use the +``@`` operator to cast to a specific Java type. In Java, one would write +``(Type)@object`` to cast the variable ``object`` to ``Type``. In Python, this +would be written as ``Type@object``. This can also be applied to array types +``JLong[:]@[1,2,3]``, collection types ``Iterable@[1,2,3]`` or Java functors +``DoubleUnaryOperator@(lambda x:x*2)``. The result of the casting operator +will be a Java object with the desired type or raise a ``TypeError`` if the +cast or conversion is not possible. For Python objects, the Java object will +generally be a copy as it is not possible to reflect changes in an array back +to Python. If one needs to retrieve the resulting changes keep a copy of the +converted array before passing it. For an existing Java object, casting +changes the resolution type for the object. This can be very useful when +trying to call a specific method overload. For example, if we have a Java +``a=String("hello")`` and there were an overload of the method ``foo`` between +``String`` and ``Object`` we would need to select the overload with +``foo(java.lang.Object@a)``. + .. _JObject: Casting is performed through the Python class ``JObject``. JObject is called @@ -778,12 +797,19 @@ indicate that the object is not available. The equivalent concept in Python is ``None``. Thus all methods that accept any object type that permit a null will accept None as an augment with implicit conversion. However, sometime it is necessary to pass an explicit type to the method resolution. To achieve this -in JPype use ``JObject(None, type)`` which will create a null pointer with the +in JPype use ``Type@None`` which will create a null pointer with the desired type. To test if something is null we have to compare the handle to None. This unfortunately trips up some code quality checkers. The idiom in Python is ``obj is None``, but as this only matches things that Python considers identical, we must instead use ``obj==None``. +Casting ``None`` is use to specify types when calling between overloads +with variadic arguments such as ``foo(Object a)`` and ``foo(Object... many)``. +If we want to call ``foo(None)`` is is ambiguous whether we intend to call the +first with a null object or the second with a null array. We can resolve the +ambiguity with ``foo(java.lang.Object@None)`` or +``foo(java.lang.Object[:]@None)`` + Type enforcement appears in three different places within JPype. These are whenever a Java method is called, whenever a Java field is set, and whenever Python returns a value back to Java. @@ -1080,6 +1106,17 @@ sequence which hold the elements of the array. If the members of the initializer sequence are not Java members then each will be converted. If any element cannot be converted a ``TypeError`` will be raised. +As a shortcut the ``[]`` operator can be used to specify an array type or +an array instance. For example, ``JInt[5]`` will allocate an array instance +of Java ints with length 5. ``JInt[:]`` will create a type instance with +an unspecific length which can be used for the casting operator. To create +an array instance with multiple dimensions we would use ``JInt[5,10]`` +which would create a rectangular array which was 5 by 10. To create a +jagged array we would substitute ``:`` for the final dimensions. So +``JInt[5,:]`` is a length 5 array of an array of ``int[]``. Multidimensional +array types are specificed like ``JInt[:,:,:]`` would be a Java type +``int[][][]``. This applied to both primitive and object types. + JArray is an abstract base class for all Java classes that are produced. Thus, one can test if something is an array class using ``issubclass`` and if Java object is an array using ``isinstance``. @@ -1146,6 +1183,30 @@ to use the Java array utilities class ``java.util.Arrays`` as it has many methods that provide additional functionality. Java arrays do not support any additional mathematical operations at this time. +Creating a Java array is also required when pass by reference syntax is required. +For example, if a Java function takes an array, modifies it and we want to +retrieve those values. In Java, all parameters are pass by value, but the contents +of a container like an array can be modified which gives the appearance of +pass by reference. For example. + +.. code-block:: java + + public void modifies(int[] v) { + for (int i=0; i<v.length; ++i) + v[i]*=2; + } + +.. code-block:: python + + orig = [1,2,3] + obj = jpype.JInt[:](orig) + a.modifies(obj) # modifies the array by multiply all by 2 + orig[:] = obj # copy all the values back from Java to Python + +If we were to call modifies on the original Python list directly, the temporary copy +would have been modified so the results would have been lost. + + Buffer classes -------------- From d5869ad6d5794d2644b693d7460af3dcd2563670 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Tue, 24 Aug 2021 11:15:55 -0700 Subject: [PATCH 007/110] Documentation on annotations --- doc/userguide.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/doc/userguide.rst b/doc/userguide.rst index c35c734a8..ec32d0558 100644 --- a/doc/userguide.rst +++ b/doc/userguide.rst @@ -3172,6 +3172,36 @@ JPype Known limitations This section lists those limitations that are unlikely to change, as they come from external sources. +Annotations +----------- + +Some frameworks such as Spring use Java annotations to indicate specific +actions. These may be either runtime annotations or compile time annotations. +Occasionally while using JPype someone would like to add a Java annotation to a +JProxy method so that a framework like Spring can pick up that annotation. + +JPype uses the Java supplied ``Proxy`` to implement an interface. That API +does not support addition of a runtime annotation to a method or class. Thus, +all methods and classes when probed with reflection that are implemented in +Python will come back with no annotations. + +Further, the majority of annotation magic within Java is actually performed at +compile time. This is accomplished using an annotation processor. When a +class or method is annotated, the compiler checks to see if there is an +annotation processor which then can produce new code or modify the class +annotations. As this is a compile time process, even if annotations were added +by Python to a class they would still not be active as the corresponding +compilation phase would not have been executed. + +This is a limitation of the implementation of annotations by the Java virtual +machine. It is technically possible though the use of specialized code +generation with the ASM library or other code generation to add a runtime +annotation. Or through exploits of the Java virtual machine annotation +implementation one can add annotation to existing Java classes. But these +annotations are unlikely to be useful. As such JPype will not be able to +support class or method annotations. + + Restarting the JVM ------------------- From 58fc762e30347cb3354ccaeff4df81ea16e7f736 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Tue, 24 Aug 2021 11:20:05 -0700 Subject: [PATCH 008/110] Fix typo --- doc/quickguide.py | 2 +- doc/quickguide.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/quickguide.py b/doc/quickguide.py index ce27fbbdb..4bb6c8453 100644 --- a/doc/quickguide.py +++ b/doc/quickguide.py @@ -112,7 +112,7 @@ def entry(Desc=None, Java=None, Python=None, Notes=None): package org.pkg; - publc class BaseClass + public class BaseClass { public void callMember(int i) {} diff --git a/doc/quickguide.rst b/doc/quickguide.rst index 8f6a04521..536b5f688 100644 --- a/doc/quickguide.rst +++ b/doc/quickguide.rst @@ -27,7 +27,7 @@ to be dangerous. package org.pkg; - publc class BaseClass + public class BaseClass { public void callMember(int i) {} From fb00ffe4ce3f6f9750e0ec7d8a268e44dea2d172 Mon Sep 17 00:00:00 2001 From: Alexander Bij <AlexanderBij@godatadriven.com> Date: Wed, 22 Sep 2021 15:15:19 +0200 Subject: [PATCH 009/110] Add try block around setting autoCommit --- jpype/dbapi2.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jpype/dbapi2.py b/jpype/dbapi2.py index ea763866a..0541fe02d 100644 --- a/jpype/dbapi2.py +++ b/jpype/dbapi2.py @@ -452,7 +452,13 @@ def __init__(self, jconnection, adapters, converters, setters, getters): self._jcx = jconnection # Required by PEP 249 # https://www.python.org/dev/peps/pep-0249/#commit - self._jcx.setAutoCommit(False) + try: + # Some driver do not support this feature. + # https://github.com/jpype-project/jpype/issues/1003 + self._jcx.setAutoCommit(False) + except Exception as ex: + if ex.getClass().getSimpleName() != 'SQLFeatureNotSupportedException': + raise ex # Handle defaults if adapters is _default: From 52695dffb624847963ae531097ed52668843cb50 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 17 Oct 2021 10:26:30 -0700 Subject: [PATCH 010/110] Fix for ssize_t issue on windows --- native/common/include/jp_booleantype.h | 4 ++-- native/common/include/jp_bytetype.h | 4 ++-- native/common/include/jp_chartype.h | 4 ++-- native/common/include/jp_doubletype.h | 4 ++-- native/common/include/jp_floattype.h | 4 ++-- native/common/include/jp_inttype.h | 4 ++-- native/common/include/jp_longtype.h | 4 ++-- native/common/include/jp_primitivetype.h | 4 ++-- native/common/include/jp_shorttype.h | 4 ++-- native/common/include/jp_voidtype.h | 4 ++-- native/common/jp_array.cpp | 4 ++-- native/common/jp_booleantype.cpp | 2 +- native/common/jp_bytetype.cpp | 2 +- native/common/jp_chartype.cpp | 2 +- native/common/jp_doubletype.cpp | 2 +- native/common/jp_floattype.cpp | 2 +- native/common/jp_gc.cpp | 4 ++-- native/common/jp_inttype.cpp | 2 +- native/common/jp_longtype.cpp | 2 +- native/common/jp_shorttype.cpp | 2 +- native/common/jp_voidtype.cpp | 4 ++-- 21 files changed, 34 insertions(+), 34 deletions(-) diff --git a/native/common/include/jp_booleantype.h b/native/common/include/jp_booleantype.h index b0215ad49..6b19e0954 100755 --- a/native/common/include/jp_booleantype.h +++ b/native/common/include/jp_booleantype.h @@ -84,7 +84,7 @@ class JPBooleanType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -94,4 +94,4 @@ class JPBooleanType : public JPPrimitiveType } ; -#endif // _JP_BOOLEAN_TYPE_H_ \ No newline at end of file +#endif // _JP_BOOLEAN_TYPE_H_ diff --git a/native/common/include/jp_bytetype.h b/native/common/include/jp_bytetype.h index 0f35aec9d..d4ee8586d 100755 --- a/native/common/include/jp_bytetype.h +++ b/native/common/include/jp_bytetype.h @@ -87,7 +87,7 @@ class JPByteType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -100,4 +100,4 @@ class JPByteType : public JPPrimitiveType static const jlong _Byte_Max = -128; } ; -#endif // _JPBYTE_TYPE_H_ \ No newline at end of file +#endif // _JPBYTE_TYPE_H_ diff --git a/native/common/include/jp_chartype.h b/native/common/include/jp_chartype.h index acd140105..730d57f22 100755 --- a/native/common/include/jp_chartype.h +++ b/native/common/include/jp_chartype.h @@ -80,7 +80,7 @@ class JPCharType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -90,4 +90,4 @@ class JPCharType : public JPPrimitiveType } ; -#endif // _JP-CHAR_TYPE_H_ \ No newline at end of file +#endif // _JP-CHAR_TYPE_H_ diff --git a/native/common/include/jp_doubletype.h b/native/common/include/jp_doubletype.h index 9e0e6222f..209a204d2 100755 --- a/native/common/include/jp_doubletype.h +++ b/native/common/include/jp_doubletype.h @@ -84,7 +84,7 @@ class JPDoubleType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -93,4 +93,4 @@ class JPDoubleType : public JPPrimitiveType JPPyBuffer& view, int subs, int base, jobject dims) override; } ; -#endif // _JP_DOUBLE_TYPE_H_ \ No newline at end of file +#endif // _JP_DOUBLE_TYPE_H_ diff --git a/native/common/include/jp_floattype.h b/native/common/include/jp_floattype.h index 56edb90f9..b0c73f4cf 100755 --- a/native/common/include/jp_floattype.h +++ b/native/common/include/jp_floattype.h @@ -84,7 +84,7 @@ class JPFloatType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -94,4 +94,4 @@ class JPFloatType : public JPPrimitiveType } ; -#endif // _JP_FLOAT_TYPE_H_ \ No newline at end of file +#endif // _JP_FLOAT_TYPE_H_ diff --git a/native/common/include/jp_inttype.h b/native/common/include/jp_inttype.h index 2d7876a83..590be95bb 100755 --- a/native/common/include/jp_inttype.h +++ b/native/common/include/jp_inttype.h @@ -89,7 +89,7 @@ class JPIntType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -99,4 +99,4 @@ class JPIntType : public JPPrimitiveType } ; -#endif // _JP_INT_TYPE_H_ \ No newline at end of file +#endif // _JP_INT_TYPE_H_ diff --git a/native/common/include/jp_longtype.h b/native/common/include/jp_longtype.h index 6319ac60d..0c2be13a0 100755 --- a/native/common/include/jp_longtype.h +++ b/native/common/include/jp_longtype.h @@ -88,7 +88,7 @@ class JPLongType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -98,4 +98,4 @@ class JPLongType : public JPPrimitiveType } ; -#endif // _JP_LONG_TYPE_H_ \ No newline at end of file +#endif // _JP_LONG_TYPE_H_ diff --git a/native/common/include/jp_primitivetype.h b/native/common/include/jp_primitivetype.h index 654251fed..281bebf71 100644 --- a/native/common/include/jp_primitivetype.h +++ b/native/common/include/jp_primitivetype.h @@ -41,7 +41,7 @@ class JPPrimitiveType : public JPClass virtual void getView(JPArrayView& view) = 0; virtual void releaseView(JPArrayView& view) = 0; virtual const char* getBufferFormat() = 0; - virtual ssize_t getItemSize() = 0; + virtual Py_ssize_t getItemSize() = 0; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) = 0; @@ -53,4 +53,4 @@ class JPPrimitiveType : public JPClass PyObject *convertLong(PyTypeObject* wrapper, PyLongObject* tmp); } ; -#endif \ No newline at end of file +#endif diff --git a/native/common/include/jp_shorttype.h b/native/common/include/jp_shorttype.h index ab9448a34..5a919cb1a 100755 --- a/native/common/include/jp_shorttype.h +++ b/native/common/include/jp_shorttype.h @@ -88,7 +88,7 @@ class JPShortType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -98,4 +98,4 @@ class JPShortType : public JPPrimitiveType } ; -#endif // _JP_SHORT_TYPE_H_ \ No newline at end of file +#endif // _JP_SHORT_TYPE_H_ diff --git a/native/common/include/jp_voidtype.h b/native/common/include/jp_voidtype.h index 98ed741cc..039119798 100755 --- a/native/common/include/jp_voidtype.h +++ b/native/common/include/jp_voidtype.h @@ -54,7 +54,7 @@ class JPVoidType : public JPPrimitiveType virtual void getView(JPArrayView& view) override; virtual void releaseView(JPArrayView& view) override; virtual const char* getBufferFormat() override; - virtual ssize_t getItemSize() override; + virtual Py_ssize_t getItemSize() override; virtual void copyElements(JPJavaFrame &frame, jarray a, jsize start, jsize len, void* memory, int offset) override; @@ -62,4 +62,4 @@ class JPVoidType : public JPPrimitiveType JPPyBuffer& view, int subs, int base, jobject dims) override; } ; -#endif // _JP_VOID_TYPE_H_ \ No newline at end of file +#endif // _JP_VOID_TYPE_H_ diff --git a/native/common/jp_array.cpp b/native/common/jp_array.cpp index 8b62444c2..2adf57e90 100644 --- a/native/common/jp_array.cpp +++ b/native/common/jp_array.cpp @@ -178,8 +178,8 @@ JPArrayView::JPArrayView(JPArray* array, jobject collection) // Second element is the shape of the array from which we compute the // memory size, the shape, and strides int dims; - ssize_t itemsize; - ssize_t sz; + Py_ssize_t itemsize; + Py_ssize_t sz; { JPPrimitiveArrayAccessor<jintArray, jint*> accessor(frame, (jintArray) item1, &JPJavaFrame::GetIntArrayElements, &JPJavaFrame::ReleaseIntArrayElements); diff --git a/native/common/jp_booleantype.cpp b/native/common/jp_booleantype.cpp index 07226e578..25a6317ce 100644 --- a/native/common/jp_booleantype.cpp +++ b/native/common/jp_booleantype.cpp @@ -333,7 +333,7 @@ const char* JPBooleanType::getBufferFormat() return "?"; } -ssize_t JPBooleanType::getItemSize() +Py_ssize_t JPBooleanType::getItemSize() { return sizeof (jboolean); } diff --git a/native/common/jp_bytetype.cpp b/native/common/jp_bytetype.cpp index 68a93b921..a81db8407 100644 --- a/native/common/jp_bytetype.cpp +++ b/native/common/jp_bytetype.cpp @@ -271,7 +271,7 @@ const char* JPByteType::getBufferFormat() return "b"; } -ssize_t JPByteType::getItemSize() +Py_ssize_t JPByteType::getItemSize() { return sizeof (jbyte); } diff --git a/native/common/jp_chartype.cpp b/native/common/jp_chartype.cpp index ff7c48dfb..24094bea5 100644 --- a/native/common/jp_chartype.cpp +++ b/native/common/jp_chartype.cpp @@ -274,7 +274,7 @@ const char* JPCharType::getBufferFormat() return "H"; } -ssize_t JPCharType::getItemSize() +Py_ssize_t JPCharType::getItemSize() { return sizeof (jchar); } diff --git a/native/common/jp_doubletype.cpp b/native/common/jp_doubletype.cpp index 7a5f4a81b..ab7c0643c 100644 --- a/native/common/jp_doubletype.cpp +++ b/native/common/jp_doubletype.cpp @@ -312,7 +312,7 @@ const char* JPDoubleType::getBufferFormat() return "d"; } -ssize_t JPDoubleType::getItemSize() +Py_ssize_t JPDoubleType::getItemSize() { return sizeof (jdouble); } diff --git a/native/common/jp_floattype.cpp b/native/common/jp_floattype.cpp index a8d7ed690..317e88f17 100644 --- a/native/common/jp_floattype.cpp +++ b/native/common/jp_floattype.cpp @@ -295,7 +295,7 @@ const char* JPFloatType::getBufferFormat() return "f"; } -ssize_t JPFloatType::getItemSize() +Py_ssize_t JPFloatType::getItemSize() { return sizeof (jfloat); } diff --git a/native/common/jp_gc.cpp b/native/common/jp_gc.cpp index 968a1da9b..8b711f997 100644 --- a/native/common/jp_gc.cpp +++ b/native/common/jp_gc.cpp @@ -234,9 +234,9 @@ void JPGarbageCollection::onEnd() } // Predict if we will cross the limit soon. - ssize_t pred = current + 2 * (current - last); + Py_ssize_t pred = current + 2 * (current - last); last = current; - if ((ssize_t) pred > (ssize_t) limit) + if ((Py_ssize_t) pred > (Py_ssize_t) limit) run_gc = 2; // printf("consider gc %d (%ld, %ld, %ld, %ld) %ld\n", run_gc, diff --git a/native/common/jp_inttype.cpp b/native/common/jp_inttype.cpp index 07d4bcdcd..5a42f98a5 100644 --- a/native/common/jp_inttype.cpp +++ b/native/common/jp_inttype.cpp @@ -298,7 +298,7 @@ const char* JPIntType::getBufferFormat() return "i"; } -ssize_t JPIntType::getItemSize() +Py_ssize_t JPIntType::getItemSize() { return sizeof (jfloat); } diff --git a/native/common/jp_longtype.cpp b/native/common/jp_longtype.cpp index d5cb995b7..cba98cfc4 100644 --- a/native/common/jp_longtype.cpp +++ b/native/common/jp_longtype.cpp @@ -297,7 +297,7 @@ const char* JPLongType::getBufferFormat() return "q"; } -ssize_t JPLongType::getItemSize() +Py_ssize_t JPLongType::getItemSize() { return sizeof (jlong); } diff --git a/native/common/jp_shorttype.cpp b/native/common/jp_shorttype.cpp index 372c8b829..12e60d979 100644 --- a/native/common/jp_shorttype.cpp +++ b/native/common/jp_shorttype.cpp @@ -294,7 +294,7 @@ const char* JPShortType::getBufferFormat() return "h"; } -ssize_t JPShortType::getItemSize() +Py_ssize_t JPShortType::getItemSize() { return sizeof (jshort); } diff --git a/native/common/jp_voidtype.cpp b/native/common/jp_voidtype.cpp index 2ab201acb..fcbe9cff3 100644 --- a/native/common/jp_voidtype.cpp +++ b/native/common/jp_voidtype.cpp @@ -122,7 +122,7 @@ const char* JPVoidType::getBufferFormat() return NULL; } -ssize_t JPVoidType::getItemSize() +Py_ssize_t JPVoidType::getItemSize() { return 0; } @@ -154,4 +154,4 @@ PyObject *JPVoidType::newMultiArray(JPJavaFrame &frame, return NULL; } -// GCOVR_EXCL_STOP \ No newline at end of file +// GCOVR_EXCL_STOP From 640299089895ace8d77ccc4a0a56fe0d103c43db Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 17 Oct 2021 10:59:25 -0700 Subject: [PATCH 011/110] Conversion hints now apply to java.lang.Class --- doc/CHANGELOG.rst | 3 +++ native/common/jp_classtype.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index ad1039609..61b768bc8 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,6 +5,9 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.3.1_dev0 - 2021-06-05** + + - Support for user defined conversions for java.lang.Class. + - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. diff --git a/native/common/jp_classtype.cpp b/native/common/jp_classtype.cpp index b79f573a7..f02c30e25 100644 --- a/native/common/jp_classtype.cpp +++ b/native/common/jp_classtype.cpp @@ -36,11 +36,10 @@ JPClassType::~JPClassType() JPMatch::Type JPClassType::findJavaConversion(JPMatch& match) { JP_TRACE_IN("JPClass::findJavaConversion"); - if (nullConversion->matches(this, match) != JPMatch::_none) - return match.type; - if (objectConversion->matches(this, match) != JPMatch::_none) - return match.type; - if (classConversion->matches(this, match) != JPMatch::_none) + if (nullConversion->matches(this, match) + || objectConversion->matches(this, match) + || classConversion->matches(this, match) + || hintsConversion->matches(this, match)) return match.type; return match.type = JPMatch::_none; JP_TRACE_OUT; @@ -52,5 +51,6 @@ void JPClassType::getConversionInfo(JPConversionInfo &info) nullConversion->getInfo(this, info); objectConversion->getInfo(this, info); classConversion->getInfo(this, info); + hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); -} \ No newline at end of file +} From 042530d56c62259e6af36c72f8f8f1c807020669 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 17 Oct 2021 11:18:46 -0700 Subject: [PATCH 012/110] Arrays and test --- doc/CHANGELOG.rst | 2 +- native/common/jp_arrayclass.cpp | 2 ++ test/jpypetest/test_classhints.py | 42 +++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 61b768bc8..4ea2494d9 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -6,7 +6,7 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.3.1_dev0 - 2021-06-05** - - Support for user defined conversions for java.lang.Class. + - Support for user defined conversions for java.lang.Class and array types. - **1.3.0 - 2021-05-19** diff --git a/native/common/jp_arrayclass.cpp b/native/common/jp_arrayclass.cpp index afc557e57..c719af0e6 100644 --- a/native/common/jp_arrayclass.cpp +++ b/native/common/jp_arrayclass.cpp @@ -43,6 +43,7 @@ JPMatch::Type JPArrayClass::findJavaConversion(JPMatch &match) || charArrayConversion->matches(this, match) || byteArrayConversion->matches(this, match) || sequenceConversion->matches(this, match) + || hintsConversion->matches(this, match) ) return match.type; JP_TRACE("None"); @@ -57,6 +58,7 @@ void JPArrayClass::getConversionInfo(JPConversionInfo &info) charArrayConversion->getInfo(this, info); byteArrayConversion->getInfo(this, info); sequenceConversion->getInfo(this, info); + hintsConversion->getInfo(this, info); PyList_Append(info.ret, PyJPClass_create(frame, this).get()); } diff --git a/test/jpypetest/test_classhints.py b/test/jpypetest/test_classhints.py index 1c3c9b72b..bcf895622 100644 --- a/test/jpypetest/test_classhints.py +++ b/test/jpypetest/test_classhints.py @@ -24,6 +24,21 @@ def blah(self): pass +class ClassProxy: + def __init__(self, proxy): + self.proxy = proxy + + +class ArrayProxy: + def __init__(self, proxy): + self.proxy = proxy + + +class StringProxy: + def __init__(self, proxy): + self.proxy = proxy + + class ClassHintsTestCase(common.JPypeTestCase): def setUp(self): @@ -85,3 +100,30 @@ def StrToCustom(jcls, args): cht.call(MyImpl()) self.assertIsInstance(cht.input, self.MyCustom) self.assertIsInstance(cht.input.arg, MyImpl) + + def testClassCustomizer(self): + + @jpype.JConversion("java.lang.Class", instanceof=ClassProxy) + def ClassCustomizer(jcls, obj): + return obj.proxy + + hints = jpype.JClass('java.lang.Class')._hints + self.assertTrue(ClassProxy in hints.implicit) + + def testArrayCustomizer(self): + + @jpype.JConversion(jpype.JInt[:], instanceof=ArrayProxy) + def ArrayCustomizer(jcls, obj): + return obj.proxy + + hints = jpype.JClass(jpype.JInt[:])._hints + self.assertTrue(ArrayProxy in hints.implicit) + + def testStringCustomizer(self): + + @jpype.JConversion("java.lang.String", instanceof=StringProxy) + def STringCustomizer(jcls, obj): + return obj.proxy + + hints = jpype.JClass("java.lang.String")._hints + self.assertTrue(StringProxy in hints.implicit) From 8c71b7c3dacc3d9c2e5f489b691da1ff8593ffa5 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 18 Oct 2021 08:58:26 -0700 Subject: [PATCH 013/110] Missed one. --- doc/CHANGELOG.rst | 3 +++ native/python/include/jp_pythontypes.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index ad1039609..b782d44ad 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,6 +5,9 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.3.1_dev0 - 2021-06-05** + + - Fixed issue with ssize_t on Windows for Python 3.10. + - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. diff --git a/native/python/include/jp_pythontypes.h b/native/python/include/jp_pythontypes.h index 93e91d8e0..58575cd1e 100755 --- a/native/python/include/jp_pythontypes.h +++ b/native/python/include/jp_pythontypes.h @@ -281,7 +281,7 @@ class JPPyObjectVector return m_Contents.size(); } - PyObject* operator[](ssize_t i) + PyObject* operator[](Py_ssize_t i) { return m_Contents[i].get(); } From 0b8b7e740c354b39380b1d39c5bc1a4ee9fd9fb8 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 22 Oct 2021 15:32:46 -0700 Subject: [PATCH 014/110] Trivial change to see why CI failed to complete. --- native/common/jp_class.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/native/common/jp_class.cpp b/native/common/jp_class.cpp index 5e220635b..3ff941aad 100644 --- a/native/common/jp_class.cpp +++ b/native/common/jp_class.cpp @@ -424,7 +424,6 @@ void JPClass::getConversionInfo(JPConversionInfo &info) JP_TRACE_OUT; } - //</editor-fold> //<editor-fold desc="hierarchy" defaultstate="collapsed"> From 9325d3715fc3140204ba9178c5cf71cc9b2dd914 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 22 Oct 2021 15:35:33 -0700 Subject: [PATCH 015/110] Try to update image. --- .azure/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 374edb661..5538848d7 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -22,19 +22,19 @@ variables: jobs: - job: Deps pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/ivy.yml - job: Documentation pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/documentation.yml - job: Coverage pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" dependsOn: Deps steps: - template: scripts/deps.yml @@ -42,7 +42,7 @@ jobs: - job: Tracing pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/tracing.yml @@ -51,19 +51,19 @@ jobs: strategy: matrix: linux-3.5: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.5' linux-3.6: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.6' linux-3.7: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.7' linux-3.8: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.8' linux-3.9: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.9' windows-3.5: imageName: "vs2017-win2016" From 32b8857afcf2919068dbb900178626fa46caea9e Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 22 Oct 2021 16:06:43 -0700 Subject: [PATCH 016/110] Update .azure so CI runs --- .azure/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 374edb661..5538848d7 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -22,19 +22,19 @@ variables: jobs: - job: Deps pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/ivy.yml - job: Documentation pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/documentation.yml - job: Coverage pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" dependsOn: Deps steps: - template: scripts/deps.yml @@ -42,7 +42,7 @@ jobs: - job: Tracing pool: - vmImage: "ubuntu-16.04" + vmImage: "ubuntu-latest" steps: - template: scripts/tracing.yml @@ -51,19 +51,19 @@ jobs: strategy: matrix: linux-3.5: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.5' linux-3.6: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.6' linux-3.7: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.7' linux-3.8: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.8' linux-3.9: - imageName: "ubuntu-16.04" + imageName: "ubuntu-latest" python.version: '3.9' windows-3.5: imageName: "vs2017-win2016" From 80153b1a39aced80274cf221f778fa91ef065c4b Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Fri, 26 Nov 2021 14:22:24 -0500 Subject: [PATCH 017/110] fix - #1016: Make functional conversion check argument count This commit modify functional conversion to check argument count: 1. If the Java functional interface have more parameters than the Python function and the Python function does not accept variable arguments, conversion type is none 2. If the Java functional interface have less parameters than the number of required positional parameters of the Python function, conversion type is none 3. Otherwise, conversion type is implict. --- native/common/include/jp_typemanager.h | 2 + native/common/jp_functional.cpp | 62 ++++++++++++++++++- native/common/jp_methoddispatch.cpp | 9 ++- native/common/jp_typemanager.cpp | 11 ++++ .../org/jpype/manager/ClassDescriptor.java | 1 + .../java/org/jpype/manager/TypeManager.java | 28 +++++++++ test/harness/jpype/overloads/Test2.java | 45 ++++++++++++++ test/jpypetest/test_overloads.py | 30 +++++++++ 8 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 test/harness/jpype/overloads/Test2.java diff --git a/native/common/include/jp_typemanager.h b/native/common/include/jp_typemanager.h index 5cec58d4c..6038d86ee 100644 --- a/native/common/include/jp_typemanager.h +++ b/native/common/include/jp_typemanager.h @@ -41,6 +41,7 @@ class JPTypeManager JPClass* findClassForObject(jobject obj); void populateMethod(void* method, jobject obj); void populateMembers(JPClass* cls); + int interfaceParameterCount(JPClass* cls); private: JPContext* m_Context; @@ -50,6 +51,7 @@ class JPTypeManager jmethodID m_FindClassForObject; jmethodID m_PopulateMethod; jmethodID m_PopulateMembers; + jmethodID m_InterfaceParameterCount; } ; #endif // _JPCLASS_H_ \ No newline at end of file diff --git a/native/common/jp_functional.cpp b/native/common/jp_functional.cpp index f94dba927..85f0f7813 100644 --- a/native/common/jp_functional.cpp +++ b/native/common/jp_functional.cpp @@ -40,9 +40,65 @@ class JPConversionFunctional : public JPConversion { if (!PyCallable_Check(match.object)) return match.type = JPMatch::_none; - match.conversion = this; - match.closure = cls; - return match.type = JPMatch::_implicit; + // Modified from https://stackoverflow.com/a/1117735 + + bool has_parameter_count = false; + // Get the __code__ attribute of the function, which contains details about the function + // Note: __code__ is func_code in Python 2.x + PyObject* function_code_object = PyObject_GetAttrString(match.object, "__code__"); + if(function_code_object) { + // get the argument count + PyObject* parameter_count_obj = PyObject_GetAttrString(function_code_object, "co_argcount"); + if(parameter_count_obj) { + has_parameter_count = true; + int optional_parameter_count = 0; + int flags = 0; + const int ACCEPT_VARGS_MASK = 0x04; // From https://docs.python.org/3/reference/datamodel.html + PyObject* flags_obj = PyObject_GetAttrString(function_code_object, "co_flags"); + if (flags_obj) { + flags = PyLong_AsLong(flags_obj); + Py_DECREF(flags_obj); + } + PyObject* optional_parameter_tuple = PyObject_GetAttrString(match.object, "__defaults__"); + if (optional_parameter_tuple && optional_parameter_tuple != Py_None) { + optional_parameter_count = PyTuple_Size(optional_parameter_tuple); + Py_DECREF(optional_parameter_tuple); + } + const int parameter_count = PyLong_AsLong(parameter_count_obj); + const int java_parameter_count = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); + const bool is_varargs = (flags & ACCEPT_VARGS_MASK) == ACCEPT_VARGS_MASK; + + // def my_func(x, y=None) should be both a Function and a BiFunction + // i.e. the number of parameters accepted by the interface MUST + // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) + // (Unless the function accept a variable number of arguments, then this restriction does not + // apply). + // 2. Be at least the minumum number of parameters accepted by the python function + // (parameter_count - optional_parameter_count = number of required parameters). + // Notes: + // - keywords vargs does not remove restriction 1 + // - keyword only arguments are not counted. + if ((!is_varargs && parameter_count < java_parameter_count) || + parameter_count - optional_parameter_count > java_parameter_count) { + match.type = JPMatch::_none; + } else { + match.conversion = this; + match.closure = cls; + match.type = JPMatch::_implicit; + } + + Py_DECREF(parameter_count_obj); + } + Py_DECREF(function_code_object); + } + + if (!has_parameter_count) { + match.conversion = this; + match.closure = cls; + return match.type = JPMatch::_implicit; + } else { + return match.type; + } } virtual void getInfo(JPClass *cls, JPConversionInfo &info) override diff --git a/native/common/jp_methoddispatch.cpp b/native/common/jp_methoddispatch.cpp index 3778dff4a..ec0d4dbc5 100644 --- a/native/common/jp_methoddispatch.cpp +++ b/native/common/jp_methoddispatch.cpp @@ -67,14 +67,16 @@ bool JPMethodDispatch::findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch // found, and one to hold the test of the next overload. JPMethodMatch match = bestMatch; - // Check each overload in order (the TypeManager orders them by priority + + bool is_first_match = true; + // Check each overload in order (the TypeManager orders them by priority // according to Java overload rules). for (JPMethodList::iterator it = m_Overloads.begin(); it != m_Overloads.end(); ++it) { JPMethod* current = *it; JP_TRACE("Trying to match", current->toString()); - current->matches(frame, match, callInstance, arg); + current->matches(frame, match, callInstance, arg); JP_TRACE(" match ended", match.m_Type); if (match.m_Type == JPMatch::_exact) @@ -88,9 +90,10 @@ bool JPMethodDispatch::findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch continue; // If this is the first match then make it the best. - if (bestMatch.m_Overload == 0) + if (is_first_match) { bestMatch = match; + is_first_match = false; continue; } diff --git a/native/common/jp_typemanager.cpp b/native/common/jp_typemanager.cpp index 91edaeb06..29ec4cbb6 100644 --- a/native/common/jp_typemanager.cpp +++ b/native/common/jp_typemanager.cpp @@ -27,6 +27,7 @@ JPTypeManager::JPTypeManager(JPJavaFrame& frame) m_FindClassForObject = frame.GetMethodID(cls, "findClassForObject", "(Ljava/lang/Object;)J"); m_PopulateMethod = frame.GetMethodID(cls, "populateMethod", "(JLjava/lang/reflect/Executable;)V"); m_PopulateMembers = frame.GetMethodID(cls, "populateMembers", "(Ljava/lang/Class;)V"); + m_InterfaceParameterCount = frame.GetMethodID(cls, "interfaceParameterCount", "(Ljava/lang/Class;)I"); // The object instance will be loaded later JP_TRACE_OUT; @@ -97,3 +98,13 @@ void JPTypeManager::populateMembers(JPClass* cls) frame.CallVoidMethodA(m_JavaTypeManager.get(), m_PopulateMembers, val); JP_TRACE_OUT; } + +int JPTypeManager::interfaceParameterCount(JPClass *cls) +{ + JP_TRACE_IN("JPTypeManager::interfaceParameterCount"); + JPJavaFrame frame = JPJavaFrame::outer(m_Context); + jvalue val[1]; + val[0].l = (jobject) cls->getJavaClass(); + return frame.CallIntMethodA(m_JavaTypeManager.get(), m_InterfaceParameterCount, val); + JP_TRACE_OUT; +} diff --git a/native/java/org/jpype/manager/ClassDescriptor.java b/native/java/org/jpype/manager/ClassDescriptor.java index 265853586..aefe1aff8 100644 --- a/native/java/org/jpype/manager/ClassDescriptor.java +++ b/native/java/org/jpype/manager/ClassDescriptor.java @@ -46,6 +46,7 @@ public class ClassDescriptor public int methodCounter = 0; public long[] fields; public long anonymous; + public int functional_interface_parameter_count = -1; // -1 = unset; -2 = not a functional interface ClassDescriptor(Class cls, long classPtr) { diff --git a/native/java/org/jpype/manager/TypeManager.java b/native/java/org/jpype/manager/TypeManager.java index a36f8d7de..3cdd9ea9d 100644 --- a/native/java/org/jpype/manager/TypeManager.java +++ b/native/java/org/jpype/manager/TypeManager.java @@ -300,6 +300,34 @@ public synchronized void populateMethod(long wrapper, Executable method) } } + /** + * Returns the number of arguments an interface only unimplemented method accept. + * + * @param interfaceClass The class of the interface + * @return the number of arguments the only unimplemented method of the interface accept. + */ + public int interfaceParameterCount(Class<?> interfaceClass) { + ClassDescriptor classDescriptor = classMap.get(interfaceClass); + if (classDescriptor.functional_interface_parameter_count != -1) { + return classDescriptor.functional_interface_parameter_count; + } + int abstractMethodParameterCount = -1; + for (Method method : interfaceClass.getMethods()) { + if (Modifier.isAbstract(method.getModifiers())) { + if (abstractMethodParameterCount != -1) { + classDescriptor.functional_interface_parameter_count = -2; + return -2; + } + abstractMethodParameterCount = method.getParameterCount(); + } + } + if (abstractMethodParameterCount == -1) { + abstractMethodParameterCount = -2; + } + classDescriptor.functional_interface_parameter_count = abstractMethodParameterCount; + return abstractMethodParameterCount; + } + /** * Get a class for an object. * diff --git a/test/harness/jpype/overloads/Test2.java b/test/harness/jpype/overloads/Test2.java new file mode 100644 index 000000000..5f19bee13 --- /dev/null +++ b/test/harness/jpype/overloads/Test2.java @@ -0,0 +1,45 @@ +/* **************************************************************************** + 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. +**************************************************************************** */ +package jpype.overloads; + +import java.util.List; + +public class Test2 +{ + interface NoArg { + public String apply(); + } + + interface SingleArg { + public String apply(int a); + } + + interface TwoArg { + public String apply(int a, int b); + } + + public String testFunctionalInterfaces(NoArg noArg) { + return "NoArg"; + } + + public String testFunctionalInterfaces(SingleArg singleArg) { + return "SingleArg"; + } + + public String testFunctionalInterfaces(TwoArg twoArg) { + return "TwoArg"; + } +} \ No newline at end of file diff --git a/test/jpypetest/test_overloads.py b/test/jpypetest/test_overloads.py index d33467493..8fd756dd3 100644 --- a/test/jpypetest/test_overloads.py +++ b/test/jpypetest/test_overloads.py @@ -190,3 +190,33 @@ def testDefaultMethods(self): pass else: self.assertEqual('B', testdefault.defaultMethod()) + + def testFunctionalInterfacesWithDifferentSignatures(self): + test2 = self.__jp.Test2() + self.assertEqual('NoArgs', test2.testFunctionalInterfaces(lambda: 'NoArgs')) + self.assertEqual('SingleArg', test2.testFunctionalInterfaces(lambda a: 'SingleArg')) + self.assertEqual('TwoArg', test2.testFunctionalInterfaces(lambda a, b: 'TwoArg')) + + def testFunctionalInterfacesWithDefaults(self): + def my_fun(x, y=None): + return 'my_fun' + + def my_fun_vargs(x, *vargs): + return 'my_fun_vargs' + + def my_fun_kwargs(x, **kwargs): + return 'my_fun_kwargs' + + def my_fun_kw(*, keyword_arg=None): + return 'my_fun_kw' + + def my_fun_pos_only(x, /, y): + return 'my_fun_pos_only' + test2 = self.__jp.Test2() + self.assertRaisesRegex( + TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun) + self.assertRaisesRegex( + TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_vargs) + self.assertEqual('SingleArg', test2.testFunctionalInterfaces(my_fun_kwargs)) + self.assertEqual('NoArgs', test2.testFunctionalInterfaces(my_fun_kw)) + self.assertEqual('TwoArg', test2.testFunctionalInterfaces(my_fun_pos_only)) From 5803b3e112b05ee7874906781a9f805220faba78 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Wed, 1 Dec 2021 16:15:37 -0500 Subject: [PATCH 018/110] Clear error indicator in functional matches, add @FunctionalInterface to Test classes - The error indicator is potentially set by PyObject_GetAttrString, which will cause an error to be thrown by JP_PY_CHECK (a null check is performed versus doing a PyObject_HasAttrString first). - Add @FunctionalInterface to the interfaces used in the functional overloads test, as the annotation is used to determine if it is possible to cast a Python function to the Java type. --- native/common/jp_functional.cpp | 4 ++-- test/harness/jpype/overloads/Test2.java | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/native/common/jp_functional.cpp b/native/common/jp_functional.cpp index 85f0f7813..d41d08066 100644 --- a/native/common/jp_functional.cpp +++ b/native/common/jp_functional.cpp @@ -40,8 +40,8 @@ class JPConversionFunctional : public JPConversion { if (!PyCallable_Check(match.object)) return match.type = JPMatch::_none; - // Modified from https://stackoverflow.com/a/1117735 + // Modified from https://stackoverflow.com/a/1117735 bool has_parameter_count = false; // Get the __code__ attribute of the function, which contains details about the function // Note: __code__ is func_code in Python 2.x @@ -91,7 +91,7 @@ class JPConversionFunctional : public JPConversion } Py_DECREF(function_code_object); } - + PyErr_Clear(); if (!has_parameter_count) { match.conversion = this; match.closure = cls; diff --git a/test/harness/jpype/overloads/Test2.java b/test/harness/jpype/overloads/Test2.java index 5f19bee13..304e34418 100644 --- a/test/harness/jpype/overloads/Test2.java +++ b/test/harness/jpype/overloads/Test2.java @@ -19,20 +19,23 @@ public class Test2 { - interface NoArg { + @FunctionalInterface + public interface NoArg { public String apply(); } - interface SingleArg { + @FunctionalInterface + public interface SingleArg { public String apply(int a); } - interface TwoArg { + @FunctionalInterface + public interface TwoArg { public String apply(int a, int b); } public String testFunctionalInterfaces(NoArg noArg) { - return "NoArg"; + return "NoArgs"; } public String testFunctionalInterfaces(SingleArg singleArg) { From 9b84f7d79f838412005120f5e73b76adc50301ce Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Wed, 1 Dec 2021 16:29:44 -0500 Subject: [PATCH 019/110] Remove positional argument test so pre-Python 3.8 can run tests --- test/jpypetest/test_overloads.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/jpypetest/test_overloads.py b/test/jpypetest/test_overloads.py index 8fd756dd3..7cbe0b884 100644 --- a/test/jpypetest/test_overloads.py +++ b/test/jpypetest/test_overloads.py @@ -210,8 +210,6 @@ def my_fun_kwargs(x, **kwargs): def my_fun_kw(*, keyword_arg=None): return 'my_fun_kw' - def my_fun_pos_only(x, /, y): - return 'my_fun_pos_only' test2 = self.__jp.Test2() self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun) @@ -219,4 +217,3 @@ def my_fun_pos_only(x, /, y): TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_vargs) self.assertEqual('SingleArg', test2.testFunctionalInterfaces(my_fun_kwargs)) self.assertEqual('NoArgs', test2.testFunctionalInterfaces(my_fun_kw)) - self.assertEqual('TwoArg', test2.testFunctionalInterfaces(my_fun_pos_only)) From 8ea01129d542ebf92c3cc03462cb77a223f5104a Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Tue, 7 Dec 2021 13:49:59 -0500 Subject: [PATCH 020/110] Add test for callable classes --- test/jpypetest/test_overloads.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/jpypetest/test_overloads.py b/test/jpypetest/test_overloads.py index 7cbe0b884..1e54e1f3f 100644 --- a/test/jpypetest/test_overloads.py +++ b/test/jpypetest/test_overloads.py @@ -210,10 +210,16 @@ def my_fun_kwargs(x, **kwargs): def my_fun_kw(*, keyword_arg=None): return 'my_fun_kw' + class my_fun_class: + def __call__(self): + return 'my_fun_class' + test2 = self.__jp.Test2() self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun) self.assertRaisesRegex( TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_vargs) + self.assertRaisesRegex( + TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_class) self.assertEqual('SingleArg', test2.testFunctionalInterfaces(my_fun_kwargs)) self.assertEqual('NoArgs', test2.testFunctionalInterfaces(my_fun_kw)) From 4b0c8371bf08ff2d0c3f7c64dd5867d5732d3531 Mon Sep 17 00:00:00 2001 From: Hyukjin Kwon <gurwls223@apache.org> Date: Tue, 21 Dec 2021 14:04:33 +0900 Subject: [PATCH 021/110] Fix java.nio.file import in AutoCloseable example --- doc/java/lang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/java/lang.py b/doc/java/lang.py index efd2f4591..371aeb3a6 100644 --- a/doc/java/lang.py +++ b/doc/java/lang.py @@ -25,7 +25,7 @@ class AutoCloseable: .. code-block:: python - from java.nio.files import Files, Paths + from java.nio.file import Files, Paths with Files.newInputStream(Paths.get("foo")) as fd: # operate on the input stream From 97e4bf69af7dfcf76c7e76e13e10176e711e1dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Zahradn=C3=ADk?= <lukaszahradnik96@seznam.cz> Date: Sat, 15 Jan 2022 00:49:05 +0100 Subject: [PATCH 022/110] Userguide documentation typos fixes --- doc/userguide.rst | 182 +++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/doc/userguide.rst b/doc/userguide.rst index ec32d0558..3c739641c 100644 --- a/doc/userguide.rst +++ b/doc/userguide.rst @@ -92,11 +92,11 @@ set the class path, start the JVM, remove all the type declarations, and you are # Copy in the patterns from the guide to replace the example code db = Database("our_records") - with db.connect() as DatabaseConnection: - c.runQuery() - while c.hasRecords(): - record = db.nextRecord() - ... + with db.connect() as DatabaseConnection: + c.runQuery() + while c.hasRecords(): + record = db.nextRecord() + ... Launch it in the interactive window. You can get back to programming in Python once you get a good night sleep. @@ -136,11 +136,11 @@ in your serialized object. from java.nio.file import Files, Paths from java.io import ObjectInputStream - with Files.newInputStream(Paths.get("myobject.ser") as stream: - ois = new ObjectInputStream(stream) - obj = ois.readObject() + with Files.newInputStream(Paths.get("myobject.ser")) as stream: + ois = ObjectInputStream(stream) + obj = ois.readObject() - print(obj) # prints org.bigstuff.MyObject@7382f612 + print(obj) # prints org.bigstuff.MyObject@7382f612 It appears that the structure is loaded. The problematic structure requires you call the getData method with the correct index. @@ -172,7 +172,7 @@ example and watch what happens. .. code-block:: python - import matplot.pyplot as plt + import matplotlib.pyplot as plt plt.plot(d) plt.show() @@ -227,13 +227,13 @@ Based on the previous examples, you start by defining a monitor class @JImplements(Monitor) class HeartMonitor: - def __init__(self): - self.readings = [] - @JOverride - def onMeasurement(self, measurement): - self.readings.append([measurement.getTime(), measurement.getHeartRate()]) - def getResults(self): - return np.array(self.readings) + def __init__(self): + self.readings = [] + @JOverride + def onMeasurement(self, measurement): + self.readings.append([measurement.getTime(), measurement.getHeartRate()]) + def getResults(self): + return np.array(self.readings) There is a bit to unpack here. You have implemented a Java class from within Python. The Java implementation is simply an ordinary Python class which has be @@ -762,7 +762,7 @@ entirely the domain of whatever JPype has defined including user defined casts. As ``JObject`` syntax is long and does not look much like Java syntax, the Python matmul operator is overloaded on JPype types such that one can use the ``@`` operator to cast to a specific Java type. In Java, one would write -``(Type)@object`` to cast the variable ``object`` to ``Type``. In Python, this +``(Type)object`` to cast the variable ``object`` to ``Type``. In Python, this would be written as ``Type@object``. This can also be applied to array types ``JLong[:]@[1,2,3]``, collection types ``Iterable@[1,2,3]`` or Java functors ``DoubleUnaryOperator@(lambda x:x*2)``. The result of the casting operator @@ -888,7 +888,7 @@ of bits up or down relative to their storage size. JPype has mapped each of the primitive types into Python classes. To avoid conflicts with Python, JPype has named each primitive with a capital letter -``J`` followed by the primitive name starting with an upper case letter. +``J`` followed by the primiti ve name starting with an upper case letter. .. _JBoolean: @@ -1656,28 +1656,28 @@ Here is an example: .. code-block:: python try : - # Code that throws a java.lang.RuntimeException + # Code that throws a java.lang.RuntimeException except java.lang.RuntimeException as ex: - print("Caught the runtime exception : ", str(ex)) - print(ex.stacktrace()) + print("Caught the runtime exception : ", str(ex)) + print(ex.stacktrace()) Multiple java exceptions can be caught together or separately: .. code-block:: python try: - # ... + # ... except (java.lang.ClassCastException, java.lang.NullPointerException) as ex: - print("Caught multiple exceptions : ", str(ex)) - print(ex.stacktrace()) + print("Caught multiple exceptions : ", str(ex)) + print(ex.stacktrace()) except java.lang.RuntimeException as ex: - print("Caught runtime exception : ", str(ex)) - print(ex.stacktrace()) - except jpype.JException: - print("Caught base exception : ", str(ex)) - print(ex.stacktrace()) + print("Caught runtime exception : ", str(ex)) + print(ex.stacktrace()) + except jpype.JException as ex: + print("Caught base exception : ", str(ex)) + print(ex.stacktrace()) except Exception as ex: - print("Caught python exception :", str(ex)) + print("Caught python exception :", str(ex)) Exceptions can be raised in proxies to throw an exception back to Java. @@ -1903,7 +1903,7 @@ should be attached to the Java Runtime object. The following pattern is used: class MyShutdownHook: @JOverride def run(self): - # perform any required shutdown activities + # perform any required shutdown activities java.lang.Runtime.getRuntime().addShutdownHook(Thread(MyShutdownHook())) @@ -1975,16 +1975,16 @@ Example taken from JPype ``java.util.Map`` customizer: @_jcustomizer.JImplementationFor('java.util.Map') class _JMap: def __jclass_init__(self): - Mapping.register(self) + Mapping.register(self) def __len__(self): - return self.size() + return self.size() def __iter__(self): - return self.keySet().iterator() + return self.keySet().iterator() def __delitem__(self, i): - return self.remove(i) + return self.remove(i) The name of the class does not matter for the purposes of customizer though it @@ -2355,19 +2355,19 @@ dispatch: @JImplements(JavaInterface) class MyImpl: - @JOverride - def callOverloaded(self, *args): - # always use the wild card args when implementing a dispatch - if len(args)==2: - return self.callMethod1(*args) - if len(args)==1 and isinstance(args[0], JString): - return self.callMethod2(*args) - raise RuntimeError("Incorrect arguments") + @JOverride + def callOverloaded(self, *args): + # always use the wild card args when implementing a dispatch + if len(args)==2: + return self.callMethod1(*args) + if len(args)==1 and isinstance(args[0], JString): + return self.callMethod2(*args) + raise RuntimeError("Incorrect arguments") def callMethod1(self, a1, a2): - # ... + # ... def callMethod2(self, jstr): - # ... + # ... Multiple interfaces ------------------- @@ -2388,7 +2388,7 @@ achieve this, specify the interface using a string and add the keyword argument @JImplements("org.foo.JavaInterface", deferred=True) class MyImpl: - # ... + # ... Deferred proxies are not checked at declaration time, but instead at the time @@ -2444,27 +2444,27 @@ First, with an object: .. code-block:: python - class C : - def testMethod(self) : - return 42 + class C: + def testMethod(self): + return 42 - def testMethod2(self) : - return "Bar" + def testMethod2(self): + return "Bar" c = C() # create an instance - proxy = JProxy("ITestInterface2", inst=c) # Convert it into a proxy + proxy = JProxy("ITestInterface2", inst=c) # Convert it into a proxy or you can use a dictionary. .. code-block:: python - def _testMethod() : - return 32 + def _testMethod(): + return 32 - def _testMethod2() : - return "Fooo!" + def _testMethod2(): + return "Fooo!" - d = { 'testMethod' : _testMethod, 'testMethod2' : _testMethod2, } + d = { 'testMethod': _testMethod, 'testMethod2': _testMethod2, } proxy = JProxy("ITestInterface2", dict=d) @@ -2548,19 +2548,19 @@ launches it from within Python. from javax.swing import * def createAndShowGUI(): - frame = JFrame("HelloWorldSwing") - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) - label = JLabel("Hello World") - frame.getContentPane().add(label) - frame.pack() - frame.setVisible(True) + frame = JFrame("HelloWorldSwing") + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) + label = JLabel("Hello World") + frame.getContentPane().add(label) + frame.pack() + frame.setVisible(True) # Start an event loop thread to handling gui events @jpype.JImplements(java.lang.Runnable) class Launch: - @jpype.JOverride - def run(self): - createAndShowGUI() + @jpype.JOverride + def run(self): + createAndShowGUI() javax.swing.SwingUtilities.invokeLater(Launch()) @@ -2658,12 +2658,12 @@ keep the synchronization on as long as the object is kept alive. For example, mySharedList = java.util.ArrayList() # Give the list to another thread that will be adding items - otherThread,setList(mySharedList) + otherThread.setList(mySharedList) # Lock the list so that we can access it without interference with synchronized(mySharedList): - if not mySharedList.isEmpty(): - ... # process elements + if not mySharedList.isEmpty(): + ... # process elements # Resource is unlocked once we leave the block The Python ``with`` statement is used to control the scope. Do not @@ -2695,26 +2695,26 @@ results. For example: .. code-block:: python - def limit(method, timeout): - """ Convert a Java method to asynchronous call with a specified timeout. """ - def f(*args): - @jpype.JImplements(java.util.concurrent.Callable) - class g: - @jpype.JOverride - def call(self): - return method(*args) - future = java.util.concurrent.FutureTask(g()) - java.lang.Thread(future).start() - try: - timeunit = java.util.concurrent.TimeUnit.MILLISECONDS - return future.get(int(timeout*1000), timeunit) - except java.util.concurrent.TimeoutException as ex: - future.cancel(True) - raise RuntimeError("canceled", ex) - return f - - print(limit(java.lang.Thread.sleep, timeout=1)(200)) - print(limit(java.lang.Thread.sleep, timeout=1)(20000)) + def limit(method, timeout): + """ Convert a Java method to asynchronous call with a specified timeout. """ + def f(*args): + @jpype.JImplements(java.util.concurrent.Callable) + class g: + @jpype.JOverride + def call(self): + return method(*args) + future = java.util.concurrent.FutureTask(g()) + java.lang.Thread(future).start() + try: + timeunit = java.util.concurrent.TimeUnit.MILLISECONDS + return future.get(int(timeout*1000), timeunit) + except java.util.concurrent.TimeoutException as ex: + future.cancel(True) + raise RuntimeError("canceled", ex) + return f + + print(limit(java.lang.Thread.sleep, timeout=1)(200)) + print(limit(java.lang.Thread.sleep, timeout=1)(20000)) Here we have limited the execution time of a Java call. @@ -3268,7 +3268,7 @@ this block as a fixture at the start of the test suite. faulthandler.enable() faulthandler.disable() except: - pass + pass This code enables fault handling and then returns the default handlers which will point back to those set by Java. From 38fedf7207e3f05c302143b36ec71aba2b7b6474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Zahradn=C3=ADk?= <lukaszahradnik96@seznam.cz> Date: Sat, 15 Jan 2022 00:59:42 +0100 Subject: [PATCH 023/110] Remove extra space --- doc/userguide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/userguide.rst b/doc/userguide.rst index 3c739641c..1648a40ba 100644 --- a/doc/userguide.rst +++ b/doc/userguide.rst @@ -888,7 +888,7 @@ of bits up or down relative to their storage size. JPype has mapped each of the primitive types into Python classes. To avoid conflicts with Python, JPype has named each primitive with a capital letter -``J`` followed by the primiti ve name starting with an upper case letter. +``J`` followed by the primitive name starting with an upper case letter. .. _JBoolean: From 0f8d7d9de45a304369da701c4beeab78eb43d980 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 16 Jan 2022 16:52:33 -0800 Subject: [PATCH 024/110] Fix issue with numpy arrays. --- doc/CHANGELOG.rst | 2 ++ native/common/jp_classhints.cpp | 12 ++++++++++++ native/common/jp_exception.cpp | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index ad1039609..673d0a54e 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,6 +5,8 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.3.1_dev0 - 2021-06-05** + - Fix issue with numpy arrays with no dimensions resulting in crash.. + - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. diff --git a/native/common/jp_classhints.cpp b/native/common/jp_classhints.cpp index 50c663640..26494102f 100644 --- a/native/common/jp_classhints.cpp +++ b/native/common/jp_classhints.cpp @@ -469,6 +469,11 @@ class JPConversionBuffer : public JPConversion // If it is a buffer we only need to test the first item in the list JPPySequence seq = JPPySequence::use(match.object); jlong length = seq.size(); + if (length == -1 && PyErr_Occurred()) + { + PyErr_Clear(); + return match.type = JPMatch::_none; + } match.type = JPMatch::_implicit; if (length > 0) { @@ -491,6 +496,7 @@ class JPConversionBuffer : public JPConversion virtual jvalue convert(JPMatch &match) override { + JP_TRACE_IN("JPConversionBuffer::convert"); JPJavaFrame frame(*match.frame); jvalue res; JPArrayClass *acls = (JPArrayClass *) match.closure; @@ -500,6 +506,7 @@ class JPConversionBuffer : public JPConversion ccls->setArrayRange(frame, array, 0, length, 1, match.object); res.l = frame.keep(array); return res; + JP_TRACE_OUT; } } _bufferConversion; @@ -516,6 +523,11 @@ class JPConversionSequence : public JPConversion JPClass *componentType = acls->getComponentType(); JPPySequence seq = JPPySequence::use(match.object); jlong length = seq.size(); + if (length==-1 && PyErr_Occurred()) + { + PyErr_Clear(); + return match.type = JPMatch::_none; + } match.type = JPMatch::_implicit; for (jlong i = 0; i < length && match.type > JPMatch::_none; i++) { diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index eb8eb20ee..477e99a86 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -286,6 +286,7 @@ void JPypeException::toPython() { string mesg; JP_TRACE_IN("JPypeException::toPython"); + JP_TRACE("err", PyErr_Occurred()); try { // Check the signals before processing the exception @@ -297,6 +298,11 @@ void JPypeException::toPython() mesg = getMessage(); JP_TRACE(m_Error.l); JP_TRACE(mesg.c_str()); + + // We already have a Python error on the stack. + if (PyErr_Occurred()) + return; + if (m_Type == JPError::_java_error) { JP_TRACE("Java exception"); From a48575d1955036238bd24a554a32dd57888811c7 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Mon, 17 Jan 2022 14:55:31 -0500 Subject: [PATCH 025/110] Use @Thrameos suggestion to clear the cache in method dispatch --- native/common/jp_methoddispatch.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/native/common/jp_methoddispatch.cpp b/native/common/jp_methoddispatch.cpp index ec0d4dbc5..d1c5af5e8 100644 --- a/native/common/jp_methoddispatch.cpp +++ b/native/common/jp_methoddispatch.cpp @@ -61,22 +61,23 @@ bool JPMethodDispatch::findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch // Anything better than explicit constitutes a hit on the cache if (bestMatch.m_Type > JPMatch::_explicit) return true; + else + // bad match so forget the overload. + bestMatch.m_Overload = 0; } // We need two copies of the match. One to hold the best match we have // found, and one to hold the test of the next overload. JPMethodMatch match = bestMatch; - - bool is_first_match = true; - // Check each overload in order (the TypeManager orders them by priority + // Check each overload in order (the TypeManager orders them by priority // according to Java overload rules). for (JPMethodList::iterator it = m_Overloads.begin(); it != m_Overloads.end(); ++it) { JPMethod* current = *it; JP_TRACE("Trying to match", current->toString()); - current->matches(frame, match, callInstance, arg); + current->matches(frame, match, callInstance, arg); JP_TRACE(" match ended", match.m_Type); if (match.m_Type == JPMatch::_exact) @@ -90,10 +91,9 @@ bool JPMethodDispatch::findOverload(JPJavaFrame& frame, JPMethodMatch &bestMatch continue; // If this is the first match then make it the best. - if (is_first_match) + if (bestMatch.m_Overload == 0) { bestMatch = match; - is_first_match = false; continue; } From 731454db00d86610999bbc9b21514a7ece9cfc38 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Mon, 17 Jan 2022 15:04:34 -0500 Subject: [PATCH 026/110] Convert spaces to tabs in C++ files --- native/common/jp_functional.cpp | 112 +++++++++++++++---------------- native/common/jp_typemanager.cpp | 12 ++-- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/native/common/jp_functional.cpp b/native/common/jp_functional.cpp index d41d08066..eaf0282ef 100644 --- a/native/common/jp_functional.cpp +++ b/native/common/jp_functional.cpp @@ -41,64 +41,64 @@ class JPConversionFunctional : public JPConversion if (!PyCallable_Check(match.object)) return match.type = JPMatch::_none; - // Modified from https://stackoverflow.com/a/1117735 - bool has_parameter_count = false; - // Get the __code__ attribute of the function, which contains details about the function - // Note: __code__ is func_code in Python 2.x - PyObject* function_code_object = PyObject_GetAttrString(match.object, "__code__"); - if(function_code_object) { - // get the argument count - PyObject* parameter_count_obj = PyObject_GetAttrString(function_code_object, "co_argcount"); - if(parameter_count_obj) { - has_parameter_count = true; - int optional_parameter_count = 0; - int flags = 0; - const int ACCEPT_VARGS_MASK = 0x04; // From https://docs.python.org/3/reference/datamodel.html - PyObject* flags_obj = PyObject_GetAttrString(function_code_object, "co_flags"); - if (flags_obj) { - flags = PyLong_AsLong(flags_obj); - Py_DECREF(flags_obj); - } - PyObject* optional_parameter_tuple = PyObject_GetAttrString(match.object, "__defaults__"); - if (optional_parameter_tuple && optional_parameter_tuple != Py_None) { - optional_parameter_count = PyTuple_Size(optional_parameter_tuple); - Py_DECREF(optional_parameter_tuple); - } - const int parameter_count = PyLong_AsLong(parameter_count_obj); - const int java_parameter_count = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); - const bool is_varargs = (flags & ACCEPT_VARGS_MASK) == ACCEPT_VARGS_MASK; + // Modified from https://stackoverflow.com/a/1117735 + bool has_parameter_count = false; + // Get the __code__ attribute of the function, which contains details about the function + // Note: __code__ is func_code in Python 2.x + PyObject* function_code_object = PyObject_GetAttrString(match.object, "__code__"); + if(function_code_object) { + // get the argument count + PyObject* parameter_count_obj = PyObject_GetAttrString(function_code_object, "co_argcount"); + if(parameter_count_obj) { + has_parameter_count = true; + int optional_parameter_count = 0; + int flags = 0; + const int ACCEPT_VARGS_MASK = 0x04; // From https://docs.python.org/3/reference/datamodel.html + PyObject* flags_obj = PyObject_GetAttrString(function_code_object, "co_flags"); + if (flags_obj) { + flags = PyLong_AsLong(flags_obj); + Py_DECREF(flags_obj); + } + PyObject* optional_parameter_tuple = PyObject_GetAttrString(match.object, "__defaults__"); + if (optional_parameter_tuple && optional_parameter_tuple != Py_None) { + optional_parameter_count = PyTuple_Size(optional_parameter_tuple); + Py_DECREF(optional_parameter_tuple); + } + const int parameter_count = PyLong_AsLong(parameter_count_obj); + const int java_parameter_count = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); + const bool is_varargs = (flags & ACCEPT_VARGS_MASK) == ACCEPT_VARGS_MASK; - // def my_func(x, y=None) should be both a Function and a BiFunction - // i.e. the number of parameters accepted by the interface MUST - // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) - // (Unless the function accept a variable number of arguments, then this restriction does not - // apply). - // 2. Be at least the minumum number of parameters accepted by the python function - // (parameter_count - optional_parameter_count = number of required parameters). - // Notes: - // - keywords vargs does not remove restriction 1 - // - keyword only arguments are not counted. - if ((!is_varargs && parameter_count < java_parameter_count) || - parameter_count - optional_parameter_count > java_parameter_count) { - match.type = JPMatch::_none; - } else { - match.conversion = this; - match.closure = cls; - match.type = JPMatch::_implicit; - } + // def my_func(x, y=None) should be both a Function and a BiFunction + // i.e. the number of parameters accepted by the interface MUST + // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) + // (Unless the function accept a variable number of arguments, then this restriction does not + // apply). + // 2. Be at least the minumum number of parameters accepted by the python function + // (parameter_count - optional_parameter_count = number of required parameters). + // Notes: + // - keywords vargs does not remove restriction 1 + // - keyword only arguments are not counted. + if ((!is_varargs && parameter_count < java_parameter_count) || + parameter_count - optional_parameter_count > java_parameter_count) { + match.type = JPMatch::_none; + } else { + match.conversion = this; + match.closure = cls; + match.type = JPMatch::_implicit; + } - Py_DECREF(parameter_count_obj); - } - Py_DECREF(function_code_object); - } - PyErr_Clear(); - if (!has_parameter_count) { - match.conversion = this; - match.closure = cls; - return match.type = JPMatch::_implicit; - } else { - return match.type; - } + Py_DECREF(parameter_count_obj); + } + Py_DECREF(function_code_object); + } + PyErr_Clear(); + if (!has_parameter_count) { + match.conversion = this; + match.closure = cls; + return match.type = JPMatch::_implicit; + } else { + return match.type; + } } virtual void getInfo(JPClass *cls, JPConversionInfo &info) override diff --git a/native/common/jp_typemanager.cpp b/native/common/jp_typemanager.cpp index 29ec4cbb6..82ff13f60 100644 --- a/native/common/jp_typemanager.cpp +++ b/native/common/jp_typemanager.cpp @@ -101,10 +101,10 @@ void JPTypeManager::populateMembers(JPClass* cls) int JPTypeManager::interfaceParameterCount(JPClass *cls) { - JP_TRACE_IN("JPTypeManager::interfaceParameterCount"); - JPJavaFrame frame = JPJavaFrame::outer(m_Context); - jvalue val[1]; - val[0].l = (jobject) cls->getJavaClass(); - return frame.CallIntMethodA(m_JavaTypeManager.get(), m_InterfaceParameterCount, val); - JP_TRACE_OUT; + JP_TRACE_IN("JPTypeManager::interfaceParameterCount"); + JPJavaFrame frame = JPJavaFrame::outer(m_Context); + jvalue val[1]; + val[0].l = (jobject) cls->getJavaClass(); + return frame.CallIntMethodA(m_JavaTypeManager.get(), m_InterfaceParameterCount, val); + JP_TRACE_OUT; } From 156f075c0dd813b292a6c51e364af0a88776f8b5 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 27 Feb 2022 08:06:36 -0800 Subject: [PATCH 027/110] Add python_requires. --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f9d8a1cda..ca7a4aa35 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,8 @@ include_dirs=[Path('native', 'common', 'include'), Path('native', 'python', 'include'), Path('native', 'embedded', 'include')], - sources=sorted(map(str, list(Path('native', 'common').glob('*.cpp'))+ - list(Path('native', 'python').glob('*.cpp'))+ + sources=sorted(map(str, list(Path('native', 'common').glob('*.cpp')) + + list(Path('native', 'python').glob('*.cpp')) + list(Path('native', 'embedded').glob('*.cpp')))), platform=platform, )) jpypeJar = Extension(name="org.jpype", @@ -61,6 +61,7 @@ author_email='devilwolf@users.sourceforge.net', maintainer='Luis Nell', maintainer_email='cooperate@originell.org', + python_requires=">=3.5", url='https://github.com/jpype-project/jpype', platforms=[ 'Operating System :: Microsoft :: Windows', From 7eb20981926055a3a75077e366324192e2facf72 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 27 Feb 2022 09:16:37 -0800 Subject: [PATCH 028/110] Fix for javadoc on Java14+ --- native/java/org/jpype/javadoc/JavadocExtractor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/java/org/jpype/javadoc/JavadocExtractor.java b/native/java/org/jpype/javadoc/JavadocExtractor.java index 2fa0ce8cf..dca342c45 100644 --- a/native/java/org/jpype/javadoc/JavadocExtractor.java +++ b/native/java/org/jpype/javadoc/JavadocExtractor.java @@ -115,7 +115,11 @@ public static Javadoc extractDocument(Class cls, Document doc) { Javadoc documentation = new Javadoc(); XPath xPath = XPathFactory.newInstance().newXPath(); - Node description = toFragment((Node) xPath.compile("//div[@class='description']/ul/li").evaluate(doc, XPathConstants.NODE)); + // Javadoc 8-13 + Node n = (Node) xPath.compile("//div[@class='description']/ul/li").evaluate(doc, XPathConstants.NODE); + if (n == null) // Javadoc 14+ + n = (Node) xPath.compile("//section[@class='description']").evaluate(doc, XPathConstants.NODE); + Node description = toFragment(n); if (description != null) { documentation.descriptionNode = description; From 18dd81063c89face746047407e52d57f8e062a7b Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 27 Feb 2022 09:19:27 -0800 Subject: [PATCH 029/110] Remove 3.5 from test matrix --- .azure/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 5538848d7..72310a9d1 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -50,9 +50,6 @@ jobs: dependsOn: Deps strategy: matrix: - linux-3.5: - imageName: "ubuntu-latest" - python.version: '3.5' linux-3.6: imageName: "ubuntu-latest" python.version: '3.6' @@ -65,9 +62,6 @@ jobs: linux-3.9: imageName: "ubuntu-latest" python.version: '3.9' - windows-3.5: - imageName: "vs2017-win2016" - python.version: '3.5' windows-3.6: imageName: "vs2017-win2016" python.version: '3.6' From 0b608f438498bdb8acafde013004bb3b56f313f2 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 27 Feb 2022 09:41:46 -0800 Subject: [PATCH 030/110] Fix for macos image --- .azure/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/build.yml b/.azure/build.yml index 5538848d7..8a25990d9 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -82,7 +82,7 @@ jobs: #python.version: '3.9' #jpypetest.fast: 'true' mac-3.9: - imageName: "macos-10.14" + imageName: "macos-11" python.version: '3.9' jpypetest.fast: 'true' From 55fa68ade900ef40b22548c7c362ec78ef9f976a Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 11 Mar 2022 08:10:31 -0800 Subject: [PATCH 031/110] Remove old requirement check --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index ca7a4aa35..1f9922f5e 100644 --- a/setup.py +++ b/setup.py @@ -23,9 +23,6 @@ from setuptools import Extension import glob -if sys.version_info[0] < 3 and sys.version_info[1] < 5: - raise RuntimeError("JPype requires Python 3.5 or later") - import setupext From d6d3c6ed81901fadaffbd612996622e6c1944a80 Mon Sep 17 00:00:00 2001 From: Charlie <krowven@gmail.com> Date: Mon, 4 Apr 2022 20:31:17 -0500 Subject: [PATCH 032/110] Update README.rst This might help someone else using Solaris/Unix and having to compile on two different environments. --- project/debug/unix/README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/project/debug/unix/README.rst b/project/debug/unix/README.rst index 0f79ea956..99c7b4d85 100644 --- a/project/debug/unix/README.rst +++ b/project/debug/unix/README.rst @@ -121,3 +121,22 @@ If the library does not match it should produce something like:: ./a.out: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory +Two different libstdc++ versions +-------------------------------- + + +If you are compiling the source on a test environment and pushing the compiled Jpype source to a different environment (Unix/Linux) you might see a situation where running:: + + ldd libstdc++.so.6 + +links to a different version of your production environment. + +You can rememdy this by copying the so file to your production system and creating a soft link to the file with:: + + ln -s libstdc++.so.6.0.29 libstdc++.so.6 + +Then override the system's (in the example below, Oracle/Solaris) 64bit dynamic linker path with:: + + export LD_LIBRARY_PATH_64=/path/to/linked/file/above + +NOTE: You will want to include the path to any other .so files you will be needing. From 8e90e5aaa4e87faae3bf8b825768986c4b089e41 Mon Sep 17 00:00:00 2001 From: "Martin K. Scherer" <marscher@users.noreply.github.com> Date: Thu, 7 Apr 2022 17:55:52 +0200 Subject: [PATCH 033/110] [readthedocs] install openjdk-8-jdk via apt (#1044) [readthedocs] install openjdk-8-jdk via apt to be able to compile jpypes java library. --- .readthedocs.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 450edf347..4b12f640c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,9 +13,16 @@ sphinx: # Optionally set the version of Python and requirements required to build your docs python: - version: 3.8 + version: "3.8" install: - method: pip path: . extra_requirements: - docs + +build: + image: latest + apt_packages: + - openjdk-8-jdk + + From d488e39c80c0ad3a72f05a1e39859202214a5ed4 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 14:47:01 -0700 Subject: [PATCH 034/110] Addressing issues. --- .azure/build.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 9d681f9e7..dca3f89cf 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -62,19 +62,25 @@ jobs: linux-3.9: imageName: "ubuntu-latest" python.version: '3.9' - windows-3.6: - imageName: "vs2017-win2016" + linux-3.10: + imageName: "ubuntu-latest" + python.version: '3.10' + windows-3.6: + imageName: "windows-2019" python.version: '3.6' windows-3.7: - imageName: "vs2017-win2016" + imageName: "windows-2019" python.version: '3.7' windows-3.8: - imageName: "vs2017-win2016" + imageName: "windows-2019" python.version: '3.8' - #windows-3.9: - #imageName: "vs2017-win2016" - #python.version: '3.9' - #jpypetest.fast: 'true' + windows-3.9: + imageName: "windows-2019" + python.version: '3.9' + windows-3.10: + imageName: "windows-2019" + python.version: '3.10' + jpypetest.fast: 'true' mac-3.9: imageName: "macos-11" python.version: '3.9' From f773ba446d476d516f047b4809793f040f9f96b9 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 14:51:09 -0700 Subject: [PATCH 035/110] Trigger not working --- .azure/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/build.yml b/.azure/build.yml index dca3f89cf..949cdaaa1 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -1,5 +1,5 @@ # JPype CI pipeline -pr: +trigger: branches: include: - master From 43c015924d0ee26a7e0cea20807a2c2035f932b7 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 14:56:14 -0700 Subject: [PATCH 036/110] Not sure what is wrong. --- .azure/build.yml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 949cdaaa1..9d681f9e7 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -1,5 +1,5 @@ # JPype CI pipeline -trigger: +pr: branches: include: - master @@ -62,25 +62,19 @@ jobs: linux-3.9: imageName: "ubuntu-latest" python.version: '3.9' - linux-3.10: - imageName: "ubuntu-latest" - python.version: '3.10' - windows-3.6: - imageName: "windows-2019" + windows-3.6: + imageName: "vs2017-win2016" python.version: '3.6' windows-3.7: - imageName: "windows-2019" + imageName: "vs2017-win2016" python.version: '3.7' windows-3.8: - imageName: "windows-2019" + imageName: "vs2017-win2016" python.version: '3.8' - windows-3.9: - imageName: "windows-2019" - python.version: '3.9' - windows-3.10: - imageName: "windows-2019" - python.version: '3.10' - jpypetest.fast: 'true' + #windows-3.9: + #imageName: "vs2017-win2016" + #python.version: '3.9' + #jpypetest.fast: 'true' mac-3.9: imageName: "macos-11" python.version: '3.9' From 633b71f0880e9b4dc3b147d245a72ba43d8d23e8 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 14:57:19 -0700 Subject: [PATCH 037/110] Not triggering. --- .azure/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/build.yml b/.azure/build.yml index 9d681f9e7..4e3e39050 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -1,5 +1,5 @@ # JPype CI pipeline -pr: +trigger: branches: include: - master From 3dc90e634a5ff2c6f342cf8bd8fac0ad705fb02e Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:07:43 -0700 Subject: [PATCH 038/110] Switch images. --- .azure/build.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 4e3e39050..8fc2d5712 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -62,19 +62,25 @@ jobs: linux-3.9: imageName: "ubuntu-latest" python.version: '3.9' + linux-3.10: + imageName: "ubuntu-latest" + python.version: '3.10' windows-3.6: - imageName: "vs2017-win2016" + imageName: "windows-2019" python.version: '3.6' windows-3.7: - imageName: "vs2017-win2016" + imageName: "windows-2019" python.version: '3.7' windows-3.8: - imageName: "vs2017-win2016" + imageName: "windows-2019" python.version: '3.8' - #windows-3.9: - #imageName: "vs2017-win2016" - #python.version: '3.9' + windows-3.9: + imageName: "windows-2019" + python.version: '3.9' #jpypetest.fast: 'true' + windows-3.10: + imageName: "windows-2019" + python.version: '3.10' mac-3.9: imageName: "macos-11" python.version: '3.9' From 6ea329cb784ac34a5254bd91b96e55b117ea9dde Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:13:49 -0700 Subject: [PATCH 039/110] Update versions --- .azure/build.yml | 6 ------ test-requirements.txt | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.azure/build.yml b/.azure/build.yml index 8fc2d5712..3274ea908 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -50,9 +50,6 @@ jobs: dependsOn: Deps strategy: matrix: - linux-3.6: - imageName: "ubuntu-latest" - python.version: '3.6' linux-3.7: imageName: "ubuntu-latest" python.version: '3.7' @@ -65,9 +62,6 @@ jobs: linux-3.10: imageName: "ubuntu-latest" python.version: '3.10' - windows-3.6: - imageName: "windows-2019" - python.version: '3.6' windows-3.7: imageName: "windows-2019" python.version: '3.7' diff --git a/test-requirements.txt b/test-requirements.txt index 5d8de99c2..e4a9c7143 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,3 @@ -pytest==4.6.9 -pyinstaller==4.0 +pytest==7.1.1 +pyinstaller==4.10 jedi==0.17.0 From ab0a34e8d5bdfd623d1f4454495382e60f7f9603 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:23:28 -0700 Subject: [PATCH 040/110] Remove deprecated tests. --- test/jpypetest/test_array.py | 6 +++--- test/jpypetest/test_conversionInt.py | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 1f35461d0..7e5b3b2fd 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -387,7 +387,7 @@ def checkArrayOf(self, jtype, dtype, mn=None, mx=None): self.assertTrue(np.all(a[:, :, 4:-2] == JArray.of(a[:, :, 4:-2]))) def checkArrayOfCast(self, jtype, dtype): - a = np.random.randint(0, 1, size=100, dtype=np.bool) + a = np.random.randint(0, 1, size=100, dtype=bool) ja = JArray.of(a, dtype=jtype) self.assertIsInstance(ja, JArray(jtype)) self.assertTrue(np.all(a.astype(dtype) == ja)) @@ -434,7 +434,7 @@ def checkArrayOfCast(self, jtype, dtype): @common.requireNumpy def testArrayOfBoolean(self): - self.checkArrayOf(JBoolean, np.bool, 0, 1) + self.checkArrayOf(JBoolean, bool, 0, 1) @common.requireNumpy def testArrayOfByte(self): @@ -462,7 +462,7 @@ def testArrayOfDouble(self): @common.requireNumpy def testArrayOfBooleanCast(self): - self.checkArrayOfCast(JBoolean, np.bool) + self.checkArrayOfCast(JBoolean, bool) @common.requireNumpy def testArrayOfByteCast(self): diff --git a/test/jpypetest/test_conversionInt.py b/test/jpypetest/test_conversionInt.py index 94651ba25..286b38e69 100644 --- a/test/jpypetest/test_conversionInt.py +++ b/test/jpypetest/test_conversionInt.py @@ -39,11 +39,6 @@ def setUp(self): def testIntFromInt(self): self.assertEqual(self.Test.callInt(int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testIntFromNPInt(self): - import numpy as np - self.assertEqual(self.Test.callInt(np.int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt8(self): import numpy as np @@ -72,12 +67,6 @@ def testIntFromFloat(self): with self.assertRaises(TypeError): self.Test.callInt(float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testIntFromNPFloat(self): - import numpy as np - with self.assertRaises(TypeError): - self.Test.callInt(np.float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat32(self): import numpy as np From edcf45e960d746ae3b2a4a80d4b99d98ceaab631 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:25:06 -0700 Subject: [PATCH 041/110] Remove more tests --- test/jpypetest/test_conversionLong.py | 11 ----------- test/jpypetest/test_conversionShort.py | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/test/jpypetest/test_conversionLong.py b/test/jpypetest/test_conversionLong.py index 2ae7aeae1..3b6ae5a2a 100644 --- a/test/jpypetest/test_conversionLong.py +++ b/test/jpypetest/test_conversionLong.py @@ -39,11 +39,6 @@ def setUp(self): def testLongFromInt(self): self.assertEqual(self.Test.callLong(int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testLongFromNPInt(self): - import numpy as np - self.assertEqual(self.Test.callLong(np.int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt8(self): import numpy as np @@ -72,12 +67,6 @@ def testLongFromFloat(self): with self.assertRaises(TypeError): self.Test.callLong(float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testLongFromNPFloat(self): - import numpy as np - with self.assertRaises(TypeError): - self.Test.callLong(np.float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat32(self): import numpy as np diff --git a/test/jpypetest/test_conversionShort.py b/test/jpypetest/test_conversionShort.py index 43427332c..d9d10cee7 100644 --- a/test/jpypetest/test_conversionShort.py +++ b/test/jpypetest/test_conversionShort.py @@ -39,11 +39,6 @@ def setUp(self): def testShortFromInt(self): self.assertEqual(self.Test.callShort(int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testShortFromNPInt(self): - import numpy as np - self.assertEqual(self.Test.callShort(np.int(123)), 123) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt8(self): import numpy as np @@ -72,12 +67,6 @@ def testShortFromFloat(self): with self.assertRaises(TypeError): self.Test.callShort(float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") - def testShortFromNPFloat(self): - import numpy as np - with self.assertRaises(TypeError): - self.Test.callShort(np.float(2)) - @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat32(self): import numpy as np From 30713013b8edd35012451b62628b41cbbc60f869 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:33:55 -0700 Subject: [PATCH 042/110] Deprecation warnings --- test/jpypetest/test_array.py | 6 +++--- test/jpypetest/test_buffer.py | 10 +++++----- test/jpypetest/test_conversionInt.py | 11 +++++++++++ test/jpypetest/test_conversionLong.py | 11 +++++++++++ test/jpypetest/test_conversionShort.py | 11 +++++++++++ test/jpypetest/test_jboolean.py | 4 ++-- test/jpypetest/test_jbyte.py | 2 +- test/jpypetest/test_jint.py | 3 ++- test/jpypetest/test_jlong.py | 3 ++- test/jpypetest/test_jshort.py | 2 +- 10 files changed, 49 insertions(+), 14 deletions(-) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 7e5b3b2fd..278cc1b10 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -387,7 +387,7 @@ def checkArrayOf(self, jtype, dtype, mn=None, mx=None): self.assertTrue(np.all(a[:, :, 4:-2] == JArray.of(a[:, :, 4:-2]))) def checkArrayOfCast(self, jtype, dtype): - a = np.random.randint(0, 1, size=100, dtype=bool) + a = np.random.randint(0, 1, size=100, dtype=np.bool_) ja = JArray.of(a, dtype=jtype) self.assertIsInstance(ja, JArray(jtype)) self.assertTrue(np.all(a.astype(dtype) == ja)) @@ -434,7 +434,7 @@ def checkArrayOfCast(self, jtype, dtype): @common.requireNumpy def testArrayOfBoolean(self): - self.checkArrayOf(JBoolean, bool, 0, 1) + self.checkArrayOf(JBoolean, np.bool_, 0, 1) @common.requireNumpy def testArrayOfByte(self): @@ -462,7 +462,7 @@ def testArrayOfDouble(self): @common.requireNumpy def testArrayOfBooleanCast(self): - self.checkArrayOfCast(JBoolean, bool) + self.checkArrayOfCast(JBoolean, np.bool_) @common.requireNumpy def testArrayOfByteCast(self): diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index 0ce730fb6..6d42c74a9 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -97,7 +97,7 @@ def testSlice(self): def executeConvert(self, jtype, dtype): n = 100 - na = np.random.randint(0, 1, size=n).astype(np.bool) + na = np.random.randint(0, 1, size=n).astype(np.bool_) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(-2**7, 2**7 - 1, size=n, dtype=np.int8) @@ -133,7 +133,7 @@ def executeConvert(self, jtype, dtype): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBoolConvert(self): - self.executeConvert(JArray(JBoolean), np.bool) + self.executeConvert(JArray(JBoolean), np.bool_) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testByteConvert(self): @@ -181,7 +181,7 @@ def executeFloatTest(self, jtype, size, dtype, code): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP1D(self): - self.executeIntTest(JBoolean, [0, 1], (100,), np.bool, "?") + self.executeIntTest(JBoolean, [0, 1], (100,), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP1D(self): @@ -213,7 +213,7 @@ def testDoubleToNP1D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP2D(self): - self.executeIntTest(JBoolean, [0, 1], (11, 10), np.bool, "?") + self.executeIntTest(JBoolean, [0, 1], (11, 10), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP2D(self): @@ -245,7 +245,7 @@ def testDoubleToNP2D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testBooleanToNP3D(self): - self.executeIntTest(JBoolean, [0, 1], (11, 10, 9), np.bool, "?") + self.executeIntTest(JBoolean, [0, 1], (11, 10, 9), np.bool_, "?") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testCharToNP3D(self): diff --git a/test/jpypetest/test_conversionInt.py b/test/jpypetest/test_conversionInt.py index 286b38e69..1499d63b4 100644 --- a/test/jpypetest/test_conversionInt.py +++ b/test/jpypetest/test_conversionInt.py @@ -39,6 +39,11 @@ def setUp(self): def testIntFromInt(self): self.assertEqual(self.Test.callInt(int(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testIntFromNPInt(self): + import numpy as np + self.assertEqual(self.Test.callInt(np.int_(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPInt8(self): import numpy as np @@ -67,6 +72,12 @@ def testIntFromFloat(self): with self.assertRaises(TypeError): self.Test.callInt(float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testIntFromNPFloat(self): + import numpy as np + with self.assertRaises(TypeError): + self.Test.callInt(np.float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat32(self): import numpy as np diff --git a/test/jpypetest/test_conversionLong.py b/test/jpypetest/test_conversionLong.py index 3b6ae5a2a..93fab4d96 100644 --- a/test/jpypetest/test_conversionLong.py +++ b/test/jpypetest/test_conversionLong.py @@ -39,6 +39,11 @@ def setUp(self): def testLongFromInt(self): self.assertEqual(self.Test.callLong(int(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testLongFromNPInt(self): + import numpy as np + self.assertEqual(self.Test.callLong(np.int_(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPInt8(self): import numpy as np @@ -67,6 +72,12 @@ def testLongFromFloat(self): with self.assertRaises(TypeError): self.Test.callLong(float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testLongFromNPFloat(self): + import numpy as np + with self.assertRaises(TypeError): + self.Test.callLong(np.float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat32(self): import numpy as np diff --git a/test/jpypetest/test_conversionShort.py b/test/jpypetest/test_conversionShort.py index d9d10cee7..33129876f 100644 --- a/test/jpypetest/test_conversionShort.py +++ b/test/jpypetest/test_conversionShort.py @@ -39,6 +39,11 @@ def setUp(self): def testShortFromInt(self): self.assertEqual(self.Test.callShort(int(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testShortFromNPInt(self): + import numpy as np + self.assertEqual(self.Test.callShort(np.int_(123)), 123) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPInt8(self): import numpy as np @@ -67,6 +72,12 @@ def testShortFromFloat(self): with self.assertRaises(TypeError): self.Test.callShort(float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") + def testShortFromNPFloat(self): + import numpy as np + with self.assertRaises(TypeError): + self.Test.callShort(np.float(2)) + @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat32(self): import numpy as np diff --git a/test/jpypetest/test_jboolean.py b/test/jpypetest/test_jboolean.py index c5e391f20..a3c00ed16 100644 --- a/test/jpypetest/test_jboolean.py +++ b/test/jpypetest/test_jboolean.py @@ -69,7 +69,7 @@ def testBooleanFromInt(self): @common.requireNumpy def testBooleanFromNPInt(self): import numpy as np - self.assertEqual(self.Test.callBoolean(np.int(123)), True) + self.assertEqual(self.Test.callBoolean(np.int_(123)), True) @common.requireNumpy def testBooleanFromNPInt8(self): @@ -130,7 +130,7 @@ def testJArrayConversionBool(self): def testSetFromNPBoolArray(self): import numpy as np n = 100 - a = np.random.randint(0, 1, size=n, dtype=np.bool) + a = np.random.randint(0, 1, size=n, dtype=np.bool_) jarr = jpype.JArray(jpype.JBoolean)(n) jarr[:] = a self.assertCountEqual(a, jarr) diff --git a/test/jpypetest/test_jbyte.py b/test/jpypetest/test_jbyte.py index 0cda7c43c..f68c9fdbc 100644 --- a/test/jpypetest/test_jbyte.py +++ b/test/jpypetest/test_jbyte.py @@ -77,7 +77,7 @@ def testByteFromInt(self): @common.requireNumpy def testByteFromNPInt(self): import numpy as np - self.assertEqual(self.fixture.callByte(np.int(123)), 123) + self.assertEqual(self.fixture.callByte(np.int_(123)), 123) @common.requireNumpy def testByteFromNPInt8(self): diff --git a/test/jpypetest/test_jint.py b/test/jpypetest/test_jint.py index 3293e6f9b..5f09d208e 100644 --- a/test/jpypetest/test_jint.py +++ b/test/jpypetest/test_jint.py @@ -166,6 +166,7 @@ def testUnBox(self): def testFromFloat(self): self.assertEqual(JInt._canConvertToJava(1.2345), "explicit") + @jpype.JImplements("java.util.function.IntSupplier") class q(object): @jpype.JOverride @@ -357,7 +358,7 @@ def testArrayConversion(self): @common.requireNumpy def testArrayInitFromNPInt(self): - a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int) + a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a) @common.requireNumpy diff --git a/test/jpypetest/test_jlong.py b/test/jpypetest/test_jlong.py index bfb66f7e3..cd4483bcc 100644 --- a/test/jpypetest/test_jlong.py +++ b/test/jpypetest/test_jlong.py @@ -166,6 +166,7 @@ def testUnBox(self): def testFromFloat(self): self.assertEqual(JLong._canConvertToJava(1.2345), "explicit") + @jpype.JImplements("java.util.function.LongSupplier") class q(object): @jpype.JOverride @@ -357,7 +358,7 @@ def testArrayConversion(self): @common.requireNumpy def testArrayInitFromNPInt(self): - a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int) + a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a) @common.requireNumpy diff --git a/test/jpypetest/test_jshort.py b/test/jpypetest/test_jshort.py index 82ad4384c..cb6b679a5 100644 --- a/test/jpypetest/test_jshort.py +++ b/test/jpypetest/test_jshort.py @@ -347,7 +347,7 @@ def testArrayConversion(self): @common.requireNumpy def testArrayInitFromNPInt(self): - a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int) + a = np.random.randint(-2**31, 2**31 - 1, size=100, dtype=np.int_) self.checkArrayType(a, a.astype(np.int16)) @common.requireNumpy From b944fb7446b664ec23c695f3cb0182cdfbaa4465 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 15:41:53 -0700 Subject: [PATCH 043/110] Fix np.float --- test/jpypetest/test_conversionInt.py | 2 +- test/jpypetest/test_conversionLong.py | 2 +- test/jpypetest/test_conversionShort.py | 2 +- test/jpypetest/test_jboolean.py | 2 +- test/jpypetest/test_jbyte.py | 2 +- test/jpypetest/test_jdouble.py | 2 +- test/jpypetest/test_jfloat.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/jpypetest/test_conversionInt.py b/test/jpypetest/test_conversionInt.py index 1499d63b4..bb94ca025 100644 --- a/test/jpypetest/test_conversionInt.py +++ b/test/jpypetest/test_conversionInt.py @@ -76,7 +76,7 @@ def testIntFromFloat(self): def testIntFromNPFloat(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callInt(np.float(2)) + self.Test.callInt(np.float_(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntFromNPFloat32(self): diff --git a/test/jpypetest/test_conversionLong.py b/test/jpypetest/test_conversionLong.py index 93fab4d96..ae3f31313 100644 --- a/test/jpypetest/test_conversionLong.py +++ b/test/jpypetest/test_conversionLong.py @@ -76,7 +76,7 @@ def testLongFromFloat(self): def testLongFromNPFloat(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callLong(np.float(2)) + self.Test.callLong(np.float_(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongFromNPFloat32(self): diff --git a/test/jpypetest/test_conversionShort.py b/test/jpypetest/test_conversionShort.py index 33129876f..d3fd81ffa 100644 --- a/test/jpypetest/test_conversionShort.py +++ b/test/jpypetest/test_conversionShort.py @@ -76,7 +76,7 @@ def testShortFromFloat(self): def testShortFromNPFloat(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callShort(np.float(2)) + self.Test.callShort(np.float_(2)) @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testShortFromNPFloat32(self): diff --git a/test/jpypetest/test_jboolean.py b/test/jpypetest/test_jboolean.py index a3c00ed16..ed0f6bd99 100644 --- a/test/jpypetest/test_jboolean.py +++ b/test/jpypetest/test_jboolean.py @@ -103,7 +103,7 @@ def testBooleanFromFloat(self): def testBooleanFromNPFloat(self): import numpy as np with self.assertRaises(TypeError): - self.Test.callBoolean(np.float(2)) + self.Test.callBoolean(np.float_(2)) @common.requireNumpy def testBooleanFromNPFloat32(self): diff --git a/test/jpypetest/test_jbyte.py b/test/jpypetest/test_jbyte.py index f68c9fdbc..55bfd4ec8 100644 --- a/test/jpypetest/test_jbyte.py +++ b/test/jpypetest/test_jbyte.py @@ -111,7 +111,7 @@ def testByteFromFloat(self): def testByteFromNPFloat(self): import numpy as np with self.assertRaises(TypeError): - self.fixture.callByte(np.float(2)) + self.fixture.callByte(np.float_(2)) @common.requireNumpy def testByteFromNPFloat32(self): diff --git a/test/jpypetest/test_jdouble.py b/test/jpypetest/test_jdouble.py index 34542c327..09c03351b 100644 --- a/test/jpypetest/test_jdouble.py +++ b/test/jpypetest/test_jdouble.py @@ -375,7 +375,7 @@ def testArraySetFromNPDouble(self): @common.requireNumpy def testArrayInitFromNPFloat(self): - a = np.random.random(100).astype(np.float) + a = np.random.random(100).astype(np.float_) jarr = JArray(JDouble)(a) self.assertElementsAlmostEqual(a, jarr) diff --git a/test/jpypetest/test_jfloat.py b/test/jpypetest/test_jfloat.py index 4be1e5765..4fbce3591 100644 --- a/test/jpypetest/test_jfloat.py +++ b/test/jpypetest/test_jfloat.py @@ -383,7 +383,7 @@ def testArraySetFromNPDouble(self): @common.requireNumpy def testArrayInitFromNPFloat(self): - a = np.random.random(100).astype(np.float) + a = np.random.random(100).astype(np.float_) jarr = JArray(JFloat)(a) self.assertElementsAlmostEqual(a, jarr) From 1a86d7d342459e0808a93f411f7a69da86b49765 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Apr 2022 16:38:57 -0700 Subject: [PATCH 044/110] Use allclose to fix conversion issue. --- test/jpypetest/test_buffer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index 6d42c74a9..d8b9fd5df 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -122,8 +122,8 @@ def executeConvert(self, jtype, dtype): self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) na = np.random.randint(0, 2**64 - 1, size=n, dtype=np.uint64) - self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) - == np.array(na, dtype=dtype))) + self.assertTrue(np.allclose(np.array(jtype(na), dtype=dtype), + np.array(na, dtype=dtype), atol=1e-8)) na = np.random.random(n).astype(np.float32) self.assertTrue(np.all(np.array(jtype(na), dtype=dtype) == np.array(na, dtype=dtype))) From 614a8de88bbc52c4d87e7a69bd795843eb4c1dff Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 13 Apr 2022 11:21:54 -0700 Subject: [PATCH 045/110] Check type for long buffers to "l" from "q" --- native/common/jp_longtype.cpp | 4 ++-- test/jpypetest/test_buffer.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/common/jp_longtype.cpp b/native/common/jp_longtype.cpp index cba98cfc4..7df87648f 100644 --- a/native/common/jp_longtype.cpp +++ b/native/common/jp_longtype.cpp @@ -274,7 +274,7 @@ void JPLongType::getView(JPArrayView& view) JPJavaFrame frame = JPJavaFrame::outer(view.getContext()); view.m_Memory = (void*) frame.GetLongArrayElements( (jlongArray) view.m_Array->getJava(), &view.m_IsCopy); - view.m_Buffer.format = "q"; + view.m_Buffer.format = "l"; view.m_Buffer.itemsize = sizeof (jlong); } @@ -294,7 +294,7 @@ void JPLongType::releaseView(JPArrayView& view) const char* JPLongType::getBufferFormat() { - return "q"; + return "l"; } Py_ssize_t JPLongType::getItemSize() diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index d8b9fd5df..20761d55d 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -201,7 +201,7 @@ def testIntToNP1D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP1D(self): - self.executeIntTest(JLong, [-2**63, 2**63 - 1], (100,), np.int64, "q") + self.executeIntTest(JLong, [-2**63, 2**63 - 1], (100,), np.int64, "l") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP1D(self): @@ -233,7 +233,7 @@ def testIntToNP2D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP2D(self): - self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10), np.int64, "q") + self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10), np.int64, "l") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP2D(self): @@ -268,7 +268,7 @@ def testIntToNP3D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP3D(self): self.executeIntTest(JLong, [-2**63, 2**63 - 1], - (11, 10, 9), np.int64, "q") + (11, 10, 9), np.int64, "l") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP3D(self): From 5772424708dd98f4f1f57f05bf3fed2468aeeda2 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 13 Apr 2022 19:36:27 -0700 Subject: [PATCH 046/110] Handle all array orders. --- native/common/jp_convert.cpp | 197 ++++++++++++++++++++++++++++++---- native/common/jp_longtype.cpp | 4 +- test/jpypetest/test_array.py | 15 +++ test/jpypetest/test_buffer.py | 7 +- 4 files changed, 199 insertions(+), 24 deletions(-) diff --git a/native/common/jp_convert.cpp b/native/common/jp_convert.cpp index 29a2a6d78..90e140770 100644 --- a/native/common/jp_convert.cpp +++ b/native/common/jp_convert.cpp @@ -80,6 +80,49 @@ class Convert } } ; + +template <jvalue func(void *c) > +class Reverse +{ +public: + + static jvalue call2(void* c) + { + char r[2]; + char* c2 = (char*)c; + r[0]=c2[1]; + r[1]=c2[0]; + return func(r); + } + + static jvalue call4(void* c) + { + char r[4]; + char* c2 = (char*)c; + r[0]=c2[3]; + r[1]=c2[2]; + r[2]=c2[1]; + r[3]=c2[0]; + return func(r); + } + + static jvalue call8(void* c) + { + char r[8]; + char* c2 = (char*)c; + r[0]=c2[7]; + r[1]=c2[6]; + r[2]=c2[5]; + r[3]=c2[4]; + r[4]=c2[3]; + r[5]=c2[2]; + r[6]=c2[1]; + r[7]=c2[0]; + return func(r); + } +} ; + + } // namespace jconverter getConverter(const char* from, int itemsize, const char* to) @@ -87,11 +130,37 @@ jconverter getConverter(const char* from, int itemsize, const char* to) // If not specified then the type is bytes if (from == NULL) from = "B"; + + // Skip specifiers + bool reverse = false; + unsigned int x = 1; + bool little = *((char*)&x)==1; + switch (from[0]) + { + case '!': + case '>': + if (little) + reverse = true; + from++; + break; + case '<': + if (!little) + reverse = true; + from++; + break; + case '@': + case '=': + from++; + default: + break; + } + // Standard size for 'l' is 4 in docs, but numpy uses format 'l' for long long if (itemsize == 8 && from[0] == 'l') from = "q"; if (itemsize == 8 && from[0] == 'L') from = "Q"; + switch (from[0]) { case '?': @@ -108,7 +177,7 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<int8_t>::toF; case 'd': return &Convert<int8_t>::toD; } - return 0; + break; case 'B': switch (to[0]) { @@ -121,9 +190,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<uint8_t>::toF; case 'd': return &Convert<uint8_t>::toD; } - return 0; + break; case 'h': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<int16_t>::toZ>::call2; + case 'b': return &Reverse<Convert<int16_t>::toB>::call2; + case 'c': return &Reverse<Convert<int16_t>::toC>::call2; + case 's': return &Reverse<Convert<int16_t>::toS>::call2; + case 'i': return &Reverse<Convert<int16_t>::toI>::call2; + case 'j': return &Reverse<Convert<int16_t>::toJ>::call2; + case 'f': return &Reverse<Convert<int16_t>::toF>::call2; + case 'd': return &Reverse<Convert<int16_t>::toD>::call2; + } + else switch (to[0]) { case 'z': return &Convert<int16_t>::toZ; case 'b': return &Convert<int16_t>::toB; @@ -134,9 +214,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<int16_t>::toF; case 'd': return &Convert<int16_t>::toD; } - return 0; + break; case 'H': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<uint16_t>::toZ>::call2; + case 'b': return &Reverse<Convert<uint16_t>::toB>::call2; + case 'c': return &Reverse<Convert<uint16_t>::toC>::call2; + case 's': return &Reverse<Convert<uint16_t>::toS>::call2; + case 'i': return &Reverse<Convert<uint16_t>::toI>::call2; + case 'j': return &Reverse<Convert<uint16_t>::toJ>::call2; + case 'f': return &Reverse<Convert<uint16_t>::toF>::call2; + case 'd': return &Reverse<Convert<uint16_t>::toD>::call2; + } + else switch (to[0]) { case 'z': return &Convert<uint16_t>::toZ; case 'b': return &Convert<uint16_t>::toB; @@ -147,10 +238,21 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<uint16_t>::toF; case 'd': return &Convert<uint16_t>::toD; } - return 0; + break; case 'i': case 'l': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<int32_t>::toZ>::call4; + case 'b': return &Reverse<Convert<int32_t>::toB>::call4; + case 'c': return &Reverse<Convert<int32_t>::toC>::call4; + case 's': return &Reverse<Convert<int32_t>::toS>::call4; + case 'i': return &Reverse<Convert<int32_t>::toI>::call4; + case 'j': return &Reverse<Convert<int32_t>::toJ>::call4; + case 'f': return &Reverse<Convert<int32_t>::toF>::call4; + case 'd': return &Reverse<Convert<int32_t>::toD>::call4; + } + else switch (to[0]) { case 'z': return &Convert<int32_t>::toZ; case 'b': return &Convert<int32_t>::toB; @@ -161,10 +263,21 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<int32_t>::toF; case 'd': return &Convert<int32_t>::toD; } - return 0; + break; case 'I': case 'L': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<uint32_t>::toZ>::call4; + case 'b': return &Reverse<Convert<uint32_t>::toB>::call4; + case 'c': return &Reverse<Convert<uint32_t>::toC>::call4; + case 's': return &Reverse<Convert<uint32_t>::toS>::call4; + case 'i': return &Reverse<Convert<uint32_t>::toI>::call4; + case 'j': return &Reverse<Convert<uint32_t>::toJ>::call4; + case 'f': return &Reverse<Convert<uint32_t>::toF>::call4; + case 'd': return &Reverse<Convert<uint32_t>::toD>::call4; + } + else switch (to[0]) { case 'z': return &Convert<uint32_t>::toZ; case 'b': return &Convert<uint32_t>::toB; @@ -175,9 +288,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<uint32_t>::toF; case 'd': return &Convert<uint32_t>::toD; } - return 0; + break; case 'q': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<int64_t>::toZ>::call8; + case 'b': return &Reverse<Convert<int64_t>::toB>::call8; + case 'c': return &Reverse<Convert<int64_t>::toC>::call8; + case 's': return &Reverse<Convert<int64_t>::toS>::call8; + case 'i': return &Reverse<Convert<int64_t>::toI>::call8; + case 'j': return &Reverse<Convert<int64_t>::toJ>::call8; + case 'f': return &Reverse<Convert<int64_t>::toF>::call8; + case 'd': return &Reverse<Convert<int64_t>::toD>::call8; + } + else switch (to[0]) { case 'z': return &Convert<int64_t>::toZ; case 'b': return &Convert<int64_t>::toB; @@ -188,9 +312,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<int64_t>::toF; case 'd': return &Convert<int64_t>::toD; } - return 0; + break; case 'Q': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<uint64_t>::toZ>::call8; + case 'b': return &Reverse<Convert<uint64_t>::toB>::call8; + case 'c': return &Reverse<Convert<uint64_t>::toC>::call8; + case 's': return &Reverse<Convert<uint64_t>::toS>::call8; + case 'i': return &Reverse<Convert<uint64_t>::toI>::call8; + case 'j': return &Reverse<Convert<uint64_t>::toJ>::call8; + case 'f': return &Reverse<Convert<uint64_t>::toF>::call8; + case 'd': return &Reverse<Convert<uint64_t>::toD>::call8; + } + else switch (to[0]) { case 'z': return &Convert<uint64_t>::toZ; case 'b': return &Convert<uint64_t>::toB; @@ -201,9 +336,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<uint64_t>::toF; case 'd': return &Convert<uint64_t>::toD; } - return 0; + break; case 'f': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<float>::toZ>::call4; + case 'b': return &Reverse<Convert<float>::toB>::call4; + case 'c': return &Reverse<Convert<float>::toC>::call4; + case 's': return &Reverse<Convert<float>::toS>::call4; + case 'i': return &Reverse<Convert<float>::toI>::call4; + case 'j': return &Reverse<Convert<float>::toJ>::call4; + case 'f': return &Reverse<Convert<float>::toF>::call4; + case 'd': return &Reverse<Convert<float>::toD>::call4; + } + else switch (to[0]) { case 'z': return &Convert<float>::toZ; case 'b': return &Convert<float>::toB; @@ -214,9 +360,20 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<float>::toF; case 'd': return &Convert<float>::toD; } - return 0; + break; case 'd': - switch (to[0]) + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<double>::toZ>::call8; + case 'b': return &Reverse<Convert<double>::toB>::call8; + case 'c': return &Reverse<Convert<double>::toC>::call8; + case 's': return &Reverse<Convert<double>::toS>::call8; + case 'i': return &Reverse<Convert<double>::toI>::call8; + case 'j': return &Reverse<Convert<double>::toJ>::call8; + case 'f': return &Reverse<Convert<double>::toF>::call8; + case 'd': return &Reverse<Convert<double>::toD>::call8; + } + else switch (to[0]) { case 'z': return &Convert<double>::toZ; case 'b': return &Convert<double>::toB; @@ -227,10 +384,12 @@ jconverter getConverter(const char* from, int itemsize, const char* to) case 'f': return &Convert<double>::toF; case 'd': return &Convert<double>::toD; } - return 0; + break; case 'n': case 'N': case 'P': - default: return 0; + default: break; } + PyErr_Format(PyExc_TypeError, "Unable to handle buffer type '%s'", from); + JP_RAISE_PYTHON(); } diff --git a/native/common/jp_longtype.cpp b/native/common/jp_longtype.cpp index 7df87648f..dc91feae1 100644 --- a/native/common/jp_longtype.cpp +++ b/native/common/jp_longtype.cpp @@ -274,7 +274,7 @@ void JPLongType::getView(JPArrayView& view) JPJavaFrame frame = JPJavaFrame::outer(view.getContext()); view.m_Memory = (void*) frame.GetLongArrayElements( (jlongArray) view.m_Array->getJava(), &view.m_IsCopy); - view.m_Buffer.format = "l"; + view.m_Buffer.format = "=q"; view.m_Buffer.itemsize = sizeof (jlong); } @@ -294,7 +294,7 @@ void JPLongType::releaseView(JPArrayView& view) const char* JPLongType::getBufferFormat() { - return "l"; + return "=q"; } Py_ssize_t JPLongType::getItemSize() diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 278cc1b10..a70010368 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -597,3 +597,18 @@ def testShortcut(self): # Test multidimensional self.assertEqual(JDouble[5, 5].getClass(), JArray(JDouble, 2)(5).getClass()) self.assertEqual(JObject[5, 5].getClass(), JArray(JObject, 2)(5).getClass()) + + def executeOrder(self, jtype, dtype): + a = np.array([1, 2, 3]) + ja2 = jpype.JLong[:](a) + for order in ("=", "<", ">"): + dt = np.dtype(dtype).newbyteorder(order) + a = np.array([1, 2, 3], dtype=dt) + ja = jpype.JLong[:](a) + self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2)) + + @common.requireNumpy + def testOrder(self): + for i in (jpype.JShort, jpype.JInt, jpype.JLong, jpype.JFloat, jpype.JDouble): + for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64): + self.executeOrder(i, j) diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index 20761d55d..30a973bf0 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -163,6 +163,7 @@ def executeIntTest(self, jtype, limits, size, dtype, code): data = np.random.randint(limits[0], limits[1], size=size, dtype=dtype) a = JArray(jtype, data.ndim)(data.tolist()) u = np.array(a) + self.assertEqual(u.dtype.type, dtype) self.assertTrue(np.all(data == u)) mv = memoryview(a) self.assertEqual(mv.format, code) @@ -201,7 +202,7 @@ def testIntToNP1D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP1D(self): - self.executeIntTest(JLong, [-2**63, 2**63 - 1], (100,), np.int64, "l") + self.executeIntTest(JLong, [-2**63, 2**63 - 1], (100,), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP1D(self): @@ -233,7 +234,7 @@ def testIntToNP2D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP2D(self): - self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10), np.int64, "l") + self.executeIntTest(JLong, [-2**63, 2**63 - 1], (11, 10), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP2D(self): @@ -268,7 +269,7 @@ def testIntToNP3D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP3D(self): self.executeIntTest(JLong, [-2**63, 2**63 - 1], - (11, 10, 9), np.int64, "l") + (11, 10, 9), np.int64, "=q") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testFloatToNP3D(self): From b74dd003a49dca468ba652616b4dda58cee88e22 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 13 Apr 2022 19:44:54 -0700 Subject: [PATCH 047/110] Fix test --- test/jpypetest/test_array.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index a70010368..9536122c9 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -600,12 +600,12 @@ def testShortcut(self): def executeOrder(self, jtype, dtype): a = np.array([1, 2, 3]) - ja2 = jpype.JLong[:](a) + ja2 = jtype[:](a) for order in ("=", "<", ">"): dt = np.dtype(dtype).newbyteorder(order) a = np.array([1, 2, 3], dtype=dt) - ja = jpype.JLong[:](a) - self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2)) + ja = jtype[:](a) + self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2), "Order issue with %s %s" % (jtype, dtype)) @common.requireNumpy def testOrder(self): From ce097c2dec84dd1bacc2f0dbc0ea28361bc8c6b7 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 13 Apr 2022 20:31:28 -0700 Subject: [PATCH 048/110] WIP --- native/common/jp_inttype.cpp | 4 ++-- test/jpypetest/test_array.py | 2 +- test/jpypetest/test_buffer.py | 30 ++++++++++++++++++++++-------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/native/common/jp_inttype.cpp b/native/common/jp_inttype.cpp index 5a42f98a5..77defb97d 100644 --- a/native/common/jp_inttype.cpp +++ b/native/common/jp_inttype.cpp @@ -275,7 +275,7 @@ void JPIntType::getView(JPArrayView& view) view.m_IsCopy = false; view.m_Memory = (void*) frame.GetIntArrayElements( (jintArray) view.m_Array->getJava(), &view.m_IsCopy); - view.m_Buffer.format = "i"; + view.m_Buffer.format = "=i"; view.m_Buffer.itemsize = sizeof (jint); } @@ -295,7 +295,7 @@ void JPIntType::releaseView(JPArrayView& view) const char* JPIntType::getBufferFormat() { - return "i"; + return "=i"; } Py_ssize_t JPIntType::getItemSize() diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 9536122c9..3bfd87d00 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -610,5 +610,5 @@ def executeOrder(self, jtype, dtype): @common.requireNumpy def testOrder(self): for i in (jpype.JShort, jpype.JInt, jpype.JLong, jpype.JFloat, jpype.JDouble): - for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64): + for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float32, np.float64): self.executeOrder(i, j) diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index 30a973bf0..b9f04e0e0 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -71,10 +71,24 @@ def testLongToBytes(self): ja = JArray(JLong)(data) self.assertEqual(len(bytes(ja)), 6 * 8) - def testMemoryViewWrite(self): + def testMemoryViewWriteShort(self): + data = [1, 2, 3, 4, 5] + ja = JArray(JShort)(data) + b = memoryview(ja).cast("B") # Create a view + with self.assertRaises(TypeError): + b[0] = 123 # Alter the memory using the view + + def testMemoryViewWriteInt(self): data = [1, 2, 3, 4, 5] ja = JArray(JInt)(data) - b = memoryview(ja) # Create a view + b = memoryview(ja).cast("B") # Create a view + with self.assertRaises(TypeError): + b[0] = 123 # Alter the memory using the view + + def testMemoryViewWriteLong(self): + data = [1, 2, 3, 4, 5] + ja = JArray(JLong)(data) + b = memoryview(ja).cast("B") # Create a view with self.assertRaises(TypeError): b[0] = 123 # Alter the memory using the view @@ -163,12 +177,12 @@ def executeIntTest(self, jtype, limits, size, dtype, code): data = np.random.randint(limits[0], limits[1], size=size, dtype=dtype) a = JArray(jtype, data.ndim)(data.tolist()) u = np.array(a) - self.assertEqual(u.dtype.type, dtype) - self.assertTrue(np.all(data == u)) mv = memoryview(a) - self.assertEqual(mv.format, code) + self.assertEqual(mv.format, code, "Type issue %s" % type(a)) self.assertEqual(mv.shape, data.shape) self.assertTrue(mv.readonly) + self.assertEqual(u.dtype.type, dtype, "Problem with %s %s" % (jtype, dtype)) + self.assertTrue(np.all(data == u)) def executeFloatTest(self, jtype, size, dtype, code): data = np.random.rand(*size).astype(dtype) @@ -198,7 +212,7 @@ def testShortToNP1D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP1D(self): - self.executeIntTest(JInt, [-2**31, 2**31 - 1], (100,), np.int32, "i") + self.executeIntTest(JInt, [-2**31, 2**31 - 1], (100,), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP1D(self): @@ -230,7 +244,7 @@ def testShortToNP2D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP2D(self): - self.executeIntTest(JInt, [-2**31, 2**31 - 1], (11, 10), np.int32, "i") + self.executeIntTest(JInt, [-2**31, 2**31 - 1], (11, 10), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP2D(self): @@ -264,7 +278,7 @@ def testShortToNP3D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testIntToNP3D(self): self.executeIntTest(JInt, [-2**31, 2**31 - 1], - (11, 10, 9), np.int32, "i") + (11, 10, 9), np.int32, "=i") @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testLongToNP3D(self): From d725bcb07a9e66c49d46c19d82731dfd85027d2c Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Thu, 14 Apr 2022 10:18:50 -0700 Subject: [PATCH 049/110] Testing --- native/common/jp_convert.cpp | 49 ++++++++++++- native/python/jp_pythontypes.cpp | 6 +- test/jpypetest/test_array.py | 15 ---- test/jpypetest/test_buffer.py | 122 +++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 20 deletions(-) diff --git a/native/common/jp_convert.cpp b/native/common/jp_convert.cpp index 90e140770..04e5f6e4d 100644 --- a/native/common/jp_convert.cpp +++ b/native/common/jp_convert.cpp @@ -386,10 +386,55 @@ jconverter getConverter(const char* from, int itemsize, const char* to) } break; case 'n': + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<Py_ssize_t>::toZ>::call8; + case 'b': return &Reverse<Convert<Py_ssize_t>::toB>::call8; + case 'c': return &Reverse<Convert<Py_ssize_t>::toC>::call8; + case 's': return &Reverse<Convert<Py_ssize_t>::toS>::call8; + case 'i': return &Reverse<Convert<Py_ssize_t>::toI>::call8; + case 'j': return &Reverse<Convert<Py_ssize_t>::toJ>::call8; + case 'f': return &Reverse<Convert<Py_ssize_t>::toF>::call8; + case 'd': return &Reverse<Convert<Py_ssize_t>::toD>::call8; + } + else switch (to[0]) + { + case 'z': return &Convert<Py_ssize_t>::toZ; + case 'b': return &Convert<Py_ssize_t>::toB; + case 'c': return &Convert<Py_ssize_t>::toC; + case 's': return &Convert<Py_ssize_t>::toS; + case 'i': return &Convert<Py_ssize_t>::toI; + case 'j': return &Convert<Py_ssize_t>::toJ; + case 'f': return &Convert<Py_ssize_t>::toF; + case 'd': return &Convert<Py_ssize_t>::toD; + } + break; case 'N': - case 'P': + if (reverse) switch (to[0]) + { + case 'z': return &Reverse<Convert<size_t>::toZ>::call8; + case 'b': return &Reverse<Convert<size_t>::toB>::call8; + case 'c': return &Reverse<Convert<size_t>::toC>::call8; + case 's': return &Reverse<Convert<size_t>::toS>::call8; + case 'i': return &Reverse<Convert<size_t>::toI>::call8; + case 'j': return &Reverse<Convert<size_t>::toJ>::call8; + case 'f': return &Reverse<Convert<size_t>::toF>::call8; + case 'd': return &Reverse<Convert<size_t>::toD>::call8; + } + else switch (to[0]) + { + case 'z': return &Convert<size_t>::toZ; + case 'b': return &Convert<size_t>::toB; + case 'c': return &Convert<size_t>::toC; + case 's': return &Convert<size_t>::toS; + case 'i': return &Convert<size_t>::toI; + case 'j': return &Convert<size_t>::toJ; + case 'f': return &Convert<size_t>::toF; + case 'd': return &Convert<size_t>::toD; + } + break; default: break; } - PyErr_Format(PyExc_TypeError, "Unable to handle buffer type '%s'", from); + PyErr_Format(PyExc_ValueError, "Unable to handle buffer type '%s'", from); JP_RAISE_PYTHON(); } diff --git a/native/python/jp_pythontypes.cpp b/native/python/jp_pythontypes.cpp index 05485e73d..3bf4b4e37 100644 --- a/native/python/jp_pythontypes.cpp +++ b/native/python/jp_pythontypes.cpp @@ -244,7 +244,7 @@ jchar JPPyString::asCharUTF16(PyObject* pyobj) Py_UCS4 value = PyUnicode_READ_CHAR(pyobj, 0); if (value > 0xffff) { - JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into java char"); + JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into Java char"); } return value; } @@ -268,12 +268,12 @@ jchar JPPyString::asCharUTF16(PyObject* pyobj) Py_UCS4 value = PyUnicode_ReadChar(pyobj, 0); if (value > 0xffff) { - JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into java char"); + JP_RAISE(PyExc_ValueError, "Unable to pack 4 byte unicode into Java char"); } return value; } #endif - PyErr_Format(PyExc_TypeError, "Unable to convert '%s' to char", Py_TYPE(pyobj)->tp_name); + PyErr_Format(PyExc_TypeError, "Unable to convert '%s' to Java char", Py_TYPE(pyobj)->tp_name); JP_RAISE_PYTHON(); } diff --git a/test/jpypetest/test_array.py b/test/jpypetest/test_array.py index 3bfd87d00..278cc1b10 100644 --- a/test/jpypetest/test_array.py +++ b/test/jpypetest/test_array.py @@ -597,18 +597,3 @@ def testShortcut(self): # Test multidimensional self.assertEqual(JDouble[5, 5].getClass(), JArray(JDouble, 2)(5).getClass()) self.assertEqual(JObject[5, 5].getClass(), JArray(JObject, 2)(5).getClass()) - - def executeOrder(self, jtype, dtype): - a = np.array([1, 2, 3]) - ja2 = jtype[:](a) - for order in ("=", "<", ">"): - dt = np.dtype(dtype).newbyteorder(order) - a = np.array([1, 2, 3], dtype=dt) - ja = jtype[:](a) - self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2), "Order issue with %s %s" % (jtype, dtype)) - - @common.requireNumpy - def testOrder(self): - for i in (jpype.JShort, jpype.JInt, jpype.JLong, jpype.JFloat, jpype.JDouble): - for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float32, np.float64): - self.executeOrder(i, j) diff --git a/test/jpypetest/test_buffer.py b/test/jpypetest/test_buffer.py index b9f04e0e0..0da865232 100644 --- a/test/jpypetest/test_buffer.py +++ b/test/jpypetest/test_buffer.py @@ -292,3 +292,125 @@ def testFloatToNP3D(self): @common.unittest.skipUnless(haveNumpy(), "numpy not available") def testDoubleToNP3D(self): self.executeFloatTest(JDouble, (11, 10, 9), np.float64, "d") + + def executeOrder(self, jtype, dtype): + a = np.array([1, 2, 3]) + ja2 = jtype[:](a) + for order in ("=", "<", ">"): + dt = np.dtype(dtype).newbyteorder(order) + a = np.array([1, 2, 3], dtype=dt) + ja = jtype[:](a) + self.assertTrue(jpype.java.util.Arrays.equals(ja, ja2), "Order issue with %s %s" % (jtype, dtype)) + + @common.requireNumpy + def testOrder(self): + for i in (jpype.JBoolean, jpype.JByte, jpype.JShort, jpype.JInt, jpype.JLong, jpype.JFloat, jpype.JDouble): + for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float32, np.float64): + self.executeOrder(i, j) + for j in (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64): + self.executeOrder(jpype.JChar, j) + + def testMemoryByte(self): + mv = memoryview(bytes(256)) + jtype = jpype.JByte[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryInt(self): + mv = memoryview(bytes(256)) + jtype = jpype.JInt[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryShort(self): + mv = memoryview(bytes(256)) + jtype = jpype.JShort[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryLong(self): + mv = memoryview(bytes(256)) + jtype = jpype.JLong[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryFloat(self): + mv = memoryview(bytes(256)) + jtype = jpype.JFloat[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryDouble(self): + mv = memoryview(bytes(256)) + jtype = jpype.JDouble[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryBoolean(self): + mv = memoryview(bytes(256)) + jtype = jpype.JBoolean[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "f", "d", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + for dtype in ("s", "p", "P", "e"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) + + def testMemoryChar(self): + mv = memoryview(bytes(256)) + jtype = jpype.JChar[:] + + # Simple checks + for dtype in ("c", "?", "b", "B", "h", "H", "i", "I", "l", "L", "q", "Q", "n", "N"): + jtype(mv.cast(dtype)) + jtype(mv.cast("@" + dtype)) + + jtype(mv.cast("P")) + for dtype in ("s", "p", "f", "d", "@f", "@d"): + with self.assertRaises(Exception): + jtype(mv.cast(dtype)) From 62ac912d0bce0706a894def99a48d0811371aefc Mon Sep 17 00:00:00 2001 From: Andrew Strelsky <ajs222@njit.edu> Date: Sat, 23 Apr 2022 06:49:39 -0400 Subject: [PATCH 050/110] Fixed jni header warning logic --- setupext/platform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setupext/platform.py b/setupext/platform.py index fb0260b72..e95722eb6 100644 --- a/setupext/platform.py +++ b/setupext/platform.py @@ -50,9 +50,9 @@ def Platform(include_dirs=None, sources=None, platform=sys.platform): found_jni = True break - if not found_jni: - distutils.log.warn('Falling back to provided JNI headers, since your provided' - ' JAVA_HOME "%s" does not provide jni.h', java_home) + if not found_jni: + distutils.log.warn('Falling back to provided JNI headers, since your provided' + ' JAVA_HOME "%s" does not provide jni.h', java_home) if not found_jni: platform_specific['include_dirs'] += [fallback_jni] From 0d51e6641e36991aa2dabc81f450ba687dac428d Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 23 Apr 2022 09:30:07 -0700 Subject: [PATCH 051/110] Fix jchar segfault. --- native/python/pyjp_char.cpp | 190 +++++++++++++++++------------------ test/jpypetest/test_jchar.py | 19 ++++ 2 files changed, 114 insertions(+), 95 deletions(-) diff --git a/native/python/pyjp_char.cpp b/native/python/pyjp_char.cpp index 0adfdf892..5b4ab19da 100644 --- a/native/python/pyjp_char.cpp +++ b/native/python/pyjp_char.cpp @@ -63,11 +63,17 @@ static int isNull(JPValue *javaSlot) return 1; } +static PyObject* notSupported() +{ + PyErr_SetString(PyExc_TypeError, "unsupported operation"); + return 0; +} + static int assertNotNull(JPValue *javaSlot) { if (!isNull(javaSlot)) return 0; - PyErr_SetString(PyExc_TypeError, "cast of null pointer"); + PyErr_SetString(PyExc_TypeError, "jchar cast of null pointer"); return 1; } @@ -295,158 +301,152 @@ static Py_ssize_t PyJPChar_len(PyJPChar *self) JP_PY_CATCH(-1); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_and(PyJPChar *self, PyObject *other) +static PyObject *apply(PyObject *first, PyObject *second, PyObject* (*func)(PyObject*, PyObject*)) +{ + JPValue *slot0 = PyJPValue_getJavaSlot(first); + JPValue *slot1 = PyJPValue_getJavaSlot(second); + if (slot0 != NULL && slot1 != NULL) + { + if (assertNotNull(slot0)) + return 0; + if (assertNotNull(slot1)) + return 0; + JPPyObject v1 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); + JPPyObject v2 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); + return func(v1.get(), v2.get()); + } + else if (slot0 != NULL) + { + if (assertNotNull(slot0)) + return 0; + // Promote to int as per Java rules + JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); + return func(v.get(), second); + } + else if (slot1 != NULL) + { + if (assertNotNull(slot1)) + return 0; + // Promote to int as per Java rules + JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); + return func(first, v.get()); + } + return notSupported(); +} + + +static PyObject *PyJPChar_and(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_and"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_And(v.get(), other); + return apply(first, second, PyNumber_And); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_or(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_or(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_or"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Or(v.get(), other); + return apply(first, second, PyNumber_Or); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_xor(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_xor(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_xor"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Xor(v.get(), other); + return apply(first, second, PyNumber_Xor); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_add(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_add(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_add"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - if (PyUnicode_Check(other)) - return PyUnicode_Type.tp_as_number->nb_add((PyObject*) self, other); + JPValue *slot0 = PyJPValue_getJavaSlot(first); + JPValue *slot1 = PyJPValue_getJavaSlot(second); + if (slot1 != NULL && slot0 != NULL) + { + if (assertNotNull(slot0)) + return 0; + if (assertNotNull(slot1)) + return 0; + JPPyObject v1 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); + JPPyObject v2 = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); + return PyNumber_Add(v1.get(), v2.get()); + } + else if (slot0 != NULL) + { + if (assertNotNull(slot0)) + return 0; + if (PyUnicode_Check(second)) + return PyUnicode_Concat(first, second); - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Add(v.get(), other); + // Promote to int as per Java rules + JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)first))); + return PyNumber_Add(v.get(), second); + } + else if (slot1 != NULL) + { + if (assertNotNull(slot1)) + return 0; + if (PyUnicode_Check(first)) + return PyUnicode_Concat(first, second); + + // Promote to int as per Java rules + JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*)second))); + return PyNumber_Add(first, v.get()); + } + return notSupported(); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_subtract(PyJPChar *self, PyObject *other) + +static PyObject *PyJPChar_subtract(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_subtract"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Subtract(v.get(), other); + return apply(first, second, PyNumber_Subtract); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_mult(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_mult(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_mult"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Multiply(v.get(), other); + return apply(first, second, PyNumber_Multiply); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_rshift(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_rshift(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_rshift"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Rshift(v.get(), other); + return apply(first, second, PyNumber_Rshift); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_lshift(PyJPChar *self, PyObject *other) +static PyObject *PyJPChar_lshift(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_lshift"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot((PyObject*) self); - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar(self))); - return PyNumber_Lshift(v.get(), other); + return apply(first, second, PyNumber_Lshift); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_floordiv(PyObject *self, PyObject *other) +static PyObject *PyJPChar_floordiv(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_floordiv"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot(self); - if (javaSlot == NULL) - { - javaSlot = PyJPValue_getJavaSlot(other); - if (assertNotNull(javaSlot)) - return 0; - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) other))); - return PyNumber_FloorDivide(self, v.get()); - } - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) self))); - return PyNumber_FloorDivide(v.get(), other); + return apply(first, second, PyNumber_FloorDivide); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } -static PyObject *PyJPChar_divmod(PyObject *self, PyObject *other) +static PyObject *PyJPChar_divmod(PyObject *first, PyObject *second) { JP_PY_TRY("PyJPChar_divmod"); PyJPModule_getContext(); // Check that JVM is running - JPValue *javaSlot = PyJPValue_getJavaSlot( self); - if (javaSlot == NULL) - { - javaSlot = PyJPValue_getJavaSlot(other); - if (assertNotNull(javaSlot)) - return 0; - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) other))); - return PyNumber_Divmod(self, v.get()); - } - if (assertNotNull(javaSlot)) - return 0; - - // Promote to int as per Java rules - JPPyObject v = JPPyObject::call(PyLong_FromLong(fromJPChar((PyJPChar*) self))); - return PyNumber_Divmod(v.get(), other); + return apply(first, second, PyNumber_Divmod); JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } diff --git a/test/jpypetest/test_jchar.py b/test/jpypetest/test_jchar.py index cda4817da..410dbc7a5 100644 --- a/test/jpypetest/test_jchar.py +++ b/test/jpypetest/test_jchar.py @@ -472,3 +472,22 @@ def testEq(self): def testPass(self): fixture = jpype.JClass('jpype.common.Fixture')() self.assertEqual(fixture.callObject(self.nc), None) + + +class JCharTestCase(common.JPypeTestCase): + + def testOps(self): + self.assertEqual(JChar("x") + "test", "xtest") + self.assertEqual("test" + JChar("x"), "testx") + self.assertEqual(JChar("x") + 1, 121) + self.assertEqual(1 + JChar("x"), 121) + self.assertEqual(JChar(1) + JChar("x"), 121) + self.assertEqual(JChar("x") - 1, 119) + self.assertEqual(1 - JChar("x"), -119) + self.assertEqual(JChar("x") - JChar(1), 119) + self.assertEqual(JChar("x") * 1, 120) + self.assertEqual(1 * JChar("x"), 120) + self.assertEqual(JChar("x") & 1, 0) + self.assertEqual(1 & JChar("x"), 0) + self.assertEqual(JChar("x") | 1, 121) + self.assertEqual(1 | JChar("x"), 121) From 0dc4cd3c9e17b89cd9095db2cb7745537e11c244 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 23 Apr 2022 10:10:15 -0700 Subject: [PATCH 052/110] Remove "__" functions from java. --- jpype/_jclass.py | 4 +++- jpype/_pykeywords.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/jpype/_jclass.py b/jpype/_jclass.py index 1840c4590..9c33622d0 100644 --- a/jpype/_jclass.py +++ b/jpype/_jclass.py @@ -124,7 +124,9 @@ def _jclassPre(name, bases, members): k2 = pysafe(k) if k2 != k: del members[k] - members[k2] = v + # Remove unmappable functions + if k2: + members[k2] = v # Apply customizers hints = _jcustomizer.getClassHints(name) diff --git a/jpype/_pykeywords.py b/jpype/_pykeywords.py index 18ee2132c..2a3313a84 100644 --- a/jpype/_pykeywords.py +++ b/jpype/_pykeywords.py @@ -28,6 +28,8 @@ def pysafe(s): + if s.startswith("__"): + return None if s in _KEYWORDS: return s + "_" return s From bc8925067db5568a0142c873fa4c2bae7ee72dca Mon Sep 17 00:00:00 2001 From: Geoffrey De Smet <gds.geoffrey.de.smet@gmail.com> Date: Tue, 26 Apr 2022 15:51:30 +0200 Subject: [PATCH 053/110] Add discussions --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index a54b693f8..0e2ddab1c 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,8 @@ access to the entirety of CPython and Java libraries. <https://github.com/jpype-project/jpype>`_ :Issue tracker: `GitHub Issues <https://github.com/jpype-project/jpype/issues>`_ +:Discussions: `GitHub Discussions + <https://github.com/jpype-project/jpype/discussions>`_ :Documentation: `Python Docs`_ :License: `Apache 2 License`_ :Build status: |TestsCI|_ |Docs|_ From a0f252d1cf3a9785162ad254115e92d6a3526788 Mon Sep 17 00:00:00 2001 From: Karl Nelson <nelson85@llnl.gov> Date: Tue, 26 Apr 2022 09:16:23 -0700 Subject: [PATCH 054/110] Fix handling of Py-functions with default parameters. Replacement for $1017 (#1054) --- native/common/jp_functional.cpp | 107 +++++++++++++++---------------- test/jpypetest/test_overloads.py | 60 +++++++++++++++++ 2 files changed, 111 insertions(+), 56 deletions(-) diff --git a/native/common/jp_functional.cpp b/native/common/jp_functional.cpp index eaf0282ef..47702256d 100644 --- a/native/common/jp_functional.cpp +++ b/native/common/jp_functional.cpp @@ -32,6 +32,7 @@ JPFunctional::~JPFunctional() { } + class JPConversionFunctional : public JPConversion { public: @@ -41,64 +42,58 @@ class JPConversionFunctional : public JPConversion if (!PyCallable_Check(match.object)) return match.type = JPMatch::_none; - // Modified from https://stackoverflow.com/a/1117735 - bool has_parameter_count = false; - // Get the __code__ attribute of the function, which contains details about the function - // Note: __code__ is func_code in Python 2.x - PyObject* function_code_object = PyObject_GetAttrString(match.object, "__code__"); - if(function_code_object) { - // get the argument count - PyObject* parameter_count_obj = PyObject_GetAttrString(function_code_object, "co_argcount"); - if(parameter_count_obj) { - has_parameter_count = true; - int optional_parameter_count = 0; - int flags = 0; - const int ACCEPT_VARGS_MASK = 0x04; // From https://docs.python.org/3/reference/datamodel.html - PyObject* flags_obj = PyObject_GetAttrString(function_code_object, "co_flags"); - if (flags_obj) { - flags = PyLong_AsLong(flags_obj); - Py_DECREF(flags_obj); - } - PyObject* optional_parameter_tuple = PyObject_GetAttrString(match.object, "__defaults__"); - if (optional_parameter_tuple && optional_parameter_tuple != Py_None) { - optional_parameter_count = PyTuple_Size(optional_parameter_tuple); - Py_DECREF(optional_parameter_tuple); - } - const int parameter_count = PyLong_AsLong(parameter_count_obj); - const int java_parameter_count = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); - const bool is_varargs = (flags & ACCEPT_VARGS_MASK) == ACCEPT_VARGS_MASK; - - // def my_func(x, y=None) should be both a Function and a BiFunction - // i.e. the number of parameters accepted by the interface MUST - // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) - // (Unless the function accept a variable number of arguments, then this restriction does not - // apply). - // 2. Be at least the minumum number of parameters accepted by the python function - // (parameter_count - optional_parameter_count = number of required parameters). - // Notes: - // - keywords vargs does not remove restriction 1 - // - keyword only arguments are not counted. - if ((!is_varargs && parameter_count < java_parameter_count) || - parameter_count - optional_parameter_count > java_parameter_count) { - match.type = JPMatch::_none; - } else { - match.conversion = this; - match.closure = cls; - match.type = JPMatch::_implicit; - } - - Py_DECREF(parameter_count_obj); - } - Py_DECREF(function_code_object); + // def my_func(x, y=None) should be both a Function and a BiFunction + // i.e. the number of parameters accepted by the interface MUST + // 1. Be at most the maximum number of parameters accepted by the python function (parameter_count) + // (Unless the function accept a variable number of arguments, then this restriction does not + // apply). + // 2. Be at least the minumum number of parameters accepted by the python function + // (parameter_count - optional_parameter_count = number of required parameters). + // Notes: + // - keywords vargs does not remove restriction 1 + // - keyword only arguments are not counted. + if (PyFunction_Check(match.object)) + { + PyObject* func = match.object; + PyCodeObject* code = (PyCodeObject*) PyFunction_GetCode(func); // borrowed + Py_ssize_t args = code->co_argcount; + bool is_varargs = ((code->co_flags&CO_VARARGS)==CO_VARARGS); + int optional = 0; + JPPyObject defaults = JPPyObject::accept(PyObject_GetAttrString(func, "__defaults__")); + if (!defaults.isNull() && defaults.get() != Py_None) + optional = PyTuple_Size(defaults.get()); + const int jargs = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); + // Too few arguments + if (!is_varargs && args < jargs) + return match.type = JPMatch::_none; + // Too many arguments + if (args - optional > jargs) + return match.type = JPMatch::_none; } - PyErr_Clear(); - if (!has_parameter_count) { - match.conversion = this; - match.closure = cls; - return match.type = JPMatch::_implicit; - } else { - return match.type; + else if (PyMethod_Check(match.object)) + { + PyObject* func = PyMethod_Function(match.object); // borrowed + PyCodeObject* code = (PyCodeObject*) PyFunction_GetCode(func); // borrowed + Py_ssize_t args = code->co_argcount; + bool is_varargs = ((code->co_flags&CO_VARARGS)==CO_VARARGS); + int optional = 0; + JPPyObject defaults = JPPyObject::accept(PyObject_GetAttrString(func, "__defaults__")); + if (!defaults.isNull() && defaults.get() != Py_None) + optional = PyTuple_Size(defaults.get()); + const int jargs = cls->getContext()->getTypeManager()->interfaceParameterCount(cls); + // Bound self argument removes one argument + if ((PyMethod_Self(match.object))!=NULL) // borrowed + args--; + // Too few arguments + if (!is_varargs && args < jargs) + return match.type = JPMatch::_none; + // Too many arguments + if (args - optional > jargs) + return match.type = JPMatch::_none; } + match.conversion = this; + match.closure = cls; + return match.type = JPMatch::_implicit; } virtual void getInfo(JPClass *cls, JPConversionInfo &info) override diff --git a/test/jpypetest/test_overloads.py b/test/jpypetest/test_overloads.py index 1e54e1f3f..b088b3632 100644 --- a/test/jpypetest/test_overloads.py +++ b/test/jpypetest/test_overloads.py @@ -24,6 +24,50 @@ java = jpype.java +class MyClass: + def fun1(self): + pass + + def fun2(self, *a): + pass + + def fun3(self, a=1): + pass + + def fun4(self, a): + pass + + +def fun1(): + pass + + +def fun2(*a): + pass + + +def fun3(a=1): + pass + + +def fun4(a): + pass + + +if __name__ == '__main__': + jpype.startJVM() + from java.lang import Runnable + mc = MyClass() + cb = Runnable @ fun1 + cb = Runnable @ fun2 + cb = Runnable @ fun3 + #cb = Runnable @ fun4 + cb = Runnable @ mc.fun1 + cb = Runnable @ mc.fun2 + cb = Runnable @ mc.fun3 + cb = Runnable @ mc.fun4 + + class OverloadTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) @@ -223,3 +267,19 @@ def __call__(self): TypeError, 'Ambiguous overloads found', test2.testFunctionalInterfaces, my_fun_class) self.assertEqual('SingleArg', test2.testFunctionalInterfaces(my_fun_kwargs)) self.assertEqual('NoArgs', test2.testFunctionalInterfaces(my_fun_kw)) + + def testRunnable(self): + Runnable = jpype.JClass("java.lang.Runnable") + mc = MyClass() + # These should work + cb = Runnable @ fun1 + cb = Runnable @ fun2 + cb = Runnable @ fun3 + cb = Runnable @ mc.fun1 + cb = Runnable @ mc.fun2 + cb = Runnable @ mc.fun3 + # These should fail + with self.assertRaises(TypeError): + cb = Runnable @ fun4 + with self.assertRaises(TypeError): + cb = Runnable @ mc.fun4 From b588869a3910e0f2a730ad898fb68d3293ced394 Mon Sep 17 00:00:00 2001 From: Phil Elson <philip.elson@cern.ch> Date: Wed, 4 May 2022 16:58:44 +0200 Subject: [PATCH 055/110] Remove Python <=3.6 support --- jpype/_core.py | 8 -------- jpype/_pykeywords.py | 3 ++- jpype/imports.py | 5 ----- jpype/pickle.py | 7 +------ jpype/protocol.py | 12 ------------ native/python/pyjp_module.cpp | 8 -------- setup.py | 6 +++--- test/jpypetest/test_closeable.py | 8 -------- test/jpypetest/test_core.py | 8 -------- test/jpypetest/test_overloads.py | 2 +- 10 files changed, 7 insertions(+), 60 deletions(-) diff --git a/jpype/_core.py b/jpype/_core.py index a71935535..0fcfba7de 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -44,14 +44,6 @@ class JVMNotRunning(RuntimeError): pass -def versionTest(): - if sys.version_info < (3,): - raise ImportError("Python 2 is not supported") - - -versionTest() - - # Activate jedi tab completion try: import jedi as _jedi diff --git a/jpype/_pykeywords.py b/jpype/_pykeywords.py index 18ee2132c..295393241 100644 --- a/jpype/_pykeywords.py +++ b/jpype/_pykeywords.py @@ -16,8 +16,9 @@ # # ***************************************************************************** -# This is a super set of the keywords in Python2 and Python3. +# This is a superset of the keywords in Python. # We use this so that jpype is a bit more version independent. +# Removing keywords from this list impacts the exposed interfaces, and therefore is a breaking change. _KEYWORDS = set(( 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', diff --git a/jpype/imports.py b/jpype/imports.py index dfc53a167..d9a68ee81 100644 --- a/jpype/imports.py +++ b/jpype/imports.py @@ -24,8 +24,6 @@ ``net`` and ``edu``. Java symbols from these domains can be imported using the standard Python syntax. -Import customizers are supported in Python 3.6 or greater. - Forms supported: - **import <java_pkg> [ as <name> ]** - **import <java_pkg>.<java_class> [ as <name> ]** @@ -36,9 +34,6 @@ For further information please read the :doc:`imports` guide. -Requires: - Python 3.6 or later - Example: .. code-block:: python diff --git a/jpype/pickle.py b/jpype/pickle.py index 34726a083..13eb97a95 100644 --- a/jpype/pickle.py +++ b/jpype/pickle.py @@ -49,9 +49,6 @@ Proxies and other JPype specific module resources cannot be pickled currently. -Requires: - Python 3.6 or later - """ from __future__ import absolute_import import _jpype @@ -89,8 +86,7 @@ def __init__(self, dispatch): # Extension dispatch table holds reduce method self._call = self.reduce - # Python2 and Python3 _Pickler use get() - + # Pure Python _Pickler uses get() def get(self, cls): if not issubclass(cls, (_jpype.JClass, _jpype.JObject)): return self._dispatch.get(cls) @@ -102,7 +98,6 @@ def __getitem__(self, cls): return self._dispatch[cls] return self._call - # For Python3 def reduce(self, obj): byte = bytes(self._encoder.pack(obj)) return (self._builder, (byte, )) diff --git a/jpype/protocol.py b/jpype/protocol.py index fc77854d0..bf447ac39 100644 --- a/jpype/protocol.py +++ b/jpype/protocol.py @@ -105,18 +105,6 @@ def _JInstantConversion(jcls, obj): return jcls.ofEpochSecond(sec, nsec) -if sys.version_info < (3, 6): # pragma: no cover - import pathlib - - @_jcustomizer.JConversion("java.nio.file.Path", instanceof=pathlib.PurePath) - def _JPathConvert(jcls, obj): - Paths = _jpype.JClass("java.nio.file.Paths") - return Paths.get(str(obj)) - - @_jcustomizer.JConversion("java.io.File", instanceof=pathlib.PurePath) - def _JFileConvert(jcls, obj): - return jcls(str(obj)) - # Types needed for SQL diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 6466af8a9..90917df62 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -714,14 +714,6 @@ PyMODINIT_FUNC PyInit__jpype() JP_PY_TRY("PyInit__jpype"); JPContext_global = new JPContext(); -#if PY_VERSION_HEX<0x03070000 - // This is required for python versions prior to 3.7. - // It is called by the python initialization starting from 3.7, - // but is safe to call afterwards. Starting 3.9 this issues a - // deprecation warning. - PyEval_InitThreads(); -#endif - // Initialize the module (depends on python version) PyObject* module = PyModule_Create(&moduledef); // PyJPModule = module; diff --git a/setup.py b/setup.py index 1f9922f5e..f587649a7 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,7 @@ author_email='devilwolf@users.sourceforge.net', maintainer='Luis Nell', maintainer_email='cooperate@originell.org', - python_requires=">=3.5", + python_requires=">=3.7", url='https://github.com/jpype-project/jpype', platforms=[ 'Operating System :: Microsoft :: Windows', @@ -68,10 +68,10 @@ ], classifiers=[ 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Software Development', 'Topic :: Scientific/Engineering', ], diff --git a/test/jpypetest/test_closeable.py b/test/jpypetest/test_closeable.py index 74a893591..d8f58a874 100644 --- a/test/jpypetest/test_closeable.py +++ b/test/jpypetest/test_closeable.py @@ -17,11 +17,6 @@ # ***************************************************************************** import jpype import common -import sys - - -def pythonNewerThan(major, minor): - return sys.version_info[0] > major or (sys.version_info[0] == major and sys.version_info[1] > minor) class CloseableTestCase(common.JPypeTestCase): @@ -38,7 +33,6 @@ def testCloseable(self): self.assertEqual(CloseableTest.printed, "hello 1") self.assertTrue(CloseableTest.closed) - @common.unittest.skipUnless(pythonNewerThan(3, 0), "requires python 3") def testCloseableFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() @@ -72,7 +66,6 @@ def testCloseablePyExcept(self): self.assertEqual(CloseableTest.printed, "hello 2") self.assertTrue(CloseableTest.closed) - @common.unittest.skipUnless(pythonNewerThan(2, 6), "Earlier python does not support stacked exceptions.") def testCloseablePyExceptFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() @@ -105,7 +98,6 @@ def testCloseableJExcept(self): self.assertEqual(CloseableTest.printed, "hello 4") self.assertTrue(CloseableTest.closed) - @common.unittest.skipUnless(pythonNewerThan(2, 6), "Earlier python does not support stacked exceptions.") def testCloseableJExceptFail(self): CloseableTest = jpype.JClass("jpype.closeable.CloseableTest") CloseableTest.reset() diff --git a/test/jpypetest/test_core.py b/test/jpypetest/test_core.py index 9bdfa74de..2cec5fbfc 100644 --- a/test/jpypetest/test_core.py +++ b/test/jpypetest/test_core.py @@ -25,14 +25,6 @@ class JCharTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) - @mock.patch('jpype._core.sys') - def testVersion(self, mock_sys): - mock_sys.version_info = (2, 7) - with self.assertRaises(ImportError): - jpype._core.versionTest() - mock_sys.version_info = (3, 8) - jpype._core.versionTest() - def testShutdownHook(self): Thread = JClass("java.lang.Thread") Runnable = JClass("java.lang.Runnable") diff --git a/test/jpypetest/test_overloads.py b/test/jpypetest/test_overloads.py index b088b3632..7604ac7ba 100644 --- a/test/jpypetest/test_overloads.py +++ b/test/jpypetest/test_overloads.py @@ -128,7 +128,7 @@ def testVarArgsCall(self): def testPrimitive(self): test1 = self.__jp.Test1() - intexpectation = 'int' if not sys.version_info[0] > 2 and sys.maxint == 2**31 - 1 else 'long' + intexpectation = 'long' # FIXME it is not possible to determine if this is bool/char/byte currently #self.assertEqual(intexpectation, test1.testPrimitive(5)) #self.assertEqual('long', test1.testPrimitive(2**31)) From b93eb2803b840ce22277470be565733bba4c2249 Mon Sep 17 00:00:00 2001 From: Phil Elson <philip.elson@cern.ch> Date: Thu, 5 May 2022 09:30:30 +0200 Subject: [PATCH 056/110] Additional PY_VERSION_HEX change removals --- native/python/pyjp_array.cpp | 16 ++++------------ native/python/pyjp_class.cpp | 6 +----- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/native/python/pyjp_array.cpp b/native/python/pyjp_array.cpp index 3a702e3f8..9dc53774c 100644 --- a/native/python/pyjp_array.cpp +++ b/native/python/pyjp_array.cpp @@ -160,16 +160,12 @@ static PyObject *PyJPArray_getItem(PyJPArray *self, PyObject *item) Py_ssize_t start, stop, step, slicelength; Py_ssize_t length = (Py_ssize_t) self->m_Array->getLength(); -#if PY_VERSION_HEX<0x03060100 - if (PySlice_GetIndicesEx(item, length, &start, &stop, &step, &slicelength) < 0) - return NULL; -#else if (PySlice_Unpack(item, &start, &stop, &step) < 0) return NULL; slicelength = PySlice_AdjustIndices(length, &start, &stop, step); -#endif - if (slicelength <= 0) + + if (slicelength <= 0) { // This should point to a null array so we don't hold worthless // memory, but this is a low priority @@ -231,16 +227,12 @@ static int PyJPArray_assignSubscript(PyJPArray *self, PyObject *item, PyObject * Py_ssize_t start, stop, step, slicelength; Py_ssize_t length = (Py_ssize_t) self->m_Array->getLength(); -#if PY_VERSION_HEX<0x03060100 - if (PySlice_GetIndicesEx(item, length, &start, &stop, &step, &slicelength) < 0) - return -1; -#else if (PySlice_Unpack(item, &start, &stop, &step) < 0) return -1; slicelength = PySlice_AdjustIndices(length, &start, &stop, step); -#endif - if (slicelength <= 0) + + if (slicelength <= 0) return 0; self->m_Array->setRange((jsize) start, (jsize) slicelength, (jsize) step, value); diff --git a/native/python/pyjp_class.cpp b/native/python/pyjp_class.cpp index 87fd68584..2b9bab9da 100644 --- a/native/python/pyjp_class.cpp +++ b/native/python/pyjp_class.cpp @@ -702,14 +702,10 @@ static bool PySlice_CheckFull(PyObject *item) if (!PySlice_Check(item)) return false; Py_ssize_t start, stop, step; -#if PY_VERSION_HEX<0x03060100 - int rc = PySlice_GetIndices(item, 0x7fffffff, &start, &stop, &step); - return (rc == 0)&&(start == 0)&&(step == 1)&&((int) stop == 0x7fffffff); -#elif defined(ANDROID) int rc = PySlice_Unpack(item, &start, &stop, &step); +#if defined(ANDROID) return (rc == 0)&&(start == 0)&&(step == 1)&&((int) stop >= 0x7fffffff); #else - int rc = PySlice_Unpack(item, &start, &stop, &step); return (rc == 0)&&(start == 0)&&(step == 1)&&((int) stop == -1); #endif } From d9c8cdbc52b5e0e35ddb9f9d878a33c51174cd10 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 6 May 2022 09:51:31 -0700 Subject: [PATCH 057/110] Work on tests. --- test/jpypetest/test_keywords.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/jpypetest/test_keywords.py b/test/jpypetest/test_keywords.py index b70e0f75f..3a57e6c7c 100644 --- a/test/jpypetest/test_keywords.py +++ b/test/jpypetest/test_keywords.py @@ -26,4 +26,6 @@ def setUp(self): def testKeywords(self): for kw in keyword.kwlist: - self.assertTrue(jpype._pykeywords.pysafe(kw).endswith("_")) + safe = jpype._pykeywords.pysafe(kw) + self.assertEquals(type(safe), str) + self.assertTrue(safe.endswith("_")) From a1f0b99334a7529c3b52a8aa2ca1efd14935d88c Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 6 May 2022 09:57:32 -0700 Subject: [PATCH 058/110] Improve diagonistics --- test/jpypetest/test_keywords.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jpypetest/test_keywords.py b/test/jpypetest/test_keywords.py index 3a57e6c7c..e0caeabc9 100644 --- a/test/jpypetest/test_keywords.py +++ b/test/jpypetest/test_keywords.py @@ -27,5 +27,5 @@ def setUp(self): def testKeywords(self): for kw in keyword.kwlist: safe = jpype._pykeywords.pysafe(kw) - self.assertEquals(type(safe), str) + self.assertEqual(type(safe), str, "Fail on keyword %s", kw) self.assertTrue(safe.endswith("_")) From 0f190f6866a25ecb3b8b7eff6f6fe91d87d44a86 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 6 May 2022 09:58:00 -0700 Subject: [PATCH 059/110] Update changelog --- doc/CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 90edb0d5b..876e50f4b 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -6,6 +6,10 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.3.1_dev0 - 2021-06-05** + - 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.. - Support for user defined conversions for java.lang.Class and array types. From 51a54f8350bc9d9af0567058efc0929215dd8ba9 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 6 May 2022 10:12:10 -0700 Subject: [PATCH 060/110] Try again --- test/jpypetest/test_keywords.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jpypetest/test_keywords.py b/test/jpypetest/test_keywords.py index e0caeabc9..467aed098 100644 --- a/test/jpypetest/test_keywords.py +++ b/test/jpypetest/test_keywords.py @@ -27,5 +27,5 @@ def setUp(self): def testKeywords(self): for kw in keyword.kwlist: safe = jpype._pykeywords.pysafe(kw) - self.assertEqual(type(safe), str, "Fail on keyword %s", kw) + self.assertEqual(type(safe), str, "Fail on keyword %s" % kw) self.assertTrue(safe.endswith("_")) From 0381c07ca7e463acfd3acca803cd005421920950 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 6 May 2022 10:21:38 -0700 Subject: [PATCH 061/110] Try again --- test/jpypetest/test_keywords.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/jpypetest/test_keywords.py b/test/jpypetest/test_keywords.py index 467aed098..6c255770d 100644 --- a/test/jpypetest/test_keywords.py +++ b/test/jpypetest/test_keywords.py @@ -27,5 +27,8 @@ def setUp(self): def testKeywords(self): for kw in keyword.kwlist: safe = jpype._pykeywords.pysafe(kw) + if kw.startswith("_"): + continue self.assertEqual(type(safe), str, "Fail on keyword %s" % kw) self.assertTrue(safe.endswith("_")) + self.assertEqual(jpype._pykeywords.pysafe("__del__"), None) From 71c09d8e1b2b6a076399b1f0fd32ba6ae84fd381 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 04:58:15 -0700 Subject: [PATCH 062/110] =?UTF-8?q?Bump=20version:=201.3.1=5Fdev0=20?= =?UTF-8?q?=E2=86=92=201.4.0=5Fdev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 3 +-- doc/CHANGELOG.rst | 1 + jpype/__init__.py | 2 +- native/java/org/jpype/JPypeContext.java | 2 +- native/python/pyjp_module.cpp | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8b3bad655..91c95a56e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.3.1_dev0 +current_version = 1.4.0_dev0 commit = True tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))? @@ -28,4 +28,3 @@ values = search = Latest Changes: replace = Latest Changes: - **{new_version} - {now:%Y-%m-%d}** - diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 876e50f4b..3abc0f988 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: +- **1.4.0_dev0 - 2022-05-14** - **1.3.1_dev0 - 2021-06-05** - Fixed issue when Java classes with dunder methods such as ``__del__`` diff --git a/jpype/__init__.py b/jpype/__init__.py index e4c7aeb6d..2f311ed20 100644 --- a/jpype/__init__.py +++ b/jpype/__init__.py @@ -51,7 +51,7 @@ __all__.extend(_jcustomizer.__all__) __all__.extend(_gui.__all__) -__version__ = "1.3.1_dev0" +__version__ = "1.4.0_dev0" __version_info__ = __version__.split('.') diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index 425e0ae44..abdadb7bf 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -73,7 +73,7 @@ public class JPypeContext { - public final String VERSION = "1.3.1_dev0"; + public final String VERSION = "1.4.0_dev0"; private static JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 6466af8a9..544305eca 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -727,7 +727,7 @@ PyMODINIT_FUNC PyInit__jpype() // PyJPModule = module; Py_INCREF(module); PyJPModule = module; - PyModule_AddStringConstant(module, "__version__", "1.3.1_dev0"); + PyModule_AddStringConstant(module, "__version__", "1.4.0_dev0"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. diff --git a/setup.py b/setup.py index 1f9922f5e..b9bc62732 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ setup( name='JPype1', - version='1.3.1_dev0', + version='1.4.0_dev0', description='A Python to Java bridge.', long_description=open('README.rst').read(), license='License :: OSI Approved :: Apache Software License', From cf4299b134ee52450ca3903477c172e76715e3b8 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 04:58:22 -0700 Subject: [PATCH 063/110] =?UTF-8?q?Bump=20version:=201.4.0=5Fdev0=20?= =?UTF-8?q?=E2=86=92=201.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- doc/CHANGELOG.rst | 1 + jpype/__init__.py | 2 +- native/java/org/jpype/JPypeContext.java | 2 +- native/python/pyjp_module.cpp | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 91c95a56e..0419b8143 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.0_dev0 +current_version = 1.4.0 commit = True tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))? diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 3abc0f988..6bf1acb0d 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: +- **1.4.0 - 2022-05-14** - **1.4.0_dev0 - 2022-05-14** - **1.3.1_dev0 - 2021-06-05** diff --git a/jpype/__init__.py b/jpype/__init__.py index 2f311ed20..82775f63b 100644 --- a/jpype/__init__.py +++ b/jpype/__init__.py @@ -51,7 +51,7 @@ __all__.extend(_jcustomizer.__all__) __all__.extend(_gui.__all__) -__version__ = "1.4.0_dev0" +__version__ = "1.4.0" __version_info__ = __version__.split('.') diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index abdadb7bf..a465503fe 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -73,7 +73,7 @@ public class JPypeContext { - public final String VERSION = "1.4.0_dev0"; + public final String VERSION = "1.4.0"; private static JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 544305eca..20cadc8ad 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -727,7 +727,7 @@ PyMODINIT_FUNC PyInit__jpype() // PyJPModule = module; Py_INCREF(module); PyJPModule = module; - PyModule_AddStringConstant(module, "__version__", "1.4.0_dev0"); + PyModule_AddStringConstant(module, "__version__", "1.4.0"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. diff --git a/setup.py b/setup.py index b9bc62732..3e7fd2a35 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ setup( name='JPype1', - version='1.4.0_dev0', + version='1.4.0', description='A Python to Java bridge.', long_description=open('README.rst').read(), license='License :: OSI Approved :: Apache Software License', From 7d2aa9b858e7a77c9e62f8a1ed42ceb2d9d97017 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 05:18:06 -0700 Subject: [PATCH 064/110] Updated changelog --- doc/CHANGELOG.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 6bf1acb0d..e28f8b9be 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,14 +5,35 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.4.0 - 2022-05-14** -- **1.4.0_dev0 - 2022-05-14** -- **1.3.1_dev0 - 2021-06-05** + + - Support for all different buffer type conversions. + + - Improved buffer transfers to numpy as guaranteed to match Java types. + However, exact dtype for conversions is os/numpy version dependent. + + - 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. + + - Fix crash when comparing JChar. + + - Order handling for numerical operations with JChar fixed. + + - Improved matching for Java functors based on parameter count. + + - Dropped support for Python 3.5 and 3.6 + + - 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 patterns are longer translated to Python. - - Fix issue with numpy arrays with no dimensions resulting in crash.. + - Fix issue with numpy arrays with no dimensions resulting in crash. - Support for user defined conversions for java.lang.Class and array types. From 29a28e254ad3a833de8b8dce265f2767237a6dc6 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 05:46:21 -0700 Subject: [PATCH 065/110] Update images for release --- .azure/release.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.azure/release.yml b/.azure/release.yml index 6d5e04355..de0d2a937 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -51,7 +51,7 @@ stages: image: quay.io/pypa/manylinux1_i686 python.architecture: x86 pool: - vmImage: "ubuntu-latest" + imageName: "ubuntu-latest" steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml @@ -61,9 +61,6 @@ stages: condition: eq(1,1) strategy: matrix: - Python36: - python.version: '3.6' - python.architecture: 'x64' Python37: python.version: '3.7' python.architecture: 'x64' @@ -73,8 +70,11 @@ stages: Python39: python.version: '3.9' python.architecture: 'x64' + Python310: + python.version: '3.10' + python.architecture: 'x64' pool: - vmImage: 'vs2017-win2016' + imageName: "windows-2019" steps: - template: scripts/deps.yml - task: UsePythonVersion@0 @@ -93,16 +93,16 @@ stages: python.architecture: 'x64' strategy: matrix: - Python36: - python.version: '3.6' Python37: python.version: '3.7' Python38: python.version: '3.8' Python39: python.version: '3.9' + Python310: + python.version: '3.10' pool: - vmImage: "macOS-10.14" + imageName: "macos-11" steps: - template: scripts/deps.yml - script: .azure/scripts/osx-python.sh '$(python.version)' From cc94f1355dab76a7953505865969f988bf54caf8 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:03:22 -0700 Subject: [PATCH 066/110] Fix javadoc causing release scripts to fail. --- test-requirements.txt | 2 +- test/harness/jpype/doc/Test.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index e4a9c7143..a6108b5c8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,3 @@ pytest==7.1.1 pyinstaller==4.10 -jedi==0.17.0 +jedi==0.18.0 diff --git a/test/harness/jpype/doc/Test.java b/test/harness/jpype/doc/Test.java index 5108b3c02..aa4b4857a 100644 --- a/test/harness/jpype/doc/Test.java +++ b/test/harness/jpype/doc/Test.java @@ -31,12 +31,14 @@ public class Test * <blockquote><pre>{@code * hey.method(1, SecretMap()); * }</pre></blockquote> + * Text. * <p> - * <table style="width:100%" summary="Test table"> + * <table style="width:100%"> * <tr><th>North</th><th>East</th><th>Steps</th></tr> * <tr><td>Y</td><td>N</td><td>10</td></tr> * <tr><td>N</td><td>Y</td><td>20</td></tr> * </table> + * Text * <p> * <dl> * <dt>Defines @@ -54,7 +56,7 @@ public class Test * * @throws java.lang.NoSuchMethodException if something bad happens. * @param i an argument <code>a</code>. {@value #QQ} - * @param j + * @param j another argument. * @return a secret code. * @since 1.5 * From 3b6b052dd2a25ac93e9395e67da1ba54fde52781 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:17:03 -0700 Subject: [PATCH 067/110] Trying to fix javadoc on JDK 11 --- setupext/test_java.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setupext/test_java.py b/setupext/test_java.py index c08780bec..d0a02dec5 100644 --- a/setupext/test_java.py +++ b/setupext/test_java.py @@ -87,4 +87,4 @@ def run(self): cmdStr = compileJava() self.announce(" %s" % " ".join(cmdStr), level=distutils.log.INFO) subprocess.check_call(cmdStr) - subprocess.check_call(shlex.split("javadoc test/harness/jpype/doc/Test.java -d test/classes/")) + subprocess.check_call(shlex.split("javadoc -Xdoclint:none test/harness/jpype/doc/Test.java -d test/classes/")) From 3572df5b9f2da3ec6c71c0fc02d7c1494d99b218 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:22:13 -0700 Subject: [PATCH 068/110] Work on build pipeline --- .azure/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.azure/release.yml b/.azure/release.yml index de0d2a937..ba104823d 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -51,7 +51,7 @@ stages: image: quay.io/pypa/manylinux1_i686 python.architecture: x86 pool: - imageName: "ubuntu-latest" + vmImage: "ubuntu-latest" steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml @@ -74,7 +74,7 @@ stages: python.version: '3.10' python.architecture: 'x64' pool: - imageName: "windows-2019" + vmImage: "windows-2019" steps: - template: scripts/deps.yml - task: UsePythonVersion@0 @@ -102,7 +102,7 @@ stages: Python310: python.version: '3.10' pool: - imageName: "macos-11" + vmImage: "macos-11" steps: - template: scripts/deps.yml - script: .azure/scripts/osx-python.sh '$(python.version)' From abadce68bf7981e5e5f3cb9c2e591676477d4ac6 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:32:55 -0700 Subject: [PATCH 069/110] Work on release --- .azure/scripts/build-wheels.sh | 2 ++ .azure/scripts/osx-python.sh | 10 +++++----- test/harness/jpype/doc/Test.java | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.azure/scripts/build-wheels.sh b/.azure/scripts/build-wheels.sh index a419ba303..a55c361b3 100755 --- a/.azure/scripts/build-wheels.sh +++ b/.azure/scripts/build-wheels.sh @@ -5,6 +5,8 @@ set -e -x pys=(/opt/python/*/bin) # Filter out Python 3.4 +pys=(${pys[@]//*36*/}) +pys=(${pys[@]//*35*/}) pys=(${pys[@]//*34*/}) pys=(${pys[@]//*27*/}) diff --git a/.azure/scripts/osx-python.sh b/.azure/scripts/osx-python.sh index 995aa2cf8..83779e28e 100755 --- a/.azure/scripts/osx-python.sh +++ b/.azure/scripts/osx-python.sh @@ -3,17 +3,17 @@ PYTHON_VERSION="$1" case $PYTHON_VERSION in -3.6) - FULL_VERSION=3.6.8 - ;; 3.7) FULL_VERSION=3.7.9 ;; 3.8) - FULL_VERSION=3.8.6 + FULL_VERSION=3.8.10 ;; 3.9) - FULL_VERSION=3.9.0 + FULL_VERSION=3.9.12 + ;; +3.10) + FULL_VERSION=3.10.4 ;; esac diff --git a/test/harness/jpype/doc/Test.java b/test/harness/jpype/doc/Test.java index aa4b4857a..a2de9ec68 100644 --- a/test/harness/jpype/doc/Test.java +++ b/test/harness/jpype/doc/Test.java @@ -31,14 +31,12 @@ public class Test * <blockquote><pre>{@code * hey.method(1, SecretMap()); * }</pre></blockquote> - * Text. * <p> - * <table style="width:100%"> + * <table style="width:100%" summary="foo"> * <tr><th>North</th><th>East</th><th>Steps</th></tr> * <tr><td>Y</td><td>N</td><td>10</td></tr> * <tr><td>N</td><td>Y</td><td>20</td></tr> * </table> - * Text * <p> * <dl> * <dt>Defines From 0662ddd04eb96127025ef575b1dc10453296102e Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:45:18 -0700 Subject: [PATCH 070/110] Work on osx --- .azure/scripts/osx-python.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.azure/scripts/osx-python.sh b/.azure/scripts/osx-python.sh index 83779e28e..72beaa0a5 100755 --- a/.azure/scripts/osx-python.sh +++ b/.azure/scripts/osx-python.sh @@ -5,19 +5,22 @@ PYTHON_VERSION="$1" case $PYTHON_VERSION in 3.7) FULL_VERSION=3.7.9 + INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.8) FULL_VERSION=3.8.10 + INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg ;; 3.9) FULL_VERSION=3.9.12 + INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg ;; 3.10) FULL_VERSION=3.10.4 + INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg ;; esac -INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg URL=https://www.python.org/ftp/python/$FULL_VERSION/$INSTALLER_NAME PY_PREFIX=/Library/Frameworks/Python.framework/Versions From ad7cf3a00d6e6995e0a597d9cd4c28d45808d5a7 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 06:52:44 -0700 Subject: [PATCH 071/110] Try again --- .azure/scripts/osx-python.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.azure/scripts/osx-python.sh b/.azure/scripts/osx-python.sh index 72beaa0a5..ba4e38979 100755 --- a/.azure/scripts/osx-python.sh +++ b/.azure/scripts/osx-python.sh @@ -9,14 +9,14 @@ case $PYTHON_VERSION in ;; 3.8) FULL_VERSION=3.8.10 - INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg + INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.9) FULL_VERSION=3.9.12 - INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg + INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.10) - FULL_VERSION=3.10.4 + FULL_VERSION=3.10.3 INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg ;; esac From add26ae5fc160f680ac25f86a8c78c7f66ce99d0 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 07:08:11 -0700 Subject: [PATCH 072/110] =?UTF-8?q?Bump=20version:=201.4.0=20=E2=86=92=201?= =?UTF-8?q?.4.1=5Fdev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- doc/CHANGELOG.rst | 1 + jpype/__init__.py | 2 +- native/java/org/jpype/JPypeContext.java | 2 +- native/python/pyjp_module.cpp | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 0419b8143..075183a65 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.0 +current_version = 1.4.1_dev0 commit = True tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))? diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index e28f8b9be..b79367435 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: +- **1.4.1_dev0 - 2022-05-14** - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. diff --git a/jpype/__init__.py b/jpype/__init__.py index 82775f63b..bd704f265 100644 --- a/jpype/__init__.py +++ b/jpype/__init__.py @@ -51,7 +51,7 @@ __all__.extend(_jcustomizer.__all__) __all__.extend(_gui.__all__) -__version__ = "1.4.0" +__version__ = "1.4.1_dev0" __version_info__ = __version__.split('.') diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index a465503fe..cd6a3aa9e 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -73,7 +73,7 @@ public class JPypeContext { - public final String VERSION = "1.4.0"; + public final String VERSION = "1.4.1_dev0"; private static JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index add8561b0..341328621 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -719,7 +719,7 @@ PyMODINIT_FUNC PyInit__jpype() // PyJPModule = module; Py_INCREF(module); PyJPModule = module; - PyModule_AddStringConstant(module, "__version__", "1.4.0"); + PyModule_AddStringConstant(module, "__version__", "1.4.1_dev0"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. diff --git a/setup.py b/setup.py index 78271fc9c..8e428e0e2 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ setup( name='JPype1', - version='1.4.0', + version='1.4.1_dev0', description='A Python to Java bridge.', long_description=open('README.rst').read(), license='License :: OSI Approved :: Apache Software License', From e2dba15ecca5ead358db5b081e135dcf2ca8fed3 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sat, 14 May 2022 08:53:28 -0700 Subject: [PATCH 073/110] Try again on macos11. --- .azure/scripts/osx-python.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure/scripts/osx-python.sh b/.azure/scripts/osx-python.sh index ba4e38979..c2ad3e0b8 100755 --- a/.azure/scripts/osx-python.sh +++ b/.azure/scripts/osx-python.sh @@ -16,8 +16,8 @@ case $PYTHON_VERSION in INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.10) - FULL_VERSION=3.10.3 - INSTALLER_NAME=python-$FULL_VERSION-macosx11.pkg + FULL_VERSION=3.10.4 + INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; esac From c710b9e49d39ee1cd06dad58319890c87ff869bb Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Sun, 15 May 2022 08:01:51 -0700 Subject: [PATCH 074/110] Fix docs --- doc/conf.py | 12 +++++------- doc/dbapi2.rst | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 30d3d2983..24091a14d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -31,7 +31,10 @@ # 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.napoleon', 'sphinx.ext.autodoc', 'sphinx.ext.autosectionlabel', 'readthedocs_ext.readthedocs', ] +extensions = ['sphinx.ext.napoleon', + 'sphinx.ext.autodoc', + 'sphinx.ext.autosectionlabel', + 'readthedocs_ext.readthedocs', ] autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. @@ -236,12 +239,7 @@ def patch(self, key): # 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'] - -html_context = { - 'css_files': [ - '_static/theme_overrides.css', # override wide tables in RTD theme - ], -} +html_css_files = ['theme_overrides.css'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/doc/dbapi2.rst b/doc/dbapi2.rst index fd63b4d67..70b7167fd 100644 --- a/doc/dbapi2.rst +++ b/doc/dbapi2.rst @@ -398,6 +398,7 @@ We can address these sort of driver specific behaviors by applying a customizer to the Java class to add additional behaviors. .. code-block:: python + @jpype.JImplementationFor("java.sql.PreparedStatement") class MyStatement(object): @jpype.JOverride(sticky=True, rename='_close') From 228effc45ebf17855b07727849ce44e21f5cf285 Mon Sep 17 00:00:00 2001 From: Phil Elson <philip.elson@cern.ch> Date: Mon, 16 May 2022 08:58:40 +0200 Subject: [PATCH 075/110] Update the release scripts to include Python 3.10, and drop Python 3.6. Manylinux2010 images (centos6) are now provided instead of manylinux1 (centos5) in order to support Python 3.10 --- .azure/release.yml | 9 ++------- .azure/scripts/build-wheels.sh | 7 ++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.azure/release.yml b/.azure/release.yml index ba104823d..d31b8f1e7 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -30,11 +30,6 @@ stages: strategy: matrix: # Disabled because it is fetching beta images - # 64Bit2010: - # arch: x86_64 - # plat: manylinux2010_x86_64 - # image: quay.io/pypa/manylinux2010_x86_64 - # python.architecture: x64 # 64Bit2014: # arch: aarch64 # plat: manylinux2014_aarch64 @@ -43,12 +38,12 @@ stages: 64Bit: arch: x86_64 plat: manylinux1_x86_64 - image: quay.io/pypa/manylinux1_x86_64 + image: quay.io/pypa/manylinux2010_x86_64 python.architecture: x64 32Bit: arch: i686 plat: manylinux1_i686 - image: quay.io/pypa/manylinux1_i686 + image: quay.io/pypa/manylinux2010_i686 python.architecture: x86 pool: vmImage: "ubuntu-latest" diff --git a/.azure/scripts/build-wheels.sh b/.azure/scripts/build-wheels.sh index a55c361b3..5b536772a 100755 --- a/.azure/scripts/build-wheels.sh +++ b/.azure/scripts/build-wheels.sh @@ -2,13 +2,10 @@ set -e -x # Collect the pythons -pys=(/opt/python/*/bin) +pys=(/opt/python/cp*/bin) -# Filter out Python 3.4 +# Exclude specific Pythons (3.6) pys=(${pys[@]//*36*/}) -pys=(${pys[@]//*35*/}) -pys=(${pys[@]//*34*/}) -pys=(${pys[@]//*27*/}) # Compile wheels for PYBIN in "${pys[@]}"; do From 6032305608cb5c8fb265344508a032b430b391c3 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 16 May 2022 04:48:36 -0700 Subject: [PATCH 076/110] Updates to release --- .azure/release.yml | 9 ++------- .azure/scripts/build-wheels.sh | 7 ++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.azure/release.yml b/.azure/release.yml index ba104823d..d31b8f1e7 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -30,11 +30,6 @@ stages: strategy: matrix: # Disabled because it is fetching beta images - # 64Bit2010: - # arch: x86_64 - # plat: manylinux2010_x86_64 - # image: quay.io/pypa/manylinux2010_x86_64 - # python.architecture: x64 # 64Bit2014: # arch: aarch64 # plat: manylinux2014_aarch64 @@ -43,12 +38,12 @@ stages: 64Bit: arch: x86_64 plat: manylinux1_x86_64 - image: quay.io/pypa/manylinux1_x86_64 + image: quay.io/pypa/manylinux2010_x86_64 python.architecture: x64 32Bit: arch: i686 plat: manylinux1_i686 - image: quay.io/pypa/manylinux1_i686 + image: quay.io/pypa/manylinux2010_i686 python.architecture: x86 pool: vmImage: "ubuntu-latest" diff --git a/.azure/scripts/build-wheels.sh b/.azure/scripts/build-wheels.sh index a55c361b3..5b536772a 100755 --- a/.azure/scripts/build-wheels.sh +++ b/.azure/scripts/build-wheels.sh @@ -2,13 +2,10 @@ set -e -x # Collect the pythons -pys=(/opt/python/*/bin) +pys=(/opt/python/cp*/bin) -# Filter out Python 3.4 +# Exclude specific Pythons (3.6) pys=(${pys[@]//*36*/}) -pys=(${pys[@]//*35*/}) -pys=(${pys[@]//*34*/}) -pys=(${pys[@]//*27*/}) # Compile wheels for PYBIN in "${pys[@]}"; do From e3d06cbc71ba413295d3b6944ec498d44b97268e Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 16 May 2022 05:14:09 -0700 Subject: [PATCH 077/110] Disable 3.10 for verification. --- .azure/scripts/build-wheels.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure/scripts/build-wheels.sh b/.azure/scripts/build-wheels.sh index 5b536772a..af63bad2f 100755 --- a/.azure/scripts/build-wheels.sh +++ b/.azure/scripts/build-wheels.sh @@ -6,6 +6,7 @@ pys=(/opt/python/cp*/bin) # Exclude specific Pythons (3.6) pys=(${pys[@]//*36*/}) +pys=(${pys[@]//*310*/}) # Compile wheels for PYBIN in "${pys[@]}"; do From bbebffbd3e624d365372e3c4bc4bd2fba01ae902 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 16 May 2022 05:18:33 -0700 Subject: [PATCH 078/110] Try again. --- .azure/release.yml | 4 ++-- .azure/scripts/build-wheels.sh | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.azure/release.yml b/.azure/release.yml index d31b8f1e7..d1fe35ef3 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -37,12 +37,12 @@ stages: # python.architecture: aarch64 64Bit: arch: x86_64 - plat: manylinux1_x86_64 + plat: manylinux2010_x86_64 image: quay.io/pypa/manylinux2010_x86_64 python.architecture: x64 32Bit: arch: i686 - plat: manylinux1_i686 + plat: manylinux2010_i686 image: quay.io/pypa/manylinux2010_i686 python.architecture: x86 pool: diff --git a/.azure/scripts/build-wheels.sh b/.azure/scripts/build-wheels.sh index af63bad2f..5b536772a 100755 --- a/.azure/scripts/build-wheels.sh +++ b/.azure/scripts/build-wheels.sh @@ -6,7 +6,6 @@ pys=(/opt/python/cp*/bin) # Exclude specific Pythons (3.6) pys=(${pys[@]//*36*/}) -pys=(${pys[@]//*310*/}) # Compile wheels for PYBIN in "${pys[@]}"; do From 19422e7f2f8fb3578e98b9f42a61ce04c8e79123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Z=C3=A9=20Loff?= <zeloff+github@zeloff.org> Date: Tue, 24 May 2022 17:30:10 +0100 Subject: [PATCH 079/110] Add OpenBSD support on setupext/platform.py --- setupext/platform.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setupext/platform.py b/setupext/platform.py index e95722eb6..37f27e668 100644 --- a/setupext/platform.py +++ b/setupext/platform.py @@ -95,6 +95,10 @@ def Platform(include_dirs=None, sources=None, platform=sys.platform): elif platform.startswith('freebsd'): distutils.log.info("Add freebsd settings") jni_md_platform = 'freebsd' + + elif platform.startswith('openbsd'): + distutils.log.info("Add openbsd settings") + jni_md_platform = 'openbsd' elif platform.startswith('android'): distutils.log.info("Add android settings") @@ -137,6 +141,7 @@ def Platform(include_dirs=None, sources=None, platform=sys.platform): ${JAVA_INCLUDE_PATH}/win32 ${JAVA_INCLUDE_PATH}/linux ${JAVA_INCLUDE_PATH}/freebsd +${JAVA_INCLUDE_PATH}/openbsd ${JAVA_INCLUDE_PATH}/solaris ${JAVA_INCLUDE_PATH}/hp-ux ${JAVA_INCLUDE_PATH}/alpha From 947c0c40180646189b10cf9bb16dfbbbf49f7e04 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 24 Jun 2022 08:39:17 -0700 Subject: [PATCH 080/110] Preserve locale through startJVM --- jpype/_core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jpype/_core.py b/jpype/_core.py index 0fcfba7de..62508069c 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -215,9 +215,17 @@ def startJVM(*args, **kwargs): % (','.join([str(i) for i in kwargs]))) try: + # Keep the current locale settings, else Java will replace them. + import locale + categories = [locale.LC_CTYPE, locale.LC_COLLATE, locale.LC_TIME, + locale.LC_MONETARY, locale.LC_MESSAGES, locale.LC_NUMERIC] + prior = [locale.getlocale(i) for i in categories] _jpype.startup(jvmpath, tuple(args), ignoreUnrecognized, convertStrings, interrupt) initializeResources() + # Restore locale + for i, j in zip(categories, prior): + locale.setlocale(i, j) except RuntimeError as ex: source = str(ex) if "UnsupportedClassVersion" in source: From ec96f5b016b4c079a2c247e9c82dc9ae6216aee2 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 24 Jun 2022 08:59:25 -0700 Subject: [PATCH 081/110] Added changelog entry. --- doc/CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index b79367435..9bd32fa55 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,6 +5,9 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.4.1_dev0 - 2022-05-14** + + - Fixed issue with startJVM changing locale settings. + - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. From 43a2915b0aa1c48f6a7e88923cb9b0b044286a46 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 24 Jun 2022 10:57:06 -0700 Subject: [PATCH 082/110] Remove messages which is not supported on windows --- jpype/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jpype/_core.py b/jpype/_core.py index 62508069c..93216c145 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -218,7 +218,7 @@ def startJVM(*args, **kwargs): # Keep the current locale settings, else Java will replace them. import locale categories = [locale.LC_CTYPE, locale.LC_COLLATE, locale.LC_TIME, - locale.LC_MONETARY, locale.LC_MESSAGES, locale.LC_NUMERIC] + locale.LC_MONETARY, locale.LC_NUMERIC] prior = [locale.getlocale(i) for i in categories] _jpype.startup(jvmpath, tuple(args), ignoreUnrecognized, convertStrings, interrupt) From b512dc483e488bace86c8163996a187d0d3fc8b7 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 27 Jun 2022 08:09:43 -0700 Subject: [PATCH 083/110] Determine locale resources to restore automatically. --- jpype/_core.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jpype/_core.py b/jpype/_core.py index 93216c145..0e2417e8d 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -215,13 +215,15 @@ def startJVM(*args, **kwargs): % (','.join([str(i) for i in kwargs]))) try: - # Keep the current locale settings, else Java will replace them. import locale - categories = [locale.LC_CTYPE, locale.LC_COLLATE, locale.LC_TIME, - locale.LC_MONETARY, locale.LC_NUMERIC] + # Gather a list of locale settings that Java may override (excluding LC_ALL) + categories = [getattr(locale, i) for i in locale.__all__ if i.startswith('LC_') and i != 'LC_ALL'] + # Keep the current locale settings, else Java will replace them. prior = [locale.getlocale(i) for i in categories] + # Start the JVM _jpype.startup(jvmpath, tuple(args), ignoreUnrecognized, convertStrings, interrupt) + # Collect required resources for operation initializeResources() # Restore locale for i, j in zip(categories, prior): From 59ce0dfe69a83b6f2dd9d9c73eb8713c2a6953db Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 29 Jun 2022 10:45:41 -0700 Subject: [PATCH 084/110] Change to dir from __all__ --- jpype/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jpype/_core.py b/jpype/_core.py index 0e2417e8d..b88e139bf 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -217,7 +217,7 @@ def startJVM(*args, **kwargs): try: import locale # Gather a list of locale settings that Java may override (excluding LC_ALL) - categories = [getattr(locale, i) for i in locale.__all__ if i.startswith('LC_') and i != 'LC_ALL'] + categories = [getattr(locale, i) for i in dir(locale) if i.startswith('LC_') and i != 'LC_ALL'] # Keep the current locale settings, else Java will replace them. prior = [locale.getlocale(i) for i in categories] # Start the JVM From 628f3ad777405197bfca11b767aa160f80a2f304 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Thu, 30 Jun 2022 08:42:28 -0700 Subject: [PATCH 085/110] Fix for broken locale on restore. --- jpype/_core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jpype/_core.py b/jpype/_core.py index b88e139bf..702a3d158 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -227,7 +227,10 @@ def startJVM(*args, **kwargs): initializeResources() # Restore locale for i, j in zip(categories, prior): - locale.setlocale(i, j) + try: + locale.setlocale(i, j) + except locale.Error: + pass except RuntimeError as ex: source = str(ex) if "UnsupportedClassVersion" in source: From 98c90b9a229cf3f41f16c1f61828f54dc346313c Mon Sep 17 00:00:00 2001 From: lautalom <lautarolombardi19@gmail.com> Date: Mon, 4 Jul 2022 10:38:26 -0300 Subject: [PATCH 086/110] fix userguide found a little bug in an example --- doc/userguide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/userguide.rst b/doc/userguide.rst index 1648a40ba..f7ba9a060 100644 --- a/doc/userguide.rst +++ b/doc/userguide.rst @@ -92,7 +92,7 @@ set the class path, start the JVM, remove all the type declarations, and you are # Copy in the patterns from the guide to replace the example code db = Database("our_records") - with db.connect() as DatabaseConnection: + with db.connect() as c: c.runQuery() while c.hasRecords(): record = db.nextRecord() From a1cce94a8b943f180c77c67839ba0c3b633a7c7f Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Jul 2022 13:11:54 -0700 Subject: [PATCH 087/110] Sanity check for JImplements --- jpype/_jproxy.py | 5 +++++ test/jpypetest/test_proxy.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/jpype/_jproxy.py b/jpype/_jproxy.py index 2efa79df4..79804d459 100644 --- a/jpype/_jproxy.py +++ b/jpype/_jproxy.py @@ -59,6 +59,9 @@ def _createJProxyDeferred(cls, *intf): @JOverride notation on methods evaluated at first instantiation. """ + if not isinstance(cls, type): + raise TypeError("InstanceOf only applies to types, not %s" % (type(cls))) + def new(tp, *args, **kwargs): # Attach a __jpype_interfaces__ attribute to this class if # one doesn't already exist. @@ -77,6 +80,8 @@ def _createJProxy(cls, *intf): """ (internal) Create a proxy from a Python class with @JOverride notation on methods evaluated at declaration. """ + if not isinstance(cls, type): + raise TypeError("InstanceOf only applies to types, not %s" % (type(cls))) actualIntf = _prepareInterfaces(cls, intf) def new(tp, *args, **kwargs): diff --git a/test/jpypetest/test_proxy.py b/test/jpypetest/test_proxy.py index 8cba2006e..0cba57b52 100644 --- a/test/jpypetest/test_proxy.py +++ b/test/jpypetest/test_proxy.py @@ -138,6 +138,7 @@ def testMethod1(self): def testProxyImplementsForm2(self): itf1 = self.package.TestInterface1 itf2 = self.package.TestInterface2 + @JImplements(itf1, itf2) class MyImpl(object): @JOverride @@ -392,6 +393,7 @@ def equals(self, _obj): def testUnwrap(self): fixture = JClass("jpype.common.Fixture")() + @JImplements("java.io.Serializable") class Q(object): pass @@ -429,6 +431,7 @@ class R(object): def testMethods(self): fixture = JClass("jpype.common.Fixture")() + @JImplements("java.io.Serializable") class R(object): pass @@ -472,6 +475,7 @@ def run(self): def testWeak(self): hc = java.lang.System.identityHashCode + @JImplements("java.io.Serializable") class MyObj(object): pass @@ -496,6 +500,14 @@ def testFunctionalLambda(self): js = JObject(lambda x: 2 * x, "java.util.function.DoubleUnaryOperator") self.assertEqual(js.applyAsDouble(1), 2.0) + def testBadImplements(self): + with self.assertRaises(TypeError): + @JImplements("java.lang.Runnable") + def MyImpl(object): + @JOverride + def run(self): + pass + @subrun.TestCase(individual=True) class TestProxyDefinitionWithoutJVM(common.JPypeTestCase): From ce7ca9f87c2bf332c92099220d2682aed5cea5be Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Jul 2022 13:13:26 -0700 Subject: [PATCH 088/110] Remove extra line From c371234250749f90940c1f89dfe9fa4f6e98d0c9 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 11 Jul 2022 13:18:07 -0700 Subject: [PATCH 089/110] Fix typo --- jpype/_jproxy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jpype/_jproxy.py b/jpype/_jproxy.py index 79804d459..8c484cae7 100644 --- a/jpype/_jproxy.py +++ b/jpype/_jproxy.py @@ -60,7 +60,7 @@ def _createJProxyDeferred(cls, *intf): instantiation. """ if not isinstance(cls, type): - raise TypeError("InstanceOf only applies to types, not %s" % (type(cls))) + raise TypeError("JImplements only applies to types, not %s" % (type(cls))) def new(tp, *args, **kwargs): # Attach a __jpype_interfaces__ attribute to this class if @@ -81,7 +81,8 @@ def _createJProxy(cls, *intf): @JOverride notation on methods evaluated at declaration. """ if not isinstance(cls, type): - raise TypeError("InstanceOf only applies to types, not %s" % (type(cls))) + raise TypeError("JImplements only applies to types, not %s" % (type(cls))) + actualIntf = _prepareInterfaces(cls, intf) def new(tp, *args, **kwargs): From 21ef638651f97b45e62e58bba08ef5b386632ef1 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Thu, 1 Sep 2022 13:27:11 -0700 Subject: [PATCH 090/110] Fixes for 3.11 --- native/common/jp_exception.cpp | 46 ++++++++++++++-------------------- native/python/pyjp_value.cpp | 4 ++- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 477e99a86..53b7185ba 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -489,18 +489,18 @@ void JPypeException::toJava(JPContext *context) JP_TRACE_OUT; // GCOVR_EXCL_LINE } -PyTracebackObject *tb_create( - PyTracebackObject *last_traceback, +PyObject *tb_create( + PyObject *last_traceback, PyObject *dict, const char* filename, const char* funcname, int linenum) { // Create a code for this frame. (ref count is 1) - PyCodeObject *code = PyCode_NewEmpty(filename, funcname, linenum); + JPPyObject code = JPPyObject::accept((PyObject*)PyCode_NewEmpty(filename, funcname, linenum)); // If we don't get the code object there is no point - if (code == NULL) + if (code.get() == NULL) return NULL; // This is a bit of a kludge. Python lacks a way to directly create @@ -520,48 +520,40 @@ PyTracebackObject *tb_create( // portable, but we have to create a big (uninitialized object) each time we // want to pass in the previous frame. PyThreadState state; + JPPyObject prev; if (last_traceback != NULL) - state.frame = last_traceback->tb_frame; + { + prev = JPPyObject::call(PyObject_GetAttrString(last_traceback, "tb_frame")); + state.frame = (PyFrameObject*) prev.get(); + } else state.frame = NULL; // Create a frame for the traceback. - PyFrameObject *frame = PyFrame_New(&state, code, dict, NULL); - - // frame just borrows the reference rather than claiming it - // so we need to get rid of the extra reference here. - Py_DECREF(code); + JPPyObject frame = JPPyObject::call((PyObject*) PyFrame_New(&state, (PyCodeObject*) code.get(), dict, NULL)); // If we don't get the frame object there is no point - if (frame == NULL) + if (frame.get() == NULL) return NULL; // Create a traceback - PyTracebackObject *traceback = (PyTracebackObject*) - PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type); + JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(((PyFrameObject*)frame.get())->f_lasti)); + JPPyObject linenuma = JPPyObject::claim(PyLong_FromLong(linenum)); + JPPyObject tuple = JPPyObject::call(PyTuple_Pack(4, Py_None, frame.get(), lasti.get(), linenuma.get())); + JPPyObject traceback = JPPyObject::accept(PyObject_Call((PyObject*) &PyTraceBack_Type, tuple.get(), NULL)); // We could fail in process - if (traceback == NULL) + if (traceback.get() == NULL) { - Py_DECREF(frame); return NULL; } - // Set the fields - traceback->tb_frame = frame; // Steal the reference from frame - traceback->tb_lasti = frame->f_lasti; - traceback->tb_lineno = linenum; - Py_XINCREF(last_traceback); - traceback->tb_next = last_traceback; - - // Allow GC on the object - PyObject_GC_Track(traceback); - return traceback; + return traceback.keep(); } PyObject* PyTrace_FromJPStackTrace(JPStackTrace& trace) { - PyTracebackObject *last_traceback = NULL; + PyObject *last_traceback = NULL; PyObject *dict = PyModule_GetDict(PyJPModule); for (JPStackTrace::iterator iter = trace.begin(); iter != trace.end(); ++iter) { @@ -575,7 +567,7 @@ PyObject* PyTrace_FromJPStackTrace(JPStackTrace& trace) JPPyObject PyTrace_FromJavaException(JPJavaFrame& frame, jthrowable th, jthrowable prev) { - PyTracebackObject *last_traceback = NULL; + PyObject *last_traceback = NULL; JPContext *context = frame.getContext(); jvalue args[2]; args[0].l = th; diff --git a/native/python/pyjp_value.cpp b/native/python/pyjp_value.cpp index 6947a7631..366e00193 100644 --- a/native/python/pyjp_value.cpp +++ b/native/python/pyjp_value.cpp @@ -23,6 +23,8 @@ extern "C" { #endif +extern PyObject * _PyObject_GC_Malloc(size_t basicsize); + /** * Allocate a new Python object with a slot for Java. * @@ -305,4 +307,4 @@ bool PyJPValue_isSetJavaSlot(PyObject* self) return false; // GCOVR_EXCL_LINE JPValue* slot = (JPValue*) (((char*) self) + offset); return slot->getClass() != NULL; -} \ No newline at end of file +} From ac2f43f83faa3d35a4c1638aa86b0ca571b745e4 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Fri, 16 Sep 2022 16:03:19 -0400 Subject: [PATCH 091/110] jpype#1091: allow null characters in strings when convertStrings=True Previously, `PyUnicode_FromString(str.c_str())` was used when convertStrings=True. If the string contains null bytes, this will terminate the string early. To allow strings with null bytes to be returned, `PyUnicode_FromStringAndSize(str.c_str(), str.length())` is now used instead, which create a Unicode object from the pointer with the given length (instead of the position of the first NULL character), thus allowing null bytes in the string. --- native/common/jp_stringtype.cpp | 2 +- test/harness/jpype/str/Test.java | 4 ++++ test/jpypetest/test_legacy.py | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/native/common/jp_stringtype.cpp b/native/common/jp_stringtype.cpp index 4ba607d6f..848c8a572 100644 --- a/native/common/jp_stringtype.cpp +++ b/native/common/jp_stringtype.cpp @@ -47,7 +47,7 @@ JPPyObject JPStringType::convertToPythonObject(JPJavaFrame& frame, jvalue val, b if (context->getConvertStrings()) { string str = frame.toStringUTF8((jstring) (val.l)); - return JPPyObject::call(PyUnicode_FromString(str.c_str())); + return JPPyObject::call(PyUnicode_FromStringAndSize(str.c_str(), str.length())); } } diff --git a/test/harness/jpype/str/Test.java b/test/harness/jpype/str/Test.java index 3552501d1..bf56880be 100644 --- a/test/harness/jpype/str/Test.java +++ b/test/harness/jpype/str/Test.java @@ -31,6 +31,10 @@ public String memberCall() return "memberCall"; } + public String callWithNullBytes() { + return "call\0With\0Null\0Bytes"; + } + public static final String array[] = { "apples", "banana", "cherries", "dates", "elderberry" diff --git a/test/jpypetest/test_legacy.py b/test/jpypetest/test_legacy.py index 4698d6e85..0a6f25abc 100644 --- a/test/jpypetest/test_legacy.py +++ b/test/jpypetest/test_legacy.py @@ -79,3 +79,8 @@ def testProxy(self): r = self._test().callProxy(p, "roundtrip") self.assertEqual(r, "roundtrip") self.assertIsInstance(r, str) + + def testNullBytes(self): + s = self._test().callWithNullBytes() + self.assertEqual(s, "call\u0000With\u0000Null\u0000Bytes") + self.assertIsInstance(s, str) From 9694d4308243aaa7662d3cf5ce4576d4f7e4e222 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli <chris.chianelli@gmail.com> Date: Fri, 16 Sep 2022 16:24:58 -0400 Subject: [PATCH 092/110] Add tests for convertString=True with NULL bytes in different encodings --- test/harness/jpype/str/Test.java | 6 +++++- test/jpypetest/test_legacy.py | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/harness/jpype/str/Test.java b/test/harness/jpype/str/Test.java index bf56880be..5803b15f7 100644 --- a/test/harness/jpype/str/Test.java +++ b/test/harness/jpype/str/Test.java @@ -31,10 +31,14 @@ public String memberCall() return "memberCall"; } - public String callWithNullBytes() { + public static String callWithNullBytes() { return "call\0With\0Null\0Bytes"; } + public static String returnArgument(String argument) { + return argument; + } + public static final String array[] = { "apples", "banana", "cherries", "dates", "elderberry" diff --git a/test/jpypetest/test_legacy.py b/test/jpypetest/test_legacy.py index 0a6f25abc..e0708d0b3 100644 --- a/test/jpypetest/test_legacy.py +++ b/test/jpypetest/test_legacy.py @@ -81,6 +81,16 @@ def testProxy(self): self.assertIsInstance(r, str) def testNullBytes(self): - s = self._test().callWithNullBytes() + s = self._test.callWithNullBytes() self.assertEqual(s, "call\u0000With\u0000Null\u0000Bytes") self.assertIsInstance(s, str) + + s = self._test.returnArgument("\u0394 16 byte encoded text\u0000with null\u0000 bytes \u1394") + self.assertEqual(s, "\u0394 16 byte encoded text\u0000with null\u0000 bytes \u1394") + self.assertIsInstance(s, str) + + s = self._test.returnArgument("\U0001F468\u200D\U0001F9B2 32 byte encoded text" + "\u0000with null\u0000 bytes \U0001F468\u200D\U0001F9B2") + self.assertEqual(s, "\U0001F468\u200D\U0001F9B2 32 byte encoded text" + "\u0000with null\u0000 bytes \U0001F468\u200D\U0001F9B2") + self.assertIsInstance(s, str) From 05b02ffd9a8b6855904cc70faaf3d5359c888e5b Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 7 Oct 2022 07:09:14 -0700 Subject: [PATCH 093/110] Work on Python 3.11 --- native/common/jp_context.cpp | 6 +----- native/common/jp_exception.cpp | 32 +++++++++----------------------- native/python/pyjp_value.cpp | 32 +++++++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/native/common/jp_context.cpp b/native/common/jp_context.cpp index 40a017216..7e1184a06 100644 --- a/native/common/jp_context.cpp +++ b/native/common/jp_context.cpp @@ -282,14 +282,10 @@ void JPContext::initializeResources(JNIEnv* env, bool interrupt) if (!m_Embedded) { - JPPyObject import = JPPyObject::call(PyImport_AddModule("importlib.util")); + JPPyObject import = JPPyObject::use(PyImport_AddModule("importlib.util")); JPPyObject jpype = JPPyObject::call(PyObject_CallMethod(import.get(), "find_spec", "s", "_jpype")); JPPyObject origin = JPPyObject::call(PyObject_GetAttrString(jpype.get(), "origin")); val[2].l = frame.fromStringUTF8(JPPyString::asStringUTF8(origin.get())); - import.incref(); // The documentation specifies that PyImport_AddModule must return a - // new reference, but that is not happening in Python 3.10 - // so we are triggering a gc assertion failure. To prevent - // the failure manually up the reference counter here. } m_JavaContext = JPObjectRef(frame, frame.CallStaticObjectMethodA(contextClass, startMethod, val)); diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 477e99a86..f0e0fc71e 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -503,30 +503,16 @@ PyTracebackObject *tb_create( if (code == NULL) return NULL; - // This is a bit of a kludge. Python lacks a way to directly create - // a frame from a code object except when creating from the threadstate. - // - // In reviewing Python implementation, I find that the only element accessed - // in the thread state was the previous frame to link to. Because frame - // objects change a lot between different Python versions, trying to - // replicate the actions of setting up a frame is difficult to keep portable. - // - // Python 3.10 introduces the additional requirement that the global - // dictionary supplied must have a __builtins__. We can do this once - // when create the module. - // - // If instead we create a thread state and point the field it needs to the - // previous frame we create the frames using the defined API. Much more - // portable, but we have to create a big (uninitialized object) each time we - // want to pass in the previous frame. - PyThreadState state; - if (last_traceback != NULL) - state.frame = last_traceback->tb_frame; - else - state.frame = NULL; // Create a frame for the traceback. - PyFrameObject *frame = PyFrame_New(&state, code, dict, NULL); + PyThreadState *state = PyThreadState_GET(); + PyFrameObject *frame = PyFrame_New(state, code, dict, NULL); + + // Swap the back pointer + //PyObject* old = frame->f_back; + //frame->f_back = last_traceback->tb_frame; + //Py_XDECREF(old); + //Py_XINCREF(last_traceback->tb_frame); // frame just borrows the reference rather than claiming it // so we need to get rid of the extra reference here. @@ -549,7 +535,7 @@ PyTracebackObject *tb_create( // Set the fields traceback->tb_frame = frame; // Steal the reference from frame - traceback->tb_lasti = frame->f_lasti; + traceback->tb_lasti = PyFrame_GetLasti(frame); traceback->tb_lineno = linenum; Py_XINCREF(last_traceback); traceback->tb_next = last_traceback; diff --git a/native/python/pyjp_value.cpp b/native/python/pyjp_value.cpp index 6947a7631..86398a312 100644 --- a/native/python/pyjp_value.cpp +++ b/native/python/pyjp_value.cpp @@ -43,8 +43,34 @@ PyObject* PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems ) JP_PY_TRY("PyJPValue_alloc"); // Modification from Python to add size elements const size_t size = _PyObject_VAR_SIZE(type, nitems + 1) + sizeof (JPValue); - PyObject *obj = (PyType_IS_GC(type)) ? _PyObject_GC_Malloc(size) - : (PyObject *) PyObject_MALLOC(size); + PyObject *obj = NULL; + if (PyType_IS_GC(type)) + { + // Horrible kludge because python lacks an API for allocating a GC type with extra memory + // The privor method _PyObject_GC_Alloc is no longer visible, so we are forced to allocate + // a different type with the extra memory and then hot swap the type to the real one. + PyTypeObject type2; + type2.tp_basicsize = size; + type2.tp_itemsize = 0; + type2.tp_name = NULL; + type2.tp_flags = type->tp_flags; + type2.tp_traverse = type->tp_traverse; + + // Allocate the fake type + obj = PyObject_GC_New(PyObject, &type2); + + // Hot swap to the required type + obj->ob_type = type; + if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) + { + Py_INCREF(type); + } + // Note the object will be inited twice which should not leak. (fingers crossed) + } + else + { + obj = (PyObject*) PyObject_MALLOC(size); + } if (obj == NULL) return PyErr_NoMemory(); // GCOVR_EXCL_LINE memset(obj, 0, size); @@ -305,4 +331,4 @@ bool PyJPValue_isSetJavaSlot(PyObject* self) return false; // GCOVR_EXCL_LINE JPValue* slot = (JPValue*) (((char*) self) + offset); return slot->getClass() != NULL; -} \ No newline at end of file +} From 68c246f0fd9f03599af08fa70b6655de6c4b409c Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Fri, 7 Oct 2022 13:11:19 -0700 Subject: [PATCH 094/110] Remove extra reference --- native/python/pyjp_value.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/native/python/pyjp_value.cpp b/native/python/pyjp_value.cpp index 65327a0d5..e9ac16637 100644 --- a/native/python/pyjp_value.cpp +++ b/native/python/pyjp_value.cpp @@ -61,12 +61,6 @@ PyObject* PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems ) // Allocate the fake type obj = PyObject_GC_New(PyObject, &type2); - // Hot swap to the required type - obj->ob_type = type; - if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) - { - Py_INCREF(type); - } // Note the object will be inited twice which should not leak. (fingers crossed) } else From 42716c1f88ff359f282c4598c1962b9598d8a3b9 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 10 Oct 2022 09:39:25 -0700 Subject: [PATCH 095/110] Minor fixes --- jpype/_jvmfinder.py | 6 +++--- setupext/test_java.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jpype/_jvmfinder.py b/jpype/_jvmfinder.py index e738ee48b..64b656c2b 100644 --- a/jpype/_jvmfinder.py +++ b/jpype/_jvmfinder.py @@ -320,10 +320,10 @@ def _javahome_binary(self): """ import platform import subprocess - from distutils.version import StrictVersion + from packaging.version import Version - current = StrictVersion(platform.mac_ver()[0][:4]) - if current >= StrictVersion('10.6') and current < StrictVersion('10.9'): + current = Version(platform.mac_ver()[0][:4]) + if current >= Version('10.6') and current < Version('10.9'): return subprocess.check_output( ['/usr/libexec/java_home']).strip() diff --git a/setupext/test_java.py b/setupext/test_java.py index d0a02dec5..5e8383a36 100644 --- a/setupext/test_java.py +++ b/setupext/test_java.py @@ -53,7 +53,7 @@ def compileJava(): srcs = glob.glob('test/harness/jpype/**/*.java', recursive=True) srcs.extend(glob.glob('test/harness/org/**/*.java', recursive=True)) exports = "" - if version > 7: + if version == 8: srcs.extend(glob.glob('test/harness/java8/**/*.java', recursive=True)) if version > 8: srcs.extend(glob.glob('test/harness/java9/**/*.java', recursive=True)) From a582302334e7614999032f8b8cab4fa0f1f9c861 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 10 Oct 2022 09:47:05 -0700 Subject: [PATCH 096/110] Multiversion --- native/common/jp_exception.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 0e7fd3272..170cf89a4 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -519,7 +519,11 @@ PyObject *tb_create( return NULL; // Create a traceback +#if PY_VERSION_HEX<0x03110000 + JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); +#else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); +#endif JPPyObject linenuma = JPPyObject::claim(PyLong_FromLong(linenum)); JPPyObject tuple = JPPyObject::call(PyTuple_Pack(4, Py_None, frame.get(), lasti.get(), linenuma.get())); JPPyObject traceback = JPPyObject::accept(PyObject_Call((PyObject*) &PyTraceBack_Type, tuple.get(), NULL)); From 2918afef51263fbe2907ec8cd65e998e8090a4a1 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 10 Oct 2022 09:48:01 -0700 Subject: [PATCH 097/110] Documentation --- doc/CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 9bd32fa55..af7b42585 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -8,6 +8,8 @@ Latest Changes: - Fixed issue with startJVM changing locale settings. + - Changes to support Python 3.11 + - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. From e8f6a09864b5165a2836aed1a54706a9b28f31b0 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 17 Oct 2022 06:47:28 -0700 Subject: [PATCH 098/110] Fixes for review --- native/common/jp_exception.cpp | 6 ------ native/python/pyjp_value.cpp | 4 +--- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 170cf89a4..ddff7ce50 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -508,12 +508,6 @@ PyObject *tb_create( PyFrameObject *pframe = PyFrame_New(state, (PyCodeObject*) code.get(), dict, NULL); JPPyObject frame = JPPyObject::accept((PyObject*)pframe); - // Swap the back pointer - //PyObject* old = frame->f_back; - //frame->f_back = last_traceback->tb_frame; - //Py_XDECREF(old); - //Py_XINCREF(last_traceback->tb_frame); - // If we don't get the frame object there is no point if (frame.get() == NULL) return NULL; diff --git a/native/python/pyjp_value.cpp b/native/python/pyjp_value.cpp index e9ac16637..93dba9505 100644 --- a/native/python/pyjp_value.cpp +++ b/native/python/pyjp_value.cpp @@ -23,8 +23,6 @@ extern "C" { #endif -extern PyObject * _PyObject_GC_Malloc(size_t basicsize); - /** * Allocate a new Python object with a slot for Java. * @@ -49,7 +47,7 @@ PyObject* PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems ) if (PyType_IS_GC(type)) { // Horrible kludge because python lacks an API for allocating a GC type with extra memory - // The privor method _PyObject_GC_Alloc is no longer visible, so we are forced to allocate + // The private method _PyObject_GC_Alloc is no longer visible, so we are forced to allocate // a different type with the extra memory and then hot swap the type to the real one. PyTypeObject type2; type2.tp_basicsize = size; From 64223ad96e94136f7a5b8ea63ff386f1c86b1840 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Mon, 17 Oct 2022 09:17:11 -0700 Subject: [PATCH 099/110] For review --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8e428e0e2..034f6637c 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,8 @@ packages=['jpype', 'jpype._pyinstaller'], package_dir={'jpype': 'jpype', }, package_data={'jpype': ['*.pyi']}, - install_requires=['typing_extensions ; python_version< "3.8"'], + install_requires=['typing_extensions ; python_version< "3.8"', + 'packaging ; python_version< "3.10"'], tests_require=['pytest'], extras_require={ 'tests': [ From ab9769913b676f0197531f7523fd762f08f12586 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:10:26 -0700 Subject: [PATCH 100/110] Tracking changes --- doc/CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index af7b42585..1273fa660 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -10,6 +10,9 @@ Latest Changes: - Changes to support Python 3.11 + - Fix truncation of strings on null when using convert strings. + + - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. From 47707c260270b2c8399db8776f62f4ed1b390d33 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:12:33 -0700 Subject: [PATCH 101/110] Packaging is required for all versions. --- doc/CHANGELOG.rst | 2 ++ setup.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 1273fa660..916af6e59 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -12,6 +12,8 @@ Latest Changes: - Fix truncation of strings on null when using convert strings. + - Replaced distutil with packaging + - **1.4.0 - 2022-05-14** diff --git a/setup.py b/setup.py index 034f6637c..7c42f7d47 100644 --- a/setup.py +++ b/setup.py @@ -79,7 +79,7 @@ package_dir={'jpype': 'jpype', }, package_data={'jpype': ['*.pyi']}, install_requires=['typing_extensions ; python_version< "3.8"', - 'packaging ; python_version< "3.10"'], + 'packaging'], tests_require=['pytest'], extras_require={ 'tests': [ From be5daa35aeccb758fcef66b2fedc2f6074d1cf55 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:13:27 -0700 Subject: [PATCH 102/110] =?UTF-8?q?Bump=20version:=201.4.1=5Fdev0=20?= =?UTF-8?q?=E2=86=92=201.4.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- doc/CHANGELOG.rst | 1 + jpype/__init__.py | 2 +- native/java/org/jpype/JPypeContext.java | 2 +- native/python/pyjp_module.cpp | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 075183a65..3dcdfe6e5 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.1_dev0 +current_version = 1.4.1 commit = True tag = False parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))? diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 916af6e59..2ee5f7e64 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: +- **1.4.1 - 2022-10-26** - **1.4.1_dev0 - 2022-05-14** - Fixed issue with startJVM changing locale settings. diff --git a/jpype/__init__.py b/jpype/__init__.py index bd704f265..573b35f06 100644 --- a/jpype/__init__.py +++ b/jpype/__init__.py @@ -51,7 +51,7 @@ __all__.extend(_jcustomizer.__all__) __all__.extend(_gui.__all__) -__version__ = "1.4.1_dev0" +__version__ = "1.4.1" __version_info__ = __version__.split('.') diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index cd6a3aa9e..19dbf1d37 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -73,7 +73,7 @@ public class JPypeContext { - public final String VERSION = "1.4.1_dev0"; + public final String VERSION = "1.4.1"; private static JPypeContext INSTANCE = new JPypeContext(); // This is the C++ portion of the context. diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 341328621..669b3e8fa 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -719,7 +719,7 @@ PyMODINIT_FUNC PyInit__jpype() // PyJPModule = module; Py_INCREF(module); PyJPModule = module; - PyModule_AddStringConstant(module, "__version__", "1.4.1_dev0"); + PyModule_AddStringConstant(module, "__version__", "1.4.1"); // Our module will be used for PyFrame object and it is a requirement that // we have a builtins in our dictionary. diff --git a/setup.py b/setup.py index 7c42f7d47..e7664ef33 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ setup( name='JPype1', - version='1.4.1_dev0', + version='1.4.1', description='A Python to Java bridge.', long_description=open('README.rst').read(), license='License :: OSI Approved :: Apache Software License', From 3ade633f51b62d1e0ddb6686f6f4337102dd5e84 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:15:57 -0700 Subject: [PATCH 103/110] Attempt to test on 3.11 --- .azure/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.azure/build.yml b/.azure/build.yml index 3274ea908..4b16e8e04 100644 --- a/.azure/build.yml +++ b/.azure/build.yml @@ -62,6 +62,9 @@ jobs: linux-3.10: imageName: "ubuntu-latest" python.version: '3.10' + linux-3.11: + imageName: "ubuntu-latest" + python.version: '3.11' windows-3.7: imageName: "windows-2019" python.version: '3.7' @@ -75,6 +78,9 @@ jobs: windows-3.10: imageName: "windows-2019" python.version: '3.10' + windows-3.11: + imageName: "windows-2019" + python.version: '3.11' mac-3.9: imageName: "macos-11" python.version: '3.9' From 5680ea7b27c60cf0c82009865255f7c7483594cc Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:21:37 -0700 Subject: [PATCH 104/110] conditional not working. --- native/common/jp_exception.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index ddff7ce50..080051ce7 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -514,6 +514,7 @@ PyObject *tb_create( // Create a traceback #if PY_VERSION_HEX<0x03110000 +#warning PY_VERSION_HEX JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); #else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); From fc22b6ebe21d17070f1d46fc64797afeb711c222 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:27:05 -0700 Subject: [PATCH 105/110] Try again --- native/common/jp_exception.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 080051ce7..560f429ec 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -513,8 +513,7 @@ PyObject *tb_create( return NULL; // Create a traceback -#if PY_VERSION_HEX<0x03110000 -#warning PY_VERSION_HEX +#if PY_VERSION_HEX<0x03109900 JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); #else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); From f764e148d2b8fc57c803828e5eb906d748279052 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:34:03 -0700 Subject: [PATCH 106/110] Testing --- native/common/jp_exception.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 560f429ec..d37d25553 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -513,7 +513,7 @@ PyObject *tb_create( return NULL; // Create a traceback -#if PY_VERSION_HEX<0x03109900 +#if PY_MINOR_VERSION<10 JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); #else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); From 977135652185fb5585795d7893185492cfb51c7f Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:38:53 -0700 Subject: [PATCH 107/110] Debugging testbench --- native/common/jp_exception.cpp | 2 +- test-requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index d37d25553..1baed06f2 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -513,7 +513,7 @@ PyObject *tb_create( return NULL; // Create a traceback -#if PY_MINOR_VERSION<10 +#if PY_MINOR_VERSION<11 JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti)); #else JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe))); diff --git a/test-requirements.txt b/test-requirements.txt index a6108b5c8..0fa49257f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,3 @@ -pytest==7.1.1 -pyinstaller==4.10 +pytest +pyinstaller jedi==0.18.0 From 24cc69477cc4795f4bd875a0b1a976bb24721a4b Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:47:36 -0700 Subject: [PATCH 108/110] Update release scripts --- .azure/release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.azure/release.yml b/.azure/release.yml index d1fe35ef3..6c83ec4c9 100644 --- a/.azure/release.yml +++ b/.azure/release.yml @@ -68,6 +68,9 @@ stages: Python310: python.version: '3.10' python.architecture: 'x64' + Python311: + python.version: '3.11' + python.architecture: 'x64' pool: vmImage: "windows-2019" steps: @@ -96,6 +99,8 @@ stages: python.version: '3.9' Python310: python.version: '3.10' + Python311: + python.version: '3.11' pool: vmImage: "macos-11" steps: From c8378720327ccccdf182ddd8cfd858e1e0d79b78 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 18:48:22 -0700 Subject: [PATCH 109/110] Fix changelog --- doc/CHANGELOG.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/CHANGELOG.rst b/doc/CHANGELOG.rst index 2ee5f7e64..aa7d7f8ae 100644 --- a/doc/CHANGELOG.rst +++ b/doc/CHANGELOG.rst @@ -5,7 +5,6 @@ This changelog *only* contains changes from the *first* pypi release (0.5.4.3) o Latest Changes: - **1.4.1 - 2022-10-26** -- **1.4.1_dev0 - 2022-05-14** - Fixed issue with startJVM changing locale settings. From f52169aebe4d58222c27e2a290f97c1d7a9f0b54 Mon Sep 17 00:00:00 2001 From: "Karl E. Nelson" <nelson85@llnl.gov> Date: Wed, 26 Oct 2022 19:06:37 -0700 Subject: [PATCH 110/110] Update mac release script --- .azure/scripts/osx-python.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.azure/scripts/osx-python.sh b/.azure/scripts/osx-python.sh index c2ad3e0b8..292ce31c2 100755 --- a/.azure/scripts/osx-python.sh +++ b/.azure/scripts/osx-python.sh @@ -19,6 +19,10 @@ case $PYTHON_VERSION in FULL_VERSION=3.10.4 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; +3.11) + FULL_VERSION=3.11.0 + INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg + ;; esac URL=https://www.python.org/ftp/python/$FULL_VERSION/$INSTALLER_NAME