Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 14 additions & 3 deletions src/intentproof/instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import inspect
import logging
import time
from contextvars import ContextVar
from datetime import datetime, timezone
Expand All @@ -14,6 +15,8 @@
from intentproof import client
from intentproof.signing import event_content_hash, sign_event

logger = logging.getLogger(__name__)

_correlation_id: ContextVar[str | None] = ContextVar(
"intentproof_correlation_id", default=None
)
Expand Down Expand Up @@ -57,6 +60,10 @@ def _untrusted_payload(inputs: list[Any], output: Any, status: str) -> bool:
return status == "ok" and output is not None


def _log_execution_record_failure(exc: BaseException) -> None:
logger.warning("[intentproof] execution record failed: %s", exc)


def _record_execution(
*,
intent: str,
Expand Down Expand Up @@ -111,7 +118,11 @@ def wrap(
action: str,
fn: F,
) -> F:
"""Wrap a callable to emit signed ExecutionEvent.v1 records."""
"""Wrap a callable to emit signed ExecutionEvent.v1 records.

When the wrapped callable returns normally but recording fails, the
result is still returned and the failure is logged.
"""

if inspect.iscoroutinefunction(fn):

Expand Down Expand Up @@ -149,7 +160,7 @@ async def async_wrapped(*args: Any, **kwargs: Any) -> Any:
except Exception as record_exc:
if reraise is not None:
raise reraise from record_exc
raise
_log_execution_record_failure(record_exc)
if reraise is not None:
raise reraise
return result
Expand Down Expand Up @@ -190,7 +201,7 @@ def sync_wrapped(*args: Any, **kwargs: Any) -> Any:
except Exception as record_exc:
if reraise is not None:
raise reraise from record_exc
raise
_log_execution_record_failure(record_exc)
if reraise is not None:
raise reraise
return result
Expand Down
13 changes: 9 additions & 4 deletions tests/test_async_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ def fail_record(**_kwargs: object) -> None:
assert isinstance(exc_info.value.__cause__, RuntimeError)


def test_async_wrap_record_failure_without_app_error_raises(
tmp_path, monkeypatch: pytest.MonkeyPatch
def test_async_wrap_record_failure_without_app_error_logs_and_returns(
tmp_path,
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
configure(
db_path=str(tmp_path / "outbox.db"),
Expand All @@ -114,5 +116,8 @@ def fail_record(**_kwargs: object) -> None:
"intentproof.instrumentation._record_execution", fail_record
)

with pytest.raises(RuntimeError, match="outbox unavailable"):
asyncio.run(fn())
with caplog.at_level("WARNING", logger="intentproof.instrumentation"):
assert asyncio.run(fn()) == 1
assert any(
"execution record failed" in record.message for record in caplog.records
)
13 changes: 9 additions & 4 deletions tests/test_e2e_local_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ def test_wrap_without_inputs_and_none_output_is_trusted(
assert ev["untrusted_payload"] is False


def test_wrap_record_failure_without_app_error_raises(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
def test_wrap_record_failure_without_app_error_logs_and_returns(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
configure(
db_path=str(tmp_path / "outbox.db"),
Expand All @@ -223,8 +225,11 @@ def fail_record(**_kwargs: object) -> None:
)

fn = wrap(intent="Record fail", action="record.fail", fn=lambda: 1)
with pytest.raises(RuntimeError, match="outbox unavailable"):
fn()
with caplog.at_level("WARNING", logger="intentproof.instrumentation"):
assert fn() == 1
assert any(
"execution record failed" in record.message for record in caplog.records
)


def test_sdk_not_configured_errors() -> None:
Expand Down
Loading