Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion scripts/ci/noema_review_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1636,7 +1636,10 @@ def call_llm(
gateway_telemetry: dict[str, str | int] = {}
if isinstance(exc, urllib.error.HTTPError):
active_phase = "response_error"
gateway_telemetry = _extract_http_error_telemetry(exc)
try:
gateway_telemetry = _extract_http_error_telemetry(exc)
finally:
exc.close()
model_value = gateway_telemetry.get("served_model")
served_model = model_value if isinstance(model_value, str) else None
elapsed = time.monotonic() - attempt_started
Expand Down
2 changes: 2 additions & 0 deletions scripts/ci/pingora_edge_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ def _github_open_json(url: str, token: str) -> object:
with github_opener.open(request, timeout=30) as response:
payload = response.read(MAX_RESPONSE_BYTES + 1)
except (HTTPError, URLError, TimeoutError) as exc:
if isinstance(exc, HTTPError):
exc.close()
raise PolicyError(f"GitHub API request failed for policy evidence: {type(exc).__name__}") from exc
if len(payload) > MAX_RESPONSE_BYTES:
raise PolicyError("GitHub API policy response exceeded the bounded response size")
Expand Down
4 changes: 3 additions & 1 deletion scripts/ci/reconcile_repository_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import sys
from pathlib import Path
from typing import Any
from urllib.error import URLError
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, Request, build_opener


Expand Down Expand Up @@ -247,6 +247,8 @@ def _pages_publication_ready(repository: str, current: dict[str, Any]) -> None:
if not response.read(1):
raise RuntimeError(f"GitHub Pages returned empty content for {repository}")
except (URLError, TimeoutError, OSError) as exc:
if isinstance(exc, HTTPError):
exc.close()
raise RuntimeError(f"GitHub Pages is not reachable for {repository}") from exc


Expand Down
4 changes: 3 additions & 1 deletion scripts/ci/sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,9 @@ def wait_for_url(url: str, timeout: int, service: Service) -> bool:
if 200 <= response.status < 500:
return True
time.sleep(1)
except (urllib.error.URLError, TimeoutError):
except (urllib.error.URLError, TimeoutError) as exc:
if isinstance(exc, urllib.error.HTTPError):
exc.close()
time.sleep(1)
return False

Expand Down
5 changes: 4 additions & 1 deletion tests/test_noema_review_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1613,10 +1613,12 @@ def test_call_llm_reports_only_safe_model_from_bounded_http_error(monkeypatch, c
}
).encode()

response_body = io.BytesIO(body)

class Opener:
def open(self, request):
raise noema.urllib.error.HTTPError(
request.full_url, 502, "Bad Gateway", {}, io.BytesIO(body)
request.full_url, 502, "Bad Gateway", {}, response_body
)

monkeypatch.setattr(noema.urllib.request, "build_opener", lambda *_args: Opener())
Expand All @@ -1637,6 +1639,7 @@ def open(self, request):
assert "terminal_reason=eligible_candidates_exhausted" in output
assert secret not in output
assert secret not in diagnostic
assert response_body.closed


@pytest.mark.parametrize(
Expand Down
2 changes: 2 additions & 0 deletions tests/test_pingora_edge_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,8 @@ def fail(_request: object, timeout: int) -> object:
monkeypatch.setattr(policy.github_opener, "open", fail)
with pytest.raises(policy.PolicyError, match=type(exc).__name__):
policy._github_open_json("https://api.github.com/repos/a/b", "token")
if isinstance(exc, HTTPError):
assert exc.fp.closed


def test_github_open_json_rejects_oversized_and_malformed_payloads(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down
22 changes: 22 additions & 0 deletions tests/test_repository_metadata_live_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import argparse
import importlib.util
import json
from io import BytesIO
from pathlib import Path

import pytest
Expand Down Expand Up @@ -63,6 +64,27 @@ def open(self, request, timeout):
return self.response


def test_pages_transport_error_closes_response_body(monkeypatch) -> None:
body = BytesIO(b"redirect")
error = RECONCILER.HTTPError(
"https://contextualwisdomlab.github.io/Repo/", 302, "redirect", {}, body
)
monkeypatch.setattr(
RECONCILER, "build_opener", lambda *_args: FakeOpener(error=error)
)

with pytest.raises(RuntimeError, match="not reachable"):
RECONCILER._pages_publication_ready(
"Repo",
{
"status": "built",
"html_url": "https://contextualwisdomlab.github.io/Repo/",
},
)

assert body.closed


def install_live_state(
monkeypatch,
*,
Expand Down
31 changes: 31 additions & 0 deletions tests/test_sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import socket
import subprocess
import sys
from io import BytesIO
from pathlib import Path

import pytest
Expand Down Expand Up @@ -695,6 +696,36 @@ def open(self, url, timeout):
assert sandboxed_web_e2e.wait_for_url("http://127.0.0.1:8000/health", 1, service) is False


def test_wait_for_url_closes_http_error_response(monkeypatch, tmp_path):
class RunningProcess:
def poll(self):
return None

body = BytesIO(b"redirect")
error = sandboxed_web_e2e.urllib.error.HTTPError(
"http://127.0.0.1:8000/health", 302, "Found", {}, body
)
ticks = iter([0, 0, 2])
monkeypatch.setattr(sandboxed_web_e2e.time, "monotonic", lambda: next(ticks))
monkeypatch.setattr(sandboxed_web_e2e.time, "sleep", lambda _seconds: None)

class FailingOpener:
def open(self, _url, timeout):
raise error

monkeypatch.setattr(
sandboxed_web_e2e.urllib.request, "build_opener", lambda *_args: FailingOpener()
)
service = sandboxed_web_e2e.Service(
"web", "serve", RunningProcess(), tmp_path / "web.log"
)

assert not sandboxed_web_e2e.wait_for_url(
"http://127.0.0.1:8000/health", 1, service
)
assert body.closed


def test_main_runs_with_stubbed_services(monkeypatch, tmp_path, capsys):
"""Main records success evidence without requiring real POSIX services."""
repo = tmp_path / "repo"
Expand Down
Loading