Skip to content

Commit 8b59ba5

Browse files
abdelbernielkorchi
authored andcommitted
[GR-65237] Backport to 24.2: Implement AttributeError builtins
PullRequest: graalpython/3820
2 parents 8889060 + 5961ee9 commit 8b59ba5

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
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(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
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___INIT__;
45+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__;
46+
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
47+
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
48+
49+
import java.util.List;
50+
51+
import com.oracle.graal.python.builtins.Builtin;
52+
import com.oracle.graal.python.builtins.CoreFunctions;
53+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
54+
import com.oracle.graal.python.builtins.PythonBuiltins;
55+
import com.oracle.graal.python.builtins.objects.PNone;
56+
import com.oracle.graal.python.builtins.objects.common.EmptyStorage;
57+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
58+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
59+
import com.oracle.graal.python.builtins.objects.dict.PDict;
60+
import com.oracle.graal.python.builtins.objects.function.PKeyword;
61+
import com.oracle.graal.python.nodes.ErrorMessages;
62+
import com.oracle.graal.python.nodes.PRaiseNode;
63+
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
64+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
65+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
66+
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
67+
import com.oracle.graal.python.nodes.object.GetClassNode;
68+
import com.oracle.graal.python.nodes.object.GetDictIfExistsNode;
69+
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
70+
import com.oracle.truffle.api.dsl.Bind;
71+
import com.oracle.truffle.api.dsl.Cached;
72+
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
73+
import com.oracle.truffle.api.dsl.NodeFactory;
74+
import com.oracle.truffle.api.dsl.Specialization;
75+
import com.oracle.truffle.api.frame.VirtualFrame;
76+
import com.oracle.truffle.api.nodes.Node;
77+
import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile;
78+
import com.oracle.truffle.api.strings.TruffleString;
79+
80+
@CoreFunctions(extendClasses = PythonBuiltinClassType.AttributeError)
81+
public final class AttributeErrorBuiltins extends PythonBuiltins {
82+
83+
@Override
84+
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
85+
return AttributeErrorBuiltinsFactory.getFactories();
86+
}
87+
88+
private static final int IDX_NAME = 0;
89+
private static final int IDX_OBJ = 1;
90+
private static final int NUM_ATTRS = IDX_OBJ + 1;
91+
92+
private static final TruffleString T_NAME = tsLiteral("name");
93+
private static final TruffleString T_OBJ = tsLiteral("obj");
94+
95+
private static final BaseExceptionAttrNode.StorageFactory ATTR_FACTORY = (args, factory) -> new Object[NUM_ATTRS];
96+
97+
@Builtin(name = J___INIT__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
98+
@GenerateNodeFactory
99+
abstract static class InitNode extends PythonVarargsBuiltinNode {
100+
101+
@Specialization
102+
static Object init(PBaseException self, Object[] args, PKeyword[] kwargs,
103+
@Bind Node inliningTarget,
104+
@Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode,
105+
@Cached TruffleString.EqualNode equalNameNode,
106+
@Cached TruffleString.EqualNode equalObjNode,
107+
@Cached InlinedLoopConditionProfile loopProfile,
108+
@Cached PRaiseNode raiseNode) {
109+
baseExceptionInitNode.execute(self, args);
110+
Object[] attrs = new Object[NUM_ATTRS];
111+
loopProfile.profileCounted(inliningTarget, kwargs.length);
112+
for (int i = 0; loopProfile.inject(inliningTarget, i < kwargs.length); i++) {
113+
PKeyword kw = kwargs[i];
114+
TruffleString kwName = kw.getName();
115+
if (equalNameNode.execute(kwName, T_NAME, TS_ENCODING)) {
116+
attrs[IDX_NAME] = kw.getValue();
117+
} else if (equalObjNode.execute(kwName, T_OBJ, TS_ENCODING)) {
118+
attrs[IDX_OBJ] = kw.getValue();
119+
} else {
120+
throw raiseNode.raise(PythonBuiltinClassType.TypeError, ErrorMessages.S_IS_AN_INVALID_ARG_FOR_S, kw.getName(), "AttributeError");
121+
}
122+
}
123+
self.setExceptionAttributes(attrs);
124+
return PNone.NONE;
125+
}
126+
}
127+
128+
@Builtin(name = "name", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "attribute name")
129+
@GenerateNodeFactory
130+
public abstract static class NameNode extends PythonBinaryBuiltinNode {
131+
@Specialization
132+
static Object generic(PBaseException self, Object value,
133+
@Cached BaseExceptionAttrNode attrNode) {
134+
return attrNode.execute(self, value, IDX_NAME, ATTR_FACTORY);
135+
}
136+
}
137+
138+
@Builtin(name = "obj", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "object")
139+
@GenerateNodeFactory
140+
public abstract static class ObjNode extends PythonBinaryBuiltinNode {
141+
@Specialization
142+
static Object generic(PBaseException self, Object value,
143+
@Cached BaseExceptionAttrNode attrNode) {
144+
return attrNode.execute(self, value, IDX_OBJ, ATTR_FACTORY);
145+
}
146+
}
147+
148+
@Builtin(name = J___GETSTATE__, minNumOfPositionalArgs = 1)
149+
@GenerateNodeFactory
150+
public abstract static class GetStateNode extends PythonUnaryBuiltinNode {
151+
152+
@Specialization
153+
static Object get(PBaseException self,
154+
@Bind("this") Node inliningTarget,
155+
@Cached BaseExceptionAttrNode attrNode,
156+
@Cached GetDictIfExistsNode getDictIfExistsNode,
157+
@Cached HashingStorageNodes.HashingStorageSetItem setHashingStorageItem,
158+
@Cached HashingStorageNodes.HashingStorageCopy copyStorageNode,
159+
@Cached PythonObjectFactory factory) {
160+
PDict dict = getDictIfExistsNode.execute(self);
161+
/*
162+
* Note from CPython: We specifically are not pickling the obj attribute since there are
163+
* many cases where it is unlikely to be picklable.
164+
*/
165+
Object name = attrNode.get(self, IDX_NAME, ATTR_FACTORY);
166+
if (name != null) {
167+
HashingStorage storage = (dict != null) ? copyStorageNode.execute(inliningTarget, dict.getDictStorage()) : EmptyStorage.INSTANCE;
168+
storage = setHashingStorageItem.execute(inliningTarget, storage, T_NAME, name);
169+
return factory.createDict(storage);
170+
} else if (dict != null) {
171+
return dict;
172+
} else {
173+
return PNone.NONE;
174+
}
175+
}
176+
}
177+
178+
@Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1)
179+
@GenerateNodeFactory
180+
public abstract static class ReduceNode extends PythonUnaryBuiltinNode {
181+
182+
@Specialization
183+
static Object reduce(VirtualFrame frame, PBaseException self,
184+
@Bind("this") Node inliningTarget,
185+
@Cached GetClassNode getClassNode,
186+
@Cached ExceptionNodes.GetArgsNode getArgsNode,
187+
@Cached GetStateNode getStateNode,
188+
@Cached PythonObjectFactory factory) {
189+
Object clazz = getClassNode.execute(inliningTarget, self);
190+
Object args = getArgsNode.execute(inliningTarget, self);
191+
Object state = getStateNode.execute(frame, self);
192+
return factory.createTuple(new Object[]{clazz, args, state});
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)