Skip to content

Commit 6161957

Browse files
Allow providing a custom retry policy. (#2946)
1 parent 4779b9d commit 6161957

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

.generator/src/generator/templates/configuration.j2

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ class Configuration:
142142
:type retry_backoff_factor: float
143143
:param max_retries: The maximum number of times a single request can be retried.
144144
:type max_retries: int
145+
:param retry_policy: Custom retry policy instance (e.g., urllib3.util.Retry). If provided, this overrides
146+
the default retry behavior and the enable_retry, retry_backoff_factor, and max_retries settings.
147+
:type retry_policy: urllib3.util.Retry
145148
:param delegated_auth_provider: The delegated authentication provider (e.g., 'aws' for AWS).
146149
:type delegated_auth_provider: str
147150
:param delegated_auth_org_uuid: The organization UUID for delegated authentication.
@@ -173,6 +176,7 @@ class Configuration:
173176
enable_retry=False,
174177
retry_backoff_factor=2,
175178
max_retries=3,
179+
retry_policy=None,
176180
delegated_auth_provider=None,
177181
delegated_auth_org_uuid=None,
178182
):
@@ -237,6 +241,7 @@ class Configuration:
237241
self.enable_retry = enable_retry
238242
self.retry_backoff_factor = retry_backoff_factor
239243
self.max_retries = max_retries
244+
self.retry_policy = retry_policy
240245

241246
# Keep track of unstable operations
242247
self.unstable_operations = _UnstableOperations({

.generator/src/generator/templates/rest.j2

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ class RESTClientObject:
6767
if configuration.assert_hostname is not None:
6868
addition_pool_args["assert_hostname"] = configuration.assert_hostname
6969

70-
if configuration.enable_retry:
70+
if configuration.retry_policy is not None:
71+
addition_pool_args["retries"] = configuration.retry_policy
72+
elif configuration.enable_retry:
7173
retries = ClientRetry(
7274
total = configuration.max_retries,
7375
backoff_factor = configuration.retry_backoff_factor,

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,29 @@ The default max retry is `3`, you can change it with `max_retries`
9999
configuration.max_retries = 5
100100
```
101101

102+
#### Custom retry policy
103+
104+
You can provide a custom retry policy by passing a `urllib3.util.Retry` instance to the configuration.
105+
When a custom retry policy is provided, it takes precedence over the `enable_retry`, `retry_backoff_factor`,
106+
and `max_retries` settings.
107+
108+
```python
109+
import urllib3
110+
from datadog_api_client import Configuration
111+
112+
# Create a custom retry policy
113+
custom_retry = urllib3.util.Retry(
114+
total=5,
115+
backoff_factor=2,
116+
status_forcelist=[429, 500, 502, 503, 504],
117+
allowed_methods=["GET", "POST", "PUT", "DELETE"]
118+
)
119+
120+
configuration = Configuration(retry_policy=custom_retry)
121+
```
122+
123+
See [urllib3.util.Retry documentation](https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html#urllib3.util.Retry) for more details.
124+
102125
### Configure proxy
103126

104127
You can configure the client to use proxy by setting the `proxy` key on configuration object:

src/datadog_api_client/configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ class Configuration:
143143
:type retry_backoff_factor: float
144144
:param max_retries: The maximum number of times a single request can be retried.
145145
:type max_retries: int
146+
:param retry_policy: Custom retry policy instance (e.g., urllib3.util.Retry). If provided, this overrides
147+
the default retry behavior and the enable_retry, retry_backoff_factor, and max_retries settings.
148+
:type retry_policy: urllib3.util.Retry
146149
:param delegated_auth_provider: The delegated authentication provider (e.g., 'aws' for AWS).
147150
:type delegated_auth_provider: str
148151
:param delegated_auth_org_uuid: The organization UUID for delegated authentication.
@@ -174,6 +177,7 @@ def __init__(
174177
enable_retry=False,
175178
retry_backoff_factor=2,
176179
max_retries=3,
180+
retry_policy=None,
177181
delegated_auth_provider=None,
178182
delegated_auth_org_uuid=None,
179183
):
@@ -238,6 +242,7 @@ def __init__(
238242
self.enable_retry = enable_retry
239243
self.retry_backoff_factor = retry_backoff_factor
240244
self.max_retries = max_retries
245+
self.retry_policy = retry_policy
241246

242247
# Keep track of unstable operations
243248
self.unstable_operations = _UnstableOperations(

src/datadog_api_client/rest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def __init__(self, configuration, pools_size=4, maxsize=4):
6969
if configuration.assert_hostname is not None:
7070
addition_pool_args["assert_hostname"] = configuration.assert_hostname
7171

72-
if configuration.enable_retry:
72+
if configuration.retry_policy is not None:
73+
addition_pool_args["retries"] = configuration.retry_policy
74+
elif configuration.enable_retry:
7375
retries = ClientRetry(
7476
total=configuration.max_retries,
7577
backoff_factor=configuration.retry_backoff_factor,

tests/test_retry.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,55 @@ def test_retry_backoff_factor_validation():
4242
configuration.retry_backoff_factor = 1
4343

4444
configuration.retry_backoff_factor = 3
45+
46+
47+
@mock.patch("time.sleep", return_value=None)
48+
def test_custom_retry_policy(sleep_mock):
49+
"""Test that a custom retry policy is used when provided"""
50+
import urllib3
51+
52+
# Create a custom retry policy with different settings
53+
custom_retry = urllib3.util.Retry(
54+
total=5, # Different from default 3
55+
backoff_factor=1, # Different from default 2
56+
status_forcelist=[500, 502, 503, 504],
57+
allowed_methods=["GET", "POST"],
58+
)
59+
60+
configuration = Configuration(retry_policy=custom_retry)
61+
62+
with vcr.use_cassette("tests/cassettes/test_retry/test_retry_errors.yaml", record_mode=vcr.mode.NONE):
63+
with ApiClient(configuration) as api_client:
64+
api_instance = logs_api.LogsApi(api_client)
65+
logs = api_instance.list_logs_get()
66+
assert len(logs.data) == 10
67+
# With backoff_factor=1, sleep times are: 2, 4
68+
assert sleep_mock.call_count == 2
69+
assert sleep_mock.call_args_list[0][0][0] == 2
70+
assert sleep_mock.call_args_list[1][0][0] == 4
71+
72+
73+
def test_custom_retry_policy_overrides_enable_retry():
74+
"""Test that retry_policy takes precedence over enable_retry"""
75+
import urllib3
76+
77+
custom_retry = urllib3.util.Retry(total=10)
78+
configuration = Configuration(
79+
enable_retry=True, # This should be ignored
80+
max_retries=3, # This should be ignored
81+
retry_policy=custom_retry,
82+
)
83+
84+
# Verify the configuration accepts the custom policy
85+
assert configuration.retry_policy is custom_retry
86+
assert configuration.retry_policy.total == 10
87+
88+
89+
def test_default_retry_when_no_custom_policy():
90+
"""Test that default retry behavior works when no custom policy is provided"""
91+
configuration = Configuration(enable_retry=True, max_retries=5)
92+
93+
# Verify no custom policy is set
94+
assert configuration.retry_policy is None
95+
assert configuration.enable_retry is True
96+
assert configuration.max_retries == 5

0 commit comments

Comments
 (0)