22import logging
33import os
44import subprocess
5+ import shlex
56import sys
67from typing import Any , Dict , List , Optional
78
@@ -48,11 +49,11 @@ def exec_worker(self, passthrough_args: List[str], language: Language):
4849 update_envs (self .env_vars )
4950
5051 if language == Language .PYTHON and sys .platform == "win32" :
51- executable = self .py_executable
52+ executable = [ self .py_executable ]
5253 elif language == Language .PYTHON :
53- executable = f "exec { self .py_executable } "
54+ executable = [ "exec" , self .py_executable ]
5455 elif language == Language .JAVA :
55- executable = "java"
56+ executable = [ "java" ]
5657 ray_jars = os .path .join (get_ray_jars_dir (), "*" )
5758
5859 local_java_jars = []
@@ -63,9 +64,9 @@ def exec_worker(self, passthrough_args: List[str], language: Language):
6364 class_path_args = ["-cp" , ray_jars + ":" + str (":" .join (local_java_jars ))]
6465 passthrough_args = class_path_args + passthrough_args
6566 elif sys .platform == "win32" :
66- executable = ""
67+ executable = []
6768 else :
68- executable = "exec "
69+ executable = [ "exec" ]
6970
7071 # By default, raylet uses the path to default_worker.py on host.
7172 # However, the path to default_worker.py inside the container
@@ -79,29 +80,29 @@ def exec_worker(self, passthrough_args: List[str], language: Language):
7980 )
8081 passthrough_args [0 ] = default_worker_path
8182
82- passthrough_args = [s .replace (" " , r"\ " ) for s in passthrough_args ]
83- exec_command = " " .join ([f"{ executable } " ] + passthrough_args )
84- command_str = " " .join (self .command_prefix + [exec_command ])
85- # TODO(SongGuyang): We add this env to command for macOS because it doesn't
86- # work for the C++ process of `os.execvp`. We should find a better way to
87- # fix it.
88- MACOS_LIBRARY_PATH_ENV_NAME = "DYLD_LIBRARY_PATH"
89- if MACOS_LIBRARY_PATH_ENV_NAME in os .environ :
90- command_str = (
91- MACOS_LIBRARY_PATH_ENV_NAME
92- + "="
93- + os .environ .get (MACOS_LIBRARY_PATH_ENV_NAME )
94- + " "
95- + command_str
96- )
97- logger .debug (f"Exec'ing worker with command: { command_str } " )
9883 if sys .platform == "win32" :
99- cmd = [* self .command_prefix , executable , * passthrough_args ]
84+ cmd = [* self .command_prefix , * executable , * passthrough_args ]
85+ logger .debug (f"Exec'ing worker with command: { cmd } " )
10086 subprocess .Popen (cmd , shell = True ).wait ()
10187 else :
88+ # We use shlex to do the necessary shell escape
89+ # of special characters in passthrough_args.
90+ passthrough_args = [shlex .quote (s ) for s in passthrough_args ]
91+ cmd = [* self .command_prefix , * executable , * passthrough_args ]
92+ # TODO(SongGuyang): We add this env to command for macOS because it doesn't
93+ # work for the C++ process of `os.execvp`. We should find a better way to
94+ # fix it.
95+ MACOS_LIBRARY_PATH_ENV_NAME = "DYLD_LIBRARY_PATH"
96+ if MACOS_LIBRARY_PATH_ENV_NAME in os .environ :
97+ cmd .insert (
98+ 0 ,
99+ f"{ MACOS_LIBRARY_PATH_ENV_NAME } ="
100+ f"{ os .environ [MACOS_LIBRARY_PATH_ENV_NAME ]} " ,
101+ )
102+ logger .debug (f"Exec'ing worker with command: { cmd } " )
102103 # PyCharm will monkey patch the os.execvp at
103104 # .pycharm_helpers/pydev/_pydev_bundle/pydev_monkey.py
104105 # The monkey patched os.execvp function has a different
105106 # signature. So, we use os.execvp("executable", args=[])
106107 # instead of os.execvp(file="executable", args=[])
107- os .execvp ("bash" , args = ["bash" , "-c" , command_str ])
108+ os .execvp ("bash" , args = ["bash" , "-c" , " " . join ( cmd ) ])
0 commit comments