Skip to content

Commit

Permalink
Merge pull request #1158 from jpype-project/py12
Browse files Browse the repository at this point in the history
Python 3.12 patch
  • Loading branch information
marscher authored Dec 5, 2023
2 parents c11c3a2 + b5c6083 commit 33597f8
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 14 deletions.
4 changes: 2 additions & 2 deletions native/common/jp_gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ size_t getWorkingSize()
return sz * page_size;

#elif defined(USE_MALLINFO)
struct mallinfo mi;
mi = mallinfo();
struct mallinfo2 mi;
mi = mallinfo2();
current = (size_t) mi.uordblks;
#endif

Expand Down
19 changes: 19 additions & 0 deletions native/common/jp_primitivetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ bool JPPrimitiveType::isPrimitive() const
return true;
}

extern "C" Py_ssize_t PyJPValue_getJavaSlotOffset(PyObject* self);

// equivalent of long_subtype_new as it isn't exposed

PyObject *JPPrimitiveType::convertLong(PyTypeObject* wrapper, PyLongObject* tmp)
{
if (wrapper == NULL)
JP_RAISE(PyExc_SystemError, "bad wrapper");

#if PY_VERSION_HEX<0x030c0000
// Determine number of digits to copy
Py_ssize_t n = Py_SIZE(tmp);
if (n < 0)
n = -n;
Expand All @@ -44,11 +48,26 @@ PyObject *JPPrimitiveType::convertLong(PyTypeObject* wrapper, PyLongObject* tmp)
if (newobj == NULL)
return NULL;

// Size is in units of digits
((PyVarObject*) newobj)->ob_size = Py_SIZE(tmp);
for (Py_ssize_t i = 0; i < n; i++)
{
newobj->ob_digit[i] = tmp->ob_digit[i];
}

#else
// 3.12 completely does away with ob_size field and repurposes it

// Determine the number of digits to copy
int n = (tmp->long_value.lv_tag >> 3);

PyLongObject *newobj = (PyLongObject *) wrapper->tp_alloc(wrapper, n);
if (newobj == NULL)
return NULL;

newobj->long_value.lv_tag = tmp->long_value.lv_tag;
memcpy(&newobj->long_value.ob_digit, &tmp->long_value.ob_digit, n*sizeof(digit));
#endif
return (PyObject*) newobj;
}

16 changes: 16 additions & 0 deletions native/python/pyjp_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,19 @@ static PyType_Slot arraySlots[] = {
{ Py_sq_length, (void*) &PyJPArray_len},
{ Py_tp_getset, (void*) &arrayGetSets},
{ Py_mp_ass_subscript, (void*) &PyJPArray_assignSubscript},
#if PY_VERSION_HEX >= 0x03090000
{ Py_bf_getbuffer, (void*) &PyJPArray_getBuffer},
{ Py_bf_releasebuffer, (void*) &PyJPArray_releaseBuffer},
#endif
{0}
};

#if PY_VERSION_HEX < 0x03090000
static PyBufferProcs arrayBuffer = {
(getbufferproc) & PyJPArray_getBuffer,
(releasebufferproc) & PyJPArray_releaseBuffer
};
#endif

PyTypeObject *PyJPArray_Type = NULL;
static PyType_Spec arraySpec = {
Expand All @@ -445,12 +451,18 @@ static PyType_Spec arraySpec = {
arraySlots
};

#if PY_VERSION_HEX < 0x03090000
static PyBufferProcs arrayPrimBuffer = {
(getbufferproc) & PyJPArrayPrimitive_getBuffer,
(releasebufferproc) & PyJPArray_releaseBuffer
};
#endif

static PyType_Slot arrayPrimSlots[] = {
#if PY_VERSION_HEX >= 0x03090000
{ Py_bf_getbuffer, (void*) &PyJPArrayPrimitive_getBuffer},
{ Py_bf_releasebuffer, (void*) &PyJPArray_releaseBuffer},
#endif
{0}
};

Expand All @@ -472,14 +484,18 @@ void PyJPArray_initType(PyObject * module)
JPPyObject tuple = JPPyObject::call(PyTuple_Pack(1, PyJPObject_Type));
PyJPArray_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&arraySpec, tuple.get());
JP_PY_CHECK();
#if PY_VERSION_HEX < 0x03090000
PyJPArray_Type->tp_as_buffer = &arrayBuffer;
#endif
PyModule_AddObject(module, "_JArray", (PyObject*) PyJPArray_Type);
JP_PY_CHECK();

