Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ca265ee

Browse files
committedNov 9, 2022
Merge branch 'master' into tidyup_virtual_override_mess
partial merge, jp_exception still to do
2 parents e8a5e86 + 4bacf4c commit ca265ee

25 files changed

+202
-112
lines changed
 

‎.azure/build.yml

+6
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ jobs:
6262
linux-3.10:
6363
imageName: "ubuntu-latest"
6464
python.version: '3.10'
65+
linux-3.11:
66+
imageName: "ubuntu-latest"
67+
python.version: '3.11'
6568
windows-3.7:
6669
imageName: "windows-2019"
6770
python.version: '3.7'
@@ -75,6 +78,9 @@ jobs:
7578
windows-3.10:
7679
imageName: "windows-2019"
7780
python.version: '3.10'
81+
windows-3.11:
82+
imageName: "windows-2019"
83+
python.version: '3.11'
7884
mac-3.9:
7985
imageName: "macos-11"
8086
python.version: '3.9'

‎.azure/release.yml

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ stages:
6868
Python310:
6969
python.version: '3.10'
7070
python.architecture: 'x64'
71+
Python311:
72+
python.version: '3.11'
73+
python.architecture: 'x64'
7174
pool:
7275
vmImage: "windows-2019"
7376
steps:
@@ -96,6 +99,8 @@ stages:
9699
python.version: '3.9'
97100
Python310:
98101
python.version: '3.10'
102+
Python311:
103+
python.version: '3.11'
99104
pool:
100105
vmImage: "macos-11"
101106
steps:

‎.azure/scripts/osx-python.sh

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ case $PYTHON_VERSION in
1919
FULL_VERSION=3.10.4
2020
INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg
2121
;;
22+
3.11)
23+
FULL_VERSION=3.11.0
24+
INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg
25+
;;
2226
esac
2327

2428
URL=https://www.python.org/ftp/python/$FULL_VERSION/$INSTALLER_NAME

‎.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.4.1_dev0
2+
current_version = 1.4.2_dev0
33
commit = True
44
tag = False
55
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\_(?P<release>[a-z]+)(?P<build>\d+))?

