Skip to content
Open
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
8 changes: 8 additions & 0 deletions src/a2a/client/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ def __init__(self, message: str, status_code: int | None = None) -> None:
super().__init__(message)
self.status_code = status_code

def __repr__(self) -> str:
"""Return a developer-friendly representation exposing the status code."""
return (
f'{self.__class__.__name__}('
f'status_code={self.status_code!r}, '
f'message={self.message!r})'
)


class A2AClientTimeoutError(A2AClientError):
"""Exception for timeout errors during a request."""
8 changes: 8 additions & 0 deletions src/a2a/utils/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ def __init__(self, message: str | None = None, data: dict | None = None):
self.data = data
super().__init__(self.message)

def __repr__(self) -> str:
"""Return a developer-friendly representation exposing message and data."""
return (
f'{self.__class__.__name__}('
f'message={self.message!r}, '
f'data={self.data!r})'
)


class TaskNotFoundError(A2AError):
"""Exception raised when a task is not found."""
Expand Down
37 changes: 36 additions & 1 deletion tests/client/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import pytest

from a2a.client import A2AClientError
from a2a.client import (
A2AClientError,
A2AClientTimeoutError,
AgentCardResolutionError,
)


class TestA2AClientError:
Expand All @@ -23,3 +27,34 @@ def test_raising_base_error(self) -> None:
raise A2AClientError('Generic client error')

assert str(excinfo.value) == 'Generic client error'


class TestClientErrorRepr:
"""Test cases for the __repr__ of client exception classes."""

@pytest.mark.parametrize(
('error', 'expected'),
[
(
A2AClientError('Network communication error'),
"A2AClientError(message='Network communication error', data=None)",
),
(
A2AClientTimeoutError('Client Request timed out'),
"A2AClientTimeoutError(message='Client Request timed out', data=None)",
),
(
AgentCardResolutionError('Failed to fetch agent card', 404),
'AgentCardResolutionError(status_code=404, '
"message='Failed to fetch agent card')",
),
(
AgentCardResolutionError('Invalid agent card JSON'),
'AgentCardResolutionError(status_code=None, '
"message='Invalid agent card JSON')",
),
],
)
def test_repr(self, error: A2AClientError, expected: str) -> None:
"""Test that repr exposes structured attributes as keyword arguments."""
assert repr(error) == expected
44 changes: 44 additions & 0 deletions tests/utils/test_a2a_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import pytest

from a2a.utils.errors import (
A2AError,
InvalidParamsError,
TaskNotFoundError,
VersionNotSupportedError,
)


class TestA2AErrorRepr:
"""Test cases for the __repr__ of A2AError and its subclasses."""

@pytest.mark.parametrize(
('error', 'expected'),
[
(A2AError(), "A2AError(message='A2A Error', data=None)"),
(
TaskNotFoundError(),
"TaskNotFoundError(message='Task not found', data=None)",
),
(
TaskNotFoundError('Task abc not found'),
"TaskNotFoundError(message='Task abc not found', data=None)",
),
(
InvalidParamsError('Invalid field', data={'field': 'id'}),
"InvalidParamsError(message='Invalid field', data={'field': 'id'})",
),
(
VersionNotSupportedError(data={'supported': ['1.0']}),
"VersionNotSupportedError(message='Version not supported', "
"data={'supported': ['1.0']})",
),
],
)
def test_repr(self, error: A2AError, expected: str) -> None:
"""Test that repr exposes message and data as keyword arguments."""
assert repr(error) == expected

def test_str_unchanged(self) -> None:
"""Test that __str__ still returns only the message."""
error = InvalidParamsError('Invalid field', data={'field': 'id'})
assert str(error) == 'Invalid field'
Loading