tuple = JPPyObject::call(PyTuple_Pack(1, PyJPArray_Type));
PyJPArrayPrimitive_Type = (PyTypeObject*)
PyJPClass_FromSpecWithBases(&arrayPrimSpec, tuple.get());
#if PY_VERSION_HEX < 0x03090000
PyJPArrayPrimitive_Type->tp_as_buffer = &arrayPrimBuffer;
#endif
JP_PY_CHECK();
PyModule_AddObject(module, "_JArrayPrimitive",
(PyObject*) PyJPArrayPrimitive_Type);
Expand Down
8 changes: 8 additions & 0 deletions native/python/pyjp_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,19 @@ int PyJPBuffer_getBuffer(PyJPBuffer *self, Py_buffer *view, int flags)
static PyType_Slot bufferSlots[] = {
{ Py_tp_dealloc, (void*) PyJPBuffer_dealloc},
{ Py_tp_repr, (void*) PyJPBuffer_repr},
#if PY_VERSION_HEX >= 0x03090000
{ Py_bf_getbuffer, (void*) PyJPBuffer_getBuffer},
{ Py_bf_releasebuffer, (void*) PyJPBuffer_releaseBuffer},
#endif
{0}
};

#if PY_VERSION_HEX < 0x03090000
static PyBufferProcs directBuffer = {
(getbufferproc) & PyJPBuffer_getBuffer,
(releasebufferproc) & PyJPBuffer_releaseBuffer
};
#endif