‎MANIFEST.in

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
recursive-include native *.h *.xml *.java *.jar *.class
22
recursive-include examples *
33
include *.rst doc/* LICENSE
4-
# include everything under test/
4+
# include test/
55
graft test
6+
prune test/classes/*
67
graft setupext
78
global-exclude *.pyc

‎doc/CHANGELOG.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,21 @@ Changelog
44
This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards.
55

66
Latest Changes:
7-
- **1.4.1_dev0 - 2022-05-14**
7+
- **1.4.2_dev0 - 2022-10-26**
8+
9+
- Fixed direct byte buffers not reporting nbytes correctly when cast to memoryview.
10+
11+
- **1.4.1 - 2022-10-26**
12+
13+
- Fixed issue with startJVM changing locale settings.
14+
15+
- Changes to support Python 3.11
16+
17+
- Fix truncation of strings on null when using convert strings.
18+
19+
- Replaced distutil with packaging
20+
21+
822
- **1.4.0 - 2022-05-14**
923

1024
- Support for all different buffer type conversions.

‎doc/userguide.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ set the class path, start the JVM, remove all the type declarations, and you are
9292
9393
# Copy in the patterns from the guide to replace the example code
9494
db = Database("our_records")
95-
with db.connect() as DatabaseConnection:
95+
with db.connect() as c:
9696
c.runQuery()
9797
while c.hasRecords():
9898
record = db.nextRecord()

‎jpype/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
__all__.extend(_jcustomizer.__all__)
5252
__all__.extend(_gui.__all__)
5353

54-
__version__ = "1.4.1_dev0"
54+
__version__ = "1.4.2_dev0"
5555
__version_info__ = __version__.split('.')
5656

5757

‎jpype/_core.py

+13
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,22 @@ def startJVM(*args, **kwargs):
218218
% (','.join([str(i) for i in kwargs])))
219219

220220
try:
221+
import locale
222+
# Gather a list of locale settings that Java may override (excluding LC_ALL)
223+
categories = [getattr(locale, i) for i in dir(locale) if i.startswith('LC_') and i != 'LC_ALL']
224+
# Keep the current locale settings, else Java will replace them.
225+
prior = [locale.getlocale(i) for i in categories]
226+
# Start the JVM
221227
_jpype.startup(jvmpath, tuple(args),
222228
ignoreUnrecognized, convertStrings, interrupt)
229+
# Collect required resources for operation
223230
initializeResources()
231+
# Restore locale
232+
for i, j in zip(categories, prior):
233+
try:
234+
locale.setlocale(i, j)
235+
except locale.Error:
236+
pass
224237
except RuntimeError as ex:
225238
source = str(ex)
226239
if "UnsupportedClassVersion" in source:

‎jpype/_jproxy.py

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ def _createJProxyDeferred(cls, *intf):
5959
@JOverride notation on methods evaluated at first
6060
instantiation.
6161
"""
62+
if not isinstance(cls, type):
63+
raise TypeError("JImplements only applies to types, not %s" % (type(cls)))
64+
6265
def new(tp, *args, **kwargs):
6366
# Attach a __jpype_interfaces__ attribute to this class if
6467
# one doesn't already exist.
@@ -77,6 +80,9 @@ def _createJProxy(cls, *intf):
7780
""" (internal) Create a proxy from a Python class with
7881
@JOverride notation on methods evaluated at declaration.
7982
"""
83+
if not isinstance(cls, type):
84+
raise TypeError("JImplements only applies to types, not %s" % (type(cls)))
85+
8086
actualIntf = _prepareInterfaces(cls, intf)
8187

8288
def new(tp, *args, **kwargs):

‎jpype/_jvmfinder.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,10 @@ def _javahome_binary(self):
320320
"""
321321
import platform
322322
import subprocess
323-
from distutils.version import StrictVersion
323+
from packaging.version import Version
324324

325-
current = StrictVersion(platform.mac_ver()[0][:4])
326-
if current >= StrictVersion('10.6') and current < StrictVersion('10.9'):
325+
current = Version(platform.mac_ver()[0][:4])
326+
if current >= Version('10.6') and current < Version('10.9'):
327327
return subprocess.check_output(
328328
['/usr/libexec/java_home']).strip()
329329

‎native/common/jp_buffer.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ JPBuffer::JPBuffer(const JPValue &value)
3737
m_Buffer.readonly = frame.isBufferReadOnly(m_Object.get());
3838
m_Buffer.shape = &m_Capacity;
3939
m_Buffer.strides = &m_Buffer.itemsize;
40-
m_Buffer.suboffsets = nullptr;
40+
m_Buffer.suboffsets = 0;
41+
m_Buffer.len = m_Buffer.itemsize * m_Capacity;
4142
JP_TRACE_OUT; // GCOVR_EXCL_LINE
4243
}
4344

‎native/common/jp_context.cpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -215,14 +215,10 @@ void JPContext::initializeResources(JNIEnv* env, bool interrupt)
215215

216216
if (!m_Embedded)
217217
{
218-
JPPyObject import = JPPyObject::call(PyImport_AddModule("importlib.util"));
218+
JPPyObject import = JPPyObject::use(PyImport_AddModule("importlib.util"));
219219
JPPyObject jpype = JPPyObject::call(PyObject_CallMethod(import.get(), "find_spec", "s", "_jpype"));
220220
JPPyObject origin = JPPyObject::call(PyObject_GetAttrString(jpype.get(), "origin"));
221221
val[2].l = frame.fromStringUTF8(JPPyString::asStringUTF8(origin.get()));
222-
import.incref(); // The documentation specifies that PyImport_AddModule must return a
223-
// new reference, but that is not happening in Python 3.10
224-
// so we are triggering a gc assertion failure. To prevent
225-
// the failure manually up the reference counter here.
226222
}
227223
m_JavaContext = JPObjectRef(frame, frame.CallStaticObjectMethodA(contextClass, startMethod, val));
228224

‎native/common/jp_exception.cpp

+63-59
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ JPypeException::JPypeException(const JPypeException &ex) noexcept
7272

7373
JPypeException& JPypeException::operator = (const JPypeException& ex)
7474
{
75-
if(this == &ex)
76-
{
77-
return *this;
78-
}
79-
m_Context = ex.m_Context;
75+
if(this == &ex)
76+
{
77+
return *this;
78+
}
79+
m_Context = ex.m_Context;
8080
m_Type = ex.m_Type;
8181
m_Trace = ex.m_Trace;
8282
m_Throwable = ex.m_Throwable;
@@ -88,9 +88,40 @@ JPypeException& JPypeException::operator = (const JPypeException& ex)
8888
void JPypeException::from(const JPStackInfo& info)
8989
{
9090
JP_TRACE("EXCEPTION FROM: ", info.getFile(), info.getLine());
91-
m_Trace.emplace_back(info);
91+
m_Trace.push_back(info);
9292
}
9393

94+
// Okay from this point on we have to suit up in full Kevlar because
95+
// this code must handle every conceivable and still reach a resolution.
96+
// Exceptions may be throws during initialization where only a fraction
97+
// of the resources are available, during the middle of normal operation,
98+
// or worst of all as the system is being yanked out from under us during
99+
// shutdown. Each and every one of these cases must be considered.
100+
// Further each and every function called here must be hardened similarly
101+
// or they will become the weak link. And remember it is not paranoia if
102+
// they are actually out to get you.
103+
//
104+
// Onward my friends to victory or a glorious segfault!
105+
/*
106+
string JPypeException::getMessage()
107+
{
108+
JP_TRACE_IN("JPypeException::getMessage");
109+
// Must be bullet proof
110+
try
111+
{
112+
stringstream str;
113+
str << m_Message << endl;
114+
JP_TRACE(str.str());
115+
return str.str();
116+
// GCOVR_EXCL_START
117+
} catch (...)
118+
{
119+
return "error during get message";
120+
}
121+
JP_TRACE_OUT;
122+
// GCOVR_EXCL_STOP
123+
}*/
124+
94125
bool isJavaThrowable(PyObject* exceptionClass)
95126
{
96127
JPClass* cls = PyJPClass_getJPClass(exceptionClass);
@@ -169,7 +200,7 @@ void JPypeException::convertJavaToPython()
169200
}
170201
// GCOVR_EXCL_STOP
171202

