Skip to content

Commit 4a294c5

Browse files
committed
[GR-47946] Fix for pandas regression
PullRequest: graalpython/2914
2 parents 3300df7 + 9c44929 commit 4a294c5

File tree

5 files changed

+92
-8
lines changed

5 files changed

+92
-8
lines changed

graalpython/com.oracle.graal.python.cext/src/capi.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,22 @@ PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void* (*getBuiltin)(int
849849
PyTruffle_Log(PY_TRUFFLE_LOG_FINE, "initialize_graal_capi: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC);
850850
}
851851

852+
/*
853+
This is a workaround for C++ modules, namely PyTorch, that declare global/static variables with destructors that call
854+
_Py_DECREF. The destructors get called by libc during exit during which we cannot make upcalls as that would segfault.
855+
So we rebind them to no-ops when exiting.
856+
*/
857+
Py_ssize_t nop_GraalPy_get_PyObject_ob_refcnt(PyObject* obj) {
858+
return 100; // large dummy refcount
859+
}
860+
void nop_GraalPy_set_PyObject_ob_refcnt(PyObject* obj, Py_ssize_t refcnt) {
861+
// do nothing
862+
}
863+
PyAPI_FUNC(void) finalizeCAPI() {
864+
GraalPy_get_PyObject_ob_refcnt = nop_GraalPy_get_PyObject_ob_refcnt;
865+
GraalPy_set_PyObject_ob_refcnt = nop_GraalPy_set_PyObject_ob_refcnt;
866+
}
867+
852868
static void unimplemented(const char* name) {
853869
printf("Function not implemented in GraalPy: %s\n", name);
854870
}

graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
GRAALPYTHON = sys.implementation.name == "graalpy"
5050

51+
IS_MANAGED_LAUNCHER = not GRAALPYTHON or __graalpython__.is_managed_launcher()
5152

5253
def assert_raises(err, fn, *args, **kwargs):
5354
raised = False
@@ -494,7 +495,7 @@ def get_value(self, key, args, kwds):
494495
return Formatter.get_value(key, args, kwds)
495496

496497

497-
def CPyExtType(name, code, **kwargs):
498+
def CPyExtType(name, code='', **kwargs):
498499
template = """
499500
#include "Python.h"
500501
/* structmember.h is not included by default in Python.h */

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_datetime.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,50 @@
3838
# SOFTWARE.
3939
import datetime
4040

41-
from . import CPyExtType, CPyExtTestCase, CPyExtFunction, unhandled_error_compare
41+
from . import CPyExtType, CPyExtTestCase, CPyExtFunction, unhandled_error_compare, is_native_object, IS_MANAGED_LAUNCHER
4242

4343
__dir__ = __file__.rpartition("/")[0]
4444

4545

46+
def create_datetime_subclass(typename):
47+
return CPyExtType(
48+
f"Native{typename}Subclass",
49+
struct_base=f'PyDateTime_{typename} base;',
50+
tp_new='0',
51+
tp_alloc='0',
52+
tp_free='0',
53+
includes='#include "datetime.h"',
54+
ready_code=f'''
55+
PyDateTime_IMPORT;
56+
PyTypeObject* t = PyDateTimeAPI->{typename}Type;
57+
Py_XINCREF(t);
58+
Native{typename}SubclassType.tp_base = t;
59+
''',
60+
)
61+
62+
63+
NativeDateSubclass = create_datetime_subclass("Date")
64+
NativeTimeSubclass = create_datetime_subclass("Time")
65+
NativeDateTimeSubclass = create_datetime_subclass("DateTime")
66+
NativeDeltaSubclass = create_datetime_subclass("Delta")
67+
68+
69+
class ManagedNativeDateSubclass(NativeDateSubclass):
70+
pass
71+
72+
73+
class ManagedNativeTimeSubclass(NativeTimeSubclass):
74+
pass
75+
76+
77+
class ManagedNativeDateTimeSubclass(NativeDateTimeSubclass):
78+
pass
79+
80+
81+
class ManagedNativeDeltaSubclass(NativeDeltaSubclass):
82+
pass
83+
84+
4685
class TestPyDateTime(CPyExtTestCase):
4786

4887
def compile_module(self, name):
@@ -577,3 +616,32 @@ def test_write_and_invoke_member(self):
577616
)
578617
tester = TestWriteAndInvokeMemeber()
579618
assert tester.getDate() == "foo"
619+
620+
621+
# GR-47546: Native subclasses of managed types don't work in managed mode
622+
if not IS_MANAGED_LAUNCHER:
623+
class TestNativeSubclasses:
624+
def test_time(self):
625+
for t in (NativeTimeSubclass, ManagedNativeTimeSubclass):
626+
x = t(hour=6)
627+
assert is_native_object(x)
628+
assert x.hour == 6
629+
630+
def test_date(self):
631+
for t in (NativeDateSubclass, ManagedNativeDateSubclass):
632+
x = t(1992, 4, 11)
633+
assert is_native_object(x)
634+
assert x.day == 11
635+
636+
def test_datetime(self):
637+
for t in (NativeDateTimeSubclass, ManagedNativeDateTimeSubclass):
638+
x = t(1992, 4, 11, hour=13)
639+
assert is_native_object(x)
640+
assert x.day == 11
641+
assert x.hour == 13
642+
643+
def test_timedelta(self):
644+
for t in (NativeDeltaSubclass, ManagedNativeDeltaSubclass):
645+
x = t(hours=6)
646+
assert is_native_object(x)
647+
assert x.seconds == 21600

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,8 +1122,8 @@ static Object addInheritedSlots(PythonAbstractNativeObject pythonClass,
11221122
int flags = readI32.readStructArrayElement(member, i, PyMemberDef__flags);
11231123
Object docPtr = readPointer.readStructArrayElement(member, i, PyMemberDef__doc);
11241124
Object doc = PGuards.isNullOrZero(docPtr, lib) ? PNone.NO_VALUE : fromCharPointer.execute(docPtr);
1125-
1126-
addMember.execute(pythonClass, dict, name, type, offset, flags & CConstants.READONLY.intValue(), doc);
1125+
boolean canSet = (flags & CConstants.READONLY.intValue()) == 0;
1126+
addMember.execute(pythonClass, dict, name, type, offset, canSet ? 1 : 0, doc);
11271127
}
11281128
}
11291129
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ private record ClosureInfo(Object closure, Object delegate, Object executable, l
187187
*/
188188
private final HashMap<Object, ClosureInfo> callableClosureByExecutable = new HashMap<>();
189189
private final HashMap<Long, ClosureInfo> callableClosures = new HashMap<>();
190-
private Object nativeLibrary;
191190

192191
/**
193192
* This list holds a strong reference to all loaded extension libraries to keep the library
@@ -591,11 +590,11 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
591590

592591
@TruffleBoundary
593592
public void finalizeCapi() {
594-
if (nativeLibrary != null) {
593+
if (useNativeBackend && getLLVMLibrary() != null) {
595594
try {
596-
Object initFunction = InteropLibrary.getUncached().readMember(nativeLibrary, "finalizeCAPI");
595+
Object finalizeFunction = InteropLibrary.getUncached().readMember(getLLVMLibrary(), "finalizeCAPI");
597596
Object signature = PythonContext.get(null).getEnv().parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "():VOID", "exec").build()).call();
598-
SignatureLibrary.getUncached().call(signature, initFunction);
597+
SignatureLibrary.getUncached().call(signature, finalizeFunction);
599598
} catch (InteropException e) {
600599
throw CompilerDirectives.shouldNotReachHere(e);
601600
}

0 commit comments

Comments
 (0)