Skip to content

Commit 6232ff3

Browse files
committed
[GR-24883] Module name should be printed in attribute error.
PullRequest: graalpython/1117
2 parents 0d66541 + 0c41c10 commit 6232ff3

File tree

6 files changed

+143
-4
lines changed

6 files changed

+143
-4
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
from . import use
41+
spam = 1
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
from . import source
41+
source.spam

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,13 @@ def test_import_package_all() :
154154
assert hasattr(package1, expected_sym), "'package1' does not have attribute '%s'" % expected_sym
155155
cnt += 1
156156
assert package1.exported.__testname__ == "package1.exported", "expected 'test_import_package_all' but was '%s'" % str(package1.exported.__testname__)
157+
158+
def test_circular_import():
159+
if sys.version_info.minor >= 8:
160+
# the message was chaged in CPython 3.8
161+
try:
162+
import circularimport.source
163+
except AttributeError as ae:
164+
assert str(ae) == "partially initialized module 'circularimport.source' has no attribute 'spam' (most likely due to a circular import)"
165+
else:
166+
assert False

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, 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
@@ -66,3 +66,23 @@ def test_user_class():
6666
t = TestClass()
6767
t.__module__ = "baz"
6868
assert t.__module__ == "baz"
69+
70+
def test_wrong_property():
71+
import time
72+
try:
73+
time.no_existing_property
74+
except AttributeError as ae:
75+
assert str(ae) == "module 'time' has no attribute 'no_existing_property'"
76+
else:
77+
assert False
78+
79+
def test_wrong_property_in_moudule_without_name():
80+
import time
81+
del time.__name__
82+
try:
83+
time.no_existing_property
84+
except AttributeError as ae:
85+
assert str(ae) == "module has no attribute 'no_existing_property'"
86+
else:
87+
assert False
88+

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -49,6 +49,7 @@
4949
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTRIBUTE__;
5050
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTR__;
5151
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INIT__;
52+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError;
5253

5354
import java.util.List;
5455

@@ -58,14 +59,18 @@
5859
import com.oracle.graal.python.builtins.PythonBuiltins;
5960
import com.oracle.graal.python.builtins.objects.PNone;
6061
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
62+
import com.oracle.graal.python.nodes.ErrorMessages;
6163
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
6264
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
6365
import com.oracle.graal.python.nodes.call.CallNode;
66+
import com.oracle.graal.python.nodes.expression.CoerceToBooleanNode;
6467
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
6568
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
6669
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
6770
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
6871
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
72+
import com.oracle.graal.python.nodes.util.CannotCastException;
73+
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
6974
import com.oracle.graal.python.runtime.exception.PException;
7075
import com.oracle.truffle.api.dsl.Cached;
7176
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
@@ -116,7 +121,9 @@ Object getattribute(VirtualFrame frame, PythonModule self, Object key,
116121
@Cached("create()") ObjectBuiltins.GetAttributeNode objectGetattrNode,
117122
@Cached("create()") ReadAttributeFromObjectNode readGetattr,
118123
@Cached("createBinaryProfile()") ConditionProfile customGetAttr,
119-
@Cached("create()") CallNode callNode) {
124+
@Cached("create()") CallNode callNode,
125+
@Cached("createIfTrueNode()") CoerceToBooleanNode castToBooleanNode,
126+
@Cached CastToJavaStringNode castToStringNode) {
120127
try {
121128
return objectGetattrNode.execute(frame, self, key);
122129
} catch (PException e) {
@@ -125,7 +132,24 @@ Object getattribute(VirtualFrame frame, PythonModule self, Object key,
125132
if (customGetAttr.profile(getAttr != PNone.NO_VALUE)) {
126133
return callNode.execute(frame, getAttr, key);
127134
} else {
128-
throw e;
135+
String moduleName;
136+
try {
137+
moduleName = castToStringNode.execute(readGetattr.execute(self, __NAME__));
138+
} catch (CannotCastException ce) {
139+
// we just don't have the module name
140+
moduleName = null;
141+
}
142+
if (moduleName != null) {
143+
Object moduleSpec = readGetattr.execute(self, __SPEC__);
144+
if (moduleSpec != PNone.NO_VALUE) {
145+
Object isInitializing = readGetattr.execute(moduleSpec, "_initializing");
146+
if (isInitializing != PNone.NO_VALUE && castToBooleanNode.executeBoolean(frame, isInitializing)) {
147+
throw raise(AttributeError, ErrorMessages.MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S, moduleName, key);
148+
}
149+
}
150+
throw raise(AttributeError, ErrorMessages.MODULE_S_HAS_NO_ATTR_S, moduleName, key);
151+
}
152+
throw raise(AttributeError, ErrorMessages.MODULE_HAS_NO_ATTR_S, key);
129153
}
130154
}
131155
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ public abstract class ErrorMessages {
324324
public static final String MISSING_D_REQUIRED_S_ARGUMENT_S_POS = "%s() missing required argument '%s' (pos %d)";
325325
public static final String MISSING_D_REQUIRED_S_ARGUMENT_S_S = "%s() missing %d required %s argument%s: '%s'";
326326
public static final String MMAP_INDEX_OUT_OF_RANGE = "mmap index out of range";
327+
public static final String MODULE_HAS_NO_ATTR_S = "module has no attribute '%s'";
328+
public static final String MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S = "partially initialized module '%s' has no attribute '%s' (most likely due to a circular import)";
329+
public static final String MODULE_S_HAS_NO_ATTR_S = "module '%s' has no attribute '%s'";
327330
public static final String MUST_BE_A_CELL = "%s must be a cell";
328331
public static final String MUST_BE_BYTE_STRING_LEGTH1_NOT_P = "must be a byte string of length 1, not %p";
329332
public static final String MUST_BE_EITHER_OR = "%s: '%s' must be either %s or %s";

0 commit comments

Comments
 (0)