Skip to content

Commit 631eade

Browse files
committed
[GR-53368] Fixes for fsspec
PullRequest: graalpython/3335
2 parents 7cb38ae + 070031f commit 631eade

File tree

19 files changed

+304
-124
lines changed

19 files changed

+304
-124
lines changed

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

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
#include "capi.h" // GraalPy change
1010
#include "Python.h"
11-
#if 0 // GraalPy change
1211
#include "pycore_call.h" // _PyObject_CallNoArgs()
12+
#if 0 // GraalPy change
1313
#include "pycore_initconfig.h" // _PyStatus_ERR()
1414
#endif // GraalPy change
1515
#include "pycore_pyerrors.h" // _PyErr_Format()
@@ -68,6 +68,7 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
6868
}
6969
return exc_info;
7070
}
71+
#endif // GraalPy change
7172

7273
static PyObject*
7374
_PyErr_CreateException(PyObject *exception_type, PyObject *value)
@@ -94,7 +95,6 @@ _PyErr_CreateException(PyObject *exception_type, PyObject *value)
9495

9596
return exc;
9697
}
97-
#endif // GraalPy change
9898

9999
void
100100
_PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
@@ -234,9 +234,107 @@ void
234234
_PyErr_NormalizeException(PyThreadState *tstate, PyObject **exc,
235235
PyObject **val, PyObject **tb)
236236
{
237-
// GraalPy change: nothing to do here from our side, the exception is already
238-
// reified
237+
int recursion_depth = 0;
238+
// GraalPy change: we don't have recursion_headroom
239+
// tstate->recursion_headroom++;
240+
PyObject *type, *value, *initial_tb;
241+
242+
restart:
243+
type = *exc;
244+
if (type == NULL) {
245+
/* There was no exception, so nothing to do. */
246+
// tstate->recursion_headroom--;
247+
return;
248+
}
249+
250+
value = *val;
251+
/* If PyErr_SetNone() was used, the value will have been actually
252+
set to NULL.
253+
*/
254+
if (!value) {
255+
value = Py_None;
256+
Py_INCREF(value);
257+
}
258+
259+
/* Normalize the exception so that if the type is a class, the
260+
value will be an instance.
261+
*/
262+
if (PyExceptionClass_Check(type)) {
263+
PyObject *inclass = NULL;
264+
int is_subclass = 0;
265+
266+
if (PyExceptionInstance_Check(value)) {
267+
inclass = PyExceptionInstance_Class(value);
268+
is_subclass = PyObject_IsSubclass(inclass, type);
269+
if (is_subclass < 0) {
270+
goto error;
271+
}
272+
}
273+
274+
/* If the value was not an instance, or is not an instance
275+
whose class is (or is derived from) type, then use the
276+
value as an argument to instantiation of the type
277+
class.
278+
*/
279+
if (!is_subclass) {
280+
PyObject *fixed_value = _PyErr_CreateException(type, value);
281+
if (fixed_value == NULL) {
282+
goto error;
283+
}
284+
Py_DECREF(value);
285+
value = fixed_value;
286+
}
287+
/* If the class of the instance doesn't exactly match the
288+
class of the type, believe the instance.
289+
*/
290+
else if (inclass != type) {
291+
Py_INCREF(inclass);
292+
Py_DECREF(type);
293+
type = inclass;
294+
}
295+
}
296+
*exc = type;
297+
*val = value;
298+
// tstate->recursion_headroom--;
239299
return;
300+
301+
error:
302+
Py_DECREF(type);
303+
Py_DECREF(value);
304+
recursion_depth++;
305+
if (recursion_depth == Py_NORMALIZE_RECURSION_LIMIT) {
306+
_PyErr_SetString(tstate, PyExc_RecursionError,
307+
"maximum recursion depth exceeded "
308+
"while normalizing an exception");
309+
}
310+
/* If the new exception doesn't set a traceback and the old
311+
exception had a traceback, use the old traceback for the
312+
new exception. It's better than nothing.
313+
*/
314+
initial_tb = *tb;
315+
_PyErr_Fetch(tstate, exc, val, tb);
316+
assert(*exc != NULL);
317+
if (initial_tb != NULL) {
318+
if (*tb == NULL)
319+
*tb = initial_tb;
320+
else
321+
Py_DECREF(initial_tb);
322+
}
323+
/* Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded, and the
324+
corresponding RecursionError could not be normalized, and the
325+
MemoryError raised when normalize this RecursionError could not be
326+
normalized. */
327+
if (recursion_depth >= Py_NORMALIZE_RECURSION_LIMIT + 2) {
328+
if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) {
329+
Py_FatalError("Cannot recover from MemoryErrors "
330+
"while normalizing exceptions.");
331+
}
332+
else {
333+
Py_FatalError("Cannot recover from the recursive normalization "
334+
"of an exception.");
335+
}
336+
}
337+
goto restart;
240338
}
241339

