Skip to content

Commit d7dd6ee

Browse files
authored
ci: Add additional ruff lint rules (#399)
1 parent 2e8fbc4 commit d7dd6ee

22 files changed

+138
-130
lines changed

.ruff.toml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ ignore = [
2929
"ANN002",
3030
"ANN003",
3131
"ANN401",
32+
"TRY003",
33+
"G004",
34+
"TRY201",
3235
]
3336

3437
select = [
@@ -57,6 +60,20 @@ select = [
5760
"TD", # flake8-todos (check TODO format - Google Style §3.7)
5861
"TCH",# flake8-type-checking (helps manage TYPE_CHECKING blocks and imports)
5962
"PYI",# flake8-pyi (best practices for .pyi stub files, some rules are useful for .py too)
63+
"S", # flake8-bandit (security issues)
64+
"DTZ",# flake8-datetimez (timezone-aware datetimes)
65+
"ERA",# flake8-eradicate (commented-out code)
66+
"Q", # flake8-quotes (quote style consistency)
67+
"RSE",# flake8-raise (modern raise statements)
68+
"TRY",# tryceratops (exception handling best practices)
69+
"PERF",# perflint (performance anti-patterns)
70+
"BLE",
71+
"T10",
72+
"ICN",
73+
"G",
74+
"FIX",
75+
"ASYNC",
76+
"INP",
6077
]
6178

6279
exclude = [
@@ -104,7 +121,7 @@ ignore-decorators = ["typing.overload", "abc.abstractmethod"]
104121

105122
[lint.flake8-annotations]
106123
mypy-init-return = true
107-
allow-star-arg-any = true
124+
allow-star-arg-any = false
108125

109126
[lint.pep8-naming]
110127
ignore-names = ["test_*", "setUp", "tearDown", "mock_*"]
@@ -139,6 +156,7 @@ inline-quotes = "single"
139156
"types.py" = ["D", "E501"] # Ignore docstring and annotation issues in types.py
140157
"proto_utils.py" = ["D102", "PLR0911"]
141158
"helpers.py" = ["ANN001", "ANN201", "ANN202"]
159+
"scripts/*.py" = ["INP001"]
142160

143161
[format]
144162
exclude = [

scripts/format.sh

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,23 @@ FORMAT_ALL=false
88
RUFF_UNSAFE_FIXES_FLAG=""
99

1010
# Process command-line arguments
11-
# We use a while loop with shift to process each argument
1211
while [[ "$#" -gt 0 ]]; do
1312
case "$1" in
14-
--all)
15-
FORMAT_ALL=true
16-
echo "Detected --all flag: Formatting all Python files."
17-
shift # Consume the argument
18-
;;
19-
--unsafe-fixes)
20-
RUFF_UNSAFE_FIXES_FLAG="--unsafe-fixes"
21-
echo "Detected --unsafe-fixes flag: Ruff will run with unsafe fixes."
22-
shift # Consume the argument
23-
;;
24-
*)
25-
# Handle unknown arguments or just ignore them if we only care about specific ones
26-
echo "Warning: Unknown argument '$1'. Ignoring."
27-
shift # Consume the argument
28-
;;
13+
--all)
14+
FORMAT_ALL=true
15+
echo "Detected --all flag: Formatting all tracked Python files."
16+
shift # Consume the argument
17+
;;
18+
--unsafe-fixes)
19+
RUFF_UNSAFE_FIXES_FLAG="--unsafe-fixes"
20+
echo "Detected --unsafe-fixes flag: Ruff will run with unsafe fixes."
21+
shift # Consume the argument
22+
;;
23+
*)
24+
# Handle unknown arguments or just ignore them
25+
echo "Warning: Unknown argument '$1'. Ignoring."
26+
shift # Consume the argument
27+
;;
2928
esac
3029
done
3130

@@ -39,47 +38,52 @@ fi
3938
CHANGED_FILES=""
4039

