Skip to content

Commit 6b3b0d8

Browse files
authored
ci: Switch Type checker to use ty from astral (#1044)
- https://docs.astral.sh/ty/ - Replacement for mypy/pyright with faster performance
1 parent 8c54c44 commit 6b3b0d8

19 files changed

Lines changed: 76 additions & 235 deletions

.github/workflows/linter.yaml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,10 @@ jobs:
4141
id: ruff-format
4242
run: uv run ruff format --check
4343
continue-on-error: true
44-
- name: Run MyPy Type Checker
45-
id: mypy
44+
- name: Run ty Type Checker
45+
id: ty
4646
continue-on-error: true
47-
run: uv run mypy src
48-
- name: Run Pyright (Pylance equivalent)
49-
id: pyright
50-
continue-on-error: true
51-
run: uv run pyright src
47+
run: uv run ty check --output-format=github
5248
- name: Run JSCPD for copy-paste detection
5349
id: jscpd
5450
continue-on-error: true
@@ -60,15 +56,13 @@ jobs:
6056
env:
6157
RUFF_LINT: ${{ steps.ruff-lint.outcome }}
6258
RUFF_FORMAT: ${{ steps.ruff-format.outcome }}
63-
MYPY: ${{ steps.mypy.outcome }}
64-
PYRIGHT: ${{ steps.pyright.outcome }}
59+
TY: ${{ steps.ty.outcome }}
6560
JSCPD: ${{ steps.jscpd.outcome }}
6661
run: |-
6762
failed=()
6863
[[ "$RUFF_LINT" == "failure" ]] && failed+=("Ruff Linter")
6964
[[ "$RUFF_FORMAT" == "failure" ]] && failed+=("Ruff Formatter")
70-
[[ "$MYPY" == "failure" ]] && failed+=("MyPy")
71-
[[ "$PYRIGHT" == "failure" ]] && failed+=("Pyright")
65+
[[ "$TY" == "failure" ]] && failed+=("ty")
7266
[[ "$JSCPD" == "failure" ]] && failed+=("JSCPD")
7367
if (( ${#failed[@]} )); then
7468
joined=$(IFS=', '; echo "${failed[*]}")

docs/ai/coding_conventions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Non-negotiable rules for code quality and style.
44

55
1. **Python Types**: All Python code MUST include type hints. All function definitions MUST include return types.
6-
2. **Type Safety**: All code MUST pass `mypy` and `pyright` checks.
6+
2. **Type Safety**: All code MUST pass `ty` checks.
77
3. **Formatting & Linting**: All code MUST be formatted with `ruff`.
88

99
#### Examples:

docs/ai/mandatory_checks.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ Exact shell commands required to test the project and fix formatting issues.
1010

1111
2. **Type Checking**:
1212
```bash
13-
uv run mypy src
14-
uv run pyright src
13+
uv run ty check
1514
```
1615

1716
3. **Testing**:

pyproject.toml

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ style = "pep440"
106106
[dependency-groups]
107107
dev = [
108108
"fastapi>=0.115.2",
109-
"mypy>=1.15.0",
110109
"PyJWT>=2.0.0",
111110
"pytest>=8.3.5",
112111
"pytest-asyncio>=0.26.0",
@@ -122,8 +121,8 @@ dev = [
122121
"trio",
123122
"uvicorn>=0.35.0",
124123
"pytest-timeout>=2.4.0",
125-
"pyright",
126124
"a2a-sdk[all]",
125+
"ty>=0.0.34",
127126
]
128127

129128
[[tool.uv.index]]
@@ -135,20 +134,12 @@ explicit = true
135134
[tool.uv.sources]
136135
a2a-sdk = { workspace = true }
137136

138-
[tool.mypy]
139-
plugins = ["pydantic.mypy"]
140-
exclude = ["src/a2a/types/a2a_pb2\\.py", "src/a2a/types/a2a_pb2_grpc\\.py"]
141-
disable_error_code = [
142-
"import-not-found",
143-
"annotation-unchecked",
144-
"import-untyped",
145-
]
137+
[tool.ty]
146138

147-
[[tool.mypy.overrides]]
148-
module = "examples.*"
149-
follow_imports = "skip"
139+
[tool.ty.environment]
140+
python-version = "3.10"
150141

151-
[tool.pyright]
142+
[tool.ty.src]
152143
include = ["src"]
153144
exclude = [
154145
"**/__pycache__",
@@ -161,8 +152,6 @@ exclude = [
161152
"src/a2a/compat/v0_3/*_pb2*.py",
162153
"src/a2a/compat/v0_3/proto_utils.py",
163154
]
164-
venvPath = "."
165-
venv = ".venv"
166155

167156
[tool.coverage.run]
168157
branch = true

scripts/lint.sh

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ echo -e "${BLUE}${BOLD}=== A2A Python Fixed-and-Lint Suite ===${NC}"
1515
echo -e "Fixing formatting and linting issues, then verifying types...\n"
1616

1717
# 1. Ruff Linter (with fix)
18-
echo -e "${YELLOW}${BOLD}--- [1/4] Running Ruff Linter (fix) ---${NC}"
18+
echo -e "${YELLOW}${BOLD}--- [1/3] Running Ruff Linter (fix) ---${NC}"
1919
if uv run ruff check --fix; then
2020
echo -e "${GREEN}✓ Ruff Linter passed (and fixed what it could)${NC}"
2121
else
@@ -24,29 +24,20 @@ else
2424
fi
2525

2626
# 2. Ruff Formatter
27-
echo -e "\n${YELLOW}${BOLD}--- [2/4] Running Ruff Formatter (apply) ---${NC}"
27+
echo -e "\n${YELLOW}${BOLD}--- [2/3] Running Ruff Formatter (apply) ---${NC}"
2828
if uv run ruff format; then
2929
echo -e "${GREEN}✓ Ruff Formatter applied${NC}"
3030
else
3131
echo -e "${RED}✗ Ruff Formatter failed${NC}"
3232
FAILED=1
3333
fi
3434

35-
# 3. MyPy Type Checker
36-
echo -e "\n${YELLOW}${BOLD}--- [3/4] Running MyPy Type Checker ---${NC}"
37-
if uv run mypy src; then
38-
echo -e "${GREEN}MyPy passed${NC}"
35+
# 3. ty Type Checker
36+
echo -e "\n${YELLOW}${BOLD}--- [3/3] Running ty Type Checker ---${NC}"
37+
if uv run ty check; then
38+
echo -e "${GREEN}ty passed${NC}"
3939
else
40-
echo -e "${RED}✗ MyPy failed${NC}"
41-
FAILED=1
42-
fi
43-
44-
# 4. Pyright Type Checker
45-
echo -e "\n${YELLOW}${BOLD}--- [4/4] Running Pyright ---${NC}"
46-
if uv run pyright; then
47-
echo -e "${GREEN}✓ Pyright passed${NC}"
48-
else
49-
echo -e "${RED}✗ Pyright failed${NC}"
40+
echo -e "${RED}✗ ty failed${NC}"
5041
FAILED=1
5142
fi
5243

src/a2a/client/optionals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@
1212
class Channel: # type: ignore[no-redef]
1313
"""Stub class for type hinting when grpc.aio is not available."""
1414

15+
async def close(self, grace: float | None = None) -> None:
16+
"""Stub for Channel.close."""
17+
1518
else:
1619
Channel = None # At runtime, pd will be None if the import failed.

src/a2a/client/transports/grpc.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,9 @@ async def close(self) -> None:
301301
def _get_grpc_metadata(
302302
self, context: ClientCallContext | None
303303
) -> list[tuple[str, str]]:
304-
metadata = [(VERSION_HEADER.lower(), PROTOCOL_VERSION_CURRENT)]
304+
metadata: list[tuple[str, str]] = [
305+
(VERSION_HEADER.lower(), PROTOCOL_VERSION_CURRENT)
306+
]
305307
if context and context.service_parameters:
306308
for key, value in context.service_parameters.items():
307309
metadata.append((key.lower(), value))

src/a2a/compat/v0_3/grpc_transport.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,9 @@ def _get_grpc_metadata(
359359
self, context: ClientCallContext | None = None
360360
) -> list[tuple[str, str]]:
361361
"""Creates gRPC metadata for extensions."""
362-
metadata = [(VERSION_HEADER.lower(), PROTOCOL_VERSION_0_3)]
362+
metadata: list[tuple[str, str]] = [
363+
(VERSION_HEADER.lower(), PROTOCOL_VERSION_0_3)
364+
]
363365

364366
if context and context.service_parameters:
365367
params = dict(context.service_parameters)

src/a2a/compat/v0_3/jsonrpc_transport.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,17 +398,15 @@ async def close(self) -> None:
398398
"""Closes the httpx client."""
399399
await self.httpx_client.aclose()
400400

401-
def _create_jsonrpc_error(
402-
self, error_dict: dict[str, Any]
403-
) -> A2AClientError:
401+
def _create_jsonrpc_error(self, error_dict: dict[str, Any]) -> Exception:
404402
"""Raises a specific error based on jsonrpc error code."""
405403
code = error_dict.get('code')
406404
message = error_dict.get('message', 'Unknown Error')
407405

408406
if isinstance(code, int):
409407
error_class = _JSON_RPC_ERROR_CODE_TO_A2A_ERROR.get(code)
410408
if error_class:
411-
return error_class(message) # type: ignore[return-value]
409+
return error_class(message)
412410

413411
return A2AClientError(message)
414412

src/a2a/compat/v0_3/request_handler.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import typing
32

43
from collections.abc import AsyncIterable
54

@@ -148,10 +147,7 @@ async def on_list_task_push_notification_configs(
148147
v03_resp.root,
149148
types_v03.ListTaskPushNotificationConfigSuccessResponse,
150149
):
151-
return typing.cast(
152-
'list[types_v03.TaskPushNotificationConfig]',
153-
v03_resp.root.result,
154-
)
150+
return v03_resp.root.result
155151
return []
156152

157153
async def on_delete_task_push_notification_config(

0 commit comments

Comments
 (0)