Skip to content

Commit 8d416d0

Browse files
hive-botclaude
andcommitted
fix: warn when the deprecated retry and timeout globals are set
Assigning deepl.http_client.max_network_retries or min_connection_timeout now emits a DeprecationWarning naming the replacement client option. The module class is swapped for a ModuleType subclass whose __setattr__ warns; the assignment still takes effect, and reads do not warn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 646fbc6 commit 8d416d0

8 files changed

Lines changed: 68 additions & 12 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7373
`deepl.http_client.min_connection_timeout` are deprecated in favour of the
7474
`max_retries` and `timeout_s` client options, and will be removed in the
7575
next major version. They still apply to clients created without these
76-
options. No warning is emitted.
76+
options. Assigning either setting emits a `DeprecationWarning`.
7777

7878
### Fixed
7979
- Fixed the `DeprecationWarning` messages of `Usage.Detail.limit_exceeded` and

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,8 @@ failure could create a second key.
13861386

13871387
The module-level settings `deepl.http_client.max_network_retries` and
13881388
`deepl.http_client.min_connection_timeout` are deprecated and will be removed
1389-
in a future major version. They still apply to clients created without the
1389+
in a future major version. Assigning either setting emits a
1390+
`DeprecationWarning`. They still apply to clients created without the
13901391
corresponding option, and affect every client in the process:
13911392

