Skip to content

Commit 6436c27

Browse files
author
Adam Hrbac
committed
Get enough working to pass jinja2 async tests
1 parent 81bd718 commit 6436c27

File tree

6 files changed

+187
-42
lines changed

6 files changed

+187
-42
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/AsyncGenSendBuiltins.java

Lines changed: 131 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,48 @@
1+
/*
2+
* Copyright (c) 2023, 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+
*/
141
package com.oracle.graal.python.builtins.objects.asyncio;
242

343
import static com.oracle.graal.python.builtins.objects.asyncio.PAsyncGenASend.AwaitableState;
444
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___AWAIT__;
45+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ITER__;
546
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___NEXT__;
647

748
import java.util.List;
@@ -12,8 +53,11 @@
1253
import com.oracle.graal.python.builtins.PythonBuiltins;
1354
import com.oracle.graal.python.builtins.objects.PNone;
1455
import com.oracle.graal.python.builtins.objects.generator.CommonGeneratorBuiltins;
56+
import com.oracle.graal.python.builtins.objects.generator.PGenerator;
57+
import com.oracle.graal.python.nodes.ErrorMessages;
1558
import com.oracle.graal.python.nodes.PRaiseNode;
1659
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
60+
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
1761
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
1862
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
1963
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles;
@@ -64,17 +108,15 @@ public Object send(VirtualFrame frame, PAsyncGenASend self, Object sent,
64108
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isStopIteration,
65109
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isGenExit,
66110
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isAsyncGenWrappedValue,
67-
@Cached PRaiseNode raiseStopAsyncIteration) {
111+
@Cached PRaiseNode raiseStopIteration) {
68112
Object result;
69113
if (self.getState() == AwaitableState.CLOSED) {
70-
throw raiseReuse.raise(PythonBuiltinClassType.RuntimeError); // todo error msg
114+
throw raiseReuse.raise(PythonBuiltinClassType.RuntimeError, ErrorMessages.CANNOT_REUSE_ASEND);
71115
}
72116

73117
if (self.getState() == AwaitableState.INIT) {
74118
if (self.receiver.isRunningAsync()) {
75-
throw raiseAlreadyRunning.raise(PythonBuiltinClassType.RuntimeError); // todo
76-
// error
77-
// msg
119+
throw raiseAlreadyRunning.raise(PythonBuiltinClassType.RuntimeError, ErrorMessages.AGEN_ALREADY_RUNNING);
78120
}
79121
if (sent == null || sent == PNone.NONE) {
80122
sent = self.message;
@@ -84,27 +126,94 @@ public Object send(VirtualFrame frame, PAsyncGenASend self, Object sent,
84126
self.receiver.setRunningAsync(true);
85127
try {
86128
result = send.execute(frame, self.receiver, sent);
87-
if (result == null) {
88-
self.receiver.markAsFinished();
89-
self.receiver.setRunningAsync(false);
90-
self.setState(AwaitableState.CLOSED);
91-
throw raiseStopAsyncIteration.raise(PythonBuiltinClassType.StopAsyncIteration);
92-
}
93-
if (isAsyncGenWrappedValue.profileObject(inliningTarget, result, PythonBuiltinClassType.PAsyncGenAWrappedValue)) {
94-
self.receiver.setRunningAsync(false);
95-
self.setState(AwaitableState.CLOSED);
96-
throw raiseStopAsyncIteration.raise(PythonBuiltinClassType.StopIteration, new Object[]{((PAsyncGenWrappedValue) result).getWrapped()});
97-
}
98129
} catch (PException e) {
99-
if (isStopIteration.profileException(inliningTarget, e, PythonBuiltinClassType.StopAsyncIteration) ||
100-
isGenExit.profileException(inliningTarget, e, PythonBuiltinClassType.GeneratorExit)) {
101-
self.receiver.markAsFinished();
102-
}
103-
self.receiver.setRunningAsync(false);
130+
self.setState(AwaitableState.CLOSED);
131+
throw handleAGError(self.receiver, e, inliningTarget, isStopIteration, isGenExit);
132+
}
133+
try {
134+
return unwrapAGYield(self.receiver, result, inliningTarget, isAsyncGenWrappedValue, raiseStopIteration);
135+
} catch (PException e) {
104136
self.setState(AwaitableState.CLOSED);
105137
throw e;
106138
}
107-
return result;
139+
}
140+
}
141+
142+
private static PException handleAGError(PGenerator self, PException exception,
143+
Node inliningTarget,
144+
BuiltinClassProfiles.IsBuiltinObjectProfile isStopAsyncIteration,
145+
BuiltinClassProfiles.IsBuiltinObjectProfile isGeneratorExit) {
146+
if (isStopAsyncIteration.profileException(inliningTarget, exception, PythonBuiltinClassType.StopAsyncIteration) ||
147+
isGeneratorExit.profileException(inliningTarget, exception, PythonBuiltinClassType.GeneratorExit)) {
148+
self.markAsFinished();
149+
}
150+
self.setRunningAsync(false);
151+
return exception;
152+
}
153+
154+
private static Object unwrapAGYield(PGenerator self, Object result,
155+
Node inliningTarget,
156+
BuiltinClassProfiles.IsBuiltinObjectProfile isAGWrappedValue,
157+
PRaiseNode raise) {
158+
if (isAGWrappedValue.profileObject(inliningTarget, result, PythonBuiltinClassType.PAsyncGenAWrappedValue)) {
159+
self.setRunningAsync(false);
160+
Object wrapped = ((PAsyncGenWrappedValue) result).getWrapped();
161+
throw raise.raise(PythonBuiltinClassType.StopIteration, new Object[]{wrapped});
162+
}
163+
return result;
164+
}
165+
166+
@Builtin(name = "throw", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 4, declaresExplicitSelf = true)
167+
@GenerateNodeFactory
168+
public static abstract class Throw extends PythonBuiltinNode {
169+
public abstract Object execute(VirtualFrame frame, PAsyncGenASend self, Object arg1, Object arg2, Object arg3);
170+
171+
@Specialization
172+
public Object doThrow(VirtualFrame frame, PAsyncGenASend self, Object arg1, Object arg2, Object arg3,
173+
@Bind("this") Node inliningTarget,
174+
@Cached PRaiseNode raiseReuse,
175+
@Cached CommonGeneratorBuiltins.ThrowNode throwNode,
176+
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isStopIteration,
177+
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isGeneratorExit,
178+
@Cached BuiltinClassProfiles.IsBuiltinObjectProfile isAGWrappedValue,
179+
@Cached PRaiseNode raiseStopIteration) {
180+
Object result;
181+
182+
if (self.getState() == AwaitableState.CLOSED) {
183+
throw raiseReuse.raise(PythonBuiltinClassType.RuntimeError, ErrorMessages.CANNOT_REUSE_ASEND);
184+
}
185+
try {
186+
result = throwNode.execute(frame, self.receiver, arg1, arg2, arg3);
187+
} catch (PException e) {
188+
self.setState(AwaitableState.CLOSED);
189+
throw handleAGError(self.receiver, e, inliningTarget, isStopIteration, isGeneratorExit);
190+
}
191+
try {
192+
return unwrapAGYield(self.receiver, result, inliningTarget, isAGWrappedValue, raiseStopIteration);
193+
} catch (PException e) {
194+
self.setState(AwaitableState.CLOSED);
195+
throw e;
196+
}
197+
198+
}
199+
}
200+
201+
@Builtin(name = J___ITER__, minNumOfPositionalArgs = 1, declaresExplicitSelf = true)
202+
@GenerateNodeFactory
203+
public static abstract class Iter extends PythonUnaryBuiltinNode {
204+
@Specialization
205+
public Object iter(PAsyncGenASend self) {
206+
return self;
207+
}
208+
}
209+
210+
@Builtin(name = "close", minNumOfPositionalArgs = 1, declaresExplicitSelf = true)
211+
@GenerateNodeFactory
212+
public static abstract class Close extends PythonUnaryBuiltinNode {
213+
@Specialization
214+
public Object close(PAsyncGenASend self) {
215+
self.setState(AwaitableState.CLOSED);
216+
return PNone.NONE;
108217
}
109218
}
110219
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/Compiler.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,8 +1511,17 @@ private Void visitComprehension(ExprTy node, String name, ComprehensionTy[] gene
15111511
exitScope();
15121512
makeClosure(code, 0);
15131513
generators[0].iter.accept(this);
1514-
addOp(GET_ITER);
1514+
if (generators[0].isAsync) {
1515+
addOp(GET_AITER);
1516+
} else {
1517+
addOp(GET_ITER);
1518+
}
15151519
addOp(CALL_COMPREHENSION);
1520+
if (generators[0].isAsync) {
1521+
addOp(GET_AWAITABLE);
1522+
addOp(LOAD_NONE);
1523+
addYieldFrom();
1524+
}
15161525
return null;
15171526
} finally {
15181527
setLocation(savedLocation);
@@ -1527,14 +1536,34 @@ private void visitComprehensionGenerator(ComprehensionTy[] generators, int i, Ex
15271536
} else {
15281537
/* Create the iterator for nested iteration */
15291538
gen.iter.accept(this);
1530-
addOp(GET_ITER);
1539+
if (gen.isAsync) {
1540+
addOp(GET_AITER);
1541+
} else {
1542+
addOp(GET_ITER);
1543+
}
15311544
}
15321545
Block start = new Block();
15331546
Block ifCleanup = new Block();
15341547
Block anchor = new Block();
1548+
Block asyncForTry = gen.isAsync ? new Block() : null;
1549+
Block asyncForExcept = gen.isAsync ? new Block() : null;
1550+
Block asyncForBody = gen.isAsync ? new Block() : null;
15351551
unit.useNextBlock(start);
1536-
addOp(FOR_ITER, anchor);
1537-
gen.target.accept(this);
1552+
if (gen.isAsync) {
1553+
addOp(DUP_TOP);
1554+
unit.useNextBlock(asyncForTry);
1555+
unit.pushBlock(new BlockInfo.AsyncForLoopExit(asyncForTry, asyncForExcept));
1556+
addOp(GET_ANEXT);
1557+
addOp(GET_AWAITABLE);
1558+
addOp(LOAD_NONE);
1559+
addYieldFrom();
1560+
unit.popBlock();
1561+
unit.useNextBlock(asyncForBody);
1562+
gen.target.accept(this);
1563+
} else {
1564+
addOp(FOR_ITER, anchor);
1565+
gen.target.accept(this);
1566+
}
15381567
for (ExprTy ifExpr : gen.ifs) {
15391568
jumpIf(ifExpr, ifCleanup, false);
15401569
}
@@ -1566,6 +1595,10 @@ private void visitComprehensionGenerator(ComprehensionTy[] generators, int i, Ex
15661595
}
15671596
unit.useNextBlock(ifCleanup);
15681597
addOp(JUMP_BACKWARD, start);
1598+
if (gen.isAsync) {
1599+
unit.useNextBlock(asyncForExcept);
1600+
addOp(END_ASYNC_FOR);
1601+
}
15691602
unit.useNextBlock(anchor);
15701603
}
15711604

@@ -2096,20 +2129,19 @@ private void visitAsyncFor(StmtTy.AsyncFor node) {
20962129
Block body = new Block();
20972130
Block end = new Block();
20982131
Block except = new Block();
2132+
Block loopTry = new Block();
20992133
Block orelse = node.orElse != null ? new Block() : null;
21002134
node.iter.accept(this);
21012135
addOp(GET_AITER);
21022136
unit.useNextBlock(head);
21032137
unit.pushBlock(new BlockInfo.AsyncForLoop(head, end));
2104-
unit.pushBlock(new BlockInfo.AsyncForLoopExit(head, except));
21052138
addOp(DUP_TOP);
21062139
addOp(GET_ANEXT);
2140+
addOp(GET_AWAITABLE);
2141+
unit.useNextBlock(loopTry);
2142+
unit.pushBlock(new BlockInfo.AsyncForLoopExit(loopTry, except));
21072143
addOp(LOAD_NONE);
21082144
addYieldFrom();
2109-
addOp(JUMP_FORWARD, body);
2110-
unit.useNextBlock(except);
2111-
addOp(END_ASYNC_FOR);
2112-
addOp(JUMP_FORWARD, node.orElse != null ? orelse : end);
21132145
unit.popBlock();
21142146
unit.useNextBlock(body);
21152147
try {
@@ -2119,6 +2151,9 @@ private void visitAsyncFor(StmtTy.AsyncFor node) {
21192151
} finally {
21202152
unit.popBlock();
21212153
}
2154+
addOp(JUMP_FORWARD, node.orElse != null ? orelse : end);
2155+
unit.useNextBlock(except);
2156+
addOp(END_ASYNC_FOR);
21222157
if (node.orElse != null) {
21232158
unit.useNextBlock(orelse);
21242159
visitSequence(node.orElse);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,9 +742,9 @@ public enum OpCodes {
742742
* Exception handler for async for loops. If the current exception is StopAsyncIteration, handle
743743
* it, otherwise, reraise.
744744
*
745-
* Pops: exception, then the async iterator
745+
* Pops: exception, then the anext coroutine, then the async iterator
746746
*/
747-
END_ASYNC_FOR(0, 2, 0),
747+
END_ASYNC_FOR(0, 3, 0),
748748

749749
// with statements
750750
/**

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,4 +1525,6 @@ public abstract class ErrorMessages {
15251525
public static final TruffleString ASYNC_FOR_NO_ANEXT_INITIAL = tsLiteral("'async for' received an object from __aiter__ that does not implement __anext__: %s");
15261526

15271527
public static final TruffleString ASYNC_FOR_NO_ANEXT_ITERATION = tsLiteral("'async for' requires an iterator with __anext__ method, got %s");
1528+
public static final TruffleString CANNOT_REUSE_ASEND = tsLiteral("cannot reuse already awaited __anext__()/asend()");
1529+
public static final TruffleString AGEN_ALREADY_RUNNING = tsLiteral("anext(): asynchronous generator is already running");
15281530
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,10 @@ private int bytecodeGetANext(VirtualFrame virtualFrame, boolean useCachedNodes,
23872387
private int bytecodeEndAsyncFor(VirtualFrame virtualFrame, boolean useCachedNodes, int stackTop, Node[] localNodes, int bci) {
23882388
EndAsyncForNode node = insertChildNode(localNodes, bci, UNCACHED_END_ASYNC_FOR, EndAsyncForNodeGen.class, NODE_END_ASYNC_FOR, useCachedNodes);
23892389
node.execute(virtualFrame.getObject(stackTop), frameIsVisibleToPython());
2390-
return stackTop - 2;
2390+
virtualFrame.setObject(stackTop, null); // pop the exception
2391+
virtualFrame.setObject(stackTop - 1, null); // the coroutine that raised the exception
2392+
virtualFrame.setObject(stackTop - 2, null); // the async iterator
2393+
return stackTop - 3;
23912394
}
23922395

23932396
@BytecodeInterpreterSwitch

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ private PythonOptions() {
174174
@Option(category = OptionCategory.USER, help = "Equivalent to setting the PYTHONPATH environment variable for the standard launcher. ':'-separated list of directories prefixed to the default module search path.", usageSyntax = "<path>[:<path>]", stability = OptionStability.STABLE) //
175175
public static final OptionKey<TruffleString> PythonPath = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
176176

177-
@EngineOption
178-
@Option(category = OptionCategory.USER, help = "Equivalent to setting the PYTHONIOENCODING environment variable for the standard launcher.", usageSyntax = "<Encoding>[:<errors>]", stability = OptionStability.STABLE) //
177+
@EngineOption @Option(category = OptionCategory.USER, help = "Equivalent to setting the PYTHONIOENCODING environment variable for the standard launcher.", usageSyntax = "<Encoding>[:<errors>]", stability = OptionStability.STABLE) //
179178
public static final OptionKey<TruffleString> StandardStreamEncoding = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
180179

181180
@Option(category = OptionCategory.USER, help = "Remove assert statements and any code conditional on the value of __debug__.", usageSyntax = "true|false", stability = OptionStability.STABLE) //
@@ -232,8 +231,7 @@ private PythonOptions() {
232231
}
233232
}));
234233

235-
@EngineOption
236-
@Option(category = OptionCategory.USER, help = "Choose the backend for the POSIX module.", usageSyntax = "java|native|llvm") //
234+
@EngineOption @Option(category = OptionCategory.USER, help = "Choose the backend for the POSIX module.", usageSyntax = "java|native|llvm") //
237235
public static final OptionKey<TruffleString> PosixModuleBackend = new OptionKey<>(T_JAVA, TS_OPTION_TYPE);
238236

239237
@Option(category = OptionCategory.USER, help = "Value of the --check-hash-based-pycs command line option" +
@@ -250,12 +248,10 @@ private PythonOptions() {
250248
@Option(category = OptionCategory.INTERNAL, help = "Set the location of C API home. Overrides any environment variables or Java options.", usageSyntax = "<path>", stability = OptionStability.STABLE) //
251249
public static final OptionKey<TruffleString> CAPI = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
252250

253-
@EngineOption
254-
@Option(category = OptionCategory.INTERNAL, help = "Expose internal sources as normal sources, so they will show up in the debugger and stacks", usageSyntax = "true|false") //
251+
@EngineOption @Option(category = OptionCategory.INTERNAL, help = "Expose internal sources as normal sources, so they will show up in the debugger and stacks", usageSyntax = "true|false") //
255252
public static final OptionKey<Boolean> ExposeInternalSources = new OptionKey<>(false);
256253

257-
@EngineOption
258-
@Option(category = OptionCategory.INTERNAL, help = "Print the java stacktrace. Possible modes:" +
254+
@EngineOption @Option(category = OptionCategory.INTERNAL, help = "Print the java stacktrace. Possible modes:" +
259255
" 1 Print Java stacktrace for Java exceptions only." +
260256
" 2 Print Java stacktrace for Python exceptions only (ATTENTION: this will have a notable performance impact)." +
261257
" 3 Combines 1 and 2.", usageSyntax = "1|2|3") //

0 commit comments

Comments
 (0)