22from __future__ import annotations
33
44import json
5- from typing import TYPE_CHECKING , Any , Generic , Iterator , AsyncIterator
6- from typing_extensions import override
5+ from types import TracebackType
6+ from typing import TYPE_CHECKING , Any , Generic , TypeVar , Iterator , AsyncIterator , cast
7+ from typing_extensions import Self , override
78
89import httpx
910
10- from ._types import ResponseT
11-
1211if TYPE_CHECKING :
1312 from ._client import Finch , AsyncFinch
1413
1514
16- class Stream (Generic [ResponseT ]):
15+ _T = TypeVar ("_T" )
16+
17+
18+ class Stream (Generic [_T ]):
1719 """Provides the core interface to iterate over a synchronous stream response."""
1820
1921 response : httpx .Response
2022
2123 def __init__ (
2224 self ,
2325 * ,
24- cast_to : type [ResponseT ],
26+ cast_to : type [_T ],
2527 response : httpx .Response ,
2628 client : Finch ,
2729 ) -> None :
@@ -31,18 +33,18 @@ def __init__(
3133 self ._decoder = SSEDecoder ()
3234 self ._iterator = self .__stream__ ()
3335
34- def __next__ (self ) -> ResponseT :
36+ def __next__ (self ) -> _T :
3537 return self ._iterator .__next__ ()
3638
37- def __iter__ (self ) -> Iterator [ResponseT ]:
39+ def __iter__ (self ) -> Iterator [_T ]:
3840 for item in self ._iterator :
3941 yield item
4042
4143 def _iter_events (self ) -> Iterator [ServerSentEvent ]:
4244 yield from self ._decoder .iter (self .response .iter_lines ())
4345
44- def __stream__ (self ) -> Iterator [ResponseT ]:
45- cast_to = self ._cast_to
46+ def __stream__ (self ) -> Iterator [_T ]:
47+ cast_to = cast ( Any , self ._cast_to )
4648 response = self .response
4749 process_data = self ._client ._process_response_data
4850 iterator = self ._iter_events ()
@@ -54,16 +56,35 @@ def __stream__(self) -> Iterator[ResponseT]:
5456 for _sse in iterator :
5557 ...
5658
59+ def __enter__ (self ) -> Self :
60+ return self
61+
62+ def __exit__ (
63+ self ,
64+ exc_type : type [BaseException ] | None ,
65+ exc : BaseException | None ,
66+ exc_tb : TracebackType | None ,
67+ ) -> None :
68+ self .close ()
69+
70+ def close (self ) -> None :
71+ """
72+ Close the response and release the connection.
73+
74+ Automatically called if the response body is read to completion.
75+ """
76+ self .response .close ()
5777
58- class AsyncStream (Generic [ResponseT ]):
78+
79+ class AsyncStream (Generic [_T ]):
5980 """Provides the core interface to iterate over an asynchronous stream response."""
6081
6182 response : httpx .Response
6283
6384 def __init__ (
6485 self ,
6586 * ,
66- cast_to : type [ResponseT ],
87+ cast_to : type [_T ],
6788 response : httpx .Response ,
6889 client : AsyncFinch ,
6990 ) -> None :
@@ -73,19 +94,19 @@ def __init__(
7394 self ._decoder = SSEDecoder ()
7495 self ._iterator = self .__stream__ ()
7596
76- async def __anext__ (self ) -> ResponseT :
97+ async def __anext__ (self ) -> _T :
7798 return await self ._iterator .__anext__ ()
7899
79- async def __aiter__ (self ) -> AsyncIterator [ResponseT ]:
100+ async def __aiter__ (self ) -> AsyncIterator [_T ]:
80101 async for item in self ._iterator :
81102 yield item
82103
83104 async def _iter_events (self ) -> AsyncIterator [ServerSentEvent ]:
84105 async for sse in self ._decoder .aiter (self .response .aiter_lines ()):
85106 yield sse
86107
87- async def __stream__ (self ) -> AsyncIterator [ResponseT ]:
88- cast_to = self ._cast_to
108+ async def __stream__ (self ) -> AsyncIterator [_T ]:
109+ cast_to = cast ( Any , self ._cast_to )
89110 response = self .response
90111 process_data = self ._client ._process_response_data
91112 iterator = self ._iter_events ()
@@ -97,6 +118,25 @@ async def __stream__(self) -> AsyncIterator[ResponseT]:
97118 async for _sse in iterator :
98119 ...
99120
121+ async def __aenter__ (self ) -> Self :
122+ return self
123+
124+ async def __aexit__ (
125+ self ,
126+ exc_type : type [BaseException ] | None ,
127+ exc : BaseException | None ,
128+ exc_tb : TracebackType | None ,
129+ ) -> None :
130+ await self .close ()
131+
132+ async def close (self ) -> None :
133+ """
134+ Close the response and release the connection.
135+
136+ Automatically called if the response body is read to completion.
137+ """
138+ await self .response .aclose ()
139+
100140
101141class ServerSentEvent :
102142 def __init__ (
0 commit comments