1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 11:22:24 +01:00

Optimize subprocess performance with close_fds=False

This commit is contained in:
J. Nick Koston
2025-08-08 16:18:04 -05:00
parent ff9ddb9d68
commit 3ded96bb26
12 changed files with 40 additions and 13 deletions

View File

@@ -90,7 +90,7 @@ def main():
def run_command(*cmd, ignore_error: bool = False): def run_command(*cmd, ignore_error: bool = False):
print(f"$ {shlex.join(list(cmd))}") print(f"$ {shlex.join(list(cmd))}")
if not args.dry_run: if not args.dry_run:
rc = subprocess.call(list(cmd)) rc = subprocess.call(list(cmd), close_fds=False)
if rc != 0 and not ignore_error: if rc != 0 and not ignore_error:
print("Command failed") print("Command failed")
sys.exit(1) sys.exit(1)

View File

@@ -36,7 +36,9 @@ def get_sdl_options(value):
if value != "": if value != "":
return value return value
try: try:
return subprocess.check_output(["sdl2-config", "--cflags", "--libs"]).decode() return subprocess.check_output(
["sdl2-config", "--cflags", "--libs"], close_fds=False
).decode()
except Exception as e: except Exception as e:
raise cv.Invalid("Unable to run sdl2-config - have you installed sdl2?") from e raise cv.Invalid("Unable to run sdl2-config - have you installed sdl2?") from e

View File

@@ -229,6 +229,7 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
close_fds=False,
) )
stdout_thread = threading.Thread(target=self._stdout_thread) stdout_thread = threading.Thread(target=self._stdout_thread)
stdout_thread.daemon = True stdout_thread.daemon = True

View File