242340

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2640,7 +2640,7 @@ void PyTruffle_SET_TYPE(PyObject *a, PyTypeObject *b) {
26402640
}
26412641
void PyTruffle_SET_SIZE(PyVarObject *a, Py_ssize_t b) {
26422642
if (points_to_py_handle_space(a)) {
2643-
printf("changing the size of an object is not supported\n");
2643+
Graal_PyTruffle_SET_SIZE(a, b);
26442644
} else {
26452645
a->ob_size = b;
26462646
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,27 @@ class TestPyBytes(CPyExtTestCase):
517517
cmpfunc=unhandled_error_compare
518518
)
519519

520+
test_set_size = CPyExtFunction(
521+
lambda args: b'1234',
522+
lambda: ((),),
523+
code="""
524+
PyObject* set_size() {
525+
PyObject* output = PyBytes_FromStringAndSize(NULL, 50);
526+
if (output == NULL)
527+
return NULL;
528+
char* s = PyBytes_AsString(output);
529+
memcpy(s, "1234", 4);
530+
Py_SET_SIZE(output, 4);
531+
return output;
532+
}
533+
""",
534+
resultspec="O",
535+
argspec="",
536+
arguments=[],
537+
callfunction="set_size",
538+
cmpfunc=unhandled_error_compare
539+
)
540+
520541

521542
class ObjectTests(unittest.TestCase):
522543
def test_create_from_buffer(self):

graalpython/com.oracle.graal.python.test/src/tests/test_descr.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,10 @@ def __getattribute__(self, name):
5151

5252
obj = EvilGetattribute()
5353
assert getattr(obj, "bar") == "original"
54-
assert getattr(obj, "bar") == "bar"
54+
assert getattr(obj, "bar") == "bar"
55+
56+
57+
def test_overwrite___weakref__():
58+
class C:
59+
__weakref__ = 1
60+
assert C.__weakref__ == 1

graalpython/com.oracle.graal.python.test/src/tests/test_functions.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -278,4 +278,20 @@ def cached_func():
278278

279279
type_entity = getattr(sys.modules['functools'], '_lru_cache_wrapper')
280280
assert isinstance(cached_func, type_entity), "lru_cache should not be using the python-based version"
281-
281+
282+
283+
def test_no_docstring():
284+
def no_doc():
285+
pass
286+
287+
assert no_doc.__doc__ is None
288+
289+
290+
def test_docstring_via_type_contructor():
291+
def foo():
292+
"""my doc"""
293+
return 1
294+
295+
assert foo.__doc__ == 'my doc'
296+
foo2 = type(foo)(foo.__code__, foo.__globals__, foo.__name__, foo.__defaults__, foo.__closure__)
297+
assert foo2.__doc__ == 'my doc'

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
5151
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectWrapper;
5252
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyThreadState;
53+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyVarObject;
5354
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_hash_t;
5455
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
5556
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.VA_LIST_PTR;
@@ -94,6 +95,7 @@
9495
import com.oracle.graal.python.builtins.objects.cext.common.GetNextVaArgNode;
9596
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
9697
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
98+
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
9799
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
98100
import com.oracle.graal.python.builtins.objects.dict.PDict;
99101
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -132,6 +134,10 @@
132134
import com.oracle.graal.python.runtime.PythonOptions;
133135
import com.oracle.graal.python.runtime.exception.PException;
134136
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
137+
import com.oracle.graal.python.runtime.sequence.PSequence;
138+
import com.oracle.graal.python.runtime.sequence.storage.BasicSequenceStorage;
139+
import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage;
140+
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
135141
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
136142
import com.oracle.graal.python.util.PythonUtils;
137143
import com.oracle.truffle.api.CompilerDirectives;
@@ -567,6 +573,33 @@ static PNone doNativeNone() {
567573
}
568574
}
569575

