11from __future__ import annotations
22
3- from typing import Iterator , AsyncIterator
3+ from typing import TypeVar , Iterator , AsyncIterator
44
55import httpx
66import pytest
77
88from anthropic import Anthropic , AsyncAnthropic
99from anthropic ._streaming import Stream , AsyncStream , ServerSentEvent
10+ from anthropic ._exceptions import APIStatusError
11+
12+ _T = TypeVar ("_T" )
1013
1114
1215@pytest .mark .asyncio
@@ -216,6 +219,25 @@ def body() -> Iterator[bytes]:
216219 assert sse .json () == {"content" : "известни" }
217220
218221
222+ @pytest .mark .parametrize ("sync" , [True , False ], ids = ["sync" , "async" ])
223+ async def test_error_type (
224+ sync : bool ,
225+ client : Anthropic ,
226+ async_client : AsyncAnthropic ,
227+ ) -> None :
228+ def body () -> Iterator [bytes ]:
229+ yield b"event: error\n "
230+ yield b'data: {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}}\n \n '
231+
232+ iterator = make_stream_iterator (content = body (), sync = sync , client = client , async_client = async_client )
233+
234+ with pytest .raises (APIStatusError ) as exc_info :
235+ await iter_next (iterator )
236+
237+ assert exc_info .value .type == "overloaded_error"
238+ assert "Overloaded" in str (exc_info .value )
239+
240+
219241def test_isinstance_check (client : Anthropic , async_client : AsyncAnthropic ) -> None :
220242 async_stream = AsyncStream (cast_to = object , client = async_client , response = httpx .Response (200 , content = b"foo" ))
221243 assert isinstance (async_stream , AsyncStream )
@@ -229,7 +251,7 @@ async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]:
229251 yield chunk
230252
231253
232- async def iter_next (iter : Iterator [ServerSentEvent ] | AsyncIterator [ServerSentEvent ]) -> ServerSentEvent :
254+ async def iter_next (iter : Iterator [_T ] | AsyncIterator [_T ]) -> _T :
233255 if isinstance (iter , AsyncIterator ):
234256 return await iter .__anext__ ()
235257
@@ -254,3 +276,27 @@ def make_event_iterator(
254276 return AsyncStream (
255277 cast_to = object , client = async_client , response = httpx .Response (200 , content = to_aiter (content ))
256278 )._iter_events ()
279+
280+
281+ # Unlike make_event_iterator which only parses SSE events using _iter_events(),
282+ # this helper uses __stream__() to process the full stream pipeline including
283+ # parsing message objects and converting error events into raised exceptions.
284+ def make_stream_iterator (
285+ content : Iterator [bytes ],
286+ * ,
287+ sync : bool ,
288+ client : Anthropic ,
289+ async_client : AsyncAnthropic ,
290+ ) -> AsyncIterator [object ] | Iterator [object ]:
291+ if sync :
292+ return Stream (
293+ cast_to = object ,
294+ client = client ,
295+ response = httpx .Response (200 , content = content , request = httpx .Request ("GET" , "https://example.com" )),
296+ ).__stream__ ()
297+
298+ return AsyncStream (
299+ cast_to = object ,
300+ client = async_client ,
301+ response = httpx .Response (200 , content = to_aiter (content ), request = httpx .Request ("GET" , "https://example.com" )),
302+ ).__stream__ ()
0 commit comments