PyTypeObject *PyJPBuffer_Type = NULL;
static PyType_Spec bufferSpec = {
Expand All @@ -138,7 +144,9 @@ void PyJPBuffer_initType(PyObject * module)
{
JPPyObject tuple = JPPyObject::call(PyTuple_Pack(1, PyJPObject_Type));
PyJPBuffer_Type = (PyTypeObject*) PyJPClass_FromSpecWithBases(&bufferSpec, tuple.get());
#if PY_VERSION_HEX < 0x03090000
PyJPBuffer_Type->tp_as_buffer = &directBuffer;
#endif
JP_PY_CHECK();
PyModule_AddObject(module, "_JBuffer", (PyObject*) PyJPBuffer_Type);
JP_PY_CHECK();
Expand Down
33 changes: 26 additions & 7 deletions native/python/pyjp_char.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,46 +79,63 @@ static int assertNotNull(JPValue *javaSlot)

PyObject *PyJPChar_Create(PyTypeObject *type, Py_UCS2 p)
{
// Allocate a new string type (derived from UNICODE)
PyJPChar *self = (PyJPChar*) PyJPValue_alloc(type, 0);
if (self == 0)
return 0;

// Set up a wide char with value of zero
self->m_Data[0] = 0;
self->m_Data[1] = 0;
self->m_Data[2] = 0;
self->m_Data[3] = 0;

// Values taken from internal/cpython/unicode.h

// Mark the type in unicode
_PyUnicode_LENGTH(self) = 1;
_PyUnicode_HASH(self) = -1;
_PyUnicode_STATE(self).kind = PyUnicode_1BYTE_KIND;

_PyUnicode_STATE(self).ascii = 0;
_PyUnicode_STATE(self).ready = 1;
_PyUnicode_STATE(self).interned = 0;
_PyUnicode_STATE(self).compact = 1;
_PyUnicode_STATE(self).interned = 0;

#if PY_VERSION_HEX < 0x030c0000
_PyUnicode_STATE(self).ready = 1;
#endif

// Copy the value based on the length
if (p < 128)
{
_PyUnicode_STATE(self).ascii = 1;
_PyUnicode_STATE(self).kind = PyUnicode_1BYTE_KIND;

char *data = (char*) (((PyASCIIObject*) self) + 1);
data[0] = p;
data[1] = 0;
} else
if (p < 256)
} else if (p < 256)
{
_PyUnicode_STATE(self).ascii = 0;
_PyUnicode_STATE(self).kind = PyUnicode_1BYTE_KIND;

char *data = (char*) ( ((PyCompactUnicodeObject*) self) + 1);
data[0] = p;
data[1] = 0;

#if PY_VERSION_HEX < 0x030c0000
_PyUnicode_WSTR_LENGTH(self) = 0;
_PyUnicode_WSTR(self) = NULL;
#endif
self->m_Obj.utf8 = NULL;
self->m_Obj.utf8_length = 0;
} else
{
_PyUnicode_STATE(self).ascii = 0;
_PyUnicode_STATE(self).kind = PyUnicode_2BYTE_KIND;

Py_UCS2 *data = (Py_UCS2*) ( ((PyCompactUnicodeObject*) self) + 1);
data[0] = p;
data[1] = 0;
_PyUnicode_STATE(self).kind = PyUnicode_2BYTE_KIND;
#if PY_VERSION_HEX < 0x030c0000
if (sizeof (wchar_t) == 2)
{
_PyUnicode_WSTR_LENGTH(self) = 1;
Expand All @@ -128,9 +145,11 @@ PyObject *PyJPChar_Create(PyTypeObject *type, Py_UCS2 p)
_PyUnicode_WSTR_LENGTH(self) = 0;
_PyUnicode_WSTR(self) = NULL;
}
#endif
self->m_Obj.utf8 = NULL;
self->m_Obj.utf8_length = 0;
}

return (PyObject*) self;
}

Expand Down
8 changes: 8 additions & 0 deletions native/python/pyjp_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@ PyObject* PyJPClass_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
case Py_tp_getset:
type->tp_getset = (PyGetSetDef*) slot->pfunc;
break;
#if PY_VERSION_HEX >= 0x03090000
case Py_bf_getbuffer:
type->tp_as_buffer->bf_getbuffer = (getbufferproc) slot->pfunc;
break;
case Py_bf_releasebuffer:
type->tp_as_buffer->bf_releasebuffer = (releasebufferproc) slot->pfunc;
break;
#endif
// GCOVR_EXCL_START
default:
PyErr_Format(PyExc_TypeError, "slot %d not implemented", slot->slot);
Expand Down
10 changes: 10 additions & 0 deletions native/python/pyjp_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,21 @@ PyObject* PyJP_GetAttrDescriptor(PyTypeObject *type, PyObject *attr_name)
if (type->tp_mro == NULL)
return NULL; // GCOVR_EXCL_LINE

// Grab the mro
PyObject *mro = type->tp_mro;

// mro should be a tuple
Py_ssize_t n = PyTuple_Size(mro);

// Search the tuple for the attribute
for (Py_ssize_t i = 0; i < n; ++i)
{
PyTypeObject *type2 = (PyTypeObject*) PyTuple_GetItem(mro, i);

// Skip objects without a functioning dictionary
if (type2->tp_dict == NULL)
continue;

PyObject *res = PyDict_GetItem(type2->tp_dict, attr_name);
if (res)
{
Expand Down
16 changes: 14 additions & 2 deletions native/python/pyjp_value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ PyObject* PyJPValue_alloc(PyTypeObject* type, Py_ssize_t nitems )
return PyErr_NoMemory(); // GCOVR_EXCL_LINE
memset(obj, 0, size);


Py_ssize_t refcnt = ((PyObject*) type)->ob_refcnt;
obj->ob_type = type;

if (type->tp_itemsize == 0)
PyObject_Init(obj, type);
else
Expand Down Expand Up @@ -107,8 +110,17 @@ Py_ssize_t PyJPValue_getJavaSlotOffset(PyObject* self)
|| type->tp_finalize != (destructor) PyJPValue_finalize)
return 0;
Py_ssize_t offset;
Py_ssize_t sz = Py_SIZE(self);
// I have no clue what negative sizes mean
Py_ssize_t sz = 0;

#if PY_VERSION_HEX>=0x030c0000
// starting in 3.12 there is no longer ob_size in PyLong
if (PyType_HasFeature(self->ob_type, Py_TPFLAGS_LONG_SUBCLASS))
sz = (((PyLongObject*)self)->long_value.lv_tag) >> 3; // Private NON_SIZE_BITS
else
#endif
if (type->tp_itemsize != 0)
sz = Py_SIZE(self);
// PyLong abuses ob_size with negative values prior to 3.12
if (sz < 0)
sz = -sz;
if (type->tp_itemsize == 0)
Expand Down
2 changes: 1 addition & 1 deletion test/jpypetest/test_bytebuffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ def testRepr(self):
self.assertEqual(repr(bb), "<java buffer 'java.nio.DirectByteBuffer'>")

def testMemoryView(self):
self.assertEquals(memoryview(jpype.java.nio.ByteBuffer.allocateDirect(100)).nbytes, 100)
self.assertEqual(memoryview(jpype.java.nio.ByteBuffer.allocateDirect(100)).nbytes, 100)
6 changes: 5 additions & 1 deletion test/jpypetest/test_classhints.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# *****************************************************************************
import jpype
import common
import sys


class MyImpl(object):
Expand Down Expand Up @@ -59,7 +60,10 @@ def testCharSequence(self):

def testInstant(self):
import datetime
now = datetime.datetime.utcnow()
if sys.version_info.major == 3 and sys.version_info.minor < 12:
now = datetime.datetime.utcnow()
else:
now = datetime.datetime.now(datetime.UTC)
Instant = jpype.JClass("java.time.Instant")
self.assertIsInstance(jpype.JObject(now, Instant), Instant)

Expand Down
4 changes: 3 additions & 1 deletion test/jpypetest/test_javadoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def testClass(self):
JC = jpype.JClass("jpype.doc.Test")
jd = JC.__doc__
self.assertIsInstance(jd, str)
self.assertRegex(jd, "random stuff")
# Disabled this test for now. Java needs a better API for accessing Java doc.
# It is hard to deal with random changes every version.
#self.assertRegex(jd, "random stuff")

def testMethod(self):
JC = jpype.JClass("jpype.doc.Test")
Expand Down

0 comments on commit 33597f8

Please sign in to comment.