576+
@CApiBuiltin(ret = Void, args = {PyVarObject, Py_ssize_t}, call = Ignored)
577+
abstract static class _PyTruffle_SET_SIZE extends CApiBinaryBuiltinNode {
578+
@Specialization
579+
static PNone set(PSequence obj, long size,
580+
@Bind("this") Node inliningTarget,
581+
@Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode,
582+
@Cached InlinedBranchProfile basicProfile,
583+
@Cached InlinedBranchProfile nativeProfile) {
584+
SequenceStorage storage = getSequenceStorageNode.execute(inliningTarget, obj);
585+
// Can't use SetLenNode as that decrefs items for native storages when shrinking
586+
if (storage instanceof BasicSequenceStorage basicStorage) {
587+
basicProfile.enter(inliningTarget);
588+
basicStorage.setNewLength((int) size);
589+
} else if (storage instanceof NativeSequenceStorage nativeStorage) {
590+
nativeProfile.enter(inliningTarget);
591+
nativeStorage.setNewLength((int) size);
592+
} else if (storage instanceof EmptySequenceStorage) {
593+
if (size > 0) {
594+
throw CompilerDirectives.shouldNotReachHere("invalid Py_SET_SIZE call");
595+
}
596+
} else {
597+
throw CompilerDirectives.shouldNotReachHere("unhandled storage type");
598+
}
599+
return PNone.NO_VALUE;
600+
}
601+
}
602+
570603
@CApiBuiltin(ret = Void, args = {PyObjectWrapper}, call = Direct)
571604
abstract static class _PyObject_Dump extends CApiUnaryBuiltinNode {
572605

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,11 @@ static boolean compatibleAssign(Node inliningTarget, SequenceStorage lhs, Sequen
190190
case Boolean:
191191
return rhsType == Boolean || rhsType == Uninitialized || rhsType == Empty;
192192
case Byte:
193-
return rhsType == Boolean || rhsType == Byte || rhsType == Uninitialized || rhsType == Empty;
193+
return rhsType == Byte || rhsType == Uninitialized || rhsType == Empty;
194194
case Int:
195-
return rhsType == Boolean || rhsType == StorageType.Byte || rhsType == StorageType.Int || rhsType == Uninitialized || rhsType == Empty;
195+
return rhsType == StorageType.Byte || rhsType == StorageType.Int || rhsType == Uninitialized || rhsType == Empty;
196196
case Long:
197-
return rhsType == Boolean || rhsType == Byte || rhsType == Int || rhsType == Long || rhsType == Uninitialized || rhsType == Empty;
197+
return rhsType == Byte || rhsType == Int || rhsType == Long || rhsType == Uninitialized || rhsType == Empty;
198198
case Double:
199199
return rhsType == Double || rhsType == Uninitialized || rhsType == Empty;
200200
case Generic:

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___CODE__;
3030
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DEFAULTS__;
31+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DOC__;
3132
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___KWDEFAULTS__;
3233
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___NAME__;
3334
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___QUALNAME__;
@@ -60,6 +61,7 @@
6061
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue;
6162
import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode;
6263
import com.oracle.graal.python.builtins.objects.dict.PDict;
64+
import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker;
6365
import com.oracle.graal.python.builtins.objects.method.PMethod;
6466
import com.oracle.graal.python.builtins.objects.str.PString;
6567
import com.oracle.graal.python.builtins.objects.str.StringNodes;
@@ -303,4 +305,25 @@ static Object setCode(PFunction self, PCode code,
303305
return PNone.NONE;
304306
}
305307
}
308+
309+
@Builtin(name = J___DOC__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true)
310+
@GenerateNodeFactory
311+
abstract static class DocNode extends PythonBinaryBuiltinNode {
312+
@Specialization(guards = "isNoValue(none)")
313+
Object get(PFunction self, @SuppressWarnings("unused") Object none) {
314+
return self.getDoc();
315+
}
316+
317+
@Specialization(guards = {"!isNoValue(value)", "!isDeleteMarker(value)"})
318+
Object set(PFunction self, Object value) {
319+
self.setDoc(value);
320+
return PNone.NONE;
321+
}
322+
323+
@Specialization
324+
Object delete(PFunction self, @SuppressWarnings("unused") DescriptorDeleteMarker marker) {
325+
self.setDoc(PNone.NONE);
326+
return PNone.NONE;
327+
}
328+
}
306329
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -27,10 +27,13 @@
2727

2828
import com.oracle.graal.python.PythonLanguage;
2929
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
30+
import com.oracle.graal.python.builtins.objects.PNone;
3031
import com.oracle.graal.python.builtins.objects.cell.PCell;
3132
import com.oracle.graal.python.builtins.objects.code.CodeNodes.GetCodeCallTargetNode;
3233
import com.oracle.graal.python.builtins.objects.code.PCode;
3334
import com.oracle.graal.python.builtins.objects.object.PythonObject;
35+
import com.oracle.graal.python.compiler.CodeUnit;
36+
import com.oracle.graal.python.lib.PyUnicodeCheckNode;
3437
import com.oracle.graal.python.nodes.PRootNode;
3538
import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode;
3639
import com.oracle.graal.python.runtime.GilNode;
@@ -69,6 +72,7 @@ public final class PFunction extends PythonObject {
6972
private Object[] defaultValues;
7073
@CompilationFinal(dimensions = 1) private PKeyword[] finalKwDefaultValues;
7174
private PKeyword[] kwDefaultValues;
75+
private Object doc;
7276

7377
public PFunction(PythonLanguage lang, TruffleString name, TruffleString qualname, PCode code, PythonObject globals, PCell[] closure) {
7478
this(lang, name, qualname, code, globals, PythonUtils.EMPTY_OBJECT_ARRAY, PKeyword.EMPTY_KEYWORDS, closure);
@@ -145,6 +149,27 @@ public boolean forceSplitDirectCalls() {
145149
return forceSplitDirectCalls;
146150
}
147151

152+
public Object getDoc() {
153+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, doc == null)) {
154+
extractDoc();
155+
}
156+
return doc;
157+
}
158+
159+
public void setDoc(Object doc) {
160+
this.doc = doc;
161+
}
162+
163+
@TruffleBoundary
164+
private void extractDoc() {
165+
CodeUnit co = getCode().getCodeUnit();
166+
if (co != null && co.constants.length > 0 && PyUnicodeCheckNode.executeUncached(co.constants[0])) {
167+
doc = co.constants[0];
168+
} else {
169+
doc = PNone.NONE;
170+
}
171+
}
172+
148173
@Override
149174
public final String toString() {
150175
CompilerAsserts.neverPartOfCompilation();

0 commit comments

Comments
 (0)