172-
auto *type = (PyObject*) Py_TYPE(pyvalue.get());
203+
PyObject *type = (PyObject*) Py_TYPE(pyvalue.get());
173204
Py_INCREF(type);
174205

175206
// Add cause to the exception
@@ -449,83 +480,56 @@ void JPypeException::toJava(JPContext *context)
449480
JP_TRACE_OUT; // GCOVR_EXCL_LINE
450481
}
451482

452-
PyTracebackObject *tb_create(
453-
PyTracebackObject *last_traceback,
483+
PyObject *tb_create(
484+
PyObject *last_traceback,
454485
PyObject *dict,
455486
const char* filename,
456487
const char* funcname,
457488
int linenum)
458489
{
459490
// Create a code for this frame. (ref count is 1)
460-
PyCodeObject *code = PyCode_NewEmpty(filename, funcname, linenum);
491+
JPPyObject* code = JPPyObject::accept((PyObject*)PyCode_NewEmpty(filename, funcname, linenum));
461492

462493
// If we don't get the code object there is no point
463494
if (code == nullptr)
464495
return nullptr;
465496

466-
// This is a bit of a kludge. Python lacks a way to directly create
467-
// a frame from a code object except when creating from the threadstate.
468-
//
469-
// In reviewing Python implementation, I find that the only element accessed
470-
// in the thread state was the previous frame to link to. Because frame
471-
// objects change a lot between different Python versions, trying to
472-
// replicate the actions of setting up a frame is difficult to keep portable.
473-
//
474-
// Python 3.10 introduces the additional requirement that the global
475-
// dictionary supplied must have a __builtins__. We can do this once
476-
// when create the module.
477-
//
478-
// If instead we create a thread state and point the field it needs to the
479-
// previous frame we create the frames using the defined API. Much more
480-
// portable, but we have to create a big (uninitialized object) each time we
481-
// want to pass in the previous frame.
482-
PyThreadState state;
483-
if (last_traceback != nullptr)
484-
state.frame = last_traceback->tb_frame;
485-
else
486-
state.frame = nullptr;
487-
488497
// Create a frame for the traceback.
489-
PyFrameObject *frame = PyFrame_New(&state, code, dict, nullptr);
490-
491-
// frame just borrows the reference rather than claiming it
492-
// so we need to get rid of the extra reference here.
493-
Py_DECREF(code);
494-
498+
PyThreadState *state = PyThreadState_GET();
499+
PyFrameObject *pframe = PyFrame_New(state, (PyCodeObject*) code.get(), dict, NULL);
500+
JPPyObject frame = JPPyObject::accept((PyObject*)pframe);
501+
495502
// If we don't get the frame object there is no point
496-
if (frame == nullptr)
503+
if (frame.get() == nullptr)
497504
return nullptr;
498505

499506
// Create a traceback
500-
auto *traceback = (PyTracebackObject*)
501-
PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
507+
#if PY_MINOR_VERSION<11
508+
JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(pframe->f_lasti));
509+
#else
510+
JPPyObject lasti = JPPyObject::claim(PyLong_FromLong(PyFrame_GetLasti(pframe)));
511+
#endif
512+
JPPyObject linenuma = JPPyObject::claim(PyLong_FromLong(linenum));
513+
JPPyObject tuple = JPPyObject::call(PyTuple_Pack(4, Py_None, frame.get(), lasti.get(), linenuma.get()));
514+
JPPyObject traceback = JPPyObject::accept(PyObject_Call((PyObject*) &PyTraceBack_Type, tuple.get(), NULL));
502515

503516
// We could fail in process
504-
if (traceback == nullptr)
517+
if (traceback.get() == nullptr)
505518
{
506519
Py_DECREF(frame);
507520
return nullptr;
508521
}
509522

510-
// Set the fields
511-
traceback->tb_frame = frame; // Steal the reference from frame
512-
traceback->tb_lasti = frame->f_lasti;
513-
traceback->tb_lineno = linenum;
514-
Py_XINCREF(last_traceback);
515-
traceback->tb_next = last_traceback;
516-
517-
// Allow GC on the object
518-
PyObject_GC_Track(traceback);
519-
return traceback;
523+
return traceback.keep();
520524
}
521525

