-
Notifications
You must be signed in to change notification settings - Fork 68
Flag tracebacks in observation summary and verify extensions after build #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: prerelease
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Comment on lines
+1265
to
+1269
|
||
| 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,10 @@ 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 | ||
| addObsParam(obs_db_conn, "traceback_count", countTracebacksInLogs(config)) | ||
|
|
||
|
Comment on lines
+1341
to
+1344
|
||
| obs_db_conn.close() | ||
|
|
||
| writeToFile(config, getRMSStyleFileName(night_data_dir, "observation_summary.txt"), night_data_dir) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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) | ||||||||||||||||||
|
Comment on lines
+1569
to
+1570
|
||||||||||||||||||
| except ImportError: | |
| failed.append(name) | |
| except (ImportError, ModuleNotFoundError, OSError) as e: | |
| # Record the module name along with basic error details so the caller can see why it failed. | |
| failed.append(f"{name} ({e.__class__.__name__}: {e})") | |
| except Exception as e: | |
| # Catch any other unexpected exceptions to ensure we still emit a FAILED: line. | |
| failed.append(f"{name} (Unexpected {e.__class__.__name__}: {e})") |
Copilot
AI
Mar 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this script runs with set -Eeuo pipefail (errexit), a non-zero exit from the python command inside verify_output=$(...) will cause the script to abort immediately (triggering the ERR trap) before the subsequent if [ $? -ne 0 ] block can print the list of failed modules. Wrap the command substitution in a conditional (e.g., if ! verify_output=$(python ...); then ... fi) or temporarily disable errexit around it so the intended error reporting executes.
| ) | |
| if [ $? -ne 0 ]; then | |
| ) || verify_status=$? | |
| if [ "${verify_status-0}" -ne 0 ]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
countTracebacksInLogsclaims to handle start times “with and without fractional seconds”, but the parsing formats don’t include%f. Also,session_start_str.replace("+00:00", "")guarantees the"%Y-%m-%d %H:%M:%S%z"parse attempt can never succeed, making that branch dead code. It would be more robust to parse the stored value withdatetime.fromisoformat(...)(and strip tzinfo) or explicitly support both%Y-%m-%d %H:%M:%S.%f%zand%Y-%m-%d %H:%M:%S%zwithout removing the offset first.