From 34cf27f2ebc80bcd20c381d0c81cc1da6423cccc Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Wed, 22 Jan 2025 13:33:48 -0500 Subject: [PATCH] TEMP: remove log files if interrupted via Ctrl-C This is not how it should be! it would depend on when it is interrupted and what was the actual process return code. Immediate usecase is my impatience with current implementation still waiting whenever command such as "ls" is done already. Then when I interrupt with Ctrl-C we should still dump all the results and proceed to treat on how we would treat if command failed or not. It would also relate to - https://github.com/con/duct/issues/184 so if Ctrl-C the child process -- it is non-0 exit, and we would treat as "failed run". So we should add it more smartly than what I did here --- src/con_duct/__main__.py | 177 ++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/src/con_duct/__main__.py b/src/con_duct/__main__.py index 843a130..f3596a4 100755 --- a/src/con_duct/__main__.py +++ b/src/con_duct/__main__.py @@ -1021,93 +1021,102 @@ def execute(args: Arguments) -> int: files_to_close.append(report.usage_file) report.start_time = time.time() - try: - report.process = process = subprocess.Popen( - [str(args.command)] + args.command_args, - stdout=stdout_file, - stderr=stderr_file, - start_new_session=True, - ) - except FileNotFoundError: - # We failed to execute due to file not found in PATH - # We should remove log etc files since they are 0-sized - # degenerates etc - safe_close_files(files_to_close) - remove_files(log_paths, assert_empty=True) - # mimicking behavior of bash and zsh. - print(f"{args.command}: command not found", file=sys.stderr) - return 127 # seems what zsh and bash return then - - lgr.info("duct is executing %r...", full_command) - lgr.info("Log files will be written to %s", log_paths.prefix) - try: - report.session_id = os.getsid(process.pid) # Get session ID of the new process - except ProcessLookupError: # process has already finished - # TODO: log this at least. - pass - stop_event = threading.Event() - if args.record_types.has_processes_samples(): - monitoring_args = [ - report, - process, - args.report_interval, - args.sample_interval, - stop_event, - ] - monitoring_thread = threading.Thread( - target=monitor_process, args=monitoring_args - ) - monitoring_thread.start() - else: - monitoring_thread = None + try: # Ctrl-C handling + try: + report.process = process = subprocess.Popen( + [str(args.command)] + args.command_args, + stdout=stdout_file, + stderr=stderr_file, + start_new_session=True, + ) + except FileNotFoundError: + # We failed to execute due to file not found in PATH + # We should remove log etc files since they are 0-sized + # degenerates etc + safe_close_files(files_to_close) + remove_files(log_paths, assert_empty=True) + # mimicking behavior of bash and zsh. + print(f"{args.command}: command not found", file=sys.stderr) + return 127 # seems what zsh and bash return then + + lgr.info("duct is executing %r...", full_command) + lgr.info("Log files will be written to %s", log_paths.prefix) + try: + report.session_id = os.getsid( + process.pid + ) # Get session ID of the new process + except ProcessLookupError: # process has already finished + # TODO: log this at least. + pass + stop_event = threading.Event() + if args.record_types.has_processes_samples(): + monitoring_args = [ + report, + process, + args.report_interval, + args.sample_interval, + stop_event, + ] + monitoring_thread = threading.Thread( + target=monitor_process, args=monitoring_args + ) + monitoring_thread.start() + else: + monitoring_thread = None - if args.record_types.has_system_summary(): - env_thread = threading.Thread(target=report.collect_environment) - env_thread.start() - sys_info_thread = threading.Thread(target=report.get_system_info) - sys_info_thread.start() - else: - env_thread, sys_info_thread = None, None - - process.wait() - report.end_time = time.time() - lgr.debug("Process ended, setting stop_event to stop monitoring thread") - stop_event.set() - if monitoring_thread is not None: - lgr.debug("Waiting for monitoring thread to finish") - monitoring_thread.join() - lgr.debug("Monitoring thread finished") - - # If we have any extra samples that haven't been written yet, do it now - if report.current_sample is not None: - report.write_subreport() - - report.process = process - if env_thread is not None: - lgr.debug("Waiting for environment collection thread to finish") - env_thread.join() - lgr.debug("Environment collection finished") - - if sys_info_thread is not None: - lgr.debug("Waiting for system information collection thread to finish") - sys_info_thread.join() - lgr.debug("System information collection finished") - - if args.record_types.has_system_summary(): - with open(log_paths.info, "w") as system_logs: - report.run_time_seconds = f"{report.end_time - report.start_time}" - system_logs.write(report.dump_json()) - safe_close_files(files_to_close) - if process.returncode != 0 and ( - report.elapsed_time < args.fail_time or args.fail_time < 0 - ): - lgr.info( - "Removing log files since command failed%s.", - f" in less than {args.fail_time} seconds" if args.fail_time > 0 else "", - ) + if args.record_types.has_system_summary(): + env_thread = threading.Thread(target=report.collect_environment) + env_thread.start() + sys_info_thread = threading.Thread(target=report.get_system_info) + sys_info_thread.start() + else: + env_thread, sys_info_thread = None, None + + process.wait() + report.end_time = time.time() + lgr.debug("Process ended, setting stop_event to stop monitoring thread") + stop_event.set() + if monitoring_thread is not None: + lgr.debug("Waiting for monitoring thread to finish") + monitoring_thread.join() + lgr.debug("Monitoring thread finished") + + # If we have any extra samples that haven't been written yet, do it now + if report.current_sample is not None: + report.write_subreport() + + report.process = process + if env_thread is not None: + lgr.debug("Waiting for environment collection thread to finish") + env_thread.join() + lgr.debug("Environment collection finished") + + if sys_info_thread is not None: + lgr.debug("Waiting for system information collection thread to finish") + sys_info_thread.join() + lgr.debug("System information collection finished") + + if args.record_types.has_system_summary(): + with open(log_paths.info, "w") as system_logs: + report.run_time_seconds = f"{report.end_time - report.start_time}" + system_logs.write(report.dump_json()) + safe_close_files(files_to_close) + except KeyboardInterrupt: + lgr.info("Received Ctrl-C, removing log files") + safe_close_files(files_to_close) remove_files(log_paths) + raise else: - lgr.info(report.execution_summary_formatted) + if process.returncode != 0 and ( + report.elapsed_time < args.fail_time or args.fail_time < 0 + ): + lgr.info( + "Removing log files since command failed%s.", + f" in less than {args.fail_time} seconds" if args.fail_time > 0 else "", + ) + remove_files(log_paths) + else: + lgr.info(report.execution_summary_formatted) return report.process.returncode