Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for 3.11 #1087

Merged
merged 9 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions jpype/_jvmfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is part of your distutils handling. I suggest that this can be a separate PR, or alternatively, you should add packaging as an explicit dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. This was required to get Python 3.11 to work for me so I think it is same theme. I will look at dependencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I dont see where it goes in setup.py. we never listed distutils as a requirement. Can you suggest the required line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it was supposed to have distutils there before.

https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/

I don't see the same format we have so I am guessing we want...

install_requires=['typing_extensions ; python_version< "3.8"',
        'packaging ; python_version< "3.10"'],

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

distutils is a standard library, which has been deprecated for a while now and will be removed in Python 3.12.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'packaging ; python_version< "3.10"'

Should just be

'packaging'

return subprocess.check_output(
['/usr/libexec/java_home']).strip()

Expand Down
6 changes: 1 addition & 5 deletions native/common/jp_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
70 changes: 21 additions & 49 deletions native/common/jp_exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,79 +489,51 @@ 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
// 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);

// frame just borrows the reference rather than claiming it
// so we need to get rid of the extra reference here.
Py_DECREF(code);

PyThreadState *state = PyThreadState_GET();
PyFrameObject *pframe = PyFrame_New(state, (PyCodeObject*) code.get(), dict, NULL);
JPPyObject frame = JPPyObject::accept((PyObject*)pframe);

// 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);
#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));

// 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)
{
Expand All @@ -575,7 +547,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;
Expand Down
26 changes: 23 additions & 3 deletions native/python/pyjp_value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,28 @@ 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);
Thrameos marked this conversation as resolved.
Show resolved Hide resolved
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 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;
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);

// 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);
Expand Down Expand Up @@ -305,4 +325,4 @@ bool PyJPValue_isSetJavaSlot(PyObject* self)
return false; // GCOVR_EXCL_LINE
JPValue* slot = (JPValue*) (((char*) self) + offset);
return slot->getClass() != NULL;
}
}
2 changes: 1 addition & 1 deletion setupext/test_java.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Thrameos marked this conversation as resolved.
Show resolved Hide resolved
srcs.extend(glob.glob('test/harness/java8/**/*.java', recursive=True))
if version > 8:
srcs.extend(glob.glob('test/harness/java9/**/*.java', recursive=True))
Expand Down