Commit e25ba7b
feat(client): expose close() and async context manager support on abstract Client (#719)
# Description
Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make
sure it goes smoothly:
- [x] Follow the [`CONTRIBUTING`
Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md).
- [x] Make your Pull Request title in the
<https://www.conventionalcommits.org/> specification.
- Important Prefixes for
[release-please](https://github.com/googleapis/release-please):
- `fix:` which represents bug fixes, and correlates to a
[SemVer](https://semver.org/) patch.
- `feat:` represents a new feature, and correlates to a SemVer minor.
- `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking
change (indicated by the `!`) and will result in a SemVer major.
- [x] Ensure the tests and linter pass (Run `bash scripts/format.sh`
from the repository root to format)
- [x] Appropriate docs were updated (if necessary)
Fixes #689 🦕
### Problem
The abstract `Client` class (`src/a2a/client/client.py`) — the public
interface returned by `ClientFactory.connect()` — does not declare
`close()`, `__aenter__`, or `__aexit__`. Code typed against `Client`
cannot use the async context manager pattern or call `close()`:
```python
client = await ClientFactory.connect(card)
# client is typed as Client, not BaseClient
async with client: # TypeError
...
await client.close() # AttributeError
```
`ClientTransport` already supports this protocol since #682, and
`BaseClient` since #688, but the gap at the abstract interface level
means the pattern is unavailable to consumers that depend on the
`Client` type.
The integration tests reflect this — they rely on `hasattr` checks
instead of the type system:
```python
if hasattr(transport, 'close'):
await transport.close()
```
### Fix
- Add `close()` as an abstract method on `Client`, consistent with the
existing 7 abstract methods.
- Add `__aenter__` and `__aexit__` as concrete methods on `Client`,
delegating to `close()`. `__aenter__` returns `Self` (via
`typing_extensions`), matching the convention established in
`ClientTransport` (#682).
`BaseClient` already implements `close()` (delegating to
`self._transport.close()`), so it satisfies the new abstract method with
no additional changes.
This enables the idiomatic pattern at the `Client` abstraction level:
```python
async with await ClientFactory.connect(card) as client:
async for event in client.send_message(msg):
...
# close() called automatically, even on exceptions
```
### Tests
No new tests needed. The existing
`test_base_client_async_context_manager` and
`test_base_client_async_context_manager_on_exception` in
`test_base_client.py` already exercise the
`__aenter__`/`__aexit__`/`close()` chain through `BaseClient`, which
inherits these methods from `Client`. Happy to add dedicated tests if
maintainers prefer a different approach.
---------
Co-authored-by: Ivan Shymko <ishymko@google.com>1 parent 7dec763 commit e25ba7b
2 files changed
Lines changed: 20 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | 3 | | |
5 | | - | |
6 | | - | |
7 | 4 | | |
8 | 5 | | |
9 | 6 | | |
| |||
51 | 48 | | |
52 | 49 | | |
53 | 50 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | 51 | | |
68 | 52 | | |
69 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
| 12 | + | |
10 | 13 | | |
11 | 14 | | |
12 | 15 | | |
| |||
110 | 113 | | |
111 | 114 | | |
112 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
113 | 129 | | |
114 | 130 | | |
115 | 131 | | |
| |||
242 | 258 | | |
243 | 259 | | |
244 | 260 | | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
0 commit comments