Skip to content

Commit b17896b

Browse files
committed
resolve
2 parents 513ea09 + 8a35315 commit b17896b

File tree

5 files changed

+440
-7
lines changed

5 files changed

+440
-7
lines changed

src/apify_client/_http_client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ def _make_request(stop_retrying: Callable, attempt: int) -> impit.Response:
223223
if response.status_code < 500 and response.status_code != HTTPStatus.TOO_MANY_REQUESTS: # noqa: PLR2004
224224
logger.debug('Status code is not retryable', extra={'status_code': response.status_code})
225225
stop_retrying()
226+
227+
# Read the response in case it is a stream, so we can raise the error properly
228+
response.read()
226229
raise ApifyApiError(response, attempt, method=method)
227230

228231
return retry_with_exp_backoff(
@@ -306,6 +309,9 @@ async def _make_request(stop_retrying: Callable, attempt: int) -> impit.Response
306309
if response.status_code < 500 and response.status_code != HTTPStatus.TOO_MANY_REQUESTS: # noqa: PLR2004
307310
logger.debug('Status code is not retryable', extra={'status_code': response.status_code})
308311
stop_retrying()
312+
313+
# Read the response in case it is a stream, so we can raise the error properly
314+
await response.aread()
309315
raise ApifyApiError(response, attempt, method=method)
310316

311317
return await retry_with_exp_backoff_async(

tests/unit/test_client_errors.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
from __future__ import annotations
22

3+
import json
4+
import time
35
from typing import TYPE_CHECKING
46

57
import pytest
8+
from werkzeug import Response
69

710
from apify_client._errors import ApifyApiError
811
from apify_client._http_client import HTTPClient, HTTPClientAsync
912

1013
if TYPE_CHECKING:
14+
from collections.abc import Iterator
15+
1116
from pytest_httpserver import HTTPServer
17+
from werkzeug import Request
1218

1319
_TEST_PATH = '/errors'
1420
_EXPECTED_MESSAGE = 'some_message'
@@ -17,6 +23,16 @@
1723
'invalidItems': {'0': ["should have required property 'name'"], '1': ["should have required property 'name'"]}
1824
}
1925

26+
RAW_ERROR = (
27+
b'{\n'
28+
b' "error": {\n'
29+
b' "type": "insufficient-permissions",\n'
30+
b' "message": "Insufficient permissions for the Actor run. Make sure you\''
31+
b're passing a correct API token and that it has the required permissions."\n'
32+
b' }\n'
33+
b'}'
34+
)
35+
2036

2137
@pytest.fixture
2238
def test_endpoint(httpserver: HTTPServer) -> str:
@@ -26,6 +42,22 @@ def test_endpoint(httpserver: HTTPServer) -> str:
2642
return str(httpserver.url_for(_TEST_PATH))
2743

2844

45+
def streaming_handler(_request: Request) -> Response:
46+
"""Handler for streaming log requests."""
47+
48+
def generate_response() -> Iterator[bytes]:
49+
for i in range(len(RAW_ERROR)):
50+
yield RAW_ERROR[i : i + 1]
51+
time.sleep(0.01)
52+
53+
return Response(
54+
response=(RAW_ERROR[i : i + 1] for i in range(len(RAW_ERROR))),
55+
status=403,
56+
mimetype='application/octet-stream',
57+
headers={'Content-Length': str(len(RAW_ERROR))},
58+
)
59+
60+
2961
def test_client_apify_api_error_with_data(test_endpoint: str) -> None:
3062
"""Test that client correctly throws ApifyApiError with error data from response."""
3163
client = HTTPClient()
@@ -48,3 +80,35 @@ async def test_async_client_apify_api_error_with_data(test_endpoint: str) -> Non
4880
assert e.value.message == _EXPECTED_MESSAGE
4981
assert e.value.type == _EXPECTED_TYPE
5082
assert e.value.data == _EXPECTED_DATA
83+
84+
85+
def test_client_apify_api_error_streamed(httpserver: HTTPServer) -> None:
86+
"""Test that client correctly throws ApifyApiError when the response has stream."""
87+
88+
error = json.loads(RAW_ERROR.decode())
89+
90+
client = HTTPClient()
91+
92+
httpserver.expect_request('/stream_error').respond_with_handler(streaming_handler)
93+
94+
with pytest.raises(ApifyApiError) as e:
95+
client.call(method='GET', url=httpserver.url_for('/stream_error'), stream=True, parse_response=False)
96+
97+
assert e.value.message == error['error']['message']
98+
assert e.value.type == error['error']['type']
99+
100+
101+
async def test_async_client_apify_api_error_streamed(httpserver: HTTPServer) -> None:
102+
"""Test that async client correctly throws ApifyApiError when the response has stream."""
103+
104+
error = json.loads(RAW_ERROR.decode())
105+
106+
client = HTTPClientAsync()
107+
108+
httpserver.expect_request('/stream_error').respond_with_handler(streaming_handler)
109+
110+
with pytest.raises(ApifyApiError) as e:
111+
await client.call(method='GET', url=httpserver.url_for('/stream_error'), stream=True, parse_response=False)
112+
113+
assert e.value.message == error['error']['message']
114+
assert e.value.type == error['error']['type']

website/docusaurus.config.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
/* eslint-disable global-require,import/no-extraneous-dependencies */
1+
const path = require('path');
2+
23
const { config } = require('@apify/docs-theme');
4+
35
const { externalLinkProcessor } = require('./tools/utils/externalLink');
46
const { groupSort } = require('./transformDocs.js');
5-
const path = require('path');
67

78
const { absoluteUrl } = config;
89

@@ -29,9 +30,8 @@ module.exports = {
2930
trailingSlash: false,
3031
organizationName: 'apify',
3132
projectName: 'apify-client-python',
32-
scripts: ['/js/custom.js'],
3333
favicon: 'img/favicon.ico',
34-
scripts: [...(config.scripts ?? [])],
34+
scripts: ['/js/custom.js', ...(config.scripts ?? [])],
3535
onBrokenLinks:
3636
/** @type {import('@docusaurus/types').ReportingSeverity} */ ('warn'),
3737
onBrokenMarkdownLinks:
@@ -106,6 +106,19 @@ module.exports = {
106106
},
107107
},
108108
],
109+
[
110+
'@signalwire/docusaurus-plugin-llms-txt',
111+
{
112+
content: {
113+
includeVersionedDocs: false,
114+
enableLlmsFullTxt: true,
115+
includeBlog: true,
116+
includeGeneratedIndex: false,
117+
includePages: true,
118+
relativePaths: false,
119+
},
120+
},
121+
],
109122
...config.plugins,
110123
],
111124
themeConfig: {

0 commit comments

Comments
 (0)