@@ -17,7 +17,9 @@ _LOGGER = logging.getLogger(__name__)
def run_git_command(cmd, cwd=None) -> str: def run_git_command(cmd, cwd=None) -> str:
_LOGGER.debug("Running git command: %s", " ".join(cmd)) _LOGGER.debug("Running git command: %s", " ".join(cmd))
try: try:
ret = subprocess.run(cmd, cwd=cwd, capture_output=True, check=False) ret = subprocess.run(
cmd, cwd=cwd, capture_output=True, check=False, close_fds=False
)
except FileNotFoundError as err: except FileNotFoundError as err:
raise cv.Invalid( raise cv.Invalid(
"git is not installed but required for external_components.\n" "git is not installed but required for external_components.\n"

View File

@@ -114,7 +114,9 @@ def cpp_string_escape(string, encoding="utf-8"):
def run_system_command(*args): def run_system_command(*args):
import subprocess import subprocess
with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: with subprocess.Popen(
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False
) as p:
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
rc = p.returncode rc = p.returncode
return rc, stdout, stderr return rc, stdout, stderr

View File

@@ -211,7 +211,7 @@ def _decode_pc(config, addr):
return return
command = [idedata.addr2line_path, "-pfiaC", "-e", idedata.firmware_elf_path, addr] command = [idedata.addr2line_path, "-pfiaC", "-e", idedata.firmware_elf_path, addr]
try: try:
translation = subprocess.check_output(command).decode().strip() translation = subprocess.check_output(command, close_fds=False).decode().strip()
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.debug("Caught exception for command %s", command, exc_info=1) _LOGGER.debug("Caught exception for command %s", command, exc_info=1)
return return

View File

@@ -239,7 +239,12 @@ def run_external_process(*cmd: str, **kwargs: Any) -> int | str:
try: try:
proc = subprocess.run( proc = subprocess.run(
cmd, stdout=sub_stdout, stderr=sub_stderr, encoding="utf-8", check=False cmd,
stdout=sub_stdout,
stderr=sub_stderr,
encoding="utf-8",
check=False,
close_fds=False,
) )
return proc.stdout if capture_stdout else proc.returncode return proc.stdout if capture_stdout else proc.returncode
except KeyboardInterrupt: # pylint: disable=try-except-raise except KeyboardInterrupt: # pylint: disable=try-except-raise

View File

@@ -31,7 +31,11 @@ def run_format(executable, args, queue, lock, failed_files):
invocation.append(path) invocation.append(path)
proc = subprocess.run( proc = subprocess.run(
invocation, capture_output=True, encoding="utf-8", check=False invocation,
capture_output=True,
encoding="utf-8",
check=False,
close_fds=False,
) )
if proc.returncode != 0: if proc.returncode != 0:
with lock: with lock:

View File

@@ -158,7 +158,11 @@ def run_tidy(executable, args, options, tmpdir, path_queue, lock, failed_files):
invocation.extend(options) invocation.extend(options)
proc = subprocess.run( proc = subprocess.run(
invocation, capture_output=True, encoding="utf-8", check=False invocation,
capture_output=True,
encoding="utf-8",
check=False,
close_fds=False,
) )
if proc.returncode != 0: if proc.returncode != 0:
with lock: with lock:
@@ -320,9 +324,11 @@ def main():
print("Applying fixes ...") print("Applying fixes ...")
try: try:
try: try:
subprocess.call(["clang-apply-replacements-18", tmpdir]) subprocess.call(
["clang-apply-replacements-18", tmpdir], close_fds=False
)
except FileNotFoundError: except FileNotFoundError:
subprocess.call(["clang-apply-replacements", tmpdir]) subprocess.call(["clang-apply-replacements", tmpdir], close_fds=False)
except FileNotFoundError: except FileNotFoundError:
print( print(
"Error please install clang-apply-replacements-18 or clang-apply-replacements.\n", "Error please install clang-apply-replacements-18 or clang-apply-replacements.\n",

View File

@@ -55,4 +55,6 @@ for section in config.sections():
tools.append("-t") tools.append("-t")
tools.append(tool) tools.append(tool)
subprocess.check_call(["platformio", "pkg", "install", "-g", *libs, *platforms, *tools]) subprocess.check_call(
["platformio", "pkg", "install", "-g", *libs, *platforms, *tools], close_fds=False
)

View File

@@ -13,7 +13,7 @@ def find_and_activate_virtualenv():
try: try:
# Get the top-level directory of the git repository # Get the top-level directory of the git repository
my_path = subprocess.check_output( my_path = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], text=True ["git", "rev-parse", "--show-toplevel"], text=True, close_fds=False
).strip() ).strip()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print( print(
@@ -44,7 +44,7 @@ def find_and_activate_virtualenv():
def run_command(): def run_command():
# Execute the remaining arguments in the new environment # Execute the remaining arguments in the new environment
if len(sys.argv) > 1: if len(sys.argv) > 1:
subprocess.run(sys.argv[1:], check=False) subprocess.run(sys.argv[1:], check=False, close_fds=False)
else: else:
print( print(
"No command provided to run in the virtual environment.", "No command provided to run in the virtual environment.",

View File

@@ -105,6 +105,7 @@ logger:
check=True, check=True,
cwd=init_dir, cwd=init_dir,
env=env, env=env,
close_fds=False,
) )
# Lock is held until here, ensuring cache is fully populated before any test proceeds # Lock is held until here, ensuring cache is fully populated before any test proceeds
@@ -245,6 +246,7 @@ async def compile_esphome(
# Start in a new process group to isolate signal handling # Start in a new process group to isolate signal handling
start_new_session=True, start_new_session=True,
env=env, env=env,
close_fds=False,
) )
await proc.wait() await proc.wait()
@@ -477,6 +479,7 @@ async def run_binary_and_wait_for_port(
# Start in a new process group to isolate signal handling # Start in a new process group to isolate signal handling
start_new_session=True, start_new_session=True,
pass_fds=(device_fd,), pass_fds=(device_fd,),
close_fds=False,
) )
# Close the device end in the parent process # Close the device end in the parent process