Skip to content

Commit ecbbb7e

Browse files
authored
ci: Add ruff format checking (#309)
1 parent c4a324d commit ecbbb7e

File tree

10 files changed

+135
-72
lines changed

10 files changed

+135
-72
lines changed

.github/workflows/linter.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ jobs:
2626
run: uv sync --dev
2727
- name: Run Ruff Linter
2828
run: uv run ruff check .
29+
- name: Run Ruff Format Check
30+
run: uv run ruff format --check .
2931
- name: Run MyPy Type Checker
3032
run: uv run mypy src
3133
- name: Run Pyright (Pylance equivalent)

src/a2a/server/request_handlers/jsonrpc_handler.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,10 +347,8 @@ async def list_push_notification_config(
347347
A `ListTaskPushNotificationConfigResponse` object containing the config or a JSON-RPC error.
348348
"""
349349
try:
350-
config = (
351-
await self.request_handler.on_list_task_push_notification_config(
352-
request.params, context
353-
)
350+
config = await self.request_handler.on_list_task_push_notification_config(
351+
request.params, context
354352
)
355353
return prepare_response_object(
356354
request.id,

src/a2a/server/request_handlers/response_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
GetTaskPushNotificationConfigResponse,
4242
SendStreamingMessageResponse,
4343
ListTaskPushNotificationConfigResponse,
44-
DeleteTaskPushNotificationConfigResponse
44+
DeleteTaskPushNotificationConfigResponse,
4545
)
4646
"""Type variable for RootModel response types."""
4747

@@ -55,7 +55,7 @@
5555
GetTaskPushNotificationConfigSuccessResponse,
5656
SendStreamingMessageSuccessResponse,
5757
ListTaskPushNotificationConfigSuccessResponse,
58-
DeleteTaskPushNotificationConfigSuccessResponse
58+
DeleteTaskPushNotificationConfigSuccessResponse,
5959
)
6060
"""Type variable for SuccessResponse types."""
6161

src/a2a/server/tasks/push_notification_config_store.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ class PushNotificationConfigStore(ABC):
77
"""Interface for storing and retrieving push notification configurations for tasks."""
88

99
@abstractmethod
10-
async def set_info(self, task_id: str, notification_config: PushNotificationConfig) -> None:
10+
async def set_info(
11+
self, task_id: str, notification_config: PushNotificationConfig
12+
) -> None:
1113
"""Sets or updates the push notification configuration for a task."""
1214

1315
@abstractmethod
1416
async def get_info(self, task_id: str) -> list[PushNotificationConfig]:
1517
"""Retrieves the push notification configuration for a task."""
1618

1719
@abstractmethod
18-
async def delete_info(self, task_id: str, config_id: str | None = None) -> None:
20+
async def delete_info(
21+
self, task_id: str, config_id: str | None = None
22+
) -> None:
1923
"""Deletes the push notification configuration for a task."""

src/a2a/server/tasks/task_updater.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,18 @@ async def update_status(
6060
"""
6161
async with self._lock:
6262
if self._terminal_state_reached:
63-
raise RuntimeError(f"Task {self.task_id} is already in a terminal state.")
63+
raise RuntimeError(
64+
f'Task {self.task_id} is already in a terminal state.'
65+
)
6466
if state in self._terminal_states:
6567
self._terminal_state_reached = True
6668
final = True
6769

68-
current_timestamp = timestamp if timestamp else datetime.now(timezone.utc).isoformat()
70+
current_timestamp = (
71+
timestamp
72+
if timestamp
73+
else datetime.now(timezone.utc).isoformat()
74+
)
6975
await self.event_queue.enqueue_event(
7076
TaskStatusUpdateEvent(
7177
taskId=self.task_id,
@@ -112,7 +118,7 @@ async def add_artifact( # noqa: PLR0913
112118
metadata=metadata,
113119
),
114120
append=append,
115-
lastChunk=last_chunk
121+
lastChunk=last_chunk,
116122
)
117123
)
118124

tests/server/request_handlers/test_jsonrpc_handler.py

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616
from a2a.server.events import QueueManager
1717
from a2a.server.events.event_queue import EventQueue
1818
from a2a.server.request_handlers import DefaultRequestHandler, JSONRPCHandler
19-
from a2a.server.tasks import TaskStore, InMemoryPushNotificationConfigStore, BasePushNotificationSender, PushNotificationConfigStore, PushNotificationSender
19+
from a2a.server.tasks import (
20+
TaskStore,
21+
InMemoryPushNotificationConfigStore,
22+
BasePushNotificationSender,
23+
PushNotificationConfigStore,
24+
PushNotificationSender,
25+
)
2026
from a2a.types import (
2127
AgentCapabilities,
2228
AgentCard,
@@ -436,8 +442,10 @@ async def streaming_coro():
436442
async def test_set_push_notification_success(self) -> None:
437443
mock_agent_executor = AsyncMock(spec=AgentExecutor)
438444
mock_task_store = AsyncMock(spec=TaskStore)
439-
mock_push_notification_store = AsyncMock(spec=PushNotificationConfigStore)
440-
445+
mock_push_notification_store = AsyncMock(
446+
spec=PushNotificationConfigStore
447+
)
448+
441449
request_handler = DefaultRequestHandler(
442450
mock_agent_executor,
443451
mock_task_store,
@@ -471,10 +479,12 @@ async def test_set_push_notification_success(self) -> None:
471479

472480
async def test_get_push_notification_success(self) -> None:
473481
mock_agent_executor = AsyncMock(spec=AgentExecutor)
474-
mock_task_store = AsyncMock(spec=TaskStore)
482+
mock_task_store = AsyncMock(spec=TaskStore)
475483
push_notification_store = InMemoryPushNotificationConfigStore()
476484
request_handler = DefaultRequestHandler(
477-
mock_agent_executor, mock_task_store, push_config_store=push_notification_store
485+
mock_agent_executor,
486+
mock_task_store,
487+
push_config_store=push_notification_store,
478488
)
479489
self.mock_agent_card.capabilities = AgentCapabilities(
480490
streaming=True, pushNotifications=True
@@ -516,9 +526,14 @@ async def test_on_message_stream_new_message_send_push_notification_success(
516526
mock_task_store = AsyncMock(spec=TaskStore)
517527
mock_httpx_client = AsyncMock(spec=httpx.AsyncClient)
518528
push_notification_store = InMemoryPushNotificationConfigStore()
519-
push_notification_sender = BasePushNotificationSender(mock_httpx_client, push_notification_store)
529+
push_notification_sender = BasePushNotificationSender(
530+
mock_httpx_client, push_notification_store
531+
)
520532
request_handler = DefaultRequestHandler(
521-
mock_agent_executor, mock_task_store, push_config_store=push_notification_store, push_sender=push_notification_sender
533+
mock_agent_executor,
534+
mock_task_store,
535+
push_config_store=push_notification_store,
536+
push_sender=push_notification_sender,
522537
)
523538
self.mock_agent_card.capabilities = AgentCapabilities(
524539
streaming=True, pushNotifications=True
@@ -585,7 +600,7 @@ async def streaming_coro():
585600
'kind': 'task',
586601
'status': {'state': 'submitted'},
587602
},
588-
headers=None
603+
headers=None,
589604
),
590605
call(
591606
'http://example.com',
@@ -606,7 +621,7 @@ async def streaming_coro():
606621
'kind': 'task',
607622
'status': {'state': 'submitted'},
608623
},
609-
headers=None
624+
headers=None,
610625
),
611626
call(
612627
'http://example.com',
@@ -627,7 +642,7 @@ async def streaming_coro():
627642
'kind': 'task',
628643
'status': {'state': 'completed'},
629644
},
630-
headers=None
645+
headers=None,
631646
),
632647
]
633648
mock_httpx_client.post.assert_has_calls(calls)
@@ -727,7 +742,7 @@ async def test_streaming_not_supported_error(
727742
pass
728743

729744
self.assertEqual(
730-
str(context.exception.error.message), # type: ignore
745+
str(context.exception.error.message), # type: ignore
731746
'Streaming is not supported by the agent',
732747
)
733748

@@ -761,7 +776,7 @@ async def test_push_notifications_not_supported_error(self) -> None:
761776
await handler.set_push_notification_config(request)
762777

763778
self.assertEqual(
764-
str(context.exception.error.message), # type: ignore
779+
str(context.exception.error.message), # type: ignore
765780
'Push notifications are not supported by the agent',
766781
)
767782

@@ -960,7 +975,7 @@ async def consume_raises_error(*args, **kwargs) -> NoReturn:
960975

961976
# Assert
962977
self.assertIsInstance(response.root, JSONRPCErrorResponse)
963-
self.assertEqual(response.root.error, UnsupportedOperationError()) # type: ignore
978+
self.assertEqual(response.root.error, UnsupportedOperationError()) # type: ignore
964979

965980
async def test_on_message_send_task_id_mismatch(self) -> None:
966981
mock_agent_executor = AsyncMock(spec=AgentExecutor)
@@ -1031,37 +1046,54 @@ async def test_on_get_push_notification(self) -> None:
10311046

10321047
mock_task = Task(**MINIMAL_TASK)
10331048
mock_task_store.get.return_value = mock_task
1034-
10351049

10361050
# Create request handler without a push notifier
10371051
request_handler = AsyncMock(spec=DefaultRequestHandler)
1038-
task_push_config = TaskPushNotificationConfig(taskId=mock_task.id, pushNotificationConfig=PushNotificationConfig(id="config1", url='http://example.com'))
1039-
request_handler.on_get_task_push_notification_config.return_value = task_push_config
1052+
task_push_config = TaskPushNotificationConfig(
1053+
taskId=mock_task.id,
1054+
pushNotificationConfig=PushNotificationConfig(
1055+
id='config1', url='http://example.com'
1056+
),
1057+
)
1058+
request_handler.on_get_task_push_notification_config.return_value = (
1059+
task_push_config
1060+
)
10401061

10411062
self.mock_agent_card.capabilities = AgentCapabilities(
10421063
pushNotifications=True
10431064
)
10441065
handler = JSONRPCHandler(self.mock_agent_card, request_handler)
10451066
list_request = GetTaskPushNotificationConfigRequest(
1046-
id='1', params=GetTaskPushNotificationConfigParams(id=mock_task.id, pushNotificationConfigId="config1")
1067+
id='1',
1068+
params=GetTaskPushNotificationConfigParams(
1069+
id=mock_task.id, pushNotificationConfigId='config1'
1070+
),
10471071
)
10481072
response = await handler.get_push_notification_config(list_request)
10491073
# Assert
1050-
self.assertIsInstance(response.root, GetTaskPushNotificationConfigSuccessResponse)
1051-
self.assertEqual(response.root.result, task_push_config) # type: ignore
1074+
self.assertIsInstance(
1075+
response.root, GetTaskPushNotificationConfigSuccessResponse
1076+
)
1077+
self.assertEqual(response.root.result, task_push_config) # type: ignore
10521078

10531079
async def test_on_list_push_notification(self) -> None:
10541080
"""Test list_push_notification_config handling"""
10551081
mock_task_store = AsyncMock(spec=TaskStore)
10561082

10571083
mock_task = Task(**MINIMAL_TASK)
10581084
mock_task_store.get.return_value = mock_task
1059-
10601085

10611086
# Create request handler without a push notifier
10621087
request_handler = AsyncMock(spec=DefaultRequestHandler)
1063-
task_push_config = TaskPushNotificationConfig(taskId=mock_task.id, pushNotificationConfig=PushNotificationConfig(url='http://example.com'))
1064-
request_handler.on_list_task_push_notification_config.return_value = [task_push_config]
1088+
task_push_config = TaskPushNotificationConfig(
1089+
taskId=mock_task.id,
1090+
pushNotificationConfig=PushNotificationConfig(
1091+
url='http://example.com'
1092+
),
1093+
)
1094+
request_handler.on_list_task_push_notification_config.return_value = [
1095+
task_push_config
1096+
]
10651097

10661098
self.mock_agent_card.capabilities = AgentCapabilities(
10671099
pushNotifications=True
@@ -1072,23 +1104,30 @@ async def test_on_list_push_notification(self) -> None:
10721104
)
10731105
response = await handler.list_push_notification_config(list_request)
10741106
# Assert
1075-
self.assertIsInstance(response.root, ListTaskPushNotificationConfigSuccessResponse)
1076-
self.assertEqual(response.root.result, [task_push_config]) # type: ignore
1107+
self.assertIsInstance(
1108+
response.root, ListTaskPushNotificationConfigSuccessResponse
1109+
)
1110+
self.assertEqual(response.root.result, [task_push_config]) # type: ignore
10771111

10781112
async def test_on_list_push_notification_error(self) -> None:
10791113
"""Test list_push_notification_config handling"""
10801114
mock_task_store = AsyncMock(spec=TaskStore)
10811115

10821116
mock_task = Task(**MINIMAL_TASK)
10831117
mock_task_store.get.return_value = mock_task
1084-
10851118

10861119
# Create request handler without a push notifier
10871120
request_handler = AsyncMock(spec=DefaultRequestHandler)
1088-
task_push_config = TaskPushNotificationConfig(taskId=mock_task.id, pushNotificationConfig=PushNotificationConfig(url='http://example.com'))
1121+
task_push_config = TaskPushNotificationConfig(
1122+
taskId=mock_task.id,
1123+
pushNotificationConfig=PushNotificationConfig(
1124+
url='http://example.com'
1125+
),
1126+
)
10891127
# throw server error
1090-
request_handler.on_list_task_push_notification_config.side_effect = ServerError(InternalError())
1091-
1128+
request_handler.on_list_task_push_notification_config.side_effect = (
1129+
ServerError(InternalError())
1130+
)
10921131

10931132
self.mock_agent_card.capabilities = AgentCapabilities(
10941133
pushNotifications=True
@@ -1100,45 +1139,55 @@ async def test_on_list_push_notification_error(self) -> None:
11001139
response = await handler.list_push_notification_config(list_request)
11011140
# Assert
11021141
self.assertIsInstance(response.root, JSONRPCErrorResponse)
1103-
self.assertEqual(response.root.error, InternalError()) # type: ignore
1104-
1142+
self.assertEqual(response.root.error, InternalError()) # type: ignore
1143+
11051144
async def test_on_delete_push_notification(self) -> None:
11061145
"""Test delete_push_notification_config handling"""
11071146

11081147
# Create request handler without a push notifier
1109-
request_handler = AsyncMock(spec=DefaultRequestHandler)
1110-
request_handler.on_delete_task_push_notification_config.return_value = None
1148+
request_handler = AsyncMock(spec=DefaultRequestHandler)
1149+
request_handler.on_delete_task_push_notification_config.return_value = (
1150+
None
1151+
)
11111152

11121153
self.mock_agent_card.capabilities = AgentCapabilities(
11131154
pushNotifications=True
11141155
)
11151156
handler = JSONRPCHandler(self.mock_agent_card, request_handler)
11161157
delete_request = DeleteTaskPushNotificationConfigRequest(
1117-
id='1', params=DeleteTaskPushNotificationConfigParams(id="task1", pushNotificationConfigId="config1")
1158+
id='1',
1159+
params=DeleteTaskPushNotificationConfigParams(
1160+
id='task1', pushNotificationConfigId='config1'
1161+
),
11181162
)
11191163
response = await handler.delete_push_notification_config(delete_request)
11201164
# Assert
1121-
self.assertIsInstance(response.root, DeleteTaskPushNotificationConfigSuccessResponse)
1122-
self.assertEqual(response.root.result, None) # type: ignore
1165+
self.assertIsInstance(
1166+
response.root, DeleteTaskPushNotificationConfigSuccessResponse
1167+
)
1168+
self.assertEqual(response.root.result, None) # type: ignore
11231169

11241170
async def test_on_delete_push_notification_error(self) -> None:
11251171
"""Test delete_push_notification_config error handling"""
1126-
11271172

11281173
# Create request handler without a push notifier
11291174
request_handler = AsyncMock(spec=DefaultRequestHandler)
11301175
# throw server error
1131-
request_handler.on_delete_task_push_notification_config.side_effect = ServerError(UnsupportedOperationError())
1132-
1176+
request_handler.on_delete_task_push_notification_config.side_effect = (
1177+
ServerError(UnsupportedOperationError())
1178+
)
11331179

11341180
self.mock_agent_card.capabilities = AgentCapabilities(
11351181
pushNotifications=True
11361182
)
11371183
handler = JSONRPCHandler(self.mock_agent_card, request_handler)
11381184
delete_request = DeleteTaskPushNotificationConfigRequest(
1139-
id='1', params=DeleteTaskPushNotificationConfigParams(id="task1", pushNotificationConfigId="config1")
1185+
id='1',
1186+
params=DeleteTaskPushNotificationConfigParams(
1187+
id='task1', pushNotificationConfigId='config1'
1188+
),
11401189
)
11411190
response = await handler.delete_push_notification_config(delete_request)
11421191
# Assert
11431192
self.assertIsInstance(response.root, JSONRPCErrorResponse)
1144-
self.assertEqual(response.root.error, UnsupportedOperationError()) # type: ignore
1193+
self.assertEqual(response.root.error, UnsupportedOperationError()) # type: ignore

tests/server/tasks/test_inmemory_push_notifications.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ async def test_send_notification_success(self):
161161
async def test_send_notification_with_token_success(self):
162162
task_id = 'task_send_success'
163163
task_data = create_sample_task(task_id=task_id)
164-
config = create_sample_push_config(url='http://notify.me/here', token='unique_token')
164+
config = create_sample_push_config(
165+
url='http://notify.me/here', token='unique_token'
166+
)
165167
await self.config_store.set_info(task_id, config)
166168

167169
# Mock the post call to simulate success
@@ -180,7 +182,7 @@ async def test_send_notification_with_token_success(self):
180182
)
181183
self.assertEqual(
182184
called_kwargs['headers'],
183-
{"X-A2A-Notification-Token": "unique_token"},
185+
{'X-A2A-Notification-Token': 'unique_token'},
184186
)
185187
self.assertNotIn(
186188
'auth', called_kwargs

0 commit comments

Comments
 (0)