Skip to content

Commit d01aae2

Browse files
fix: ensure streams are always closed
1 parent b9e0729 commit d01aae2

1 file changed

Lines changed: 32 additions & 32 deletions

File tree

src/runloop_api_client/_streaming.py

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,22 @@ def __stream__(self) -> Iterator[_T]:
7373
process_data = self._client._process_response_data
7474
iterator = self._iter_events()
7575

76-
for sse in iterator:
77-
# Surface server-sent error events as API errors to allow callers to handle/retry
78-
if sse.event == "error":
79-
try:
80-
error_obj = json.loads(sse.data)
81-
status_code = int(error_obj.get("code", 500))
82-
# Build a synthetic response to mirror normal error handling
83-
fake_resp = httpx.Response(status_code, request=response.request, content=sse.data)
84-
except Exception:
85-
fake_resp = httpx.Response(500, request=response.request, content=sse.data)
86-
raise self._client._make_status_error_from_response(fake_resp)
87-
88-
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
89-
90-
# As we might not fully consume the response stream, we need to close it explicitly
91-
response.close()
76+
try:
77+
for sse in iterator:
78+
# Surface server-sent error events as API errors to allow callers to handle/retry
79+
if sse.event == "error":
80+
try:
81+
error_obj = json.loads(sse.data)
82+
status_code = int(error_obj.get("code", 500))
83+
fake_resp = httpx.Response(status_code, request=response.request, content=sse.data)
84+
except Exception:
85+
fake_resp = httpx.Response(500, request=response.request, content=sse.data)
86+
raise self._client._make_status_error_from_response(fake_resp)
87+
88+
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
89+
finally:
90+
# Ensure the response is closed even if the consumer doesn't read all data
91+
response.close()
9292

9393
def __enter__(self) -> Self:
9494
return self
@@ -147,22 +147,22 @@ async def __stream__(self) -> AsyncIterator[_T]:
147147
process_data = self._client._process_response_data
148148
iterator = self._iter_events()
149149

150-
async for sse in iterator:
151-
# Surface server-sent error events as API errors to allow callers to handle/retry
152-
if sse.event == "error":
153-
try:
154-
error_obj = json.loads(sse.data)
155-
status_code = int(error_obj.get("code", 500))
156-
# Build a synthetic response to mirror normal error handling
157-
fake_resp = httpx.Response(status_code, request=response.request, content=sse.data)
158-
except Exception:
159-
fake_resp = httpx.Response(500, request=response.request, content=sse.data)
160-
raise self._client._make_status_error_from_response(fake_resp)
161-
162-
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
163-
164-
# As we might not fully consume the response stream, we need to close it explicitly
165-
await response.aclose()
150+
try:
151+
async for sse in iterator:
152+
# Surface server-sent error events as API errors to allow callers to handle/retry
153+
if sse.event == "error":
154+
try:
155+
error_obj = json.loads(sse.data)
156+
status_code = int(error_obj.get("code", 500))
157+
fake_resp = httpx.Response(status_code, request=response.request, content=sse.data)
158+
except Exception:
159+
fake_resp = httpx.Response(500, request=response.request, content=sse.data)
160+
raise self._client._make_status_error_from_response(fake_resp)
161+
162+
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
163+
finally:
164+
# Ensure the response is closed even if the consumer doesn't read all data
165+
await response.aclose()
166166

167167
async def __aenter__(self) -> Self:
168168
return self

0 commit comments

Comments
 (0)