Skip to content

Commit 7244431

Browse files
authored
Merge branch 'main' into jbu/eval
2 parents 31471b8 + 1aa4dd7 commit 7244431

28 files changed

+227
-171
lines changed

.github/workflows/unit-tests.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,15 @@ jobs:
4040
steps:
4141
- name: Checkout code
4242
uses: actions/checkout@v4
43-
- name: Set up Python ${{ matrix.python-version }}
44-
uses: actions/setup-python@v5
45-
with:
46-
python-version: ${{ matrix.python-version }}
4743
- name: Set up test environment variables
4844
run: |
4945
echo "POSTGRES_TEST_DSN=postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test" >> $GITHUB_ENV
5046
echo "MYSQL_TEST_DSN=mysql+aiomysql://a2a:a2a_password@localhost:3306/a2a_test" >> $GITHUB_ENV
5147
52-
- name: Install uv
48+
- name: Install uv for Python ${{ matrix.python-version }}
5349
uses: astral-sh/setup-uv@v6
50+
with:
51+
python-version: ${{ matrix.python-version }}
5452
- name: Add uv to PATH
5553
run: |
5654
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

.ruff.toml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ ignore = [
2929
"ANN002",
3030
"ANN003",
3131
"ANN401",
32+
"TRY003",
33+
"G004",
34+
"TRY201",
35+
"FIX002",
3236
]
3337

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

6280
exclude = [
@@ -104,7 +122,7 @@ ignore-decorators = ["typing.overload", "abc.abstractmethod"]
104122

105123
[lint.flake8-annotations]
106124
mypy-init-return = true
107-
allow-star-arg-any = true
125+
allow-star-arg-any = false
108126

109127
[lint.pep8-naming]
110128
ignore-names = ["test_*", "setUp", "tearDown", "mock_*"]
@@ -139,6 +157,7 @@ inline-quotes = "single"
139157
"types.py" = ["D", "E501"] # Ignore docstring and annotation issues in types.py
140158
"proto_utils.py" = ["D102", "PLR0911"]
141159
"helpers.py" = ["ANN001", "ANN201", "ANN202"]
160+
"scripts/*.py" = ["INP001"]
142161

143162
[format]
144163
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/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

0 commit comments

Comments
 (0)