705b694649940189cdb66af57560c97a0cd606f7 lrnassar Fri May 15 14:42:52 2026 -0700 Three small rtsUpdate fixes uncovered during first live test on Non_Coding_SNVs_hg38: line-buffer stdout so git diff output interleaves correctly with surrounding prints, replace deprecated datetime.utcnow() with timezone-aware datetime.now(datetime.timezone.utc), and print a trailing newline after confirm() so the post-prompt status message doesn't land on the same line as the prompt. refs #32768 diff --git src/hg/utils/rts/rtsUpdate src/hg/utils/rts/rtsUpdate index 4c634089494..3213c3f480b 100755 --- src/hg/utils/rts/rtsUpdate +++ src/hg/utils/rts/rtsUpdate @@ -29,51 +29,53 @@ } VALIDATE_URL = { "dev": "https://hgwdev.gi.ucsc.edu", "beta": "https://hgwbeta.soe.ucsc.edu", "rr": "https://genome.ucsc.edu", } # Session and userName must match this. Excludes anything that could be SQL- or # shell-special; permits the URL-encoding form ('%' and digits) used for sessions # whose human-readable names contain spaces (e.g. 'CNVs%20Clinical'). SAFE_NAME = re.compile(r"^[A-Za-z0-9_%.\-]+$") def log(msg): - ts = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") line = f"{ts} {getpass.getuser()} {msg}\n" LOGFILE.parent.mkdir(parents=True, exist_ok=True) with open(LOGFILE, "a") as f: f.write(line) def die(msg, code=1): print(f"ERROR: {msg}", file=sys.stderr) sys.exit(code) def confirm(prompt): while True: try: ans = input(prompt + " (yes/no): ").strip().lower() except EOFError: return False if ans == "yes": + print() return True if ans == "no": + print() return False print(" please type 'yes' or 'no'") def validate_name(name, label): if not name or not SAFE_NAME.match(name): die(f"unsafe {label}: {name!r} (must match {SAFE_NAME.pattern})") # ---------- hgsql shim ---------- def hgsql_read(host_key, sql): """Run a SELECT. Returns list[list[str]]. Caller is responsible for interpolating only validated values into sql.""" host, db = HOSTS[host_key] @@ -353,31 +355,31 @@ db = find_db_for_session(target) or find_db_in_tree(target) if db is None: die(f"cannot find {target!r} in recTrackSets.<db>.tab nor in rts/<db>/") pairs = read_kent_file(db, target) if not pairs: die(f"kent tree file is empty: {kent_path(db, target)}") new_contents = join_pairs(pairs) dest_h, dest_db = HOSTS[dest] print(f"\nReading current row from {dest} ({dest_h}:{dest_db})...") dest_contents, dest_settings, dest_exists = fetch_session_row(dest, TARGET_USER, target) if not dest_exists and not args.allow_create: die(f"View/{target} does not exist on {dest}; pass --allow-create to INSERT") - ts = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") bdir = pathlib.Path(tempfile.gettempdir()) / f"rtsUpdate-{ts}" / dest bdir.mkdir(parents=True, exist_ok=True) bfile = bdir / f"{target}.tsv" if dest_exists: bfile.write_text(f"{TARGET_USER}\t{target}\t{dest_contents}\t{dest_settings}\n") print(f" pre-write backup: {bfile}") else: print(f" no existing row on {dest} -- nothing to back up") dest_pairs = sorted(set(split_pairs(dest_contents))) if dest_exists else [] print(f"\n--- diff: kent tree ({db}/{target}) vs {dest} ---") show_pair_diff(dest, dest_pairs, "tree", pairs, args.verbose) if dest == "rr": if not args.i_confirm_rr: @@ -494,27 +496,33 @@ pp.add_argument("--allow-create", action="store_true", help="INSERT row if missing on dest") pp.add_argument("--i-confirm-rr", action="store_true", help="required in addition to interactive yes for --to rr") pp.add_argument("--verbose", "-v", action="store_true") pd = sub.add_parser("diff", help="read-only: compare kent tree vs dev/beta/rr") pd.add_argument("--target-session", required=True) pd.add_argument("--verbose", "-v", action="store_true") return p def main(): + # Line-buffer stdout so prints interleave correctly with subprocess output + # (git diff, hgsql) that writes directly via the OS. + try: + sys.stdout.reconfigure(line_buffering=True) + except AttributeError: + pass args = build_parser().parse_args() if args.cmd == "fetch": return cmd_fetch(args) if args.cmd == "push": return cmd_push(args) if args.cmd == "diff": return cmd_diff(args) return 0 if __name__ == "__main__": sys.exit(main() or 0)