Skip to content

Commit 246df4e

Browse files
msimacekelkorchi
authored andcommitted
Implement AttributeError builtins
(cherry picked from commit 16a4dc6)
1 parent 76d4966 commit 246df4e

File tree

3 files changed

+206
-1
lines changed

3 files changed

+206
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@
248248
import com.oracle.graal.python.builtins.objects.dict.PDict;
249249
import com.oracle.graal.python.builtins.objects.ellipsis.EllipsisBuiltins;
250250
import com.oracle.graal.python.builtins.objects.enumerate.EnumerateBuiltins;
251+
import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins;
251252
import com.oracle.graal.python.builtins.objects.exception.BaseExceptionBuiltins;
252253
import com.oracle.graal.python.builtins.objects.exception.BaseExceptionGroupBuiltins;
253254
import com.oracle.graal.python.builtins.objects.exception.ImportErrorBuiltins;
@@ -571,6 +572,7 @@ private static PythonBuiltins[] initializeBuiltins(boolean nativeAccessAllowed,
571572
new GenericAliasIteratorBuiltins(),
572573
new com.oracle.graal.python.builtins.objects.types.UnionTypeBuiltins(),
573574
// exceptions
575+
new AttributeErrorBuiltins(),
574576
new SystemExitBuiltins(),
575577
new ImportErrorBuiltins(),
576578
new StopIterationBuiltins(),

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
import com.oracle.graal.python.builtins.objects.dict.DictBuiltins;
136136
import com.oracle.graal.python.builtins.objects.dict.DictValuesBuiltins;
137137
import com.oracle.graal.python.builtins.objects.dict.DictViewBuiltins;
138+
import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins;
138139
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
139140
import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins;
140141
import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins;
@@ -468,7 +469,7 @@ public enum PythonBuiltinClassType implements TruffleObject {
468469
OverflowError("OverflowError", J_BUILTINS, Flags.EXCEPTION),
469470
ZeroDivisionError("ZeroDivisionError", J_BUILTINS, Flags.EXCEPTION),
470471
AssertionError("AssertionError", J_BUILTINS, Flags.EXCEPTION),
471-
AttributeError("AttributeError", J_BUILTINS, Flags.EXCEPTION),
472+
AttributeError("AttributeError", J_BUILTINS, Flags.EXCEPTION, AttributeErrorBuiltins.SLOTS),
472473
BufferError("BufferError", J_BUILTINS, Flags.EXCEPTION),
473474
EOFError("EOFError", J_BUILTINS, Flags.EXCEPTION),
474475
ImportError("ImportError", J_BUILTINS, Flags.EXCEPTION),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.builtins.objects.exception;
42+
43+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETSTATE__;
44+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__;
45+
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
46+
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
47+
48+
import java.util.List;
49+
50+
import com.oracle.graal.python.PythonLanguage;
51+
import com.oracle.graal.python.annotations.Slot;
52+
import com.oracle.graal.python.annotations.Slot.SlotKind;
53+
import com.oracle.graal.python.annotations.Slot.SlotSignature;
54+
import com.oracle.graal.python.builtins.Builtin;
55+
import com.oracle.graal.python.builtins.CoreFunctions;
56+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
57+
import com.oracle.graal.python.builtins.PythonBuiltins;
58+
import com.oracle.graal.python.builtins.objects.PNone;
59+
import com.oracle.graal.python.builtins.objects.common.EmptyStorage;
60+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
61+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
62+
import com.oracle.graal.python.builtins.objects.dict.PDict;
63+
import com.oracle.graal.python.builtins.objects.function.PKeyword;
64+
import com.oracle.graal.python.builtins.objects.type.TpSlots;
65+
import com.oracle.graal.python.nodes.ErrorMessages;
66+
import com.oracle.graal.python.nodes.PRaiseNode;
67+
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
68+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
69+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
70+
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
71+
import com.oracle.graal.python.nodes.object.GetClassNode;
72+
import com.oracle.graal.python.nodes.object.GetDictIfExistsNode;
73+
import com.oracle.graal.python.runtime.object.PFactory;
74+
import com.oracle.truffle.api.dsl.Bind;
75+
import com.oracle.truffle.api.dsl.Cached;
76+
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
77+
import com.oracle.truffle.api.dsl.NodeFactory;
78+
import com.oracle.truffle.api.dsl.Specialization;
79+
import com.oracle.truffle.api.frame.VirtualFrame;
80+
import com.oracle.truffle.api.nodes.Node;
81+
import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile;
82+
import com.oracle.truffle.api.strings.TruffleString;
83+
84+
@CoreFunctions(extendClasses = PythonBuiltinClassType.AttributeError)
85+
public final class AttributeErrorBuiltins extends PythonBuiltins {
86+
87+
public static final TpSlots SLOTS = AttributeErrorBuiltinsSlotsGen.SLOTS;
88+
89+
@Override
90+
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
91+
return AttributeErrorBuiltinsFactory.getFactories();
92+
}
93+
94+
private static final int IDX_NAME = 0;
95+
private static final int IDX_OBJ = 1;
96+
private static final int NUM_ATTRS = IDX_OBJ + 1;
97+
98+
private static final TruffleString T_NAME = tsLiteral("name");
99+
private static final TruffleString T_OBJ = tsLiteral("obj");
100+
101+
private static final BaseExceptionAttrNode.StorageFactory ATTR_FACTORY = (args) -> new Object[NUM_ATTRS];
102+
103+
@Slot(value = SlotKind.tp_init, isComplex = true)
104+
@SlotSignature(minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
105+
@GenerateNodeFactory
106+
abstract static class InitNode extends PythonVarargsBuiltinNode {
107+
108+
@Specialization
109+
static Object init(PBaseException self, Object[] args, PKeyword[] kwargs,
110+
@Bind Node inliningTarget,
111+
@Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode,
112+
@Cached TruffleString.EqualNode equalNameNode,
113+
@Cached TruffleString.EqualNode equalObjNode,
114+
@Cached InlinedLoopConditionProfile loopProfile,
115+
@Cached PRaiseNode raiseNode) {
116+
baseExceptionInitNode.execute(self, args);
117+
Object[] attrs = new Object[NUM_ATTRS];
118+
loopProfile.profileCounted(inliningTarget, kwargs.length);
119+
for (int i = 0; loopProfile.inject(inliningTarget, i < kwargs.length); i++) {
120+
PKeyword kw = kwargs[i];
121+
TruffleString kwName = kw.getName();
122+
if (equalNameNode.execute(kwName, T_NAME, TS_ENCODING)) {
123+
attrs[IDX_NAME] = kw.getValue();
124+
} else if (equalObjNode.execute(kwName, T_OBJ, TS_ENCODING)) {
125+
attrs[IDX_OBJ] = kw.getValue();
126+
} else {
127+
throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.S_IS_AN_INVALID_ARG_FOR_S, kw.getName(), "AttributeError");
128+
}
129+
}
130+
self.setExceptionAttributes(attrs);
131+
return PNone.NONE;
132+
}
133+
}
134+
135+
@Builtin(name = "name", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "attribute name")
136+
@GenerateNodeFactory
137+
public abstract static class NameNode extends PythonBinaryBuiltinNode {
138+
@Specialization
139+
static Object generic(PBaseException self, Object value,
140+
@Cached BaseExceptionAttrNode attrNode) {
141+
return attrNode.execute(self, value, IDX_NAME, ATTR_FACTORY);
142+
}
143+
}
144+
145+
@Builtin(name = "obj", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "object")
146+
@GenerateNodeFactory
147+
public abstract static class ObjNode extends PythonBinaryBuiltinNode {
148+
@Specialization
149+
static Object generic(PBaseException self, Object value,
150+
@Cached BaseExceptionAttrNode attrNode) {
151+
return attrNode.execute(self, value, IDX_OBJ, ATTR_FACTORY);
152+
}
153+
}
154+
155+
@Builtin(name = J___GETSTATE__, minNumOfPositionalArgs = 1)
156+
@GenerateNodeFactory
157+
public abstract static class GetStateNode extends PythonUnaryBuiltinNode {
158+
159+
@Specialization
160+
static Object get(PBaseException self,
161+
@Bind("this") Node inliningTarget,
162+
@Cached BaseExceptionAttrNode attrNode,
163+
@Cached GetDictIfExistsNode getDictIfExistsNode,
164+
@Cached HashingStorageNodes.HashingStorageSetItem setHashingStorageItem,
165+
@Cached HashingStorageNodes.HashingStorageCopy copyStorageNode,
166+
@Bind PythonLanguage language) {
167+
PDict dict = getDictIfExistsNode.execute(self);
168+
/*
169+
* Note from CPython: We specifically are not pickling the obj attribute since there are
170+
* many cases where it is unlikely to be picklable.
171+
*/
172+
Object name = attrNode.get(self, IDX_NAME, ATTR_FACTORY);
173+
if (name != null) {
174+
HashingStorage storage = (dict != null) ? copyStorageNode.execute(inliningTarget, dict.getDictStorage()) : EmptyStorage.INSTANCE;
175+
storage = setHashingStorageItem.execute(inliningTarget, storage, T_NAME, name);
176+
return PFactory.createDict(language, storage);
177+
} else if (dict != null) {
178+
return dict;
179+
} else {
180+
return PNone.NONE;
181+
}
182+
}
183+
}
184+
185+
@Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1)
186+
@GenerateNodeFactory
187+
public abstract static class ReduceNode extends PythonUnaryBuiltinNode {
188+
189+
@Specialization
190+
static Object reduce(VirtualFrame frame, PBaseException self,
191+
@Bind("this") Node inliningTarget,
192+
@Cached GetClassNode getClassNode,
193+
@Cached ExceptionNodes.GetArgsNode getArgsNode,
194+
@Cached GetStateNode getStateNode,
195+
@Bind PythonLanguage language) {
196+
Object clazz = getClassNode.execute(inliningTarget, self);
197+
Object args = getArgsNode.execute(inliningTarget, self);
198+
Object state = getStateNode.execute(frame, self);
199+
return PFactory.createTuple(language, new Object[]{clazz, args, state});
200+
}
201+
}
202+
}

0 commit comments

Comments
 (0)