Skip to content

Commit 833c1b1

Browse files
authored
Merge branch 'main' into fix-bug#367-client_hangs
2 parents dc217a1 + d7dd6ee commit 833c1b1

26 files changed

+205
-154
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 = [

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ dev = [
8282
"pytest-cov>=6.1.1",
8383
"pytest-mock>=3.14.0",
8484
"respx>=0.20.2",
85-
"ruff>=0.11.6",
85+
"ruff>=0.12.8",
8686
"uv-dynamic-versioning>=0.8.2",
8787
"types-protobuf",
8888
"types-requests",

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/client/client_factory.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def _register_defaults(
9191
if GrpcTransport is None:
9292
raise ImportError(
9393
'To use GrpcClient, its dependencies must be installed. '
94-
'You can install them with \'pip install "a2a-sdk[grpc]"\''
94+
'You can install them with \'pip install "a2a-sdk[grpc]"\'',
9595
)
9696
self.register(
9797
TransportProtocol.grpc,
@@ -124,6 +124,20 @@ def create(
124124
If there is no valid matching of the client configuration with the
125125
server configuration, a `ValueError` is raised.
126126
"""
127+
valid_transports = {member.value for member in TransportProtocol}
128+
configured_transports = set(self._config.supported_transports)
129+
130+
invalid_transports = configured_transports.difference(valid_transports)
131+
if invalid_transports:
132+
invalid_str = ', '.join(
133+
sorted(f"'{t}'" for t in invalid_transports)
134+
)
135+
valid_str = ', '.join(sorted(valid_transports))
136+
raise ValueError(
137+
f'Unsupported transport type(s) in ClientConfig: {invalid_str}. '
138+
f'Valid types are: {valid_str}'
139+
)
140+
127141
server_preferred = card.preferred_transport or TransportProtocol.jsonrpc
128142
server_set = {server_preferred: card.url}
129143
if card.additional_interfaces:

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
@@ -158,8 +158,9 @@ async def close(self, immediate: bool = False) -> None:
158158
await self.clear_events(True)
159159
return
160160
tasks = [asyncio.create_task(self.queue.join())]
161-
for child in self._children:
162-
tasks.append(asyncio.create_task(child.close()))
161+
tasks.extend(
162+
asyncio.create_task(child.close()) for child in self._children
163+
)
163164
await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
164165

165166
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

0 commit comments

Comments
 (0)