522526
PyObject* PyTrace_FromJPStackTrace(JPStackTrace& trace)
523527
{
524-
PyTracebackObject *last_traceback = nullptr;
528+
PyObject *last_traceback = nullptr;
525529
PyObject *dict = PyModule_GetDict(PyJPModule);
526-
for (auto & iter : trace)
530+
for (auto& iter : trace)
527531
{
528-
last_traceback = tb_create(last_traceback, dict, iter.getFile(),
532+
last_traceback = tb_create(last_traceback, dict, iter.getFile(),
529533
iter.getFunction(), iter.getLine());
530534
}
531535
if (last_traceback == nullptr)
@@ -544,8 +548,8 @@ JPPyObject PyTrace_FromJavaException(JPJavaFrame& frame, jthrowable th, jthrowab
544548
return {};
545549

546550
JNIEnv* env = frame.getEnv();
547-
auto obj = (jobjectArray) env->CallObjectMethodA(context->getJavaContext(),
548-
context->m_Context_GetStackFrameID, args);
551+
jobjectArray obj = static_cast<jobjectArray>(env->CallObjectMethodA(context->getJavaContext(),
552+
context->m_Context_GetStackFrameID, args));
549553

550554
// Eat any exceptions that were generated
551555
if (env->ExceptionCheck() == JNI_TRUE)
@@ -559,8 +563,8 @@ JPPyObject PyTrace_FromJavaException(JPJavaFrame& frame, jthrowable th, jthrowab
559563
{
560564
string filename, method;
561565
auto jclassname = static_cast<jstring>(frame.GetObjectArrayElement(obj, i));
562-
auto jmethodname = (jstring) frame.GetObjectArrayElement(obj, i + 1);
563-
auto jfilename = (jstring) frame.GetObjectArrayElement(obj, i + 2);
566+
auto jmethodname = static_cast<jstring>(frame.GetObjectArrayElement(obj, i + 1));
567+
auto jfilename = static_cast<jstring>(frame.GetObjectArrayElement(obj, i + 2));
564568
if (jfilename != nullptr)
565569
filename = frame.toStringUTF8(jfilename);
566570
else

‎native/common/jp_stringtype.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ JPPyObject JPStringType::convertToPythonObject(JPJavaFrame& frame, jvalue val, b
4646
if (context->getConvertStrings())
4747
{
4848
string str = frame.toStringUTF8((jstring) (val.l));
49-
return JPPyObject::call(PyUnicode_FromString(str.c_str()));
49+
return JPPyObject::call(PyUnicode_FromStringAndSize(str.c_str(), str.length()));
5050
}
5151
}
5252

0 commit comments

Comments
 (0)
Please sign in to comment.