newsdataapi 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/PKG-INFO +1 -1
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/__init__.py +1 -1
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/client.py +17 -1
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/tests/test_unit.py +16 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/.github/workflows/ci.yml +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/.github/workflows/publish.yml +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/.gitignore +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/.python-version +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/LICENSE +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/README.md +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/examples/basic_usage.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/examples/websocket_usage.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/newsdata-logo.png +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/pyproject.toml +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/constants.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/csv_export.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/exceptions.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/py.typed +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/src/newsdataapi/websocket.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/tests/__init__.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/tests/conftest.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/tests/test_integration.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/tests/test_websocket.py +0 -0
- {newsdataapi-0.3.0 → newsdataapi-0.3.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: newsdataapi
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Official Python client (SDK) for the Newsdata.io News API — fetch real-time, historical, crypto, and stock-market news via REST, with validation, retries, pagination, and typed exceptions.
|
|
5
5
|
Project-URL: Homepage, https://newsdata.io
|
|
6
6
|
Project-URL: Documentation, https://newsdata.io/documentation
|
|
@@ -84,6 +84,8 @@ _MUTEX_GROUPS: tuple[tuple[str, ...], ...] = (
|
|
|
84
84
|
("domain", "domainurl", "excludedomain"),
|
|
85
85
|
)
|
|
86
86
|
|
|
87
|
+
_QUOTA_EXHAUSTED_CODES = frozenset({"ApiKeyLimitExceeded", "ApiLimitExceeded"})
|
|
88
|
+
|
|
87
89
|
|
|
88
90
|
def _validate_params(user_params: Mapping[str, Any]) -> dict[str, Any]:
|
|
89
91
|
"""Validate and normalize user-provided endpoint parameters.
|
|
@@ -298,6 +300,17 @@ def _parse_retry_after(value: str | None) -> int | None:
|
|
|
298
300
|
return None
|
|
299
301
|
|
|
300
302
|
|
|
303
|
+
def _extract_error_code(body: Any) -> str | None:
|
|
304
|
+
"""Return the API error code from an error response body, if present."""
|
|
305
|
+
if isinstance(body, dict):
|
|
306
|
+
results = body.get("results")
|
|
307
|
+
if isinstance(results, dict):
|
|
308
|
+
code = results.get("code")
|
|
309
|
+
if isinstance(code, str):
|
|
310
|
+
return code
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
|
|
301
314
|
def _redact_url(url: str, param: str = "apikey") -> str:
|
|
302
315
|
"""Return ``url`` with the value of ``param`` replaced by ``REDACTED``.
|
|
303
316
|
|
|
@@ -1201,7 +1214,10 @@ class NewsDataApiClient:
|
|
|
1201
1214
|
# Rate limit.
|
|
1202
1215
|
if response.status_code == 429:
|
|
1203
1216
|
retry_after = _parse_retry_after(response.headers.get("Retry-After"))
|
|
1204
|
-
if
|
|
1217
|
+
if (
|
|
1218
|
+
_extract_error_code(body) in _QUOTA_EXHAUSTED_CODES
|
|
1219
|
+
or attempt >= self.max_retries
|
|
1220
|
+
):
|
|
1205
1221
|
raise NewsdataRateLimitError(
|
|
1206
1222
|
body,
|
|
1207
1223
|
status_code=429,
|
|
@@ -823,6 +823,22 @@ def test_429_exhausted_raises_rate_limit_with_retry_after(
|
|
|
823
823
|
assert exc_info.value.retry_after == 5
|
|
824
824
|
|
|
825
825
|
|
|
826
|
+
@pytest.mark.parametrize("code", ["ApiKeyLimitExceeded", "ApiLimitExceeded"])
|
|
827
|
+
def test_429_quota_exhausted_raises_without_retry(
|
|
828
|
+
code: str,
|
|
829
|
+
client: NewsDataApiClient,
|
|
830
|
+
mocked_responses: responses.RequestsMock,
|
|
831
|
+
no_sleep: None,
|
|
832
|
+
) -> None:
|
|
833
|
+
"""A 429 whose error code means exhausted credits is never retried."""
|
|
834
|
+
body = {"status": "error", "results": {"message": "limit", "code": code}}
|
|
835
|
+
mocked_responses.get(LATEST_URL, json=body, status=429)
|
|
836
|
+
with pytest.raises(NewsdataRateLimitError) as exc_info:
|
|
837
|
+
client.latest_api()
|
|
838
|
+
assert len(mocked_responses.calls) == 1
|
|
839
|
+
assert exc_info.value.response_body == body
|
|
840
|
+
|
|
841
|
+
|
|
826
842
|
def test_500_then_200_succeeds(
|
|
827
843
|
client: NewsDataApiClient,
|
|
828
844
|
mocked_responses: responses.RequestsMock,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|