Skip to content

Commit 0dd805a

Browse files
committed
Align method attribute lookup with CPython GR-53090
1 parent 39cfe7d commit 0dd805a

3 files changed

Lines changed: 52 additions & 23 deletions

File tree

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,35 @@ def g(self):
107107
assert types.MethodType(f, A).__qualname__ == "test_method_qualname_uses_wrapped_callable.<locals>.f"
108108
assert A.m.__qualname__ == "test_method_qualname_uses_wrapped_callable.<locals>.f"
109109
assert A.__dict__["m"].__qualname__ == "test_method_qualname_uses_wrapped_callable.<locals>.f"
110+
111+
112+
def test_method_getattribute_does_not_swallow_method_descriptor_attribute_error():
113+
import types
114+
115+
class Callable:
116+
def __init__(self):
117+
self.name_calls = 0
118+
119+
def __call__(self, *args):
120+
pass
121+
122+
def __getattribute__(self, name):
123+
if name == "__name__":
124+
calls = object.__getattribute__(self, "name_calls")
125+
object.__setattr__(self, "name_calls", calls + 1)
126+
if calls == 0:
127+
raise AttributeError("first lookup failure")
128+
return "fallback-name"
129+
return object.__getattribute__(self, name)
130+
131+
func = Callable()
132+
method = types.MethodType(func, object())
133+
try:
134+
method.__name__
135+
except AttributeError as e:
136+
assert str(e) == "first lookup failure"
137+
else:
138+
assert False, "AttributeError was not raised"
139+
assert func.name_calls == 1
140+
assert method.__class__ is types.MethodType
141+
assert method.__name__ == "fallback-name"

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@
5151
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
5252
import com.oracle.graal.python.builtins.objects.function.PFunction;
5353
import com.oracle.graal.python.builtins.objects.function.PKeyword;
54-
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
54+
import com.oracle.graal.python.builtins.objects.str.StringNodes;
5555
import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
5656
import com.oracle.graal.python.builtins.objects.type.TpSlots;
57+
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
58+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
5759
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode;
5860
import com.oracle.graal.python.lib.PyCallableCheckNode;
5961
import com.oracle.graal.python.lib.PyObjectGetAttr;
@@ -62,17 +64,17 @@
6264
import com.oracle.graal.python.nodes.ErrorMessages;
6365
import com.oracle.graal.python.nodes.PGuards;
6466
import com.oracle.graal.python.nodes.PRaiseNode;
67+
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
6568
import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetDefaultsNode;
6669
import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetKeywordDefaultsNode;
6770
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
6871
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
6972
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
7073
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
71-
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
74+
import com.oracle.graal.python.nodes.object.GetClassNode;
7275
import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
7376
import com.oracle.graal.python.nodes.util.CannotCastException;
7477
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
75-
import com.oracle.graal.python.runtime.exception.PException;
7678
import com.oracle.graal.python.runtime.object.PFactory;
7779
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
7880
import com.oracle.truffle.api.dsl.Bind;
@@ -162,25 +164,22 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode {
162164
@Specialization
163165
static Object doIt(VirtualFrame frame, PMethod self, Object keyObj,
164166
@Bind Node inliningTarget,
165-
@Cached ObjectBuiltins.GetAttributeNode objectGetattrNode,
166-
@Cached IsBuiltinObjectProfile errorProfile,
167-
@Cached CastToTruffleStringNode castKeyToStringNode,
168-
@Cached PRaiseNode raiseNode) {
169-
// TODO: (GR-53090) this is different to what CPython does and CPython also does not
170-
// define tp_descrget for method
171-
TruffleString key;
172-
try {
173-
key = castKeyToStringNode.execute(inliningTarget, keyObj);
174-
} catch (CannotCastException e) {
175-
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj);
176-
}
177-
178-
try {
179-
return objectGetattrNode.execute(frame, self, key);
180-
} catch (PException e) {
181-
e.expectAttributeError(inliningTarget, errorProfile);
182-
return objectGetattrNode.execute(frame, self.getFunction(), key);
167+
@Cached StringNodes.CastToTruffleStringChecked1Node castToString,
168+
@Cached GetClassNode.GetPythonObjectClassNode getClassNode,
169+
@Cached LookupAttributeInMRONode.Dynamic lookup,
170+
@Cached GetObjectSlotsNode getDescrSlotsNode,
171+
@Cached CallSlotDescrGet callSlotDescrGet,
172+
@Cached PyObjectGetAttr getAttrNode) {
173+
TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj);
174+
Object type = getClassNode.execute(inliningTarget, self);
175+
Object descr = lookup.execute(type, key);
176+
if (!PGuards.isNoValue(descr)) {
177+
TpSlots descrSlots = getDescrSlotsNode.execute(inliningTarget, descr);
178+
if (descrSlots.tp_descr_get() != null) {
179+
return callSlotDescrGet.execute(frame, inliningTarget, descrSlots.tp_descr_get(), descr, self, type);
180+
}
183181
}
182+
return getAttrNode.execute(frame, inliningTarget, self.function, key);
184183
}
185184

186185
@Specialization(guards = "!isPMethod(self)")

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ static boolean isSubtypeOfCached(Object derived, Object cls,
209209
@Shared @Cached IsSameTypeNode isSameDerivedNode,
210210
@Shared @Cached IsSameTypeNode isSameClsNode,
211211
@Shared @Cached IsSameTypeNode isSameTypeInLoopNode,
212-
@Shared @Cached GetMroStorageNode getMro,
213212
@Cached(value = "getMroUncached(derived)", weak = true) MroSequenceStorage mro,
214213
@Cached("isInMro(inliningTarget, cachedCls, mro, mro.getInternalClassArray().length, isSameTypeInLoopNode)") boolean isInMro) {
215214
return isInMro;
@@ -230,7 +229,6 @@ static boolean isSubtypeOfCached(Object derived, Object cls,
230229
static boolean isSubtypeOfVariableTypeCached(@SuppressWarnings("unused") Object derived, Object cls,
231230
@Bind Node inliningTarget,
232231
@Cached(value = "derived", weak = true) @SuppressWarnings("unused") Object cachedDerived,
233-
@SuppressWarnings("unused") @Shared @Cached GetMroStorageNode getMro,
234232
@Cached(value = "getMroUncached(derived)", weak = true) MroSequenceStorage mro,
235233
@Cached("mro.getInternalClassArray().length") int sz,
236234
@Shared @Cached IsSameTypeNode isSameTypeInLoopNode,

0 commit comments

Comments
 (0)