3939import java .util .Set ;
4040
4141import org .graalvm .launcher .AbstractLanguageLauncher ;
42+ import org .graalvm .nativeimage .ImageInfo ;
4243import org .graalvm .nativeimage .ProcessProperties ;
4344import org .graalvm .options .OptionCategory ;
4445import org .graalvm .polyglot .Context ;
@@ -72,11 +73,15 @@ 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 > arguments , Map <String , String > polyglotOptions ) {
79+ protected List <String > preprocessArguments (List <String > givenArgs , Map <String , String > polyglotOptions ) {
7880 ArrayList <String > unrecognized = new ArrayList <>();
79- List <String > inputArgs = new ArrayList <>(arguments );
81+ ArrayList <String > inputArgs = new ArrayList <>(getDefaultEnvironmentArgs ());
82+ inputArgs .addAll (givenArgs );
83+ givenArguments = new ArrayList <>(inputArgs );
84+ List <String > arguments = new ArrayList <>(inputArgs );
8085 List <String > subprocessArgs = new ArrayList <>();
8186 programArgs = new ArrayList <>();
8287 for (int i = 0 ; i < arguments .size (); i ++) {
@@ -201,6 +206,60 @@ protected List<String> preprocessArguments(List<String> arguments, Map<String, S
201206 return unrecognized ;
202207 }
203208
209+ private static enum State {
210+ NORMAL ,
211+ SINGLE_QUOTE ,
212+ DOUBLE_QUOTE ,
213+ ESCAPE_SINGLE_QUOTE ,
214+ ESCAPE_DOUBLE_QUOTE ,
215+ }
216+
217+ private static List <String > getDefaultEnvironmentArgs () {
218+ String envArgsOpt = System .getenv ("GRAAL_PYTHON_OPTIONS" );
219+ ArrayList <String > envArgs = new ArrayList <>();
220+ State s = State .NORMAL ;
221+ StringBuilder sb = new StringBuilder ();
222+ if (envArgsOpt != null ) {
223+ for (char x : envArgsOpt .toCharArray ()) {
224+ if (s == State .NORMAL && Character .isWhitespace (x )) {
225+ if (sb .length () > 0 ) {
226+ envArgs .add (sb .toString ());
227+ sb .setLength (0 );
228+ }
229+ } else {
230+ if (x == '"' ) {
231+ if (s == State .NORMAL ) {
232+ s = State .DOUBLE_QUOTE ;
233+ } else if (s == State .DOUBLE_QUOTE ) {
234+ s = State .NORMAL ;
235+ } else if (s == State .ESCAPE_DOUBLE_QUOTE ) {
236+ s = State .DOUBLE_QUOTE ;
237+ sb .append (x );
238+ }
239+ } else if (x == '\'' ) {
240+ if (s == State .NORMAL ) {
241+ s = State .SINGLE_QUOTE ;
242+ } else if (s == State .SINGLE_QUOTE ) {
243+ s = State .NORMAL ;
244+ } else if (s == State .ESCAPE_SINGLE_QUOTE ) {
245+ s = State .SINGLE_QUOTE ;
246+ sb .append (x );
247+ }
248+ } else if (x == '\\' ) {
249+ if (s == State .SINGLE_QUOTE ) {
250+ s = State .ESCAPE_SINGLE_QUOTE ;
251+ } else if (s == State .DOUBLE_QUOTE ) {
252+ s = State .ESCAPE_DOUBLE_QUOTE ;
253+ }
254+ } else {
255+ sb .append (x );
256+ }
257+ }
258+ }
259+ }
260+ return envArgs ;
261+ }
262+
204263 private static void printShortHelp () {
205264 print ("usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...\n " +
206265 "Try `python -h' for more information." );
@@ -210,6 +269,43 @@ private static void print(String string) {
210269 System .out .println (string );
211270 }
212271
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+ String [] executableList = getExecutableList ();
300+ for (int i = 0 ; i < executableList .length ; i ++) {
301+ if (executableList [i ].matches ("\\ s" )) {
302+ executableList [i ] = "'" + executableList [i ].replace ("'" , "\\ '" ) + "'" ;
303+ }
304+ }
305+ return String .join (" " , executableList );
306+ }
307+ }
308+
213309 @ Override
214310 protected void launch (Builder contextBuilder ) {
215311 if (runLLI ) {
@@ -233,7 +329,11 @@ protected void launch(Builder contextBuilder) {
233329 noUserSite = noUserSite || System .getenv ("PYTHONNOUSERSITE" ) != null ;
234330 verboseFlag = verboseFlag || System .getenv ("PYTHONVERBOSE" ) != null ;
235331 }
236- 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 ());
237337
238338 // setting this to make sure our TopLevelExceptionHandler calls the excepthook
239339 // to print Python exceptions
@@ -246,6 +346,9 @@ protected void launch(Builder contextBuilder) {
246346 contextBuilder .option ("python.QuietFlag" , Boolean .toString (quietFlag ));
247347 contextBuilder .option ("python.NoUserSiteFlag" , Boolean .toString (noUserSite ));
248348 contextBuilder .option ("python.NoSiteFlag" , Boolean .toString (noSite ));
349+ contextBuilder .option ("python.IgnoreEnvironmentFlag" , Boolean .toString (ignoreEnv ));
350+
351+ sulongLibraryPath = System .getenv ("SULONG_LIBRARY_PATH" );
249352 if (sulongLibraryPath != null ) {
250353 contextBuilder .option ("llvm.libraryPath" , sulongLibraryPath );
251354 }
@@ -302,6 +405,18 @@ protected void launch(Builder contextBuilder) {
302405 System .exit (rc );
303406 }
304407
408+ private void setContextOptionIfUnset (Builder contextBuilder , String key , String value ) {
409+ if (System .getProperty ("polyglot." + key ) != null ) {
410+ return ;
411+ }
412+ for (String f : givenArguments ) {
413+ if (f .startsWith ("--" + key )) {
414+ return ;
415+ }
416+ }
417+ contextBuilder .option (key , value );
418+ }
419+
305420 private static void printFileNotFoundException (NoSuchFileException e ) {
306421 String reason = e .getReason ();
307422 if (reason == null ) {
@@ -423,7 +538,10 @@ protected void printHelp(OptionCategory maxCategory) {
423538 " str, bytes and datetime objects. It can also be set to an integer\n " +
424539 " in the range [0,4294967295] to get hash values with a predictable seed.\n " +
425540 "SULONG_LIBRARY_PATH: Specifies the library path for Sulong.\n " +
426- " This is required when starting subprocesses of python." );
541+ " This is required when starting subprocesses of python.\n " +
542+ "GRAAL_PYTHON_OPTIONS: This environment variable can include default options that\n " +
543+ " are always passed to the launcher. These are not shell expanded and given to\n " +
544+ " the launcher as-is." );
427545 if (maxCategory .compareTo (OptionCategory .DEBUG ) >= 0 ) {
428546 print ("\n GraalPython performance debugging options:\n " +
429547 "-debug-perf : Enable tracing of Truffle compilations and its warnings\n " +
0 commit comments