Skip to content

Commit 62fa26b

Browse files
committed
Allow setting __name__/__qualname__ for native heaptypes
1 parent 8254b31 commit 62fa26b

File tree

3 files changed

+132
-66
lines changed

3 files changed

+132
-66
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
767767
{slots}
768768
}};
769769
770-
PyType_Spec spec = {{ "{name}Type", sizeof({name}Object), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
770+
PyType_Spec spec = {{ "{name}", sizeof({name}Object), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
771771
772772
static PyObject* create(PyObject* unused, PyObject* bases) {{
773773
{ready_code}

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
import sys
4141

42-
from . import CPyExtTestCase, CPyExtFunction
42+
from . import CPyExtTestCase, CPyExtFunction, CPyExtType, assert_raises, CPyExtHeapType
4343

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

@@ -53,7 +53,6 @@ def foo(self):
5353

5454

5555
class TestClassobject(CPyExtTestCase):
56-
5756
testmod = type(sys)("foo")
5857

5958
test_PyMethod_Function = CPyExtFunction(
@@ -79,3 +78,18 @@ class TestClassobject(CPyExtTestCase):
7978
argspec="O",
8079
arguments=["PyObject* func"],
8180
)
81+
82+
def test_name_qualname(self):
83+
TypeWithName = CPyExtType('TypeWithName')
84+
assert TypeWithName.__name__ == 'TypeWithName'
85+
assert TypeWithName.__qualname__ == 'TypeWithName'
86+
assert_raises(TypeError, setattr, '__name__', TypeWithName, "foo")
87+
assert_raises(TypeError, setattr, '__qualname__', TypeWithName, "foo")
88+
89+
HeapTypeWithName = CPyExtHeapType('HeapTypeWithName')
90+
assert HeapTypeWithName.__name__ == 'HeapTypeWithName'
91+
assert HeapTypeWithName.__qualname__ == 'HeapTypeWithName'
92+
HeapTypeWithName.__name__ = 'HeapTypeWithNameRenamed'
93+
assert HeapTypeWithName.__name__ == 'HeapTypeWithNameRenamed'
94+
HeapTypeWithName.__qualname__ = 'foo.HeapTypeWithNameRenamed'
95+
assert HeapTypeWithName.__qualname__ == 'foo.HeapTypeWithNameRenamed'

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 115 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
package com.oracle.graal.python.builtins.objects.type;
2828

2929
import static com.oracle.graal.python.builtins.objects.PNone.NO_VALUE;
30+
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_name;
3031
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_qualname;
3132
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name;
3233
import static com.oracle.graal.python.nodes.BuiltinNames.T_BUILTINS;
@@ -54,6 +55,7 @@
5455
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__;
5556
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__;
5657
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
58+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___QUALNAME__;
5759
import static com.oracle.graal.python.nodes.SpecialMethodNames.J_MRO;
5860
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CALL__;
5961
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___DIR__;
@@ -87,10 +89,13 @@
8789
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
8890
import com.oracle.graal.python.builtins.PythonBuiltins;
8991
import com.oracle.graal.python.builtins.modules.BuiltinConstructorsFactory;
92+
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
9093
import com.oracle.graal.python.builtins.objects.PNone;
9194
import com.oracle.graal.python.builtins.objects.PNotImplemented;
95+
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
9296
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
9397
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
98+
import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper;
9499
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
95100
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
96101
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
@@ -111,6 +116,7 @@
111116
import com.oracle.graal.python.builtins.objects.object.ObjectNodes;
112117
import com.oracle.graal.python.builtins.objects.object.PythonObject;
113118
import com.oracle.graal.python.builtins.objects.set.PSet;
119+
import com.oracle.graal.python.builtins.objects.str.PString;
114120
import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
115121
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
116122
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
@@ -1045,7 +1051,29 @@ static PList getSubclasses(Object cls,
10451051
abstract static class AbstractSlotNode extends PythonBinaryBuiltinNode {
10461052
}
10471053

1048-
@Builtin(name = J___NAME__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
1054+
@GenerateInline
1055+
@GenerateCached(false)
1056+
static abstract class CheckSetSpecialTypeAttrNode extends Node {
1057+
abstract void execute(Node inliningTarget, Object type, Object value, TruffleString name);
1058+
1059+
@Specialization
1060+
static void check(Node inliningTarget, Object type, Object value, TruffleString name,
1061+
@Cached PRaiseNode.Lazy raiseNode,
1062+
@Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode,
1063+
@Cached SysModuleBuiltins.AuditNode auditNode) {
1064+
if (PGuards.isKindOfBuiltinClass(type) || (getTypeFlagsNode.execute(type) & TypeFlags.IMMUTABLETYPE) != 0) {
1065+
throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_S_OF_IMMUTABLE_TYPE_N, name, type);
1066+
}
1067+
if (value == DescriptorDeleteMarker.INSTANCE) {
1068+
// Sic, it's not immutable, but CPython has this message
1069+
throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.CANT_DELETE_ATTRIBUTE_S_OF_IMMUTABLE_TYPE_N, name, type);
1070+
}
1071+
auditNode.audit(inliningTarget, "object.__setattr__", type, name, value);
1072+
}
1073+
}
1074+
1075+
@Builtin(name = J___NAME__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, //
1076+
allowsDelete = true /* Delete handled by CheckSetSpecialTypeAttrNode */)
10491077
abstract static class NameNode extends AbstractSlotNode {
10501078
@Specialization(guards = "isNoValue(value)")
10511079
static TruffleString getNameType(PythonBuiltinClassType cls, @SuppressWarnings("unused") PNone value) {
@@ -1057,42 +1085,6 @@ static TruffleString getNameBuiltin(PythonManagedClass cls, @SuppressWarnings("u
10571085
return cls.getName();
10581086
}
10591087

1060-
@Specialization(guards = "!isNoValue(value)")
1061-
static Object setName(@SuppressWarnings("unused") PythonBuiltinClassType cls, @SuppressWarnings("unused") Object value,
1062-
@Shared @Cached PRaiseNode raiseNode) {
1063-
throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE, "built-in/extension 'type'");
1064-
}
1065-
1066-
@Specialization(guards = "!isNoValue(value)")
1067-
static Object setName(@SuppressWarnings("unused") PythonBuiltinClass cls, @SuppressWarnings("unused") Object value,
1068-
@Shared @Cached PRaiseNode raiseNode) {
1069-
throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE, "built-in/extension 'type'");
1070-
}
1071-
1072-
@Specialization(guards = {"!isNoValue(value)", "!isPythonBuiltinClass(cls)"})
1073-
static Object setName(VirtualFrame frame, PythonClass cls, Object value,
1074-
@Bind("this") Node inliningTarget,
1075-
@Exclusive @Cached CastToTruffleStringNode castToTruffleStringNode,
1076-
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode,
1077-
@Cached TruffleString.IsValidNode isValidNode,
1078-
@Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode,
1079-
@Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode,
1080-
@Cached PRaiseNode.Lazy raiseNode) {
1081-
try {
1082-
TruffleString string = castToTruffleStringNode.execute(inliningTarget, value);
1083-
if (indexOfCodePointNode.execute(string, 0, 0, codePointLengthNode.execute(string, TS_ENCODING), TS_ENCODING) >= 0) {
1084-
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.ValueError, ErrorMessages.TYPE_NAME_NO_NULL_CHARS);
1085-
}
1086-
if (!isValidNode.execute(string, TS_ENCODING)) {
1087-
throw constructAndRaiseNode.get(inliningTarget).raiseUnicodeEncodeError(frame, "utf-8", string, 0, string.codePointLengthUncached(TS_ENCODING), "can't encode classname");
1088-
}
1089-
cls.setName(string);
1090-
return PNone.NONE;
1091-
} catch (CannotCastException e) {
1092-
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.TypeError, ErrorMessages.CAN_ONLY_ASSIGN_S_TO_P_S_NOT_P, "string", cls, T___NAME__, value);
1093-
}
1094-
}
1095-
10961088
@Specialization(guards = "isNoValue(value)")
10971089
static Object getName(PythonAbstractNativeObject cls, @SuppressWarnings("unused") PNone value,
10981090
@Cached CStructAccess.ReadCharPtrNode getTpNameNode,
@@ -1109,10 +1101,59 @@ static Object getName(PythonAbstractNativeObject cls, @SuppressWarnings("unused"
11091101
return substringNode.execute(tpName, lastDot + 1, nameLen - lastDot - 1, TS_ENCODING, true);
11101102
}
11111103

1104+
@GenerateInline
1105+
@GenerateCached(false)
1106+
abstract static class SetNameInnerNode extends Node {
1107+
abstract void execute(Node inliningTarget, Object type, TruffleString value);
1108+
1109+
@Specialization
1110+
static void set(PythonClass type, TruffleString value) {
1111+
type.setName(value);
1112+
}
1113+
1114+
@Specialization
1115+
static void set(PythonAbstractNativeObject type, TruffleString value,
1116+
@Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
1117+
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject,
1118+
@Cached(inline = false) TruffleString.SwitchEncodingNode switchEncodingNode,
1119+
@Cached(inline = false) TruffleString.CopyToByteArrayNode copyToByteArrayNode,
1120+
@Cached(inline = false) PythonObjectFactory factory) {
1121+
value = switchEncodingNode.execute(value, TruffleString.Encoding.UTF_8);
1122+
byte[] bytes = copyToByteArrayNode.execute(value, TruffleString.Encoding.UTF_8);
1123+
PBytes bytesObject = factory.createBytes(bytes);
1124+
writePointerNode.writeToObj(type, PyTypeObject__tp_name, PySequenceArrayWrapper.ensureNativeSequence(bytesObject));
1125+
PString pString = factory.createString(value);
1126+
pString.setUtf8Bytes(bytesObject);
1127+
writeObject.writeToObject(type, PyHeapTypeObject__ht_name, pString);
1128+
}
1129+
}
1130+
11121131
@Specialization(guards = "!isNoValue(value)")
1113-
static Object getModule(@SuppressWarnings("unused") PythonAbstractNativeObject cls, @SuppressWarnings("unused") Object value,
1114-
@Shared @Cached PRaiseNode raiseNode) {
1115-
throw raiseNode.raise(PythonErrorType.RuntimeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE, "native type");
1132+
static Object setName(VirtualFrame frame, Object cls, Object value,
1133+
@Bind("this") Node inliningTarget,
1134+
@Cached CheckSetSpecialTypeAttrNode check,
1135+
@Exclusive @Cached CastToTruffleStringNode castToTruffleStringNode,
1136+
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode,
1137+
@Cached TruffleString.IsValidNode isValidNode,
1138+
@Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode,
1139+
@Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode,
1140+
@Cached SetNameInnerNode innerNode,
1141+
@Cached PRaiseNode.Lazy raiseNode) {
1142+
check.execute(inliningTarget, cls, value, T___NAME__);
1143+
TruffleString string;
1144+
try {
1145+
string = castToTruffleStringNode.execute(inliningTarget, value);
1146+
} catch (CannotCastException e) {
1147+
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.TypeError, ErrorMessages.CAN_ONLY_ASSIGN_S_TO_P_S_NOT_P, "string", cls, T___NAME__, value);
1148+
}
1149+
if (indexOfCodePointNode.execute(string, 0, 0, codePointLengthNode.execute(string, TS_ENCODING), TS_ENCODING) >= 0) {
1150+
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.ValueError, ErrorMessages.TYPE_NAME_NO_NULL_CHARS);
1151+
}
1152+
if (!isValidNode.execute(string, TS_ENCODING)) {
1153+
throw constructAndRaiseNode.get(inliningTarget).raiseUnicodeEncodeError(frame, "utf-8", string, 0, string.codePointLengthUncached(TS_ENCODING), "can't encode classname");
1154+
}
1155+
innerNode.execute(inliningTarget, cls, string);
1156+
return PNone.NONE;
11161157
}
11171158
}
11181159

@@ -1205,7 +1246,8 @@ static Object setModuleBuiltin(@SuppressWarnings("unused") PythonBuiltinClass cl
12051246
}
12061247
}
12071248

1208-
@Builtin(name = J___QUALNAME__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
1249+
@Builtin(name = J___QUALNAME__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, //
1250+
allowsDelete = true /* Delete handled by CheckSetSpecialTypeAttrNode */)
12091251
abstract static class QualNameNode extends AbstractSlotNode {
12101252
@Specialization(guards = "isNoValue(value)")
12111253
static TruffleString getName(PythonBuiltinClassType cls, @SuppressWarnings("unused") PNone value) {
@@ -1217,25 +1259,6 @@ static TruffleString getName(PythonManagedClass cls, @SuppressWarnings("unused")
12171259
return cls.getQualName();
12181260
}
12191261

1220-
@Specialization(guards = "!isNoValue(value)")
1221-
static Object setName(@SuppressWarnings("unused") PythonBuiltinClass cls, @SuppressWarnings("unused") Object value,
1222-
@Shared @Cached PRaiseNode raiseNode) {
1223-
throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE, "built-in/extension 'type'");
1224-
}
1225-
1226-
@Specialization(guards = {"!isNoValue(value)", "!isPythonBuiltinClass(cls)"})
1227-
static Object setName(PythonClass cls, Object value,
1228-
@Bind("this") Node inliningTarget,
1229-
@Cached CastToTruffleStringNode castToStringNode,
1230-
@Cached PRaiseNode.Lazy raiseNode) {
1231-
try {
1232-
cls.setQualName(castToStringNode.execute(inliningTarget, value));
1233-
return PNone.NONE;
1234-
} catch (CannotCastException e) {
1235-
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.TypeError, ErrorMessages.CAN_ONLY_ASSIGN_STR_TO_QUALNAME, cls, value);
1236-
}
1237-
}
1238-
12391262
@Specialization(guards = "isNoValue(value)")
12401263
static Object getNative(PythonNativeClass cls, @SuppressWarnings("unused") PNone value,
12411264
@Cached GetTypeFlagsNode getTypeFlagsNode,
@@ -1258,10 +1281,39 @@ static Object getNative(PythonNativeClass cls, @SuppressWarnings("unused") PNone
12581281
}
12591282
}
12601283

1284+
@GenerateInline
1285+
@GenerateCached(false)
1286+
abstract static class SetQualNameInnerNode extends Node {
1287+
abstract void execute(Node inliningTarget, Object type, TruffleString value);
1288+
1289+
@Specialization
1290+
static void set(PythonClass type, TruffleString value) {
1291+
type.setQualName(value);
1292+
}
1293+
1294+
@Specialization
1295+
static void set(PythonAbstractNativeObject type, TruffleString value,
1296+
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject) {
1297+
writeObject.writeToObject(type, PyHeapTypeObject__ht_qualname, value);
1298+
}
1299+
}
1300+
12611301
@Specialization(guards = "!isNoValue(value)")
1262-
static Object setNative(@SuppressWarnings("unused") PythonNativeClass cls, @SuppressWarnings("unused") Object value,
1263-
@Shared @Cached PRaiseNode raiseNode) {
1264-
throw raiseNode.raise(PythonErrorType.RuntimeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE, "native type");
1302+
static Object setName(Object cls, Object value,
1303+
@Bind("this") Node inliningTarget,
1304+
@Cached CheckSetSpecialTypeAttrNode check,
1305+
@Cached CastToTruffleStringNode castToStringNode,
1306+
@Cached SetQualNameInnerNode innerNode,
1307+
@Cached PRaiseNode.Lazy raiseNode) {
1308+
check.execute(inliningTarget, cls, value, T___QUALNAME__);
1309+
TruffleString stringValue;
1310+
try {
1311+
stringValue = castToStringNode.execute(inliningTarget, value);
1312+
} catch (CannotCastException e) {
1313+
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.TypeError, ErrorMessages.CAN_ONLY_ASSIGN_STR_TO_QUALNAME, cls, value);
1314+
}
1315+
innerNode.execute(inliningTarget, cls, stringValue);
1316+
return PNone.NONE;
12651317
}
12661318
}
12671319

0 commit comments

Comments
 (0)