From dc827d1b04386b6af2799546ccaed6492c50d2d0 Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:15:54 -0700 Subject: [PATCH 1/3] Flag tracebacks in observation summary by scanning log files --- RMS/Formats/ObservationSummary.py | 79 ++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/RMS/Formats/ObservationSummary.py b/RMS/Formats/ObservationSummary.py index 5444e02b0..311b42334 100644 --- a/RMS/Formats/ObservationSummary.py +++ b/RMS/Formats/ObservationSummary.py @@ -996,7 +996,8 @@ def retrieveObservationData(conn, config, night_directory=None, ordering=None): 'capture_duration_from_ephemeris', 'total_expected_fits_ephemeris', 'fits_file_shortfall_ephemeris', 'fits_file_shortfall_as_time_ephemeris', 'detections_after_ml', - 'media_backend','protocol_in_use','jitter_quality','dropped_frame_rate'] + 'media_backend','protocol_in_use','jitter_quality','dropped_frame_rate', + 'traceback_count'] # Use this print call to check the ordering # print("Ordering {}".format(ordering)) @@ -1202,6 +1203,76 @@ def startObservationSummaryReport(config, duration, force_delete=False): return "Opening a new observations summary" +def countTracebacksInLogs(config): + """Count the number of tracebacks in log files from the current session. + + Scans all log files in the log directory that were modified after the session's + start_time (from the observation database) for lines containing 'Traceback + (most recent call last)'. + + Arguments: + config: [config] RMS configuration instance. + + Return: + count: [int] Number of tracebacks found, or 0 if logs cannot be read. + """ + + log_dir = os.path.join(config.data_dir, config.log_dir) + + if not os.path.isdir(log_dir): + return 0 + + # Get the session start time from the observation database to filter log files + try: + conn = getObsDBConn(config) + if conn is None: + return 0 + cursor = conn.cursor() + row = cursor.execute( + "SELECT Value FROM records WHERE Key = 'start_time' ORDER BY id DESC LIMIT 1" + ).fetchone() + conn.close() + if row is None: + return 0 + session_start_str = row[0] + # Parse the start time — handle both with and without fractional seconds + for fmt in ("%Y-%m-%d %H:%M:%S%z", "%Y-%m-%d %H:%M:%S"): + try: + session_start = datetime.datetime.strptime(session_start_str.replace("+00:00", ""), fmt) + break + except ValueError: + continue + else: + return 0 + except Exception: + return 0 + + # Find log files modified after the session start + traceback_count = 0 + log_pattern = "log_{}_".format(config.stationID) + + for filename in sorted(os.listdir(log_dir)): + if not filename.endswith(".log") or log_pattern not in filename: + continue + + filepath = os.path.join(log_dir, filename) + + # Only consider log files modified after the session started + file_mtime = UTCFromTimestamp.utcfromtimestamp(os.path.getmtime(filepath)) + if file_mtime < session_start: + continue + + try: + with open(filepath, 'r', errors='replace') as f: + for line in f: + if 'Traceback (most recent call last)' in line: + traceback_count += 1 + except Exception: + continue + + return traceback_count + + def finalizeObservationSummary(config, night_data_dir, platepar=None): """ Enters the parameters known at the end of observation into the database. @@ -1267,6 +1338,12 @@ def finalizeObservationSummary(config, night_data_dir, platepar=None): addObsParam(obs_db_conn, "remote_branch", os.path.basename(remote_branch)) except: addObsParam(obs_db_conn, "repository_lag_remote_days", "Not determined") + + # Scan log files for tracebacks during this session + traceback_count = countTracebacksInLogs(config) + if traceback_count > 0: + addObsParam(obs_db_conn, "traceback_count", traceback_count) + obs_db_conn.close() writeToFile(config, getRMSStyleFileName(night_data_dir, "observation_summary.txt"), night_data_dir) From fe6eb9c1b0072836bd5897891f83d30addc7377d Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:16:04 -0700 Subject: [PATCH 2/3] Verify compiled extensions are importable after build --- Scripts/RMS_Update.sh | 44 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/Scripts/RMS_Update.sh b/Scripts/RMS_Update.sh index 8d59e1ee6..1988d59ef 100755 --- a/Scripts/RMS_Update.sh +++ b/Scripts/RMS_Update.sh @@ -1547,8 +1547,48 @@ PY print_status "info" "Backup files are available in: $RMSBACKUPDIR" exit 1 fi - - print_status "success" "Build completed successfully" + + # Verify compiled extensions are actually importable. + # pip install -e can exit 0 even when C/C++ compilation fails silently. + print_status "info" "Verifying compiled extensions..." + local verify_output + verify_output=$(python << 'VERIFY_EOF' +import sys, importlib, re +# Parse extension module names from setup.py without importing it +# (importing setup.py would re-run the entire build). +with open("setup.py") as f: + src = f.read() +names = re.findall(r'Extension\(\s*["\x27]([\w.]+)["\x27]', src) +if not names: + print("WARN:no extensions parsed from setup.py") + sys.exit(0) +failed = [] +for name in names: + try: + importlib.import_module(name) + except ImportError: + failed.append(name) +if failed: + print("FAILED:" + ",".join(failed)) + sys.exit(1) +print("OK:" + str(len(names))) +VERIFY_EOF + ) + + if [ $? -ne 0 ]; then + # Extract the failed module names from the output + local failed_modules="${verify_output#FAILED:}" + print_status "error" "Build reported success but these compiled extensions failed to import:" + IFS=',' read -ra modules <<< "$failed_modules" + for mod in "${modules[@]}"; do + print_status "error" " - $mod" + done + print_status "error" "The C/C++ compiler may have failed silently during pip install." + print_status "info" "Backup files are available in: $RMSBACKUPDIR" + exit 1 + fi + + print_status "success" "Build completed successfully (all extensions verified)" # Print the update report print_update_report From 9676d269f24d4b6e381747ccd58cc93bd5c52dfc Mon Sep 17 00:00:00 2001 From: Luc Busquin <133058544+Cybis320@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:19:11 -0700 Subject: [PATCH 3/3] Always write traceback_count to observation summary --- RMS/Formats/ObservationSummary.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/RMS/Formats/ObservationSummary.py b/RMS/Formats/ObservationSummary.py index 311b42334..2aa031482 100644 --- a/RMS/Formats/ObservationSummary.py +++ b/RMS/Formats/ObservationSummary.py @@ -1340,9 +1340,7 @@ def finalizeObservationSummary(config, night_data_dir, platepar=None): addObsParam(obs_db_conn, "repository_lag_remote_days", "Not determined") # Scan log files for tracebacks during this session - traceback_count = countTracebacksInLogs(config) - if traceback_count > 0: - addObsParam(obs_db_conn, "traceback_count", traceback_count) + addObsParam(obs_db_conn, "traceback_count", countTracebacksInLogs(config)) obs_db_conn.close()