Skip to content

Commit 3612c85

Browse files
committed
[GR-23226] More fixes for test_exceptions
PullRequest: graalpython/1011
2 parents 9051186 + ee05c4a commit 3612c85

File tree

6 files changed

+89
-25
lines changed

6 files changed

+89
-25
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,19 +728,26 @@ PCode compile(VirtualFrame frame, PBytes source, String filename, String mode, O
728728
@Specialization
729729
@TruffleBoundary
730730
PCode compile(String expression, String filename, String mode, Object kwFlags, Object kwDontInherit, Object kwOptimize) {
731+
String code = expression;
731732
PythonContext context = getContext();
732733
ParserMode pm;
733734
if (mode.equals("exec")) {
734735
pm = ParserMode.File;
736+
// CPython adds a newline and we need to do the same in order to produce
737+
// SyntaxError with the same offset when the line is incomplete
738+
if (!code.endsWith("\n")) {
739+
code += '\n';
740+
}
735741
} else if (mode.equals("eval")) {
736742
pm = ParserMode.Eval;
737743
} else if (mode.equals("single")) {
738744
pm = ParserMode.Statement;
739745
} else {
740746
throw raise(ValueError, ErrorMessages.COMPILE_MUST_BE);
741747
}
748+
final String codeToCompile = code;
742749
Supplier<CallTarget> createCode = () -> {
743-
Source source = PythonLanguage.newSource(context, expression, filename, mayBeFromFile);
750+
Source source = PythonLanguage.newSource(context, codeToCompile, filename, mayBeFromFile);
744751
return Truffle.getRuntime().createCallTarget((RootNode) getCore().getParser().parse(pm, getCore(), source, null));
745752
};
746753
RootCallTarget ct;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionBuiltins.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
9090
public abstract static class InitNode extends PythonBuiltinNode {
9191
@Specialization(guards = "args.length == 0")
9292
Object initNoArgs(@SuppressWarnings("unused") PBaseException self, @SuppressWarnings("unused") Object[] args) {
93+
self.setArgs(null);
9394
return PNone.NONE;
9495
}
9596

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/parser/PythonParserImpl.java

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2020, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -25,10 +25,9 @@
2525
*/
2626
package com.oracle.graal.python.parser;
2727

28-
import com.oracle.graal.python.builtins.objects.PNone;
29-
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
3028
import org.antlr.v4.runtime.CharStreams;
3129
import org.antlr.v4.runtime.CommonTokenStream;
30+
import org.antlr.v4.runtime.Token;
3231

3332
import com.oracle.graal.python.parser.antlr.DescriptiveBailErrorListener;
3433
import com.oracle.graal.python.parser.antlr.Python3Lexer;
@@ -40,14 +39,12 @@
4039
import com.oracle.graal.python.runtime.PythonParser;
4140
import com.oracle.graal.python.runtime.exception.PException;
4241
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
43-
import com.oracle.truffle.api.TruffleException;
4442
import com.oracle.truffle.api.TruffleLanguage.Env;
4543
import com.oracle.truffle.api.frame.Frame;
4644
import com.oracle.truffle.api.frame.FrameDescriptor;
4745
import com.oracle.truffle.api.nodes.Node;
4846
import com.oracle.truffle.api.source.Source;
4947
import com.oracle.truffle.api.source.SourceSection;
50-
import org.antlr.v4.runtime.Token;
5148

5249
public final class PythonParserImpl implements PythonParser {
5350

@@ -152,18 +149,18 @@ public Node parseN(ParserMode mode, ParserErrorCallback errors, Source source, F
152149
parser.reset();
153150
parserSSTResult = parser.eval_input().result;
154151
} catch (Exception e2) {
155-
throw handleParserError(errors, source, e, !(mode == ParserMode.InteractiveStatement || mode == ParserMode.Statement));
152+
throw handleParserError(errors, source, e);
156153
}
157154
} else {
158-
throw handleParserError(errors, source, e, !(mode == ParserMode.InteractiveStatement || mode == ParserMode.Statement));
155+
throw handleParserError(errors, source, e);
159156
}
160157
}
161158

162159
lastGlobalScope = sstFactory.getScopeEnvironment().getGlobalScope();
163160
try {
164161
return sstFactory.createParserResult(parserSSTResult, mode, currentFrame);
165162
} catch (Exception e) {
166-
throw handleParserError(errors, source, e, !(mode == ParserMode.InteractiveStatement || mode == ParserMode.Statement));
163+
throw handleParserError(errors, source, e);
167164
}
168165
}
169166

@@ -197,20 +194,11 @@ public String unescapeJavaString(String str) {
197194
return StringUtils.unescapeJavaString(str);
198195
}
199196

200-
private static PException handleParserError(ParserErrorCallback errors, Source source, Exception e, boolean showBadLine) {
201-
if (e instanceof TruffleException && ((TruffleException) e).isSyntaxError() && e instanceof PException) {
202-
if (!showBadLine) {
203-
PBaseException instance = ((PException) e).getExceptionObject();
204-
// In cpython shell the line with the error is not displayed, so we should do it in
205-
// the same way.
206-
// This rely on implementation in traceback.py file. See comment in
207-
// Python3Core.raiseInvalidSyntax method
208-
instance.setAttribute("text", PNone.NONE);
209-
}
210-
return (PException) e;
197+
private static PException handleParserError(ParserErrorCallback errors, Source source, Exception e) {
198+
if (e instanceof PException && ((PException) e).isSyntaxError()) {
199+
throw (PException) e;
211200
}
212-
213-
SourceSection section = showBadLine ? PythonErrorStrategy.getPosition(source, e) : source.createUnavailableSection();
201+
SourceSection section = PythonErrorStrategy.getPosition(source, e);
214202
// from parser we are getting RuntimeExceptions
215203
String message = e instanceof RuntimeException && e.getMessage() != null ? e.getMessage() : "invalid syntax";
216204
throw errors.raiseInvalidSyntax(source, section, message);

graalpython/lib-graalpython/exceptions.py

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@ def SystemExit__init__(self, *args):
5555
SystemExit.__init__ = SystemExit__init__
5656
del SystemExit__init__
5757

58-
def ImportError__init__(self, msg=None, /, *args, name=None, path=None):
59-
self.msg = msg
58+
def ImportError__init__(self, *args, name=None, path=None, **kwargs):
59+
if kwargs:
60+
kwarg = next(iter(kwargs))
61+
raise TypeError(f"'{kwarg}' is an invalid keyword argument for ImportError")
62+
BaseException.__init__(self, *args)
63+
self.msg = args[0] if args else None
6064
self.name = name
6165
self.path = path
6266

@@ -116,8 +120,25 @@ def UnicodeEncodeError__init__(self, encoding, object, start, end, reason):
116120
self.reason = reason
117121

118122

123+
def UnicodeEncodeError__str__(self):
124+
if not hasattr(self, 'object'):
125+
return ""
126+
if self.start < len(self.object) and self.start + 1 == self.end:
127+
badchar = ord(self.object[self.start])
128+
if badchar <= 0xff:
129+
fmt = "'%s' codec can't encode character '\\x%02x' in position %d: %s"
130+
elif badchar <= 0xffff:
131+
fmt = "'%s' codec can't encode character '\\u%04x' in position %d: %s"
132+
else:
133+
fmt = "'%s' codec can't encode character '\\U%08x' in position %d: %s"
134+
return fmt % (self.encoding, badchar, self.start, self.reason)
135+
return "'%s' codec can't encode characters in position %d-%d: %s" % (self.encoding, self.start, self.end - 1, self.reason)
136+
137+
119138
UnicodeEncodeError.__init__ = UnicodeEncodeError__init__
139+
UnicodeEncodeError.__str__ = UnicodeEncodeError__str__
120140
del UnicodeEncodeError__init__
141+
del UnicodeEncodeError__str__
121142

122143

123144
def UnicodeDecodeError__init__(self, encoding, object, start, end, reason):
@@ -131,9 +152,28 @@ def UnicodeDecodeError__init__(self, encoding, object, start, end, reason):
131152
self.end = end
132153
self.reason = reason
133154

155+
def UnicodeEncodeError__init__(self, encoding, object, start, end, reason):
156+
BaseException.__init__(self, encoding, object, start, end, reason)
157+
self.encoding = encoding
158+
self.object = object
159+
self.start = start
160+
self.end = end
161+
self.reason = reason
162+
163+
164+
def UnicodeDecodeError__str__(self):
165+
if not hasattr(self, 'object'):
166+
return ""
167+
if self.start < len(self.object) and self.start + 1 == self.end:
168+
byte = self.object[self.start]
169+
return "'%s' codec can't decode byte 0x%02x in position %d: %s" % (self.encoding, byte, self.start, self.reason)
170+
return "'%s' codec can't decode bytes in position %d-%d: %s" % (self.encoding, self.start, self.end - 1, self.reason)
171+
134172

135173
UnicodeDecodeError.__init__ = UnicodeDecodeError__init__
174+
UnicodeDecodeError.__str__ = UnicodeDecodeError__str__
136175
del UnicodeDecodeError__init__
176+
del UnicodeDecodeError__str__
137177

138178

139179
def UnicodeTranslateError__init__(self, object, start, end, reason):
@@ -143,8 +183,25 @@ def UnicodeTranslateError__init__(self, object, start, end, reason):
143183
self.reason = reason
144184

145185

186+
def UnicodeTranslateError__str__(self):
187+
if not hasattr(self, 'object'):
188+
return ""
189+
if self.start < len(self.object) and self.start + 1 == self.end:
190+
badchar = ord(self.object[self.start])
191+
if badchar <= 0xff:
192+
fmt = "can't translate character '\\x%02x' in position %d: %s"
193+
elif badchar <= 0xffff:
194+
fmt = "can't translate character '\\u%04x' in position %d: %s"
195+
else:
196+
fmt = "can't translate character '\\U%08x' in position %d: %s"
197+
return fmt % (badchar, self.start, self.reason)
198+
return "can't translate characters in position %d-%d: %s" % (self.start, self.end - 1, self.reason)
199+
200+
146201
UnicodeTranslateError.__init__ = UnicodeTranslateError__init__
202+
UnicodeTranslateError.__str__ = UnicodeTranslateError__str__
147203
del UnicodeTranslateError__init__
204+
del UnicodeTranslateError__str__
148205

149206

150207
# These errors are just an alias of OSError (i.e. 'EnvironmentError is OSError == True')

graalpython/lib-graalpython/sys.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,13 @@ def simple_print_traceback(e):
184184
def __print_traceback__(typ, value, tb):
185185
try:
186186
import traceback
187-
traceback.print_exception(typ, value, tb)
187+
lines = traceback.format_exception(typ, value, tb)
188+
# CPython's C traceback printer diverges from traceback.print_exception in this small detail.
189+
# We'd like to contribute to CPython to fix the divergence, but for now we do just
190+
# a string substitution to pass the tests
191+
lines[-1] = lines[-1].replace(f'<unprintable {typ.__name__} object>', f'<exception str() failed>')
192+
for line in lines:
193+
print(line, file=stderr, end="")
188194
except BaseException as exc:
189195
print("Error in sys.excepthook:\n", file=stderr)
190196
simple_print_traceback(exc)

graalpython/lib-python/3/test/test_exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ def testExceptionCleanupNames(self):
587587
del e
588588
self.assertNotIn('e', locals())
589589

590+
@support.impl_detail("refcounting", graalvm=False)
590591
def testExceptionCleanupState(self):
591592
# Make sure exception state is cleaned up as soon as the except
592593
# block is left. See #2507
@@ -831,6 +832,7 @@ def run_gen():
831832
gc_collect()
832833
self.assertEqual(sys.exc_info(), (None, None, None))
833834

835+
@support.impl_detail("refcounting", graalvm=False)
834836
def _check_generator_cleanup_exc_state(self, testfunc):
835837
# Issue #12791: exception state is cleaned up as soon as a generator
836838
# is closed (reference cycles are broken).
@@ -893,6 +895,7 @@ def do_send(g):
893895
self.fail("should have raised StopIteration")
894896
self._check_generator_cleanup_exc_state(do_send)
895897

898+
@support.impl_detail("refcounting", graalvm=False)
896899
def test_3114(self):
897900
# Bug #3114: in its destructor, MyObject retrieves a pointer to
898901
# obsolete and/or deallocated objects.
@@ -1163,6 +1166,7 @@ def inner():
11631166
self.assertEqual(wr(), None)
11641167

11651168
@no_tracing
1169+
@support.impl_detail("refcounting", graalvm=False)
11661170
def test_recursion_error_cleanup(self):
11671171
# Same test as above, but with "recursion exceeded" errors
11681172
class C:
@@ -1188,6 +1192,7 @@ def test_errno_ENOTDIR(self):
11881192
os.listdir(__file__)
11891193
self.assertEqual(cm.exception.errno, errno.ENOTDIR, cm.exception)
11901194

1195+
@support.impl_detail("refcounting", graalvm=False)
11911196
def test_unraisable(self):
11921197
# Issue #22836: PyErr_WriteUnraisable() should give sensible reports
11931198
class BrokenDel:

0 commit comments

Comments
 (0)