Skip to content

Commit c64124e

Browse files
committed
let sys.executable be defined by the launcher, same for the executable_list
1 parent ddef15d commit c64124e

File tree

7 files changed

+104
-103
lines changed

7 files changed

+104
-103
lines changed

graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Set;
4040

4141
import org.graalvm.launcher.AbstractLanguageLauncher;
42+
import org.graalvm.nativeimage.ImageInfo;
4243
import org.graalvm.nativeimage.ProcessProperties;
4344
import org.graalvm.options.OptionCategory;
4445
import org.graalvm.polyglot.Context;
@@ -72,9 +73,11 @@ public static void main(String[] args) {
7273
private boolean runLLI = false;
7374
private VersionAction versionAction = VersionAction.None;
7475
private String sulongLibraryPath = null;
76+
private List<String> givenArguments;
7577

7678
@Override
77-
protected List<String> preprocessArguments(List<String> givenArguments, Map<String, String> polyglotOptions) {
79+
protected List<String> preprocessArguments(List<String> givenArgs, Map<String, String> polyglotOptions) {
80+
givenArguments = new ArrayList<>(givenArgs);
7881
ArrayList<String> unrecognized = new ArrayList<>();
7982
ArrayList<String> inputArgs = new ArrayList<>(getDefaultEnvironmentArgs());
8083
inputArgs.addAll(givenArguments);
@@ -266,6 +269,43 @@ private static void print(String string) {
266269
System.out.println(string);
267270
}
268271

272+
private static String[] getExecutableList() {
273+
if (ImageInfo.inImageCode()) {
274+
return new String[]{getExecutable()};
275+
} else {
276+
StringBuilder sb = new StringBuilder();
277+
ArrayList<String> exec_list = new ArrayList<>();
278+
sb.append(System.getProperty("java.home")).append(File.separator).append("bin").append(File.separator).append("java");
279+
exec_list.add(sb.toString());
280+
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
281+
if (arg.matches("-Xrunjdwp:transport=dt_socket,server=y,address=\\d+,suspend=y")) {
282+
arg = arg.replace("suspend=y", "suspend=n");
283+
}
284+
exec_list.add(arg);
285+
}
286+
exec_list.add("-classpath");
287+
exec_list.add(System.getProperty("java.class.path"));
288+
exec_list.add(GraalPythonMain.class.getName());
289+
return exec_list.toArray(new String[exec_list.size()]);
290+
}
291+
}
292+
293+
private static String getExecutable() {
294+
if (ImageInfo.inImageRuntimeCode()) {
295+
return ProcessProperties.getExecutableName();
296+
} else if (ImageInfo.inImageBuildtimeCode()) {
297+
return "";
298+
} else {
299+
// we quote all arguments here, because using sys.executable directly will only work on
300+
// a shell, anyways.
301+
String[] executableList = getExecutableList();
302+
for (int i = 0; i < executableList.length; i++) {
303+
executableList[i] = executableList[i].replace("'", "\\'");
304+
}
305+
return "'" + String.join("' '", executableList) + "'";
306+
}
307+
}
308+
269309
@Override
270310
protected void launch(Builder contextBuilder) {
271311
if (runLLI) {
@@ -289,7 +329,11 @@ protected void launch(Builder contextBuilder) {
289329
noUserSite = noUserSite || System.getenv("PYTHONNOUSERSITE") != null;
290330
verboseFlag = verboseFlag || System.getenv("PYTHONVERBOSE") != null;
291331
}
292-
sulongLibraryPath = System.getenv("SULONG_LIBRARY_PATH");
332+
333+
// The unlikely separator is used because options need to be strings. See
334+
// PythonOptions.getExecutableList()
335+
contextBuilder.option("python.ExecutableList", String.join("🏆", getExecutableList()));
336+
setContextOptionIfUnset(contextBuilder, "python.Executable", getExecutable());
293337

294338
// setting this to make sure our TopLevelExceptionHandler calls the excepthook
295339
// to print Python exceptions
@@ -302,6 +346,8 @@ protected void launch(Builder contextBuilder) {
302346
contextBuilder.option("python.QuietFlag", Boolean.toString(quietFlag));
303347
contextBuilder.option("python.NoUserSiteFlag", Boolean.toString(noUserSite));
304348
contextBuilder.option("python.NoSiteFlag", Boolean.toString(noSite));
349+
350+
sulongLibraryPath = System.getenv("SULONG_LIBRARY_PATH");
305351
if (sulongLibraryPath != null) {
306352
contextBuilder.option("llvm.libraryPath", sulongLibraryPath);
307353
}
@@ -358,6 +404,12 @@ protected void launch(Builder contextBuilder) {
358404
System.exit(rc);
359405
}
360406

407+
private void setContextOptionIfUnset(Builder contextBuilder, String key, String value) {
408+
if (!givenArguments.contains("--" + key) && System.getProperty("polyglot." + key) == null) {
409+
contextBuilder.option(key, value);
410+
}
411+
}
412+
361413
private static void printFileNotFoundException(NoSuchFileException e) {
362414
String reason = e.getReason();
363415
if (reason == null) {

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
import java.util.List;
5252
import java.util.Map;
5353

54+
import org.graalvm.nativeimage.ImageInfo;
55+
5456
import com.oracle.graal.python.builtins.Builtin;
5557
import com.oracle.graal.python.builtins.CoreFunctions;
5658
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
@@ -59,9 +61,7 @@
5961
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
6062
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
6163
import com.oracle.graal.python.builtins.objects.list.PList;
62-
import com.oracle.graal.python.builtins.objects.module.PythonModule;
6364
import com.oracle.graal.python.builtins.objects.str.PString;
64-
import com.oracle.graal.python.nodes.SpecialAttributeNames;
6565
import com.oracle.graal.python.nodes.expression.CastToBooleanNode;
6666
import com.oracle.graal.python.nodes.expression.CastToListNode;
6767
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -70,8 +70,8 @@
7070
import com.oracle.graal.python.nodes.util.CastToStringNode;
7171
import com.oracle.graal.python.runtime.PosixResources;
7272
import com.oracle.graal.python.runtime.PythonContext;
73+
import com.oracle.graal.python.runtime.PythonOptions;
7374
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
74-
import com.oracle.truffle.api.TruffleOptions;
7575
import com.oracle.truffle.api.dsl.Cached;
7676
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
7777
import com.oracle.truffle.api.dsl.NodeFactory;
@@ -119,13 +119,13 @@ synchronized int forkExec(PList args, @SuppressWarnings("unused") PList execList
119119
// TODO: fix this better? sys.executable is often used in subprocess tests, but on Java
120120
// that actually gives you a whole cmdline, which we need to split up for process
121121
// builder
122-
PythonModule sysModule = getCore().lookupBuiltinModule("sys");
123-
if (!TruffleOptions.AOT && !argStrings.isEmpty() && argStrings.get(0).equals(sysModule.getAttribute("executable"))) {
124-
PList exec_list = (PList) sysModule.getAttribute(SpecialAttributeNames.GRAAL_PYTHON_EXECUTABLE_LIST);
125-
Object[] internalArray = exec_list.getSequenceStorage().getCopyOfInternalArray();
126-
argStrings.remove(0);
127-
for (int i = internalArray.length - 1; i >= 0; i--) {
128-
argStrings.add(0, (String) internalArray[i]);
122+
if (!ImageInfo.inImageCode() && !argStrings.isEmpty()) {
123+
if (argStrings.get(0).equals(PythonOptions.getOption(context, PythonOptions.Executable))) {
124+
String[] executableList = PythonOptions.getExecutableList();
125+
argStrings.remove(0);
126+
for (int i = executableList.length - 1; i >= 0; i--) {
127+
argStrings.add(0, executableList[i]);
128+
}
129129
}
130130
}
131131

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

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@
4545
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SIZEOF__;
4646

4747
import java.io.IOException;
48-
import java.lang.management.ManagementFactory;
4948
import java.nio.ByteOrder;
5049
import java.nio.charset.Charset;
51-
import java.util.ArrayList;
5250
import java.util.Date;
5351
import java.util.List;
5452

@@ -60,7 +58,6 @@
6058
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
6159
import com.oracle.graal.python.builtins.objects.ints.PInt;
6260
import com.oracle.graal.python.builtins.objects.str.PString;
63-
import com.oracle.graal.python.nodes.SpecialAttributeNames;
6461
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
6562
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.NoAttributeHandler;
6663
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -113,43 +110,6 @@ public void initialize(PythonCore core) {
113110
builtinConstants.put("byteorder", ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "little" : "big");
114111
builtinConstants.put("copyright", LICENSE);
115112
builtinConstants.put("dont_write_bytecode", true);
116-
117-
String executable = PythonOptions.getOption(core.getContext(), PythonOptions.ExecutablePath);
118-
if (TruffleOptions.AOT || !core.getContext().isExecutableAccessAllowed()) {
119-
// cannot set the path at this time since the binary is not yet known; will be patched
120-
// in the context
121-
builtinConstants.put("executable", PNone.NONE);
122-
builtinConstants.put(SpecialAttributeNames.GRAAL_PYTHON_EXECUTABLE_LIST, PNone.NONE);
123-
} else {
124-
StringBuilder sb = new StringBuilder();
125-
ArrayList<String> exec_list = new ArrayList<>();
126-
sb.append(System.getProperty("java.home")).append(PythonCore.FILE_SEPARATOR).append("bin").append(PythonCore.FILE_SEPARATOR).append("java");
127-
exec_list.add(sb.toString());
128-
sb.append(' ');
129-
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
130-
if (arg.matches("-Xrunjdwp:transport=dt_socket,server=y,address=\\d+,suspend=y")) {
131-
arg = arg.replace("suspend=y", "suspend=n");
132-
}
133-
sb.append(arg).append(' ');
134-
exec_list.add(arg);
135-
}
136-
sb.append("-classpath ");
137-
exec_list.add("-classpath");
138-
sb.append(System.getProperty("java.class.path")).append(' ');
139-
exec_list.add(System.getProperty("java.class.path"));
140-
// we really don't care what the main class or its arguments were - this should
141-
// always help us launch Graal.Python
142-
sb.append("com.oracle.graal.python.shell.GraalPythonMain");
143-
exec_list.add("com.oracle.graal.python.shell.GraalPythonMain");
144-
builtinConstants.put("executable", sb.toString());
145-
builtinConstants.put(SpecialAttributeNames.GRAAL_PYTHON_EXECUTABLE_LIST, core.factory().createList(exec_list.toArray()));
146-
}
147-
148-
String executable = PythonOptions.getOption(core.getContext(), PythonOptions.ExecutablePath);
149-
if (!executable.isEmpty() && core.getContext().isExecutableAccessAllowed()) {
150-
builtinConstants.put("executable", executable);
151-
}
152-
153113
builtinConstants.put("modules", core.factory().createDict());
154114
builtinConstants.put("path", core.factory().createList());
155115
builtinConstants.put("builtin_module_names", core.factory().createTuple(core.builtinModuleNames()));

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,4 @@ public abstract class SpecialAttributeNames {
7272
public static final String __DICTOFFSET__ = "__dictoffset__";
7373
public static final String __ITEMSIZE__ = "__itemsize__";
7474
public static final String __WEAKREF__ = "__weakref__";
75-
76-
// GraalPython specific
77-
public static final String GRAAL_PYTHON_EXECUTABLE_LIST = "graal_python_executable_list";
7875
}

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,14 @@
3636
import java.util.concurrent.atomic.AtomicLong;
3737
import java.util.concurrent.locks.ReentrantLock;
3838

39-
import org.graalvm.nativeimage.ImageInfo;
40-
import org.graalvm.nativeimage.ProcessProperties;
4139
import org.graalvm.options.OptionValues;
4240

4341
import com.oracle.graal.python.PythonLanguage;
44-
import com.oracle.graal.python.builtins.objects.PNone;
4542
import com.oracle.graal.python.builtins.objects.bytes.OpaqueBytes;
4643
import com.oracle.graal.python.builtins.objects.cext.NativeWrappers.PThreadState;
4744
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4845
import com.oracle.graal.python.builtins.objects.dict.PDict;
4946
import com.oracle.graal.python.builtins.objects.module.PythonModule;
50-
import com.oracle.graal.python.nodes.SpecialAttributeNames;
5147
import com.oracle.graal.python.runtime.exception.PException;
5248
import com.oracle.truffle.api.Assumption;
5349
import com.oracle.truffle.api.CallTarget;
@@ -238,12 +234,7 @@ public void patch(Env newEnv) {
238234

239235
private void setupRuntimeInformation() {
240236
PythonModule sysModule = core.initializeSysModule();
241-
if (ImageInfo.inImageRuntimeCode() && isExecutableAccessAllowed()) {
242-
if (sysModule.getAttribute("executable") == PNone.NONE) {
243-
sysModule.setAttribute("executable", ProcessProperties.getExecutableName());
244-
}
245-
sysModule.setAttribute(SpecialAttributeNames.GRAAL_PYTHON_EXECUTABLE_LIST, core.factory().createList(new Object[]{ProcessProperties.getExecutableName()}));
246-
}
237+
sysModule.setAttribute("executable", PythonOptions.getOption(core.getContext(), PythonOptions.Executable));
247238
sysModules = (PDict) sysModule.getAttribute("modules");
248239
builtinsModule = (PythonModule) sysModules.getItem("builtins");
249240
mainModule = core.factory().createPythonModule(__MAIN__);

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
@Option.Group(PythonLanguage.ID)
3737
public final class PythonOptions {
38+
private static final String EXECUTABLE_LIST_SEPARATOR = "🏆";
3839

3940
private PythonOptions() {
4041
// no instances
@@ -128,8 +129,11 @@ private PythonOptions() {
128129
@Option(category = OptionCategory.EXPERT, help = "Set by the launcher to the terminal height.") //
129130
public static final OptionKey<Integer> TerminalHeight = new OptionKey<>(25);
130131

131-
@Option(category = OptionCategory.EXPERT, help = "The sys.executable path") //
132-
public static final OptionKey<String> ExecutablePath = new OptionKey<>("");
132+
@Option(category = OptionCategory.EXPERT, help = "The sys.executable path. Set by the launcher, but can may need to be overridden in certain special situations.") //
133+
public static final OptionKey<String> Executable = new OptionKey<>("");
134+
135+
@Option(category = OptionCategory.EXPERT, help = "The executed command list as string joined by the executable list separator char. This must always correspond to the real, valid command list used to run GraalPython.") //
136+
public static final OptionKey<String> ExecutableList = new OptionKey<>("");
133137

134138
public static OptionDescriptors createDescriptors() {
135139
return new PythonOptionsOptionDescriptors();
@@ -190,4 +194,9 @@ public static int getTerminalHeight() {
190194
public static int getTerminalWidth() {
191195
return getOption(PythonLanguage.getContextRef().get(), TerminalWidth);
192196
}
197+
198+
@TruffleBoundary
199+
public static String[] getExecutableList() {
200+
return getOption(PythonLanguage.getContextRef().get(), ExecutableList).split(EXECUTABLE_LIST_SEPARATOR);
201+
}
193202
}

graalpython/lib-python/3/venv/__init__.py

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __init__(self, system_site_packages=False, clear=False,
4848
self.with_pip = with_pip
4949
# Truffle change
5050
if with_pip:
51-
logger.warning("Not install pip on GraalPython")
51+
logger.warning("We cannot install pip on Graal Python, yet")
5252
self.with_pip = False
5353
# End Truffle change
5454
self.prompt = prompt
@@ -119,45 +119,37 @@ def create_if_needed(d):
119119
context.python_dir = dirname
120120
context.python_exe = exename
121121

122-
# Truffle change: when our executable is not just a file (e.g. we're
123-
# running through java), we need to provide a script for launching
124-
if hasattr(sys, "executable_list"):
125-
# our 'sys.executable' is really a full commandline, so we need a
126-
# script to launch it
127-
import atexit, tempfile
128-
tempdir = tempfile.mkdtemp()
129-
script = os.path.join(tempdir, "graalpython")
130-
if sys.platform == 'win32':
131-
script += ".bat"
132-
133-
with open(script, "w") as f:
134-
if sys.platform != "win32":
135-
f.write("#!/bin/sh\n")
136-
for s in sys.executable_list:
137-
if " " in s:
138-
f.write('"')
139-
f.write(s.replace('"', '\\"'))
140-
f.write('" ')
141-
else:
142-
f.write(s)
143-
f.write(" ")
144-
if sys.platform == "win32":
145-
f.write("%*")
146-
else:
147-
f.write("$@")
122+
# Truffle change: our executable may not just be a file (e.g. we're
123+
# running through java), we always provide a script for launching in
124+
# venv
125+
import atexit, tempfile
126+
tempdir = tempfile.mkdtemp()
127+
script = os.path.join(tempdir, "graalpython")
128+
if sys.platform == 'win32':
129+
script += ".bat"
148130

131+
with open(script, "w") as f:
149132
if sys.platform != "win32":
150-
os.chmod(script, 0o777)
133+
f.write("#!/bin/sh\n")
134+
f.write(sys.executable)
135+
if sys.platform == "win32":
136+
f.write(" %*")
137+
else:
138+
f.write(" $@")
139+
140+
if sys.platform != "win32":
141+
os.chmod(script, 0o777)
151142

152-
atexit.register(lambda: os.unlink(script))
153-
atexit.register(lambda: os.rmdir(tempdir))
143+
atexit.register(lambda: os.unlink(script))
144+
atexit.register(lambda: os.rmdir(tempdir))
154145

155-
dirname, exename = context.python_dir, context.python_exe = sys.graal_python_home, "graalpython"
156-
context.executable = script
146+
dirname = context.python_dir = sys.graal_python_home
147+
exename = context.python_exe = "graalpython"
148+
context.executable = script
157149

158-
if self.symlinks:
159-
logger.warning("We're not using symlinks on GraalPython")
160-
self.symlinks = False
150+
if self.symlinks:
151+
logger.warning("We're not using symlinks in a Graal Python venv")
152+
self.symlinks = False
161153
# End of Truffle change
162154

163155
if sys.platform == 'win32':
@@ -335,7 +327,7 @@ def replace_variables(self, text, context):
335327
# Truffle change: we need to set some extra options for the launcher to work
336328
text = text.replace(
337329
'__VENV_GRAAL_PYTHON_OPTIONS__',
338-
"--python.CoreHome='%s' --python.StdLibHome='%s' --python.ExecutablePath='%s'" % (
330+
"--python.CoreHome='%s' --python.StdLibHome='%s' --python.Executable='%s'" % (
339331
sys.graal_python_core_home,
340332
sys.graal_python_stdlib_home,
341333
context.env_exe,

0 commit comments

Comments
 (0)