Skip to content

Commit 881f81d

Browse files
committed
[GR-63044] Implement tp_repr and tp_str slots
PullRequest: graalpython/3723
2 parents fab3eb0 + fb13c36 commit 881f81d

File tree

98 files changed

+1329
-967
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+1329
-967
lines changed

graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/Slot.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,11 @@ enum SlotKind {
196196
/** iter(obj) */
197197
tp_iter("__iter__"),
198198
/** next(obj) */
199-
tp_iternext("__next__");
199+
tp_iternext("__next__"),
200+
/** str(obj) */
201+
tp_str("__str__"),
202+
/** repr(obj) */
203+
tp_repr("__repr__");
200204

201205
SlotKind(@SuppressWarnings("unused") String specialMethods) {
202206
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2024, Oracle and/or its affiliates.
1+
/* Copyright (c) 2024, 2025, Oracle and/or its affiliates.
22
* Copyright (C) 1996-2022 Python Software Foundation
33
*
44
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -385,6 +385,7 @@ static PyMethodDef structseq_methods[] = {
385385
{"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
386386
{NULL, NULL}
387387
};
388+
#endif // GraalPy change
388389

389390
static Py_ssize_t
390391
count_members(PyStructSequence_Desc *desc, Py_ssize_t *n_unnamed_members) {
@@ -399,6 +400,7 @@ count_members(PyStructSequence_Desc *desc, Py_ssize_t *n_unnamed_members) {
399400
return i;
400401
}
401402

403+
#if 0 // GraalPy change
402404
static int
403405
initialize_structseq_dict(PyStructSequence_Desc *desc, PyObject* dict,
404406
Py_ssize_t n_members, Py_ssize_t n_unnamed_members) {
@@ -455,6 +457,7 @@ initialize_structseq_dict(PyStructSequence_Desc *desc, PyObject* dict,
455457
Py_DECREF(keys);
456458
return -1;
457459
}
460+
#endif // GraalPy change
458461

459462
static void
460463
initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
@@ -478,7 +481,6 @@ initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
478481
}
479482
members[k].name = NULL;
480483
}
481-
#endif // GraalPy change
482484

483485

484486
int
@@ -518,7 +520,6 @@ _PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
518520
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
519521
type->tp_traverse = (traverseproc) structseq_traverse;
520522

521-
#if 0 // GraalPy change: initialize members in an upcall later
522523
n_members = count_members(desc, &n_unnamed_members);
523524
members = PyMem_NEW(PyMemberDef, n_members - n_unnamed_members + 1);
524525
if (members == NULL) {
@@ -527,9 +528,6 @@ _PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
527528
}
528529
initialize_members(desc, members, n_members);
529530
type->tp_members = members;
530-
#else // GraalPy change
531-
type->tp_members = NULL;
532-
#endif // GraalPy change
533531

534532
if (PyType_Ready(type) < 0) {
535533
// GraalPy change: not initialized

graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/SlotsMapping.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static String getSlotBaseClass(Slot s) {
5353
return switch (s.value()) {
5454
case nb_bool -> "TpSlotInquiry.TpSlotInquiryBuiltin";
5555
case nb_index, nb_int, nb_float, nb_absolute, nb_positive, nb_negative, nb_invert,
56-
tp_iter ->
56+
tp_iter, tp_str, tp_repr ->
5757
"TpSlotUnaryFunc.TpSlotUnaryFuncBuiltin";
5858
case nb_add, nb_subtract, nb_multiply, nb_remainder, nb_divmod, nb_lshift, nb_rshift, nb_and, nb_xor, nb_or,
5959
nb_floor_divide, nb_true_divide, nb_matrix_multiply ->
@@ -85,7 +85,7 @@ static String getSlotNodeBaseClass(Slot s) {
8585
case tp_descr_get -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode";
8686
case nb_bool -> "com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode";
8787
case nb_index, nb_int, nb_float, nb_absolute, nb_positive, nb_negative, nb_invert,
88-
tp_iter, tp_iternext ->
88+
tp_iter, tp_iternext, tp_str, tp_repr ->
8989
"com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode";
9090
case nb_add, nb_subtract, nb_multiply, nb_remainder, nb_divmod, nb_lshift, nb_rshift, nb_and, nb_xor, nb_or,
9191
nb_floor_divide, nb_true_divide, nb_matrix_multiply ->
@@ -181,6 +181,8 @@ public static String getExtraCtorArgs(TpSlotData slot) {
181181
case sq_inplace_concat -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___IADD__";
182182
case sq_inplace_repeat -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___IMUL__";
183183
case tp_iter -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___ITER__";
184+
case tp_str -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___STR__";
185+
case tp_repr -> ", com.oracle.graal.python.nodes.SpecialMethodNames.J___REPR__";
184186
default -> "";
185187
};
186188
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2019, 2025, 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
@@ -48,6 +48,7 @@ def test_properties(self):
4848
static PyStructSequence_Field typeinfo_fields[] = {
4949
{"element0", "The first element."},
5050
{"element1", "The second element."},
51+
{"element2", "The third element."},
5152
{NULL, NULL,}
5253
};
5354
@@ -69,11 +70,18 @@ def test_properties(self):
6970
)
7071
assert TestPyStructSequence.__doc__ == "Information about some custom struct type"
7172

72-
tester = TestPyStructSequence(("hello", "world"))
73+
tester = TestPyStructSequence(("hello", "world"), {"element2": 1})
7374
assert hasattr(tester, "element0")
7475
assert hasattr(tester, "element1")
7576
assert tester.element0 == "hello"
7677
assert tester.element1 == "world"
78+
assert tester.element2 == 1
79+
assert len(tester) == 2
80+
assert tuple(tester) == ("hello", "world")
81+
assert repr(tester) == "TestPyStructSequenceTypes.TestPyStructSequence(element0='hello', element1='world')"
82+
assert tester.__repr__() == "TestPyStructSequenceTypes.TestPyStructSequence(element0='hello', element1='world')"
83+
assert tester.__reduce__() == (TestPyStructSequence, (('hello', 'world'), {'element2': 1}))
84+
assert tester.__reduce__.__qualname__ == "TestPyStructSequence.__reduce__"
7785

7886
def test_structseq_newtype(self):
7987
TestPyStructSequenceNewType = CPyExtType("TestPyStructSequenceNewType", """

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2021, 2025, 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
@@ -79,7 +79,8 @@ def test_constructor_err(self):
7979
with self.assertRaisesRegex(TypeError, r'os.stat_result\(\) takes an at most \d+-sequence \(30-sequence given\)'):
8080
posix.stat_result((1,) * 30)
8181
with self.assertRaisesRegex(TypeError, r'cannot create \'sys.flags\' instances'):
82-
type(sys.flags)()
82+
# GraalPy change: add argument to avoid hitting the argument error first
83+
type(sys.flags)(())
8384

8485
def test_no_subclass(self):
8586
with self.assertRaises(TypeError):

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import java.io.IOException;
3838
import java.io.InputStream;
3939
import java.lang.invoke.VarHandle;
40-
import java.lang.ref.WeakReference;
4140
import java.nio.charset.StandardCharsets;
4241
import java.nio.file.InvalidPathException;
4342
import java.util.ArrayList;
@@ -71,9 +70,6 @@
7170
import com.oracle.graal.python.builtins.objects.module.PythonModule;
7271
import com.oracle.graal.python.builtins.objects.object.PythonObject;
7372
import com.oracle.graal.python.builtins.objects.tuple.StructSequence;
74-
import com.oracle.graal.python.builtins.objects.tuple.StructSequence.BuiltinTypeDescriptor;
75-
import com.oracle.graal.python.builtins.objects.tuple.StructSequence.Descriptor;
76-
import com.oracle.graal.python.builtins.objects.tuple.StructSequence.DescriptorCallTargets;
7773
import com.oracle.graal.python.builtins.objects.type.MroShape;
7874
import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass;
7975
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
@@ -363,46 +359,6 @@ public boolean isSingleContext() {
363359

364360
@CompilationFinal(dimensions = 1) private final RootCallTarget[] builtinSlotsCallTargets;
365361

366-
/**
367-
* Weak hash map of call targets for builtin functions associated with named tuples generated at
368-
* runtime from C extensions. We hold the cached call targets also weakly, because otherwise we
369-
* would have a cycle from the value (call targets reference builtin nodes which wrap the
370-
* descriptor) to the key. The key should be GC'ed when the corresponding generated named tuple
371-
* class is GC'ed.
372-
*/
373-
private final WeakHashMap<StructSequence.Descriptor, WeakReference<StructSequence.DescriptorCallTargets>> structSequenceTargets = new WeakHashMap<>();
374-
375-
/**
376-
* The same as {@link #structSequenceTargets}, but for builtin named tuples. There is a bounded
377-
* statically known number of builtin named tuples.
378-
*/
379-
private final ConcurrentHashMap<StructSequence.Descriptor, StructSequence.DescriptorCallTargets> structSequenceBuiltinTargets = new ConcurrentHashMap<>();
380-
381-
public StructSequence.DescriptorCallTargets getOrCreateStructSequenceCallTargets(StructSequence.Descriptor descriptor,
382-
Function<StructSequence.Descriptor, StructSequence.DescriptorCallTargets> factory) {
383-
if (singleContext) {
384-
return factory.apply(descriptor);
385-
}
386-
if (descriptor instanceof BuiltinTypeDescriptor builtinDescriptor) {
387-
// There must be finite set of objects initialized in static final fields, no need for a
388-
// weak map
389-
return structSequenceBuiltinTargets.computeIfAbsent(builtinDescriptor, factory);
390-
}
391-
return getOrCreateStructSeqNonBuiltinTargets(descriptor, factory);
392-
}
393-
394-
private DescriptorCallTargets getOrCreateStructSeqNonBuiltinTargets(Descriptor descriptor, Function<Descriptor, DescriptorCallTargets> factory) {
395-
synchronized (structSequenceTargets) {
396-
WeakReference<DescriptorCallTargets> weakResult = structSequenceTargets.computeIfAbsent(descriptor, d -> new WeakReference<>(factory.apply(d)));
397-
DescriptorCallTargets result = weakResult.get();
398-
if (result == null) {
399-
result = factory.apply(descriptor);
400-
structSequenceTargets.put(descriptor, new WeakReference<>(result));
401-
}
402-
return result;
403-
}
404-
}
405-
406362
/**
407363
* We cannot initialize call targets in language ctor and the next suitable hook is context
408364
* initialization, but that is called multiple times. We use this flag to run the language
@@ -482,7 +438,6 @@ protected void finalizeContext(PythonContext context) {
482438
context.finalizeContext();
483439
super.finalizeContext(context);
484440
// trigger cleanup of stale entries in weak hash maps
485-
structSequenceTargets.size();
486441
indirectCallDataMap.size();
487442
}
488443

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import static com.oracle.graal.python.nodes.BuiltinNames.T___BUILTINS__;
3838
import static com.oracle.graal.python.nodes.BuiltinNames.T___IMPORT__;
3939
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PACKAGE__;
40-
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___REPR__;
4140
import static com.oracle.graal.python.nodes.StringLiterals.J_PY_EXTENSION;
4241
import static com.oracle.graal.python.nodes.StringLiterals.T_DOT;
4342
import static com.oracle.graal.python.nodes.StringLiterals.T_GRAALPYTHON;
@@ -312,6 +311,7 @@
312311
import com.oracle.graal.python.builtins.objects.method.BuiltinClassmethodBuiltins;
313312
import com.oracle.graal.python.builtins.objects.method.BuiltinFunctionOrMethodBuiltins;
314313
import com.oracle.graal.python.builtins.objects.method.ClassmethodBuiltins;
314+
import com.oracle.graal.python.builtins.objects.method.ClassmethodCommonBuiltins;
315315
import com.oracle.graal.python.builtins.objects.method.DecoratedMethodBuiltins;
316316
import com.oracle.graal.python.builtins.objects.method.InstancemethodBuiltins;
317317
import com.oracle.graal.python.builtins.objects.method.MethodBuiltins;
@@ -355,6 +355,7 @@
355355
import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins;
356356
import com.oracle.graal.python.builtins.objects.tokenize.TokenizerIterBuiltins;
357357
import com.oracle.graal.python.builtins.objects.traceback.TracebackBuiltins;
358+
import com.oracle.graal.python.builtins.objects.tuple.StructSequenceBuiltins;
358359
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
359360
import com.oracle.graal.python.builtins.objects.tuple.TupleGetterBuiltins;
360361
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
@@ -462,6 +463,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) {
462463
new BuiltinFunctions(),
463464
new DecoratedMethodBuiltins(),
464465
new ClassmethodBuiltins(),
466+
new ClassmethodCommonBuiltins(),
465467
new StaticmethodBuiltins(),
466468
new InstancemethodBuiltins(),
467469
new SimpleNamespaceBuiltins(),
@@ -491,6 +493,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) {
491493
new RangeBuiltins(),
492494
new SliceBuiltins(),
493495
new TupleBuiltins(),
496+
new StructSequenceBuiltins(),
494497
new StringBuiltins(),
495498
new BaseSetBuiltins(),
496499
new SetBuiltins(),
@@ -961,11 +964,6 @@ private void initializeImportlib() {
961964
importFunc = (PFunction) __import__;
962965
importlib = bootstrap;
963966

964-
PythonBuiltinClass moduleType = lookupType(PythonBuiltinClassType.PythonModule);
965-
writeNode.execute(moduleType, T___REPR__, readNode.execute(bootstrap, toTruffleStringUncached("_module_repr")));
966-
967-
SpecialMethodSlot.reinitializeSpecialMethodSlots(moduleType, getLanguage());
968-
969967
// see CPython's init_importlib_external
970968
callNode.execute(null, null, bootstrap, toTruffleStringUncached("_install_external_importers"));
971969
if (!PythonImageBuildOptions.WITHOUT_COMPRESSION_LIBRARIES) {

0 commit comments

Comments
 (0)