celitech-sdk 2.0.6__tar.gz → 2.0.7__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.
- {celitech_sdk-2.0.6/src/celitech_sdk.egg-info → celitech_sdk-2.0.7}/PKG-INFO +5 -5
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/PyPI_README.md +4 -4
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/README.md +4 -4
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/pyproject.toml +1 -1
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/http_handler.py +3 -3
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/retry_handler.py +98 -5
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/sdk_config.py +4 -2
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/serializer.py +1 -1
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/sdk.py +33 -3
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/sdk_async.py +8 -1
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/utils/base_service.py +16 -4
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7/src/celitech_sdk.egg-info}/PKG-INFO +5 -5
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/LICENSE +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/setup.cfg +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/bad_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_v2_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_v2_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/edit_purchase_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/edit_purchase_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_esim_device_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_esim_history_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_esim_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_purchase_consumption_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/grant_type.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/list_destinations_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/list_packages_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/list_purchases_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/o_auth_token_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/o_auth_token_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/token_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/top_up_esim_ok_response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/top_up_esim_request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/unauthorized.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/base_error.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/base_model.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/cast_models.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/json_map.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/one_of_base_model.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/utils/sentinel.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/environment/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/environment/environment.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/headers/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/headers/base_header.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/oauth/token_manager.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/base_handler.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/oauth_handler.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/request_chain.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/api_error.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/request.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/request_error.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/response.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/transport/utils.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/destinations.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/e_sim.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/i_frame.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/o_auth.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/packages.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/purchases.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/utils/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/async_/utils/to_async.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/destinations.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/e_sim.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/i_frame.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/o_auth.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/packages.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/purchases.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/utils/__init__.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/utils/default_headers.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/services/utils/validator.py +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech_sdk.egg-info/SOURCES.txt +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech_sdk.egg-info/dependency_links.txt +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech_sdk.egg-info/requires.txt +0 -0
- {celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: celitech-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.7
|
|
4
4
|
Summary: Welcome to the CELITECH API documentation!
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/Celitech/CelitechSDKPython
|
|
@@ -11,14 +11,14 @@ Requires-Dist: requests>=2.31.0
|
|
|
11
11
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
12
12
|
Dynamic: license-file
|
|
13
13
|
|
|
14
|
-
# Celitech Python SDK 2.0.
|
|
14
|
+
# Celitech Python SDK 2.0.7<a id="celitech-python-sdk-207"></a>
|
|
15
15
|
|
|
16
16
|
Welcome to the Celitech SDK documentation. This guide will help you get started with integrating and using the Celitech SDK in your project.
|
|
17
17
|
|
|
18
18
|
## Versions<a id="versions"></a>
|
|
19
19
|
|
|
20
|
-
- API version: `2.0.
|
|
21
|
-
- SDK version: `2.0.
|
|
20
|
+
- API version: `2.0.7`
|
|
21
|
+
- SDK version: `2.0.7`
|
|
22
22
|
|
|
23
23
|
## About the API<a id="about-the-api"></a>
|
|
24
24
|
|
|
@@ -102,7 +102,7 @@ You can set a custom timeout for the SDK's HTTP requests as follows:
|
|
|
102
102
|
```py
|
|
103
103
|
from celitech import Celitech
|
|
104
104
|
|
|
105
|
-
sdk = Celitech(timeout=
|
|
105
|
+
sdk = Celitech(timeout=10)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
# Sample Usage<a id="sample-usage"></a>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Celitech Python SDK 2.0.
|
|
1
|
+
# Celitech Python SDK 2.0.7<a id="celitech-python-sdk-207"></a>
|
|
2
2
|
|
|
3
3
|
Welcome to the Celitech SDK documentation. This guide will help you get started with integrating and using the Celitech SDK in your project.
|
|
4
4
|
|
|
5
5
|
## Versions<a id="versions"></a>
|
|
6
6
|
|
|
7
|
-
- API version: `2.0.
|
|
8
|
-
- SDK version: `2.0.
|
|
7
|
+
- API version: `2.0.7`
|
|
8
|
+
- SDK version: `2.0.7`
|
|
9
9
|
|
|
10
10
|
## About the API<a id="about-the-api"></a>
|
|
11
11
|
|
|
@@ -89,7 +89,7 @@ You can set a custom timeout for the SDK's HTTP requests as follows:
|
|
|
89
89
|
```py
|
|
90
90
|
from celitech import Celitech
|
|
91
91
|
|
|
92
|
-
sdk = Celitech(timeout=
|
|
92
|
+
sdk = Celitech(timeout=10)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
# Sample Usage<a id="sample-usage"></a>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# Celitech Python SDK 2.0.
|
|
1
|
+
# Celitech Python SDK 2.0.7
|
|
2
2
|
|
|
3
3
|
Welcome to the Celitech SDK documentation. This guide will help you get started with integrating and using the Celitech SDK in your project.
|
|
4
4
|
|
|
5
5
|
## Versions
|
|
6
6
|
|
|
7
|
-
- API version: `2.0.
|
|
8
|
-
- SDK version: `2.0.
|
|
7
|
+
- API version: `2.0.7`
|
|
8
|
+
- SDK version: `2.0.7`
|
|
9
9
|
|
|
10
10
|
## About the API
|
|
11
11
|
|
|
@@ -90,7 +90,7 @@ You can set a custom timeout for the SDK's HTTP requests as follows:
|
|
|
90
90
|
```py
|
|
91
91
|
from celitech import Celitech
|
|
92
92
|
|
|
93
|
-
sdk = Celitech(timeout=
|
|
93
|
+
sdk = Celitech(timeout=10)
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
# Sample Usage
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "celitech-sdk"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.7"
|
|
8
8
|
license = { text = "MIT" }
|
|
9
9
|
description = """Welcome to the CELITECH API documentation!\n\nUseful links: [Homepage](https://www.celitech.com) | [Support email](mailto:support@celitech.com) | [Blog](https://www.celitech.com/blog/)\n"""
|
|
10
10
|
readme = "PyPI_README.md"
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/http_handler.py
RENAMED
|
@@ -17,12 +17,12 @@ class HttpHandler(BaseHandler):
|
|
|
17
17
|
:ivar int _timeout_in_seconds: The timeout for the HTTP request in seconds.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
def __init__(self, timeout=
|
|
20
|
+
def __init__(self, timeout=60):
|
|
21
21
|
"""
|
|
22
22
|
Initialize a new instance of HttpHandler.
|
|
23
23
|
"""
|
|
24
24
|
super().__init__()
|
|
25
|
-
self._timeout_in_seconds = timeout
|
|
25
|
+
self._timeout_in_seconds = timeout
|
|
26
26
|
|
|
27
27
|
def handle(
|
|
28
28
|
self, request: Request
|
|
@@ -191,5 +191,5 @@ class HttpHandler(BaseHandler):
|
|
|
191
191
|
:rtype: float
|
|
192
192
|
"""
|
|
193
193
|
if request.config and "timeout" in request.config:
|
|
194
|
-
return request.config["timeout"]
|
|
194
|
+
return request.config["timeout"]
|
|
195
195
|
return self._timeout_in_seconds
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/retry_handler.py
RENAMED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import random
|
|
2
|
+
import re
|
|
2
3
|
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from email.utils import parsedate_to_datetime
|
|
3
6
|
from typing import Generator, Optional, Tuple
|
|
4
7
|
from time import sleep
|
|
5
8
|
from .base_handler import BaseHandler
|
|
@@ -31,6 +34,7 @@ class RetryHandler(BaseHandler):
|
|
|
31
34
|
self._max_attempts = 3
|
|
32
35
|
self._delay_in_milliseconds = 150
|
|
33
36
|
self._max_delay_in_milliseconds = 5000
|
|
37
|
+
self._max_retry_after_delay_in_milliseconds = 60000
|
|
34
38
|
self._backoff_factor = 2
|
|
35
39
|
self._jitter_in_milliseconds = 50
|
|
36
40
|
self._status_codes_to_retry = None
|
|
@@ -62,7 +66,7 @@ class RetryHandler(BaseHandler):
|
|
|
62
66
|
|
|
63
67
|
try_count = 0
|
|
64
68
|
while try_count < max_attempts and self._should_retry(error, request):
|
|
65
|
-
self._delay(try_count, request)
|
|
69
|
+
self._delay(try_count, request, error)
|
|
66
70
|
response, error = self._next_handler.handle(request)
|
|
67
71
|
try_count += 1
|
|
68
72
|
|
|
@@ -92,7 +96,7 @@ class RetryHandler(BaseHandler):
|
|
|
92
96
|
while True:
|
|
93
97
|
response, error = next(stream)
|
|
94
98
|
if try_count < max_attempts and self._should_retry(error, request):
|
|
95
|
-
self._delay(try_count, request)
|
|
99
|
+
self._delay(try_count, request, error)
|
|
96
100
|
try_count += 1
|
|
97
101
|
stream = self._next_handler.stream(request) # Retry the request
|
|
98
102
|
elif try_count >= max_attempts:
|
|
@@ -104,16 +108,29 @@ class RetryHandler(BaseHandler):
|
|
|
104
108
|
except StopIteration:
|
|
105
109
|
pass
|
|
106
110
|
|
|
107
|
-
def _delay(
|
|
111
|
+
def _delay(
|
|
112
|
+
self, try_count: int, request: Request, error: Optional[ApiError] = None
|
|
113
|
+
) -> None:
|
|
108
114
|
"""
|
|
109
|
-
Calculate and apply delay before next retry attempt
|
|
110
|
-
|
|
115
|
+
Calculate and apply delay before next retry attempt.
|
|
116
|
+
A server rate-limit timing header (Retry-After / X-RateLimit-Reset) on the error,
|
|
117
|
+
when present, overrides the computed exponential backoff; otherwise exponential
|
|
118
|
+
backoff (capped at max delay, with optional jitter) is used.
|
|
111
119
|
Per-request retry config overrides SDK defaults.
|
|
112
120
|
|
|
113
121
|
:param int try_count: Current retry attempt number (0-indexed).
|
|
114
122
|
:param Request request: The request being retried, used to read per-request retry config.
|
|
123
|
+
:param Optional[ApiError] error: The error that triggered the retry (carries headers).
|
|
115
124
|
"""
|
|
116
125
|
retry_config = (request.config or {}).get("retry") or {}
|
|
126
|
+
max_retry_after = retry_config.get(
|
|
127
|
+
"max_retry_after_delay_ms", self._max_retry_after_delay_in_milliseconds
|
|
128
|
+
)
|
|
129
|
+
header_delay_ms = self._retry_after_delay(error, max_retry_after)
|
|
130
|
+
if header_delay_ms is not None:
|
|
131
|
+
sleep(header_delay_ms / 1000)
|
|
132
|
+
return
|
|
133
|
+
|
|
117
134
|
base_delay = retry_config.get("delay_ms", self._delay_in_milliseconds)
|
|
118
135
|
max_delay = retry_config.get("max_delay_ms", self._max_delay_in_milliseconds)
|
|
119
136
|
backoff_factor = retry_config.get("backoff_factor", self._backoff_factor)
|
|
@@ -132,6 +149,82 @@ class RetryHandler(BaseHandler):
|
|
|
132
149
|
# Convert to seconds and sleep
|
|
133
150
|
sleep(delay / 1000)
|
|
134
151
|
|
|
152
|
+
def _retry_after_delay(
|
|
153
|
+
self, error: Optional[ApiError], max_retry_after_ms: float
|
|
154
|
+
) -> Optional[float]:
|
|
155
|
+
"""
|
|
156
|
+
Return the server-directed retry delay (in milliseconds) from rate-limit response
|
|
157
|
+
headers, honoring Retry-After (delta-seconds or HTTP-date) and, when absent,
|
|
158
|
+
X-RateLimit-Reset (epoch seconds), clamped to max_retry_after_ms. Returns None when
|
|
159
|
+
no usable header is present so the caller falls back to exponential backoff.
|
|
160
|
+
|
|
161
|
+
:param Optional[ApiError] error: The error that triggered the retry.
|
|
162
|
+
:param float max_retry_after_ms: Upper bound for a server-directed delay.
|
|
163
|
+
:return: The delay in milliseconds, or None to use exponential backoff.
|
|
164
|
+
:rtype: Optional[float]
|
|
165
|
+
"""
|
|
166
|
+
headers = getattr(getattr(error, "response", None), "headers", None)
|
|
167
|
+
if headers is None or max_retry_after_ms <= 0:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
# retry-after-ms (milliseconds) is a non-standard but finer-grained hint some APIs
|
|
171
|
+
# send (e.g. OpenAI); it takes precedence over the whole-second Retry-After.
|
|
172
|
+
retry_after_ms = headers.get("retry-after-ms")
|
|
173
|
+
if retry_after_ms is not None and re.fullmatch(
|
|
174
|
+
r"\d+(\.\d+)?", str(retry_after_ms).strip()
|
|
175
|
+
):
|
|
176
|
+
return min(float(str(retry_after_ms).strip()), max_retry_after_ms)
|
|
177
|
+
|
|
178
|
+
seconds = self._parse_retry_after(headers.get("Retry-After"))
|
|
179
|
+
if seconds is not None:
|
|
180
|
+
return min(max(seconds * 1000, 0), max_retry_after_ms)
|
|
181
|
+
|
|
182
|
+
# X-RateLimit-Reset is interpreted as epoch seconds (the common convention).
|
|
183
|
+
reset = headers.get("X-RateLimit-Reset")
|
|
184
|
+
if reset is not None and str(reset).strip() != "":
|
|
185
|
+
try:
|
|
186
|
+
epoch = float(reset)
|
|
187
|
+
except (TypeError, ValueError):
|
|
188
|
+
epoch = None
|
|
189
|
+
if epoch is not None:
|
|
190
|
+
delta_ms = epoch * 1000 - datetime.now(timezone.utc).timestamp() * 1000
|
|
191
|
+
if delta_ms > 0:
|
|
192
|
+
return min(delta_ms, max_retry_after_ms)
|
|
193
|
+
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
def _parse_retry_after(self, value: Optional[str]) -> Optional[float]:
|
|
197
|
+
"""
|
|
198
|
+
Parse a Retry-After header value: an integer/float number of seconds, or an
|
|
199
|
+
HTTP-date (a past date yields 0). Returns the delay in seconds, or None if the
|
|
200
|
+
value is empty or unparseable.
|
|
201
|
+
|
|
202
|
+
:param Optional[str] value: The raw Retry-After header value.
|
|
203
|
+
:return: The delay in seconds, or None.
|
|
204
|
+
:rtype: Optional[float]
|
|
205
|
+
"""
|
|
206
|
+
if value is None:
|
|
207
|
+
return None
|
|
208
|
+
trimmed = str(value).strip()
|
|
209
|
+
if trimmed == "":
|
|
210
|
+
return None
|
|
211
|
+
if re.fullmatch(r"\d+(\.\d+)?", trimmed):
|
|
212
|
+
return float(trimmed)
|
|
213
|
+
# Parsing and the timestamp math both run under the guard: a pathological but
|
|
214
|
+
# well-formed HTTP-date (e.g. year 9999) can raise OverflowError/OSError from
|
|
215
|
+
# timestamp(), and a malformed value can raise beyond TypeError/ValueError. A bad
|
|
216
|
+
# server header must never crash the retry layer, so treat any failure as "no delay".
|
|
217
|
+
try:
|
|
218
|
+
parsed = parsedate_to_datetime(trimmed)
|
|
219
|
+
if parsed is None:
|
|
220
|
+
return None
|
|
221
|
+
if parsed.tzinfo is None:
|
|
222
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
223
|
+
delta = parsed.timestamp() - datetime.now(timezone.utc).timestamp()
|
|
224
|
+
except (TypeError, ValueError, OverflowError, OSError):
|
|
225
|
+
return None
|
|
226
|
+
return delta if delta > 0 else 0.0
|
|
227
|
+
|
|
135
228
|
def _should_retry(self, error: Optional[ApiError], request: Request) -> bool:
|
|
136
229
|
"""
|
|
137
230
|
Determine whether the request should be retried based on status code and HTTP method.
|
|
@@ -14,6 +14,7 @@ class RetryConfig(TypedDict, total=False):
|
|
|
14
14
|
:ivar int jitter_ms: Maximum random jitter in milliseconds to add to retry delays.
|
|
15
15
|
:ivar list[int] status_codes_to_retry: Specific HTTP status codes to retry (overrides default 5xx + 408, 429).
|
|
16
16
|
:ivar list[str] http_methods_to_retry: HTTP methods to retry (e.g., ['GET', 'POST']).
|
|
17
|
+
:ivar int max_retry_after_delay_ms: Upper bound (ms) for a server-directed retry delay parsed from Retry-After / X-RateLimit-Reset response headers.
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
20
|
attempts: int
|
|
@@ -23,6 +24,7 @@ class RetryConfig(TypedDict, total=False):
|
|
|
23
24
|
jitter_ms: int
|
|
24
25
|
status_codes_to_retry: list[int]
|
|
25
26
|
http_methods_to_retry: list[str]
|
|
27
|
+
max_retry_after_delay_ms: int
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class ValidationConfig(TypedDict, total=False):
|
|
@@ -47,7 +49,7 @@ class SdkConfig(TypedDict, total=False):
|
|
|
47
49
|
|
|
48
50
|
:ivar str base_url: Base URL for API requests. Can be a string URL or Environment enum.
|
|
49
51
|
:ivar Environment environment: Environment enum value for base URL.
|
|
50
|
-
:ivar
|
|
52
|
+
:ivar float timeout: Request timeout in seconds.
|
|
51
53
|
:ivar str client_id: OAuth client ID.
|
|
52
54
|
:ivar str client_secret: OAuth client secret.
|
|
53
55
|
:ivar RetryConfig retry: Retry configuration.
|
|
@@ -56,7 +58,7 @@ class SdkConfig(TypedDict, total=False):
|
|
|
56
58
|
|
|
57
59
|
base_url: str
|
|
58
60
|
environment: Environment
|
|
59
|
-
timeout:
|
|
61
|
+
timeout: float
|
|
60
62
|
client_id: str
|
|
61
63
|
client_secret: str
|
|
62
64
|
retry: RetryConfig
|
|
@@ -45,7 +45,7 @@ class Serializer:
|
|
|
45
45
|
self.errors: dict[int, ApiError] = {}
|
|
46
46
|
self.config: Optional["SdkConfig"] = config
|
|
47
47
|
|
|
48
|
-
self.headers["User-Agent"] = "postman-codegen/
|
|
48
|
+
self.headers["User-Agent"] = "postman-codegen/2.6.0 celitech-sdk/2.0.7 (python)"
|
|
49
49
|
|
|
50
50
|
# Apply default headers
|
|
51
51
|
for header in default_headers:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from typing import Union
|
|
2
3
|
from .services.destinations import DestinationsService
|
|
3
4
|
from .services.packages import PackagesService
|
|
@@ -17,10 +18,13 @@ class Celitech:
|
|
|
17
18
|
|
|
18
19
|
def __init__(
|
|
19
20
|
self,
|
|
21
|
+
*,
|
|
20
22
|
client_id: str = None,
|
|
21
23
|
client_secret: str = None,
|
|
22
24
|
base_url: Union[Environment, str, None] = None,
|
|
23
|
-
timeout:
|
|
25
|
+
timeout: float = None,
|
|
26
|
+
timeout_ms: int = None,
|
|
27
|
+
retry: "RetryConfig" = None,
|
|
24
28
|
base_oauth_url: str = None,
|
|
25
29
|
):
|
|
26
30
|
"""
|
|
@@ -52,7 +56,18 @@ class Celitech:
|
|
|
52
56
|
)
|
|
53
57
|
self.set_client_id(client_id)
|
|
54
58
|
self.set_client_secret(client_secret)
|
|
59
|
+
if timeout_ms is not None:
|
|
60
|
+
warnings.warn(
|
|
61
|
+
"`timeout_ms` is deprecated; use `timeout` (in seconds) instead.",
|
|
62
|
+
DeprecationWarning,
|
|
63
|
+
stacklevel=2,
|
|
64
|
+
)
|
|
65
|
+
timeout = timeout_ms / 1000 if timeout is None else timeout
|
|
66
|
+
if timeout is None:
|
|
67
|
+
timeout = 60
|
|
55
68
|
self.set_timeout(timeout)
|
|
69
|
+
if retry is not None:
|
|
70
|
+
self.set_retry(retry)
|
|
56
71
|
|
|
57
72
|
def set_base_url(self, base_url: Union[Environment, str]):
|
|
58
73
|
"""
|
|
@@ -83,11 +98,11 @@ class Celitech:
|
|
|
83
98
|
|
|
84
99
|
return self
|
|
85
100
|
|
|
86
|
-
def set_timeout(self, timeout:
|
|
101
|
+
def set_timeout(self, timeout: float):
|
|
87
102
|
"""
|
|
88
103
|
Sets the timeout for the entire SDK.
|
|
89
104
|
|
|
90
|
-
:param
|
|
105
|
+
:param float timeout: The timeout (in seconds) to be set.
|
|
91
106
|
:return: The SDK instance.
|
|
92
107
|
"""
|
|
93
108
|
self.destinations.set_timeout(timeout)
|
|
@@ -98,6 +113,21 @@ class Celitech:
|
|
|
98
113
|
|
|
99
114
|
return self
|
|
100
115
|
|
|
116
|
+
def set_retry(self, retry: "RetryConfig"):
|
|
117
|
+
"""
|
|
118
|
+
Sets the retry configuration for the entire SDK.
|
|
119
|
+
|
|
120
|
+
:param RetryConfig retry: The retry configuration to be set.
|
|
121
|
+
:return: The SDK instance.
|
|
122
|
+
"""
|
|
123
|
+
self.destinations.set_retry(retry)
|
|
124
|
+
self.packages.set_retry(retry)
|
|
125
|
+
self.purchases.set_retry(retry)
|
|
126
|
+
self.e_sim.set_retry(retry)
|
|
127
|
+
self.i_frame.set_retry(retry)
|
|
128
|
+
|
|
129
|
+
return self
|
|
130
|
+
|
|
101
131
|
def set_client_id(self, client_id: str):
|
|
102
132
|
"""
|
|
103
133
|
Sets the client_id for the entire SDK.
|
|
@@ -16,10 +16,13 @@ class CelitechAsync(Celitech):
|
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
18
18
|
self,
|
|
19
|
+
*,
|
|
19
20
|
client_id: str = None,
|
|
20
21
|
client_secret: str = None,
|
|
21
22
|
base_url: Union[Environment, str, None] = None,
|
|
22
|
-
timeout:
|
|
23
|
+
timeout: float = None,
|
|
24
|
+
timeout_ms: int = None,
|
|
25
|
+
retry: "RetryConfig" = None,
|
|
23
26
|
base_oauth_url: str = None,
|
|
24
27
|
):
|
|
25
28
|
super().__init__(
|
|
@@ -27,6 +30,8 @@ class CelitechAsync(Celitech):
|
|
|
27
30
|
client_secret=client_secret,
|
|
28
31
|
base_url=base_url,
|
|
29
32
|
timeout=timeout,
|
|
33
|
+
timeout_ms=timeout_ms,
|
|
34
|
+
retry=retry,
|
|
30
35
|
base_oauth_url=base_oauth_url,
|
|
31
36
|
)
|
|
32
37
|
|
|
@@ -48,3 +53,5 @@ class CelitechAsync(Celitech):
|
|
|
48
53
|
self.o_auth = OAuthServiceAsync(
|
|
49
54
|
base_url=self._base_url, token_manager=self._token_manager
|
|
50
55
|
)
|
|
56
|
+
if retry is not None:
|
|
57
|
+
self.set_retry(retry)
|
|
@@ -11,7 +11,7 @@ from ...net.oauth.token_manager import TokenManager
|
|
|
11
11
|
from ...net.request_chain.handlers.oauth_handler import OauthHandler
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
-
from ...net.sdk_config import SdkConfig
|
|
14
|
+
from ...net.sdk_config import SdkConfig, RetryConfig
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class BaseService:
|
|
@@ -30,18 +30,18 @@ class BaseService:
|
|
|
30
30
|
"""
|
|
31
31
|
self.base_url = base_url.rstrip("/") if base_url else base_url
|
|
32
32
|
self._default_headers = DefaultHeaders()
|
|
33
|
-
self._timeout =
|
|
33
|
+
self._timeout = 60
|
|
34
34
|
self._service_config: "SdkConfig" = {}
|
|
35
35
|
|
|
36
36
|
self._token_manager = token_manager
|
|
37
37
|
|
|
38
38
|
self._update_request_handler()
|
|
39
39
|
|
|
40
|
-
def set_timeout(self, timeout:
|
|
40
|
+
def set_timeout(self, timeout: float):
|
|
41
41
|
"""
|
|
42
42
|
Sets the timeout for the service.
|
|
43
43
|
|
|
44
|
-
:param
|
|
44
|
+
:param float timeout: The timeout (in seconds) to be set.
|
|
45
45
|
:return: The service instance.
|
|
46
46
|
"""
|
|
47
47
|
self._timeout = timeout
|
|
@@ -69,6 +69,18 @@ class BaseService:
|
|
|
69
69
|
self._service_config = config
|
|
70
70
|
return self
|
|
71
71
|
|
|
72
|
+
def set_retry(self, retry: "RetryConfig"):
|
|
73
|
+
"""
|
|
74
|
+
Sets the service-level retry configuration, merging it into any existing config.
|
|
75
|
+
|
|
76
|
+
:param RetryConfig retry: The retry configuration to be set.
|
|
77
|
+
:return: The service instance for method chaining.
|
|
78
|
+
"""
|
|
79
|
+
self._service_config = self._deep_merge(
|
|
80
|
+
self._service_config or {}, {"retry": retry}
|
|
81
|
+
)
|
|
82
|
+
return self
|
|
83
|
+
|
|
72
84
|
@staticmethod
|
|
73
85
|
def _deep_merge(base: dict, override: dict) -> dict:
|
|
74
86
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: celitech-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.7
|
|
4
4
|
Summary: Welcome to the CELITECH API documentation!
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/Celitech/CelitechSDKPython
|
|
@@ -11,14 +11,14 @@ Requires-Dist: requests>=2.31.0
|
|
|
11
11
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
12
12
|
Dynamic: license-file
|
|
13
13
|
|
|
14
|
-
# Celitech Python SDK 2.0.
|
|
14
|
+
# Celitech Python SDK 2.0.7<a id="celitech-python-sdk-207"></a>
|
|
15
15
|
|
|
16
16
|
Welcome to the Celitech SDK documentation. This guide will help you get started with integrating and using the Celitech SDK in your project.
|
|
17
17
|
|
|
18
18
|
## Versions<a id="versions"></a>
|
|
19
19
|
|
|
20
|
-
- API version: `2.0.
|
|
21
|
-
- SDK version: `2.0.
|
|
20
|
+
- API version: `2.0.7`
|
|
21
|
+
- SDK version: `2.0.7`
|
|
22
22
|
|
|
23
23
|
## About the API<a id="about-the-api"></a>
|
|
24
24
|
|
|
@@ -102,7 +102,7 @@ You can set a custom timeout for the SDK's HTTP requests as follows:
|
|
|
102
102
|
```py
|
|
103
103
|
from celitech import Celitech
|
|
104
104
|
|
|
105
|
-
sdk = Celitech(timeout=
|
|
105
|
+
sdk = Celitech(timeout=10)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
# Sample Usage<a id="sample-usage"></a>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_ok_response.py
RENAMED
|
File without changes
|
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/create_purchase_v2_ok_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_esim_device_ok_response.py
RENAMED
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/get_esim_history_ok_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/models/list_destinations_ok_response.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/__init__.py
RENAMED
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/base_handler.py
RENAMED
|
File without changes
|
{celitech_sdk-2.0.6 → celitech_sdk-2.0.7}/src/celitech/net/request_chain/handlers/oauth_handler.py
RENAMED
|
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
|
|
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
|