Skip to content

Commit 52d12f5

Browse files
committed
[GR-13792] Fix "single" parsing to use the displayhook
PullRequest: graalpython/401
2 parents 41a821e + 6d5a97b commit 52d12f5

20 files changed

+174
-39
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,19 @@ def func():
9898
pass
9999
return func
100100
assert run_me() == "just a string, not func", run_me()
101+
102+
103+
def test_single_input_non_interactive():
104+
import sys
105+
oldhook = sys.displayhook
106+
got_value = None
107+
def newhook(value):
108+
nonlocal got_value
109+
got_value = value
110+
sys.displayhook = newhook
111+
try:
112+
code = compile('sum([1, 2, 3])', '', 'single')
113+
assert exec(code) == None
114+
assert got_value == 6
115+
finally:
116+
sys.displayhook = oldhook

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,17 +233,33 @@ protected CallTarget parse(ParsingRequest request) {
233233
if (core.isInitialized()) {
234234
context.initializeMainModule(source.getPath());
235235
}
236-
RootNode root = doParse(core, source);
236+
RootNode root = doParse(context, source);
237237
if (core.isInitialized()) {
238238
return Truffle.getRuntime().createCallTarget(new TopLevelExceptionHandler(this, root));
239239
} else {
240240
return Truffle.getRuntime().createCallTarget(root);
241241
}
242242
}
243243