4140
if $FORMAT_ALL; then
42-
echo "Formatting all Python files in the repository."
43-
# Find all Python files, excluding grpc generated files as per original logic.
44-
# `sort -u` ensures unique files and consistent ordering for display/xargs.
45-
CHANGED_FILES=$(find . -name '*.py' -not -path './src/a2a/grpc/*' | sort -u)
46-
47-
if [ -z "$CHANGED_FILES" ]; then
48-
echo "No Python files found to format."
49-
exit 0
50-
fi
41+
echo "Finding all tracked Python files in the repository..."
42+
CHANGED_FILES=$(git ls-files -- '*.py' ':!src/a2a/grpc/*')
5143
else
52-
echo "No '--all' flag found. Formatting changed Python files based on git diff."
44+
echo "Finding changed Python files based on git diff..."
5345
TARGET_BRANCH="origin/${GITHUB_BASE_REF:-main}"
5446
git fetch origin "${GITHUB_BASE_REF:-main}" --depth=1
5547

5648
MERGE_BASE=$(git merge-base HEAD "$TARGET_BRANCH")
5749

58-
# Get python files changed in this PR, excluding grpc generated files
50+
# Get python files changed in this PR, excluding grpc generated files.
5951
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRTUXB "$MERGE_BASE" HEAD -- '*.py' ':!src/a2a/grpc/*')
60-
61-
if [ -z "$CHANGED_FILES" ]; then
62-
echo "No changed Python files to format."
63-
exit 0
64-
fi
6552
fi
6653

67-
echo "Files to be formatted:"
68-
echo "$CHANGED_FILES"
54+
# Exit if no files were found
55+
if [ -z "$CHANGED_FILES" ]; then
56+
echo "No changed or tracked Python files to format."
57+
exit 0
58+
fi
6959

70-
# Helper function to run formatters with the list of files.
71-
# The list of files is passed to xargs via stdin.
60+
# --- Helper Function ---
61+
# Runs a command on a list of files passed via stdin.
62+
# $1: A string containing the list of files (space-separated).
63+
# $2...: The command and its arguments to run.
7264
run_formatter() {
73-
echo "$CHANGED_FILES" | xargs -r "$@"
65+
local files_to_format="$1"
66+
shift # Remove the file list from the arguments
67+
if [ -n "$files_to_format" ]; then
68+
echo "$files_to_format" | xargs -r "$@"
69+
fi
7470
}
7571

76-
echo "Running pyupgrade..."
77-
run_formatter pyupgrade --exit-zero-even-if-changed --py310-plus
78-
echo "Running autoflake..."
79-
run_formatter autoflake -i -r --remove-all-unused-imports
80-
echo "Running ruff check (fix-only)..."
81-
run_formatter ruff check --fix $RUFF_UNSAFE_FIXES_FLAG
82-
echo "Running ruff format..."
83-
run_formatter ruff format
72+
# --- Python File Formatting ---
73+
if [ -n "$CHANGED_FILES" ]; then
74+
echo "--- Formatting Python Files ---"
75+
echo "Files to be formatted:"
76+
echo "$CHANGED_FILES"
77+
78+
echo "Running autoflake..."
79+
run_formatter "$CHANGED_FILES" autoflake -i -r --remove-all-unused-imports
80+
echo "Running ruff check (fix-only)..."
81+
run_formatter "$CHANGED_FILES" ruff check --fix-only $RUFF_UNSAFE_FIXES_FLAG
82+
echo "Running ruff format..."
83+
run_formatter "$CHANGED_FILES" ruff format
84+
echo "Python formatting complete."
85+
else
86+
echo "No Python files to format."
87+
fi
8488

85-
echo "Formatting complete."
89+
echo "All formatting tasks are complete."

scripts/grpc_gen_post_processor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def process_generated_code(src_folder: str = 'src/a2a/grpc') -> None:
4747
else:
4848
print('No changes needed')
4949

50-
except Exception as e:
50+
except Exception as e: # noqa: BLE001
5151
print(f'Error processing file {file}: {e}')
5252
sys.exit(1)
5353

