Skip to content

Commit 3cfb6bc

Browse files
committed
Set sys._base_executable properly
1 parent 69f82a4 commit 3cfb6bc

File tree

3 files changed

+49
-22
lines changed

3 files changed

+49
-22
lines changed

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

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -872,13 +872,14 @@ private static String toAbsolutePath(String executable) {
872872

873873
// Rough equivalent of CPython's pyvenv.cfg logic in Modules/getpath.py
874874
private void findAndApplyVenvCfg(Builder contextBuilder, String executable) {
875-
Path binDir;
875+
Path executablePath;
876876
try {
877-
binDir = Paths.get(executable).getParent();
877+
executablePath = Paths.get(executable);
878878
} catch (InvalidPathException e) {
879-
log("cannot determine the parent directory of the executable");
879+
log("cannot determine path of the executable");
880880
return;
881881
}
882+
Path binDir = executablePath.getParent();
882883
if (binDir == null) {
883884
log("parent directory of the executable does not exist");
884885
return;
@@ -906,23 +907,44 @@ private void findAndApplyVenvCfg(Builder contextBuilder, String executable) {
906907
}
907908
String name = parts[0].trim();
908909
if (name.equals("home")) {
909-
Path homeProperty = Paths.get(parts[1].trim());
910-
/*
911-
* (tfel): According to PEP 405, the home key is the directory of the Python
912-
* executable from which this virtual environment was created, that is, it
913-
* usually ends with "/bin" on a Unix system. On Windows, the base Python should
914-
* be in the top-level directory or under "\Scripts". To support running from
915-
* Maven artifacts where we don't have a working executable, we patched our
916-
* shipped venv module to set the home path without a "/bin" or "\\Scripts"
917-
* suffix, so we explicitly check for those two subfolder cases and otherwise
918-
* assume the home key is directly pointing to the Python home.
919-
*/
920-
if (homeProperty.endsWith("bin") || homeProperty.endsWith("Scripts")) {
921-
homeProperty = homeProperty.getParent();
922-
}
923910
try {
924-
contextBuilder.option("python.PythonHome", homeProperty.toString());
925-
} catch (NullPointerException ex) {
911+
Path homeProperty = Paths.get(parts[1].trim());
912+
Path graalpyHome = homeProperty;
913+
/*
914+
* (tfel): According to PEP 405, the home key is the directory of the Python
915+
* executable from which this virtual environment was created, that is, it
916+
* usually ends with "/bin" on a Unix system. On Windows, the base Python
917+
* should be in the top-level directory or under "\Scripts". To support
918+
* running from Maven artifacts where we don't have a working executable, we
919+
* patched our shipped venv module to set the home path without a "/bin" or
920+
* "\\Scripts" suffix, so we explicitly check for those two subfolder cases
921+
* and otherwise assume the home key is directly pointing to the Python
922+
* home.
923+
*/
924+
if (graalpyHome.endsWith("bin") || graalpyHome.endsWith("Scripts")) {
925+
graalpyHome = graalpyHome.getParent();
926+
}
927+
contextBuilder.option("python.PythonHome", graalpyHome.toString());
928+
/*
929+
* First try to resolve symlinked executables, since that may be more
930+
* accurate than assuming the executable in 'home'.
931+
*/
932+
Path baseExecutable = null;
933+
try {
934+
Path realPath = executablePath.toRealPath();
935+
if (!realPath.equals(executablePath.toAbsolutePath())) {
936+
baseExecutable = realPath;
937+
}
938+
} catch (IOException ex) {
939+
// Ignore
940+
}
941+
if (baseExecutable == null) {
942+
baseExecutable = homeProperty.resolve(executablePath.getFileName());
943+
}
944+
if (Files.exists(baseExecutable)) {
945+
contextBuilder.option("python.BaseExecutable", baseExecutable.toString());
946+
}
947+
} catch (NullPointerException | InvalidPathException ex) {
926948
// NullPointerException covers the possible null result of getParent()
927949
warn("Could not set PYTHONHOME according to the pyvenv.cfg file.");
928950
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,8 +655,10 @@ public void postInitialize0(Python3Core core) {
655655
TruffleString capiHome = context.getCAPIHome();
656656

657657
if (!ImageInfo.inImageBuildtimeCode()) {
658-
sys.setAttribute(tsLiteral("executable"), context.getOption(PythonOptions.Executable));
659-
sys.setAttribute(tsLiteral("_base_executable"), context.getOption(PythonOptions.Executable));
658+
TruffleString executable = context.getOption(PythonOptions.Executable);
659+
TruffleString baseExecutable = context.getOption(PythonOptions.BaseExecutable);
660+
sys.setAttribute(tsLiteral("executable"), executable);
661+
sys.setAttribute(tsLiteral("_base_executable"), baseExecutable.isEmpty() ? executable : baseExecutable);
660662
}
661663
sys.setAttribute(tsLiteral("dont_write_bytecode"), context.getOption(PythonOptions.DontWriteBytecodeFlag));
662664
TruffleString pycachePrefix = context.getOption(PythonOptions.PyCachePrefix);

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,12 @@ private PythonOptions() {
315315
@Option(category = OptionCategory.EXPERT, usageSyntax = "<height>", help = "Set by the launcher to the terminal height.") //
316316
public static final OptionKey<Integer> TerminalHeight = new OptionKey<>(25);
317317

318-
@Option(category = OptionCategory.EXPERT, usageSyntax = "<path>", help = "The sys.executable path. Set by the launcher, but can may need to be overridden in certain special situations.", stability = OptionStability.STABLE) //
318+
@Option(category = OptionCategory.EXPERT, usageSyntax = "<path>", help = "The sys.executable path. Set by the launcher, but may need to be overridden in certain special situations.", stability = OptionStability.STABLE) //
319319
public static final OptionKey<TruffleString> Executable = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
320320

321+
@Option(category = OptionCategory.EXPERT, usageSyntax = "<path>", help = "The sys._base_executable path. Set by the launcher, but may need to be overridden in certain special situations.", stability = OptionStability.STABLE) //
322+
public static final OptionKey<TruffleString> BaseExecutable = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);
323+
321324
@Option(category = OptionCategory.EXPERT, usageSyntax = "<cmdPart>[" + J_STRING_LIST_SEPARATOR +
322325
"<cmdPart>]", 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 GraalPy.") //
323326
public static final OptionKey<TruffleString> ExecutableList = new OptionKey<>(T_EMPTY_STRING, TS_OPTION_TYPE);

0 commit comments

Comments
 (0)