244-
private RootNode doParse(PythonCore pythonCore, Source source) {
244+
private RootNode doParse(PythonContext context, Source source) {
245+
ParserMode mode = null;
246+
if (source.isInteractive()) {
247+
if (PythonOptions.getOption(context, PythonOptions.TerminalIsInteractive)) {
248+
// if we run through our own launcher, the sys.__displayhook__ would provide the
249+
// printing
250+
mode = ParserMode.Statement;
251+
} else {
252+
// if we're not run through our own launcher, the embedder will expect the normal
253+
// Truffle printing
254+
mode = ParserMode.InteractiveStatement;
255+
}
256+
} else {
257+
// by default we assume a module
258+
mode = ParserMode.File;
259+
}
260+
PythonCore pythonCore = context.getCore();
245261
try {
246-
return (RootNode) pythonCore.getParser().parse(source.isInteractive() ? ParserMode.InteractiveStatement : ParserMode.File, pythonCore, source, null);
262+
return (RootNode) pythonCore.getParser().parse(mode, pythonCore, source, null);
247263
} catch (PException e) {
248264
// handle PException during parsing (PIncompleteSourceException will propagate through)
249265
Truffle.getRuntime().createCallTarget(new TopLevelExceptionHandler(this, e)).call();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public abstract class BuiltinNames {
5656
public static final String __DEBUG__ = "__debug__";
5757

5858
// sys
59+
public static final String DISPLAYHOOK = "displayhook";
5960
public static final String BREAKPOINTHOOK = "breakpointhook";
6061
public static final String EXCEPTHOOK = "excepthook";
6162
public static final String LAST_TYPE = "last_type";

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
import com.oracle.graal.python.nodes.statement.ImportFromNode;
102102
import com.oracle.graal.python.nodes.statement.ImportNode;
103103
import com.oracle.graal.python.nodes.statement.ImportStarNode;
104+
import com.oracle.graal.python.nodes.statement.PrintExpressionNode;
104105
import com.oracle.graal.python.nodes.statement.StatementNode;
105106
import com.oracle.graal.python.nodes.statement.TryExceptNode;
106107
import com.oracle.graal.python.nodes.statement.TryFinallyNode;
@@ -565,4 +566,8 @@ public PDataModelEmulationNode createIsCallable() {
565566
public PDataModelEmulationNode createIsIterable() {
566567
return IsIterableNode.create();
567568
}
569+
570+
public PrintExpressionNode createPrintExpression(ExpressionNode body) {
571+
return PrintExpressionNode.create(body);
572+
}
568573
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (c) 2019, 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.nodes.statement;
42+
43+
import static com.oracle.graal.python.nodes.BuiltinNames.DISPLAYHOOK;
44+
45+
import com.oracle.graal.python.builtins.objects.PNone;
46+
import com.oracle.graal.python.builtins.objects.module.PythonModule;
47+
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
48+
import com.oracle.graal.python.nodes.call.CallNode;
49+
import com.oracle.graal.python.nodes.expression.ExpressionNode;
50+
import com.oracle.truffle.api.frame.VirtualFrame;
51+
52+
public class PrintExpressionNode extends ExpressionNode {
53+
@Child GetAttributeNode getAttribute = GetAttributeNode.create(DISPLAYHOOK);
54+
@Child CallNode callNode = CallNode.create();
55+
@Child ExpressionNode valueNode;
56+
57+
public PrintExpressionNode(ExpressionNode valueNode) {
58+
this.valueNode = valueNode;
59+
}
60+
61+
@Override
62+
public Object execute(VirtualFrame frame) {
63+
Object value = valueNode.execute(frame);
64+
PythonModule sysModule = getContext().getCore().lookupBuiltinModule("sys");
65+
Object displayhook = getAttribute.executeObject(sysModule);
66+
callNode.execute(frame, displayhook, value);
67+
return PNone.NONE;
68+
}
69+
70+
public static PrintExpressionNode create(ExpressionNode valueNode) {
71+
return new PrintExpressionNode(valueNode);
72+
}
73+
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -71,15 +71,14 @@ public Node parse(ParserMode mode, ParserErrorCallback errors, Source source, Fr
7171
throw new RuntimeException("unexpected mode: " + mode);
7272
}
7373
} catch (Exception e) {
74-
if (mode == ParserMode.InteractiveStatement || mode == ParserMode.InlineEvaluation) {
74+
if (mode == ParserMode.InteractiveStatement && e instanceof PIncompleteSourceException) {
75+
((PIncompleteSourceException) e).setSource(source);
76+
throw e;
77+
} else if (mode == ParserMode.InlineEvaluation) {
7578
try {
7679
parser.reset();
7780
input = parser.eval_input();
7881
} catch (Exception e2) {
79-
if (mode == ParserMode.InteractiveStatement && e instanceof PIncompleteSourceException) {
80-
((PIncompleteSourceException) e).setSource(source);
81-
throw e;
82-
}
8382
throw handleParserError(errors, source, e);
8483
}
8584
} else {
@@ -98,7 +97,7 @@ public Node parse(ParserMode mode, ParserErrorCallback errors, Source source, Fr
9897
defineScopes.createFrameSlotsForCellAndFreeVars();
9998

10099
// create Truffle ASTs
101-
return PythonTreeTranslator.translate(errors, source.getName(), input, environment, source, mode == ParserMode.InlineEvaluation);
100+
return PythonTreeTranslator.translate(errors, source.getName(), input, environment, source, mode);
102101
}
103102

104103
@Override

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import com.oracle.graal.python.parser.antlr.Python3Parser.Lambdef_nocond_bodyContext;
9696
import com.oracle.graal.python.parser.antlr.Python3Parser.VarargslistContext;
9797
import com.oracle.graal.python.runtime.PythonParser.ParserErrorCallback;
98+
import com.oracle.graal.python.runtime.PythonParser.ParserMode;
9899
import com.oracle.graal.python.runtime.exception.PException;
99100
import com.oracle.truffle.api.RootCallTarget;
100101
import com.oracle.truffle.api.Truffle;
@@ -114,33 +115,32 @@ public final class PythonTreeTranslator extends Python3BaseVisitor<Object> {
114115
protected final Source source;
115116
protected final String name;
116117

117-
protected final boolean isInlineMode;
118+
protected final ParserMode mode;
118119

119-
public PythonTreeTranslator(ParserErrorCallback errors, String name, TranslationEnvironment environment, Source source, boolean isInlineMode) {
120+
public PythonTreeTranslator(ParserErrorCallback errors, String name, TranslationEnvironment environment, Source source, ParserMode mode) {
120121
this.errors = errors;
121122
this.name = name;
122123
this.environment = environment;
123124
this.source = source;
124-
this.isInlineMode = isInlineMode;
125+
this.mode = mode;
125126
this.factory = errors.getLanguage().getNodeFactory();
126127
this.loops = new LoopsBookKeeper();
127128
this.assigns = new AssignmentTranslator(errors, environment, this);
128129
}
129130

130-
public static Node translate(ParserErrorCallback errors, String name, ParserRuleContext input, TranslationEnvironment environment, Source source, boolean isInlineMode) {
131-
PythonTreeTranslator translator = new PythonTreeTranslator(errors, name, environment, source, isInlineMode);
131+
public static Node translate(ParserErrorCallback errors, String name, ParserRuleContext input, TranslationEnvironment environment, Source source, ParserMode mode) {
132+
PythonTreeTranslator translator = new PythonTreeTranslator(errors, name, environment, source, mode);
132133
try {
133134
Object parseResult = input.accept(translator);
134-
if (!isInlineMode && parseResult instanceof RootNode || isInlineMode && parseResult instanceof PNode) {
135+
if (mode == ParserMode.InlineEvaluation) {
136+
assert parseResult instanceof PNode : "expected PNode result for InlineEvaluation";
135137
return (Node) parseResult;
136138
} else {
137-
throw new RuntimeException("Unexpected parse result");
139+
assert parseResult instanceof RootNode : "expected RootNode result from parsing";
140+
return (Node) parseResult;
138141
}
139142
} catch (PException e) {
140143
throw e;
141-
} catch (Exception t) {
142-
t.printStackTrace();
143-
throw new RuntimeException("Failed in " + translator + " with error " + t, t);
144144
}
145145
}
146146

@@ -253,8 +253,12 @@ public Object visitSingle_input(Python3Parser.Single_inputContext ctx) {
253253
ExpressionNode body = asExpression(super.visitSingle_input(ctx));
254254
deriveSourceSection(ctx, body);
255255
environment.popScope();
256-
if (isInlineMode) {
256+
if (mode == ParserMode.InlineEvaluation) {
257257
return body;
258+
} else if (mode == ParserMode.Statement) {
259+
body = factory.createPrintExpression(body);
260+
deriveSourceSection(ctx, body);
261+
return factory.createModuleRoot("<expression>", getModuleDoc(ctx), body, ctx.scope.getFrameDescriptor());
258262
} else {
259263
return factory.createModuleRoot("<expression>", getModuleDoc(ctx), body, ctx.scope.getFrameDescriptor());
260264
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,15 @@ public T visitFile_input(Python3Parser.File_inputContext ctx) {
7676

7777
@Override
7878
public T visitSingle_input(Single_inputContext ctx) {
79-
if (interactive) {
80-
ctx.scope = environment.pushScope(ctx, ScopeInfo.ScopeKind.Module);
81-
} else if (curInlineLocals != null) {
79+
if (!interactive && curInlineLocals != null) {
8280
ctx.scope = environment.pushScope(ctx, ScopeInfo.ScopeKind.Function, curInlineLocals);
81+
} else {
82+
ctx.scope = environment.pushScope(ctx, ScopeInfo.ScopeKind.Module);
8383
}
8484
try {
8585
return super.visitSingle_input(ctx);
8686
} finally {
87-
if (interactive || curInlineLocals != null) {
88-
environment.popScope();
89-
}
87+
environment.popScope();
9088
}
9189
}
9290

graalpython/lib-graalpython/sys.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,27 @@ def breakpointhook(*args, **kws):
204204
@__builtin__
205205
def getrecursionlimit():
206206
return 1000
207+
208+
209+
@__builtin__
210+
def displayhook(value):
211+
if value is None:
212+
return
213+
builtins = modules['builtins']
214+
# Set '_' to None to avoid recursion
215+
builtins._ = None
216+
text = repr(value)
217+
try:
218+
stdout.write(text)
219+
except UnicodeEncodeError:
220+
bytes = text.encode(stdout.encoding, 'backslashreplace')
221+
if hasattr(stdout, 'buffer'):
222+
stdout.buffer.write(bytes)
223+
else:
224+
text = bytes.decode(stdout.encoding, 'strict')
225+
stdout.write(text)
226+
stdout.write("\n")
227+
builtins._ = value
228+
229+
230+
__displayhook__ = displayhook

mx.graalpython/copyrights/benchmarks.copyright.hash

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
# Copyright 2008-2010 Isaac Gouy
33
# Copyright (c) 2013, 2014, Regents of the University of California
4-
# Copyright (c) 2018, Oracle and/or its affiliates.
4+
# Copyright (c) 2019, Oracle and/or its affiliates.
55
# All rights reserved.
66
#
77
# Revised BSD license
@@ -35,4 +35,3 @@
3535
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3636
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3737
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38-

0 commit comments

Comments
 (0)