13921393
```python

‎deepl/admin_client.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ class DeepLAdminClient(BaseClient):
5151
the requests verify documentation for more information.
5252
:param max_retries: (Optional) Maximum number of times a failed request
5353
is retried, for failures that may be transient (e.g. HTTP 429). If
54-
None, deepl.http_client.max_network_retries is used (default 5).
54+
None, the deprecated deepl.http_client.max_network_retries is used
55+
(default 5).
5556
:param timeout_s: (Optional) Minimum timeout in seconds for each attempt
56-
of a request. If None, deepl.http_client.min_connection_timeout is used
57-
(default 10.0).
57+
of a request. If None, the deprecated
58+
deepl.http_client.min_connection_timeout is used (default 10.0).
5859
5960
All functions may raise DeepLException or a subclass if a connection error
6061
occurs.

‎deepl/http_client.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,42 @@
99
import random
1010
import requests # type: ignore
1111
import socket
12+
import sys
1213
import traceback
1314
import time
15+
import types
16+
import warnings
1417
from functools import lru_cache
1518
from typing import Dict, List, Optional, Tuple, Union
1619
from .util import log_info
1720
from deepl import util
1821

1922
user_agent = None
23+
# Deprecated in favour of the max_retries and timeout_s client options; will be
24+
# removed in the next major version. Assigning either emits a warning.
2025
max_network_retries = 5
2126
min_connection_timeout = 10.0
2227

28+
_DEPRECATED_GLOBALS = {
29+
"max_network_retries": "max_retries",
30+
"min_connection_timeout": "timeout_s",
31+
}
32+
33+
34+
class _HttpClientModule(types.ModuleType):
35+
def __setattr__(self, name, value):
36+
if name in _DEPRECATED_GLOBALS:
37+
warnings.warn(
38+
f"deepl.http_client.{name} is deprecated, use the "
39+
f"{_DEPRECATED_GLOBALS[name]} client option",
40+
DeprecationWarning,
41+
stacklevel=2,
42+
)
43+
super().__setattr__(name, value)
44+
45+
46+
sys.modules[__name__].__class__ = _HttpClientModule
47+
2348

2449
class _BackoffTimer:
2550
"""Implements exponential-backoff strategy.

‎deepl/translator.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ class Translator(BaseClient):
7979
the requests verify documentation for more information.
8080
:param max_retries: (Optional) Maximum number of times a failed request
8181
is retried, for failures that may be transient (e.g. HTTP 429). If
82-
None, deepl.http_client.max_network_retries is used (default 5).
82+
None, the deprecated deepl.http_client.max_network_retries is used
83+
(default 5).
8384
:param timeout_s: (Optional) Minimum timeout in seconds for each attempt
84-
of a request. If None, deepl.http_client.min_connection_timeout is used
85-
(default 10.0).
85+
of a request. If None, the deprecated
86+
deepl.http_client.min_connection_timeout is used (default 10.0).
8687
:param skip_language_check: Deprecated, and now has no effect as the
8788
corresponding internal functionality has been removed. This parameter
8889
will be removed in a future version.

‎tests/offline/conftest.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ def mocked():
2020
def no_retries(monkeypatch):
2121
"""Makes the SDK fail on the first 429, 5xx or connection error instead
2222
of retrying with backoff."""
23-
monkeypatch.setattr(deepl.http_client, "max_network_retries", 0)
23+
# Patch the module dict: assigning the deprecated global would warn.
24+
monkeypatch.setitem(vars(deepl.http_client), "max_network_retries", 0)

‎tests/offline/test_http_client.py‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_retries_request_after_timeout(mocked):
4343

4444

4545
def test_stops_retrying_after_max_network_retries(mocked, monkeypatch):
46-
monkeypatch.setattr(deepl.http_client, "max_network_retries", 1)
46+
monkeypatch.setitem(vars(deepl.http_client), "max_network_retries", 1)
4747
mocked.get(url("v2/usage"), status=500)
4848
mocked.get(url("v2/usage"), status=500)
4949

@@ -65,7 +65,7 @@ def test_max_retries_option_overrides_global(mocked, no_retries, make_client):
6565

6666

6767
def test_max_retries_option_stops_retrying(mocked, monkeypatch):
68-
monkeypatch.setattr(deepl.http_client, "max_network_retries", 5)
68+
monkeypatch.setitem(vars(deepl.http_client), "max_network_retries", 5)
6969
mocked.get(url("v2/usage"), status=500)
7070

7171
with pytest.raises(deepl.DeepLException, match="Unexpected status code"):
@@ -83,6 +83,33 @@ def test_passes_timeout_option_to_requests(mocked, make_client):
8383
assert mocked.calls[0].request.req_kwargs["timeout"] == 42.0
8484

8585

86+
@pytest.mark.parametrize(
87+
"name, value",
88+
[("max_network_retries", 0), ("min_connection_timeout", 42.0)],
89+
)
90+
def test_assigning_deprecated_global_warns_and_takes_effect(
91+
mocked, monkeypatch, name, value
92+
):
93+
# Registers the restore of the original value at teardown.
94+
module_dict = vars(deepl.http_client)
95+
monkeypatch.setitem(module_dict, name, module_dict[name])
96+
mocked.get(url("v2/usage"), status=503)
97+
98+
with pytest.warns(DeprecationWarning, match=f"http_client.{name}") as rec:
99+
setattr(deepl.http_client, name, value)
100+
101+
assert rec[0].filename == __file__
102+
assert module_dict[name] == value
103+
with pytest.raises(deepl.DeepLException, match="Service unavailable"):
104+
make_translator(
105+
**({} if name == "max_network_retries" else {"max_retries": 0})
106+
).get_usage()
107+
# max_network_retries=0: no retry; min_connection_timeout: sent timeout
108+
assert len(mocked.calls) == 1
109+
if name == "min_connection_timeout":
110+
assert mocked.calls[0].request.req_kwargs["timeout"] == 42.0
111+
112+
86113
def test_raises_connection_exception_for_timeout_without_retries(
87114
mocked, no_retries
88115
):

‎tests/real_api/test_proxy.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313

1414
def test_sends_requests_through_proxy(monkeypatch, tmp_path):
15-
monkeypatch.setattr(deepl.http_client, "max_network_retries", 0)
15+
monkeypatch.setitem(vars(deepl.http_client), "max_network_retries", 0)
1616

1717
# proxy.py writes below the home directory unless told otherwise, which CI
1818
# runners do not have.

0 commit comments

Comments
 (0)