Report the real cause when jrunner fails
--strict means a failure now exits non-zero, so the returncode branch raises instead of _detect_silent_failure. That branch used raw stderr, whose first line is always the JVM's "Picked up JAVA_TOOL_OPTIONS: ..." banner (_subprocess_env sets JAVA_TOOL_OPTIONS to keep jt400 headless). Every failed run would therefore have recorded that banner in run_log instead of the actual error — a regression against the old path, which extracted the exception header. _error_message strips the banner and prefers the Java exception header: org.postgresql.util.PSQLException: ERROR: relation "..." does not exist org.postgresql.util.PSQLException: ERROR: syntax error at or near "SELEKT" Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
002deb8ad6
commit
63e900466c
@ -158,7 +158,7 @@ def query(
|
|||||||
_unlink_quietly(sql_path, pass_path)
|
_unlink_quietly(sql_path, pass_path)
|
||||||
|
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
raise JrunnerError(r.stderr.strip() or r.stdout.strip(),
|
raise JrunnerError(_error_message(r.stdout, r.stderr),
|
||||||
stdout=r.stdout, stderr=r.stderr)
|
stdout=r.stdout, stderr=r.stderr)
|
||||||
silent = _detect_silent_failure(r.stdout, r.stderr)
|
silent = _detect_silent_failure(r.stdout, r.stderr)
|
||||||
if silent:
|
if silent:
|
||||||
@ -250,7 +250,7 @@ def migrate(
|
|||||||
_unlink_quietly(sql_path, pass_path)
|
_unlink_quietly(sql_path, pass_path)
|
||||||
|
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
raise JrunnerError(stderr.strip() or stdout.strip(),
|
raise JrunnerError(_error_message(stdout, stderr),
|
||||||
stdout=stdout, stderr=stderr)
|
stdout=stdout, stderr=stderr)
|
||||||
silent = _detect_silent_failure(stdout, stderr)
|
silent = _detect_silent_failure(stdout, stderr)
|
||||||
if silent:
|
if silent:
|
||||||
@ -317,6 +317,26 @@ def _parse_row_count(text: str) -> int | None:
|
|||||||
# nearly every failure site (see jrunner.java). Detect those by scanning
|
# nearly every failure site (see jrunner.java). Detect those by scanning
|
||||||
# for a Java stack-trace signature so callers don't treat silent failures
|
# for a Java stack-trace signature so callers don't treat silent failures
|
||||||
# as success.
|
# as success.
|
||||||
|
_JVM_NOISE_RE = re.compile(r"^Picked up JAVA_TOOL_OPTIONS:.*$", re.M)
|
||||||
|
|
||||||
|
|
||||||
|
def _error_message(stdout: str, stderr: str) -> str:
|
||||||
|
"""Best single-line cause for a failed jrunner call.
|
||||||
|
|
||||||
|
The JVM prints "Picked up JAVA_TOOL_OPTIONS: ..." as the first stderr line
|
||||||
|
whenever JAVA_TOOL_OPTIONS is set (which _subprocess_env always does, to
|
||||||
|
keep jt400 headless), so raising raw stderr surfaces that banner instead of
|
||||||
|
the actual error. Prefer the Java exception header, which names the real
|
||||||
|
failure, and fall back to whatever output remains once the banner is gone.
|
||||||
|
"""
|
||||||
|
cleaned_err = _JVM_NOISE_RE.sub("", stderr or "").strip()
|
||||||
|
cleaned_out = _JVM_NOISE_RE.sub("", stdout or "").strip()
|
||||||
|
m = _EXCEPTION_HEADER_RE.search(cleaned_err) or _EXCEPTION_HEADER_RE.search(cleaned_out)
|
||||||
|
if m:
|
||||||
|
return m.group(0).strip()
|
||||||
|
return cleaned_err or cleaned_out or "jrunner failed with no output"
|
||||||
|
|
||||||
|
|
||||||
_STACK_FRAME_RE = re.compile(r"^\s*at [\w.$<>]+\([^)\n]*\.java:\d+\)", re.M)
|
_STACK_FRAME_RE = re.compile(r"^\s*at [\w.$<>]+\([^)\n]*\.java:\d+\)", re.M)
|
||||||
_EXCEPTION_HEADER_RE = re.compile(
|
_EXCEPTION_HEADER_RE = re.compile(
|
||||||
r"^(?:[\w.$]+\.)*[\w$]+(?:Exception|Error)(?::[^\n]*)?$", re.M)
|
r"^(?:[\w.$]+\.)*[\w$]+(?:Exception|Error)(?::[^\n]*)?$", re.M)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user