src/a2a/extensions/__init__.py

Whitespace-only changes.

src/a2a/server/apps/jsonrpc/jsonrpc_app.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,7 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
317317
)
318318
raise e
319319
except Exception as e:
320-
logger.error(f'Unhandled exception: {e}')
321-
traceback.print_exc()
320+
logger.exception('Unhandled exception')
322321
return self._generate_error_response(
323322
request_id, A2AError(root=InternalError(message=str(e)))
324323
)

src/a2a/server/events/event_consumer.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,11 @@ async def consume_all(self) -> AsyncGenerator[Event]:
140140
# python 3.12 and get a queue empty error on an open queue
141141
if self.queue.is_closed():
142142
break
143-
except ValidationError as e:
144-
logger.error(f'Invalid event format received: {e}')
143+
except ValidationError:
144+
logger.exception('Invalid event format received')
145145
continue
146146
except Exception as e:
147-
logger.error(
148-
f'Stopping event consumption due to exception: {e}'
149-
)
147+
logger.exception('Stopping event consumption due to exception')
150148
self._exception = e
151149
continue
152150

src/a2a/server/events/event_queue.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ async def close(self) -> None:
147147
# Otherwise, join the queue
148148
else:
149149
tasks = [asyncio.create_task(self.queue.join())]
150-
for child in self._children:
151-
tasks.append(asyncio.create_task(child.close()))
150+
tasks.extend(
151+
asyncio.create_task(child.close()) for child in self._children
152+
)
152153
await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
153154

154155
def is_closed(self) -> bool:

src/a2a/server/events/in_memory_queue_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def add(self, task_id: str, queue: EventQueue) -> None:
3434
"""
3535
async with self._lock:
3636
if task_id in self._task_queue:
37-
raise TaskQueueExists()
37+
raise TaskQueueExists
3838
self._task_queue[task_id] = queue
3939

4040
async def get(self, task_id: str) -> EventQueue | None:
@@ -67,7 +67,7 @@ async def close(self, task_id: str) -> None:
6767
"""
6868
async with self._lock:
6969
if task_id not in self._task_queue:
70-
raise NoTaskQueue()
70+
raise NoTaskQueue
7171
queue = self._task_queue.pop(task_id)
7272
await queue.close()
7373

src/a2a/server/request_handlers/default_request_handler.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ async def on_message_send(
302302
task_id, result_aggregator
303303
)
304304

305-
except Exception as e:
306-
logger.error(f'Agent execution failed. Error: {e}')
305+
except Exception:
306+
logger.exception('Agent execution failed')
307307
raise
308308
finally:
309309
if interrupted_or_non_blocking:
@@ -478,16 +478,12 @@ async def on_list_task_push_notification_config(
478478
params.id
479479
)
480480

481-
task_push_notification_config = []
482-
if push_notification_config_list:
483-
for config in push_notification_config_list:
484-
task_push_notification_config.append(
485-
TaskPushNotificationConfig(
486-
task_id=params.id, push_notification_config=config
487-
)
488-
)
489-
490-
return task_push_notification_config
481+
return [
482+
TaskPushNotificationConfig(
483+
task_id=params.id, push_notification_config=config
484+
)
485+
for config in push_notification_config_list
486+
]
491487

492488
async def on_delete_task_push_notification_config(
493489
self,

src/a2a/server/tasks/base_push_notification_sender.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ async def _dispatch_notification(
6464
logger.info(
6565
f'Push-notification sent for task_id={task.id} to URL: {url}'
6666
)
67-
return True
68-
except Exception as e:
69-
logger.error(
70-
f'Error sending push-notification for task_id={task.id} to URL: {url}. Error: {e}'
67+
except Exception:
68+
logger.exception(
69+
f'Error sending push-notification for task_id={task.id} to URL: {url}.'
7170
)
7271
return False
72+
return True

0 commit comments

Comments
 (0)