From f38b427acb728a1ccbfa2fc44223d8f20e671aa9 Mon Sep 17 00:00:00 2001 From: Karl Nelson Date: Fri, 29 Nov 2024 10:10:18 -0800 Subject: [PATCH 1/3] Cleanup and test --- jpype/_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jpype/_core.py b/jpype/_core.py index 5015ba782..88053eb98 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -179,13 +179,13 @@ def _findTemp(): else: dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ]) - name = str(os.getpid) + name = str(os.getpid()) for d in dirlist: p = Path("%s/%s"%(d,name)) try: p.touch() p.unlink() - except Exception: + except Exception as ex: continue return d raise SystemError("Unable to find non-ansii path") From 37647491e3f81bbbcb5af2bd49d3e62cc1ec88e0 Mon Sep 17 00:00:00 2001 From: Karl Nelson Date: Fri, 29 Nov 2024 15:17:13 -0800 Subject: [PATCH 2/3] Try again for delete. --- jpype/_core.py | 19 +++++++------- native/common/jp_context.cpp | 47 ++++++++++++++++++++++++++++++++--- native/python/pyjp_module.cpp | 32 ++++++++++++++++++++++-- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/jpype/_core.py b/jpype/_core.py index 33ab4f10d..20e520278 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -158,7 +158,6 @@ def _expandClassPath( _JVM_started = False -_tmp = None def interactive(): return bool(getattr(sys, 'ps1', sys.flags.interactive)) @@ -303,6 +302,7 @@ def startJVM( java_class_path.append(support_lib) java_class_path = list(filter(len, java_class_path)) classpath = _classpath._SEP.join(java_class_path) + tmp = None # Make sure our module is always on the classpath if not classpath.isascii(): @@ -314,13 +314,14 @@ def startJVM( if not support_lib.isascii(): import tempfile import shutil - global _tmp - _tmp = tempfile.TemporaryDirectory(dir = _findTemp()) - if not _tmp.name.isascii(): - raise ValueError("Unable to find ascii temp directory. Clear TEMPDIR, TEMP, and TMP environment variables") - sl2 = os.path.join(_tmp.name, "org.jpype.jar") - shutil.copyfile(support_lib, sl2) - support_lib = sl2 + fd, path = tempfile.mkstemp(dir = _findTemp()) + if not path.isascii(): + raise ValueError("Unable to find ascii temp directory.") + shutil.copyfile(support_lib, path) + support_lib = path + tmp = path + os.close(fd) + # Don't remove # ok, setup the jpype system classloader and add to the path after startup # this guarentees all classes have the same permissions as they did in the past @@ -343,7 +344,7 @@ def startJVM( prior = [locale.getlocale(i) for i in categories] # Start the JVM _jpype.startup(jvmpath, tuple(jvm_args + extra_jvm_args), - ignoreUnrecognized, convertStrings, interrupt) + ignoreUnrecognized, convertStrings, interrupt, tmp) # Collect required resources for operation initializeResources() # Restore locale diff --git a/native/common/jp_context.cpp b/native/common/jp_context.cpp index 67c5526f9..eaf6e44c6 100644 --- a/native/common/jp_context.cpp +++ b/native/common/jp_context.cpp @@ -22,6 +22,18 @@ #include "jp_platform.h" #include "jp_gc.h" +#ifdef WIN32 +#include +#else +#if defined(_HPUX) && !defined(_IA64) +#include +#else +#include +#endif // HPUX +#include +#endif + + JPResource::~JPResource() = default; @@ -159,6 +171,35 @@ void JPContext::attachJVM(JNIEnv* env) initializeResources(env, false); } +std::string getShared() +{ +#ifdef WIN32 + // Windows specific + char path[MAX_PATH]; + HMODULE hm = NULL; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR) &getShared, &hm) != 0 && + GetModuleFileName(hm, path, sizeof(path)) != 0) + { + // This is needed when there is no-ascii characters in path + char shortPathBuffer[MAX_PATH]; + GetShortPathName(path, shortPathBuffer, MAX_PATH); + return shortPathBuffer; + } +#else + // Linux specific + Dl_info info; + if (dladdr((void*)getShared, &info)) + return info.dli_fname; +#endif + // Generic + 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")); + return JPPyString::asStringUTF8(origin.get()); +} + void JPContext::initializeResources(JNIEnv* env, bool interrupt) { JPJavaFrame frame = JPJavaFrame::external(this, env); @@ -215,10 +256,8 @@ void JPContext::initializeResources(JNIEnv* env, bool interrupt) if (!m_Embedded) { - 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())); + std::string shared = getShared(); + val[2].l = frame.fromStringUTF8(shared); } // Required before launch diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 338e92d24..2999689c1 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -20,6 +20,9 @@ #include "jp_gc.h" #include "jp_stringtype.h" #include "jp_classloader.h" +#ifdef WIN32 +#include +#endif void PyJPModule_installGC(PyObject* module); @@ -229,6 +232,7 @@ int PyJP_IsInstanceSingle(PyObject* obj, PyTypeObject* type) #ifndef ANDROID extern JNIEnv *Android_JNI_GetEnv(); +static string jarTmpPath; static PyObject* PyJPModule_startup(PyObject* module, PyObject* pyargs) { JP_PY_TRY("PyJPModule_startup"); @@ -238,11 +242,23 @@ static PyObject* PyJPModule_startup(PyObject* module, PyObject* pyargs) char ignoreUnrecognized = true; char convertStrings = false; char interrupt = false; + PyObject* tmp; - if (!PyArg_ParseTuple(pyargs, "OO!bbb", &vmPath, &PyTuple_Type, &vmOpt, - &ignoreUnrecognized, &convertStrings, &interrupt)) + if (!PyArg_ParseTuple(pyargs, "OO!bbbO", &vmPath, &PyTuple_Type, &vmOpt, + &ignoreUnrecognized, &convertStrings, &interrupt, &tmp)) return nullptr; + if (tmp != Py_None) + { + if (!(JPPyString::check(tmp))) + { + PyErr_SetString(PyExc_TypeError, "Java jar path must be a string"); + return nullptr; + } + jarTmpPath = JPPyString::asStringUTF8(tmp); + } + + if (!(JPPyString::check(vmPath))) { PyErr_SetString(PyExc_TypeError, "Java JVM path must be a string"); @@ -298,6 +314,18 @@ static PyObject* PyJPModule_shutdown(PyObject* obj, PyObject* pyargs, PyObject* return nullptr; JPContext_global->shutdownJVM(destroyJVM, freeJVM); + +#ifdef WIN32 + // Thus far this doesn't work on WINDOWS. The issue is a bug in the JVM + // is holding the file open and there is no apparent method to close it + // so that this can succeed + if (jarTmpPath != "") + remove(jarTmpPath.c_str()); +#else + if (jarTmpPath != "") + unlink(jarTmpPath.c_str()); +#endif + Py_RETURN_NONE; JP_PY_CATCH(nullptr); } From 67f732d436f7a3de44520af55071ddc44c996b14 Mon Sep 17 00:00:00 2001 From: Karl Nelson Date: Fri, 29 Nov 2024 15:26:07 -0800 Subject: [PATCH 3/3] Fix broken test --- test/jpypetest/test_fault.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/jpypetest/test_fault.py b/test/jpypetest/test_fault.py index c84ab3e30..7c0853def 100644 --- a/test/jpypetest/test_fault.py +++ b/test/jpypetest/test_fault.py @@ -1083,13 +1083,15 @@ def testStartupBadArg(self): with self.assertRaisesRegex(TypeError, "takes exactly"): _jpype.startup() with self.assertRaisesRegex(TypeError, "must be tuple"): - _jpype.startup(object(), object(), True, True, True) + _jpype.startup(object(), object(), True, True, True, None) with self.assertRaisesRegex(TypeError, "must be strings"): - _jpype.startup("", (object(),), True, True, True) + _jpype.startup("", (object(),), True, True, True, None) with self.assertRaisesRegex(TypeError, "must be a string"): - _jpype.startup(object(), tuple(), True, True, True) + _jpype.startup(object(), tuple(), True, True, True, None) + with self.assertRaisesRegex(TypeError, "must be a string"): + _jpype.startup("", tuple(), True, True, True, object()) with self.assertRaisesRegex(OSError, "started"): - _jpype.startup("", tuple(), True, True, True) + _jpype.startup("", tuple(), True, True, True, None) def testGetClass(self): with self.assertRaisesRegex(TypeError, "not found"):