Skip to content

Commit 31c5df1

Browse files
author
Adam Hrbac
committed
Make future annotations inherited by exec
1 parent 5957c70 commit 31c5df1

File tree

8 files changed

+145
-48
lines changed

8 files changed

+145
-48
lines changed

graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/FutureFeature.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@
4141
package com.oracle.graal.python.pegparser;
4242

4343
public enum FutureFeature {
44-
ANNOTATIONS,
45-
BARRY_AS_BDFL
44+
ANNOTATIONS(0x1000000), // these values are duplicated in BuiltinFunctions.Compiler
45+
BARRY_AS_BDFL(0x400000);
46+
47+
public final int flagValue;
48+
49+
FutureFeature(int flagValue) {
50+
this.flagValue = flagValue;
51+
}
4652
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.oracle.graal.python.test.basic;
2+
3+
import static com.oracle.graal.python.test.PythonTests.assertPrints;
4+
5+
import org.junit.Test;
6+
7+
public class FutureAnnotationTests {
8+
@Test
9+
public void withoutEvaluates() {
10+
assertPrints("hello\n", "def f() -> print('hello'): pass");
11+
}
12+
13+
@Test
14+
public void withDoesNotEvaluate() {
15+
assertPrints("", "from __future__ import annotations\ndef f() -> print('hello'): pass");
16+
}
17+
18+
@Test
19+
public void worksInExec() {
20+
assertPrints("hello\n", "exec('def f() -> print(\\'hello\\'): pass')");
21+
assertPrints("", "exec('from __future__ import annotations\\ndef f() -> print(\\'hello\\'): pass')");
22+
}
23+
24+
@Test
25+
public void execInherits() {
26+
assertPrints("", "from __future__ import annotations\nexec('def f() -> print(\\'hello\\'): pass')");
27+
}
28+
29+
}

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ private static CodeUnit assemble(String src, InputType type) {
10961096
Parser parser = Compiler.createParser(src, errorCallback, type, false);
10971097
ModTy result = (ModTy) parser.parse();
10981098
Compiler compiler = new Compiler(errorCallback);
1099-
CompilationUnit cu = compiler.compile(result, EnumSet.noneOf(Compiler.Flags.class), 2);
1099+
CompilationUnit cu = compiler.compile(result, EnumSet.noneOf(Compiler.Flags.class), 2, false);
11001100
return cu.assemble();
11011101
}
11021102

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

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@
131131
name = PythonLanguage.NAME, //
132132
implementationName = PythonLanguage.IMPLEMENTATION_NAME, //
133133
version = PythonLanguage.VERSION, //
134-
characterMimeTypes = {PythonLanguage.MIME_TYPE,
135-
PythonLanguage.MIME_TYPE_COMPILE0, PythonLanguage.MIME_TYPE_COMPILE1, PythonLanguage.MIME_TYPE_COMPILE2,
136-
PythonLanguage.MIME_TYPE_EVAL0, PythonLanguage.MIME_TYPE_EVAL1, PythonLanguage.MIME_TYPE_EVAL2}, //
134+
characterMimeTypes = {PythonLanguage.MIME_TYPE, "text/x-python-eval-0-0", "text/x-python-eval-0-1", "text/x-python-eval-1-0", "text/x-python-eval-1-1", "text/x-python-eval-2-0",
135+
"text/x-python-eval-2-1", "text/x-python-compile-0-0", "text/x-python-compile-0-1", "text/x-python-compile-1-0", "text/x-python-compile-1-1",
136+
"text/x-python-compile-2-0",
137+
"text/x-python-compile-2-1"}, //
137138
byteMimeTypes = {PythonLanguage.MIME_TYPE_BYTECODE}, //
138139
defaultMimeType = PythonLanguage.MIME_TYPE, //
139140
dependentLanguages = {"nfi", "llvm"}, //
@@ -206,14 +207,20 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
206207
public static final int API_VERSION = 1013;
207208

208209
public static final String MIME_TYPE = "text/x-python";
209-
static final String MIME_TYPE_COMPILE0 = "text/x-python-compile0";
210-
static final String MIME_TYPE_COMPILE1 = "text/x-python-compile1";
211-
static final String MIME_TYPE_COMPILE2 = "text/x-python-compile2";
212-
static final String[] MIME_TYPE_COMPILE = {PythonLanguage.MIME_TYPE_COMPILE0, PythonLanguage.MIME_TYPE_COMPILE1, PythonLanguage.MIME_TYPE_COMPILE2};
213-
static final String[] MIME_TYPE_EVAL = {PythonLanguage.MIME_TYPE_EVAL0, PythonLanguage.MIME_TYPE_EVAL1, PythonLanguage.MIME_TYPE_EVAL2};
214-
static final String MIME_TYPE_EVAL0 = "text/x-python-eval0";
215-
static final String MIME_TYPE_EVAL1 = "text/x-python-eval1";
216-
static final String MIME_TYPE_EVAL2 = "text/x-python-eval2";
210+
211+
// the syntax for mime types is as follows
212+
// <mime> ::= "text/x-python-" <kind> "-" <optlevel> "-" <future_annotations>
213+
// <kind> ::= "compile" | "eval"
214+
// <optlevel> ::= "0" | "1" | "2"
215+
// <future_annotations> ::= "0" | "1"
216+
static final String MIME_PREFIX = MIME_TYPE + "-";
217+
static final String MIME_KIND_COMPILE = "compile";
218+
static final String MIME_KIND_EVAL = "eval";
219+
static final int MIME_KIND_IDX = 0;
220+
static final int MIME_OPTLEVEL_IDX = 1;
221+
static final int MIME_FUTURE_ANNOTATIONS_IDX = 2;
222+
static final int MIME_FIELD_COUNT = 3;
223+
217224
public static final String MIME_TYPE_BYTECODE = "application/x-python-bytecode";
218225

219226
public static final TruffleString[] T_DEFAULT_PYTHON_EXTENSIONS = new TruffleString[]{T_PY_EXTENSION, tsLiteral(".pyc")};
@@ -388,23 +395,28 @@ protected void initializeContext(PythonContext context) {
388395
context.initialize();
389396
}
390397

391-
public static String getCompileMimeType(int optimize) {
398+
public static String getCompileMimeType(int optimize, boolean futureAnnotations) {
399+
String futureAnnField = futureAnnotations ? "1" : "0";
400+
String compile = MIME_PREFIX + "compile-";
392401
if (optimize <= 0) {
393-
return MIME_TYPE_COMPILE0;
402+
return compile + "0-" + futureAnnField;
394403
} else if (optimize == 1) {
395-
return MIME_TYPE_COMPILE1;
404+
return compile + "1-" + futureAnnField;
396405
} else {
397-
return MIME_TYPE_COMPILE2;
406+
return compile + "2-" + futureAnnField;
398407
}
399408
}
400409

401-
public static String getEvalMimeType(int optimize) {
410+
// TODO inherit other implemented
411+
public static String getEvalMimeType(int optimize, boolean futureAnnotations) {
412+
String futureAnnField = futureAnnotations ? "1" : "0";
413+
String eval = MIME_PREFIX + "eval-";
402414
if (optimize <= 0) {
403-
return MIME_TYPE_EVAL0;
415+
return eval + "0-" + futureAnnField;
404416
} else if (optimize == 1) {
405-
return MIME_TYPE_EVAL1;
417+
return eval + "1-" + futureAnnField;
406418
} else {
407-
return MIME_TYPE_EVAL2;
419+
return eval + "2-" + futureAnnField;
408420
}
409421
}
410422

@@ -417,7 +429,7 @@ protected CallTarget parse(ParsingRequest request) {
417429
throw new IllegalStateException("parse with arguments not allowed for interactive sources");
418430
}
419431
InputType inputType = source.isInteractive() ? InputType.SINGLE : InputType.FILE;
420-
return parse(context, source, inputType, true, 0, source.isInteractive(), request.getArgumentNames());
432+
return parse(context, source, inputType, true, 0, source.isInteractive(), request.getArgumentNames(), false);
421433
}
422434
if (!request.getArgumentNames().isEmpty()) {
423435
throw new IllegalStateException("parse with arguments is only allowed for " + MIME_TYPE + " mime type");
@@ -452,19 +464,38 @@ protected CallTarget parse(ParsingRequest request) {
452464
PBytecodeRootNode rootNode = PBytecodeRootNode.create(this, code, source);
453465
return PythonUtils.getOrCreateCallTarget(rootNode);
454466
}
455-
for (int optimize = 0; optimize < MIME_TYPE_EVAL.length; optimize++) {
456-
if (MIME_TYPE_EVAL[optimize].equals(source.getMimeType())) {
457-
assert !source.isInteractive();
458-
return parse(context, source, InputType.EVAL, false, optimize, false, null);
459-
}
467+
468+
String mime = source.getMimeType();
469+
String prefix = mime.substring(0, MIME_PREFIX.length());
470+
if (!prefix.equals(MIME_PREFIX)) {
471+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
460472
}
461-
for (int optimize = 0; optimize < MIME_TYPE_COMPILE.length; optimize++) {
462-
if (MIME_TYPE_COMPILE[optimize].equals(source.getMimeType())) {
463-
assert !source.isInteractive();
464-
return parse(context, source, InputType.FILE, false, optimize, false, null);
465-
}
473+
String[] fields = mime.substring(MIME_PREFIX.length()).split("-");
474+
if (fields.length != MIME_FIELD_COUNT) {
475+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
476+
}
477+
String kind = fields[MIME_KIND_IDX];
478+
InputType type;
479+
if (kind.equals(MIME_KIND_COMPILE)) {
480+
type = InputType.FILE;
481+
} else if (kind.equals(MIME_KIND_EVAL)) {
482+
type = InputType.EVAL;
483+
} else {
484+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
485+
}
486+
int optimize;
487+
int futureAnnotations;
488+
try {
489+
optimize = Integer.parseInt(fields[MIME_OPTLEVEL_IDX]);
490+
futureAnnotations = Integer.parseInt(fields[MIME_FUTURE_ANNOTATIONS_IDX]);
491+
} catch (NumberFormatException e) {
492+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
493+
}
494+
if (0 > optimize || optimize > 2 || 0 > futureAnnotations || futureAnnotations > 2) {
495+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
466496
}
467-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
497+
assert !source.isInteractive();
498+
return parse(context, source, type, false, optimize, false, null, futureAnnotations == 1);
468499
}
469500

470501
private static Source tryLoadSource(PythonContext context, CodeUnit code, boolean internal, String path) {
@@ -475,13 +506,14 @@ private static Source tryLoadSource(PythonContext context, CodeUnit code, boolea
475506
}
476507
}
477508

478-
public RootCallTarget parse(PythonContext context, Source source, InputType type, boolean topLevel, int optimize, boolean interactiveTerminal, List<String> argumentNames) {
509+
public RootCallTarget parse(PythonContext context, Source source, InputType type, boolean topLevel, int optimize, boolean interactiveTerminal, List<String> argumentNames,
510+
boolean futureAnnotations) {
479511
RaisePythonExceptionErrorCallback errorCb = new RaisePythonExceptionErrorCallback(source, PythonOptions.isPExceptionWithJavaStacktrace(this));
480512
try {
481513
Parser parser = Compiler.createParser(source.getCharacters().toString(), errorCb, type, interactiveTerminal);
482514
ModTy mod = (ModTy) parser.parse();
483515
assert mod != null;
484-
return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCb);
516+
return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCb, futureAnnotations);
485517
} catch (PException e) {
486518
if (topLevel) {
487519
PythonUtils.getOrCreateCallTarget(new TopLevelExceptionHandler(this, e)).call();
@@ -492,7 +524,7 @@ public RootCallTarget parse(PythonContext context, Source source, InputType type
492524

493525
@TruffleBoundary
494526
public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy mod, Source source, boolean topLevel, int optimize, List<String> argumentNames,
495-
RaisePythonExceptionErrorCallback errorCallback) {
527+
RaisePythonExceptionErrorCallback errorCallback, boolean futureAnnotations) {
496528
RaisePythonExceptionErrorCallback errorCb = errorCallback;
497529
if (errorCb == null) {
498530
errorCb = new RaisePythonExceptionErrorCallback(source, PythonOptions.isPExceptionWithJavaStacktrace(this));
@@ -503,7 +535,7 @@ public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy
503535
if (hasArguments) {
504536
mod = transformASTForExecutionWithArguments(argumentNames, mod);
505537
}
506-
CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize);
538+
CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureAnnotations);
507539
CodeUnit co = cu.assemble();
508540
RootNode rootNode = PBytecodeRootNode.create(this, co, source, errorCb);
509541
if (topLevel) {
@@ -588,7 +620,7 @@ public String getName() {
588620
@Override
589621
public ExecutableNode parse(InlineParsingRequest request) {
590622
PythonContext context = PythonContext.get(null);
591-
RootCallTarget callTarget = parse(context, request.getSource(), InputType.EVAL, false, 0, false, null);
623+
RootCallTarget callTarget = parse(context, request.getSource(), InputType.EVAL, false, 0, false, null, false);
592624
return new ExecutableNode(this) {
593625
@Child private GilNode gilNode = GilNode.create();
594626
@Child private GenericInvokeNode invokeNode = GenericInvokeNode.create();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ private void loadFile(TruffleString s, TruffleString prefix, PythonModule mod) {
12171217
}
12181218
Supplier<CallTarget> getCode = () -> {
12191219
Source source = getInternalSource(s, prefix);
1220-
return getLanguage().parse(getContext(), source, InputType.FILE, false, 0, false, null);
1220+
return getLanguage().parse(getContext(), source, InputType.FILE, false, 0, false, null, false);
12211221
};
12221222
RootCallTarget callTarget = (RootCallTarget) getLanguage().cacheCode(s, getCode);
12231223
GenericInvokeNode.getUncached().execute(callTarget, PArguments.withGlobals(mod));

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,17 @@ protected abstract Object executeInternal(VirtualFrame frame, Object source, Tru
10771077
int featureVersion);
10781078

10791079
@Specialization
1080+
Object doCompile(VirtualFrame frame, TruffleString expression, TruffleString filename, TruffleString mode, int flags, @SuppressWarnings("unused") boolean dontInherit, int optimize,
1081+
int featureVersion,
1082+
@Cached ReadCallerFrameNode readCallerFrame) {
1083+
if (!dontInherit) {
1084+
PFrame fr = readCallerFrame.executeWith(frame, 0);
1085+
PCode code = factory().createCode(fr.getTarget());
1086+
flags |= code.getFlags() & PyCF_MASK;
1087+
}
1088+
return compile(expression, filename, mode, flags, dontInherit, optimize, featureVersion);
1089+
}
1090+
10801091
@TruffleBoundary
10811092
Object compile(TruffleString expression, TruffleString filename, TruffleString mode, int flags, @SuppressWarnings("unused") boolean dontInherit, int optimize, int featureVersion) {
10821093
checkFlags(flags);
@@ -1120,14 +1131,14 @@ Object compile(TruffleString expression, TruffleString filename, TruffleString m
11201131
TruffleString finalCode = code;
11211132
Supplier<CallTarget> createCode = () -> {
11221133
if (type == InputType.FILE) {
1123-
Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getCompileMimeType(optimize));
1134+
Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getCompileMimeType(optimize, (flags & CO_FUTURE_ANNOTATIONS) != 0));
11241135
return context.getEnv().parsePublic(source);
11251136
} else if (type == InputType.EVAL) {
1126-
Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getEvalMimeType(optimize));
1137+
Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getEvalMimeType(optimize, (flags & CO_FUTURE_ANNOTATIONS) != 0));
11271138
return context.getEnv().parsePublic(source);
11281139
} else {
11291140
Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.MIME_TYPE);
1130-
return context.getLanguage().parse(context, source, InputType.SINGLE, false, optimize, false, null);
1141+
return context.getLanguage().parse(context, source, InputType.SINGLE, false, optimize, false, null, (flags & CO_FUTURE_ANNOTATIONS) != 0);
11311142
}
11321143
};
11331144
if (getCore().isCoreInitialized()) {
@@ -1148,7 +1159,8 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri
11481159
@Cached PyUnicodeFSDecoderNode asPath,
11491160
@Cached WarnNode warnNode,
11501161
@Cached TruffleString.FromByteArrayNode fromByteArrayNode,
1151-
@Cached TruffleString.SwitchEncodingNode switchEncodingNode) {
1162+
@Cached TruffleString.SwitchEncodingNode switchEncodingNode,
1163+
@Cached ReadCallerFrameNode readCallerFrame) {
11521164
if (wSource instanceof PCode) {
11531165
return wSource;
11541166
}
@@ -1168,10 +1180,17 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri
11681180
} else {
11691181
filename = asPath.execute(frame, wFilename);
11701182
}
1183+
1184+
if (!dontInherit) {
1185+
PFrame fr = readCallerFrame.executeWith(frame, 0);
1186+
PCode code = factory().createCode(fr.getTarget());
1187+
flags |= code.getFlags() & PyCF_MASK;
1188+
}
1189+
11711190
if (AstModuleBuiltins.isAst(getContext(), wSource)) {
11721191
ModTy mod = AstModuleBuiltins.obj2sst(getContext(), wSource, getParserInputType(mode, flags));
11731192
Source source = PythonUtils.createFakeSource(filename);
1174-
RootCallTarget rootCallTarget = getLanguage().compileForBytecodeInterpreter(getContext(), mod, source, false, optimize, null, null);
1193+
RootCallTarget rootCallTarget = getLanguage().compileForBytecodeInterpreter(getContext(), mod, source, false, optimize, null, null, (flags & CO_FUTURE_ANNOTATIONS) != 0);
11751194
return wrapRootCallTarget(rootCallTarget);
11761195
}
11771196
TruffleString source = sourceAsString(frame, wSource, filename, interopLib, acquireLib, bufferLib, handleDecodingErrorNode, asStrNode, switchEncodingNode);

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import java.util.TreeSet;
6262

6363
import com.oracle.graal.python.builtins.objects.code.PCode;
64+
import com.oracle.graal.python.pegparser.FutureFeature;
6465
import com.oracle.graal.python.pegparser.scope.Scope;
6566
import com.oracle.graal.python.pegparser.scope.ScopeEnvironment;
6667
import com.oracle.graal.python.pegparser.tokenizer.SourceRange;
@@ -96,8 +97,10 @@ public final class CompilationUnit {
9697
SourceRange startLocation;
9798
SourceRange currentLocation;
9899

100+
final EnumSet<FutureFeature> futureFeatures;
101+
99102
CompilationUnit(CompilationScope scopeType, Scope scope, String name, CompilationUnit parent, int scopeDepth, int argCount, int positionalOnlyArgCount, int kwOnlyArgCount, boolean takesVarArgs,
100-
boolean takesVarKeywordArgs, SourceRange startLocation) {
103+
boolean takesVarKeywordArgs, SourceRange startLocation, EnumSet<FutureFeature> futureFeatures) {
101104
this.scopeType = scopeType;
102105
this.scope = scope;
103106
this.name = name;
@@ -107,6 +110,7 @@ public final class CompilationUnit {
107110
this.takesVarArgs = takesVarArgs;
108111
this.takesVarKeywordArgs = takesVarKeywordArgs;
109112
this.startLocation = startLocation;
113+
this.futureFeatures = futureFeatures;
110114
currentLocation = startLocation;
111115

112116
if (scopeType == Class) {
@@ -265,6 +269,10 @@ public CodeUnit assemble() {
265269
flags |= PCode.CO_COROUTINE;
266270
}
267271

272+
for (FutureFeature flag : futureFeatures) {
273+
flags |= flag.flagValue;
274+
}
275+
268276
final int rangeElements = 4;
269277
int[] exceptionHandlerRanges = new int[finishedExceptionHandlerRanges.size() * rangeElements];
270278
int rangeIndex = 0;

0 commit comments

Comments
 (0)