ca19bd51444a79e823160124e602ac92b9c6469e mspeir Fri Aug 21 13:41:12 2026 -0700 G2P otto: keep a successful build quiet again, refs #38142 Separating stdout from stderr in cd98d642c99 also echoed stderr onward, and bedToBigBed narrates its progress there, so every build gained a dozen lines of "pass1 - making usageList" in the otto mail. stderr is now only reported when the command fails. The streams stay separate, so the parsing fix holds. No change to the track data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> diff --git src/hg/utils/otto/g2p/doG2p.py src/hg/utils/otto/g2p/doG2p.py index b279fc774fc..576d10a3cb0 100755 --- src/hg/utils/otto/g2p/doG2p.py +++ src/hg/utils/otto/g2p/doG2p.py @@ -41,41 +41,43 @@ GBDB_BB = "/gbdb/%s/g2p/g2p.bb" # live symlink, per-db COUNT_TOLERANCE = 0.10 # 10% item-count change requires --force parser = argparse.ArgumentParser(description="Build and update the G2P track.") parser.add_argument("--force", action="store_true", help="Rebuild even if the download is unchanged, and bypass " "the >10%% item-count safety check.") args = parser.parse_args() def bash(cmd): """Run cmd in a bash subprocess, returning stdout; raise on non-zero exit. stdout is kept apart from stderr on purpose. loadCoordinates parses this return value as data, so folding the two together means one warning from the underlying - tool arrives looking like a row of a bigBed. Whatever the command puts on stderr - is passed through to ours, so the otto mail still carries it. + tool arrives looking like a row of a bigBed. + + stderr is captured, not echoed: it comes back in the exception when the command + fails, and is dropped when it succeeds. bedToBigBed narrates its progress there, + and g2pWrapper.sh mails everything this script prints, so echoing it would put a + dozen lines of "pass1 - making usageList" into the otto mail on every build. """ try: out = subprocess.run(cmd, check=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) except subprocess.CalledProcessError as e: raise RuntimeError("command '{}' returned error (code {}): {}".format( e.cmd, e.returncode, (e.stderr or "") + (e.output or ""))) - if out.stderr: - sys.stderr.write(out.stderr) return out.stdout def acquireLock(): """Take an exclusive lock so two runs cannot interleave. Two runs share one build directory and both finish by moving AllG2P.csv over prevAllG2P.csv, so an overlap can leave the "has the download changed" check comparing against a file the other run wrote. The lock is held until this process exits; the returned handle only needs to stay referenced. """ fh = open(LOCK_FILE, "w") try: fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: