Skip to content

Commit 00d6caa

Browse files
feat: change internal functions to send API requests as JSON
1 parent bbc426f commit 00d6caa

4 files changed

Lines changed: 40 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77

88
## Unreleased
9+
### Changed
10+
* DeepL API calls now send requests with JSON-encoded bodies where possible.
911
### Fixed
1012
* Catch failures while constructing the user agent string.
1113

deepl/http_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def request_with_backoff(
9797
method: str,
9898
url: str,
9999
data: Optional[dict],
100+
json: Optional[dict],
100101
headers: dict,
101102
stream: bool = False,
102103
**kwargs,
@@ -117,7 +118,7 @@ def request_with_backoff(
117118
),
118119
)
119120
request = requests.Request(
120-
method, url, data=data, headers=headers, **kwargs
121+
method, url, data=data, json=json, headers=headers, **kwargs
121122
).prepare()
122123
except Exception as e:
123124
raise DeepLException(
@@ -160,6 +161,7 @@ def request(
160161
method: str,
161162
url: str,
162163
data: Optional[dict],
164+
json: Optional[dict],
163165
headers: dict,
164166
stream: bool = False,
165167
**kwargs,
@@ -182,7 +184,7 @@ def request(
182184
),
183185
)
184186
request = requests.Request(
185-
method, url, data=data, headers=headers, **kwargs
187+
method, url, data=data, json=json, headers=headers, **kwargs
186188
).prepare()
187189
except Exception as e:
188190
raise DeepLException(

deepl/translator.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ def _api_call(
511511
*,
512512
method: str = "POST",
513513
data: Optional[dict] = None,
514+
json: Optional[dict] = None,
514515
stream: bool = False,
515516
headers: Optional[dict] = None,
516517
**kwargs,
@@ -519,12 +520,15 @@ def _api_call(
519520
Makes a request to the API, and returns response as status code,
520521
content and JSON object.
521522
"""
523+
if data is not None and json is not None:
524+
raise ValueError("cannot accept both json and data")
525+
522526
if data is None:
523527
data = {}
524528
url = urllib.parse.urljoin(self._server_url, url)
525529

526530
util.log_info("Request to DeepL API", method=method, url=url)
527-
util.log_debug("Request details", data=data)
531+
util.log_debug("Request details", data=data, json=json)
528532

529533
if headers is None:
530534
headers = dict()
@@ -536,6 +540,7 @@ def _api_call(
536540
method,
537541
url,
538542
data=data,
543+
json=json,
539544
stream=stream,
540545
headers=headers,
541546
**kwargs,
@@ -703,7 +708,7 @@ def _create_glossary(
703708
}
704709

705710
status, content, json = self._api_call(
706-
"v2/glossaries", data=request_data
711+
"v2/glossaries", json=request_data
707712
)
708713
self._raise_for_status(status, content, json, glossary=True)
709714
return GlossaryInfo.from_json(json)
@@ -777,9 +782,11 @@ def translate_text(
777782
if isinstance(text, str):
778783
if len(text) == 0:
779784
raise ValueError("text must not be empty")
785+
text = [text]
780786
multi_input = False
781787
elif hasattr(text, "__iter__"):
782788
multi_input = True
789+
text = list(text)
783790
else:
784791
raise TypeError(
785792
"text parameter must be a string or an iterable of strings"
@@ -796,22 +803,20 @@ def translate_text(
796803
if split_sentences is not None:
797804
request_data["split_sentences"] = str(split_sentences)
798805
if preserve_formatting is not None:
799-
request_data["preserve_formatting"] = (
800-
"1" if preserve_formatting else "0"
801-
)
806+
request_data["preserve_formatting"] = bool(preserve_formatting)
802807
if tag_handling is not None:
803808
request_data["tag_handling"] = tag_handling
804809
if outline_detection is not None:
805-
request_data["outline_detection"] = (
806-
"1" if outline_detection else "0"
807-
)
810+
request_data["outline_detection"] = bool(outline_detection)
808811

809-
def join_tags(tag_argument: Union[str, Iterable[str]]) -> str:
810-
return (
811-
tag_argument
812-
if isinstance(tag_argument, str)
813-
else ",".join(tag_argument)
814-
)
812+
def join_tags(tag_argument: Union[str, Iterable[str]]) -> List[str]:
813+
if isinstance(tag_argument, str):
814+
tag_argument = [tag_argument]
815+
return [
816+
tag
817+
for arg_string in tag_argument
818+
for tag in arg_string.split(",")
819+
]
815820

816821
if non_splitting_tags is not None:
817822
request_data["non_splitting_tags"] = join_tags(non_splitting_tags)
@@ -821,7 +826,7 @@ def join_tags(tag_argument: Union[str, Iterable[str]]) -> str:
821826
request_data["ignore_tags"] = join_tags(ignore_tags)
822827

823828
status, content, json = self._api_call(
824-
"v2/translate", data=request_data
829+
"v2/translate", json=request_data
825830
)
826831

827832
self._raise_for_status(status, content, json)
@@ -1051,7 +1056,7 @@ def translate_document_get_status(
10511056
data = {"document_key": handle.document_key}
10521057
url = f"v2/document/{handle.document_id}"
10531058

1054-
status, content, json = self._api_call(url, data=data)
1059+
status, content, json = self._api_call(url, json=data)
10551060

10561061
self._raise_for_status(status, content, json)
10571062

@@ -1113,7 +1118,7 @@ def translate_document_download(
11131118
url = f"v2/document/{handle.document_id}/result"
11141119

11151120
status_code, response, json = self._api_call(
1116-
url, data=data, stream=True
1121+
url, json=data, stream=True
11171122
)
11181123

11191124
self._raise_for_status(

tests/test_cli.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,20 @@ def test_text(runner):
140140
("--formality more", "'formality': 'more'"),
141141
("--formality prefer_less", "'formality': 'prefer_less'"),
142142
("--split-sentences 0", "'split_sentences': '0'"),
143-
("--preserve-formatting", "'preserve_formatting': '1'"),
143+
("--preserve-formatting", "'preserve_formatting': True"),
144144
("--tag-handling xml", "'tag_handling': 'xml'"),
145-
("--outline-detection-off", "'outline_detection': '0'"),
146-
("--ignore-tags a,b --ignore-tags c", "'ignore_tags': 'a,b,c'"),
145+
("--outline-detection-off", "'outline_detection': False"),
146+
(
147+
"--ignore-tags a,b --ignore-tags c",
148+
"'ignore_tags': ['a', 'b', 'c']",
149+
),
147150
(
148151
"--splitting-tags a,b --splitting-tags c",
149-
"'splitting_tags': 'a,b,c'",
152+
"'splitting_tags': ['a', 'b', 'c']",
150153
),
151154
(
152155
"--non-splitting-tags a,b --non-splitting-tags c",
153-
"'non_splitting_tags': 'a,b,c'",
156+
"'non_splitting_tags': ['a', 'b', 'c']",
154157
),
155158
]
156159
for args, search_str in extra_options:
@@ -209,12 +212,14 @@ def test_text_tags(runner):
209212
)
210213
assert result.exit_code == 0, f"exit: {result.exit_code}\n {result.output}"
211214
# Check ignore_tags parameter is sent in HTTP request
212-
regex = re.compile("Request details.*'ignore_tags': 'a,b,c,d'")
215+
regex = re.compile(
216+
"Request details.*'ignore_tags': \\['a', 'b', 'c', 'd']"
217+
)
213218
assert any(
214219
regex.match(line) is not None for line in result.output.split("\n")
215220
), f"output:\n{result.output}"
216221
# Check splitting_tags parameter is sent in HTTP request
217-
regex = re.compile("Request details.*'splitting_tags': 'split'")
222+
regex = re.compile("Request details.*'splitting_tags': \\['split']")
218223
assert any(
219224
regex.match(line) is not None for line in result.output.split("\n")
220225
), f"output:\n{result.output}"

0 commit comments

Comments
 (0)