Skip to content

Commit 742e935

Browse files
committed
Enhance YandexProvider functionality and tests
- Updated YandexProvider to support folder ID and IAM token authentication. - Refactored get_translation method to handle response parsing and error handling more robustly. - Added new tests for YandexProvider covering various scenarios including autodetection, explicit language setting, and error handling. - Adjusted main function to accept new parameters for folder ID and IAM token usage.
1 parent f3bd689 commit 742e935

4 files changed

Lines changed: 134 additions & 44 deletions

File tree

tests/test_provider.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,84 @@ def raise_for_status(self):
4545
@mock.patch('requests.Session.post')
4646
def test_provider_yandex_make_request(mock_requests_post):
4747
class MockResponse:
48-
text = '"dummyjson"'
48+
text = '{"translations": [{"text": "test translation"}]}'
4949
def raise_for_status(self):
5050
return False
5151

5252
mock_requests_post.return_value = MockResponse()
5353
provider = YandexProvider(to_lang='en', secret_access_key='secret', folder_id='some_id')
54-
provider.get_translation('test')
54+
result = provider.get_translation('test')
5555
assert mock_requests_post.called
56+
assert result == 'test translation'
5657

5758
args, kwargs = mock_requests_post.call_args
5859
assert 'Authorization' in kwargs['headers']
60+
assert kwargs['headers']['Authorization'] == 'Api-Key secret'
5961
assert kwargs['json']['folderId'] == 'some_id'
6062
assert kwargs['json']['targetLanguageCode'] == 'en'
61-
assert kwargs['json']['texts'] == 'test'
63+
assert kwargs['json']['texts'] == ['test'] # Yandex API expects texts as array
64+
65+
66+
@mock.patch('requests.Session.post')
67+
def test_provider_yandex_autodetect(mock_requests_post):
68+
class MockResponse:
69+
text = '{"translations": [{"text": "translated text"}]}'
70+
def raise_for_status(self):
71+
return False
72+
73+
mock_requests_post.return_value = MockResponse()
74+
provider = YandexProvider(to_lang='fr', secret_access_key='secret')
75+
result = provider.get_translation('hello')
76+
assert result == 'translated text'
77+
78+
args, kwargs = mock_requests_post.call_args
79+
# Should not include sourceLanguageCode when autodetect
80+
assert 'sourceLanguageCode' not in kwargs['json']
81+
82+
83+
@mock.patch('requests.Session.post')
84+
def test_provider_yandex_explicit_from_lang(mock_requests_post):
85+
class MockResponse:
86+
text = '{"translations": [{"text": "bonjour"}]}'
87+
def raise_for_status(self):
88+
return False
89+
90+
mock_requests_post.return_value = MockResponse()
91+
provider = YandexProvider(to_lang='fr', from_lang='en', secret_access_key='secret')
92+
result = provider.get_translation('hello')
93+
assert result == 'bonjour'
94+
95+
args, kwargs = mock_requests_post.call_args
96+
assert kwargs['json']['sourceLanguageCode'] == 'en'
97+
98+
99+
@mock.patch('requests.Session.post')
100+
def test_provider_yandex_iam_token(mock_requests_post):
101+
class MockResponse:
102+
text = '{"translations": [{"text": "test"}]}'
103+
def raise_for_status(self):
104+
return False
105+
106+
mock_requests_post.return_value = MockResponse()
107+
provider = YandexProvider(to_lang='en', secret_access_key='iam_token', is_iam=True)
108+
provider.get_translation('test')
109+
110+
args, kwargs = mock_requests_post.call_args
111+
assert kwargs['headers']['Authorization'] == 'Bearer iam_token'
112+
113+
114+
@mock.patch('requests.Session.post')
115+
def test_provider_yandex_error_handling(mock_requests_post):
116+
import requests
117+
class MockResponse:
118+
text = '{"error": {"message": "Invalid API key"}}'
119+
def raise_for_status(self):
120+
raise requests.HTTPError("401 Client Error")
121+
122+
mock_requests_post.return_value = MockResponse()
123+
provider = YandexProvider(to_lang='en', secret_access_key='invalid_key')
124+
125+
from translate.exceptions import TranslationError
126+
import pytest
127+
with pytest.raises(TranslationError):
128+
provider.get_translation('test')

translate/main.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import locale
1010
import sys
1111

12-
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
13-
from translate import Translator
14-
from translate.version import __version__
12+
from .translate import Translator
13+
from .version import __version__
1514
from .constants import CONFIG_FILE_PATH, DEFAULT_PROVIDER, TRANSLATION_FROM_DEFAULT
1615

1716

@@ -20,7 +19,7 @@
2019

2120
def get_config_info(option):
2221
config_file_path = os.path.expanduser(CONFIG_FILE_PATH)
23-
options = ('from_lang', 'to_lang', 'provider', 'secret_access_key')
22+
options = ('from_lang', 'to_lang', 'provider', 'secret_access_key', 'region', 'folder_id')
2423
if not os.path.exists(config_file_path) or option not in options:
2524
return ''
2625

@@ -147,8 +146,21 @@ def config_file(ctx, from_lang, to_lang, provider, secret_access_key):
147146
help="Region for apis",
148147
required=False,
149148
)
149+
@click.option(
150+
'folder_id', '--folder_id',
151+
default=get_config_info('folder_id'),
152+
help="Folder ID for Yandex API",
153+
required=False,
154+
)
155+
@click.option(
156+
'is_iam', '--is_iam',
157+
default=False,
158+
is_flag=True,
159+
help="Use IAM token authentication for Yandex API",
160+
required=False,
161+
)
150162
@click.argument('text', nargs=-1, type=click.STRING, required=True)
151-
def main(from_lang, to_lang, provider, secret_access_key, output_only, pro, text, region):
163+
def main(from_lang, to_lang, provider, secret_access_key, output_only, pro, text, region, folder_id, is_iam):
152164
"""
153165
Python command line tool to make online translations
154166
@@ -172,6 +184,7 @@ def main(from_lang, to_lang, provider, secret_access_key, output_only, pro, text
172184
kwargs['pro'] = pro
173185
kwargs['region'] = region
174186
kwargs['folder_id'] = folder_id
187+
kwargs['is_iam'] = is_iam
175188

176189
translator = Translator(**kwargs)
177190
translation = translator.translate(text)

translate/providers/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
from abc import ABCMeta, abstractmethod
55
import requests
66

7+
from ..constants import TRANSLATION_FROM_DEFAULT
8+
79

810
class BaseProvider:
911
__metaclass__ = ABCMeta
1012

1113
name = ''
1214
base_url = ''
1315

14-
def __init__(self, to_lang, from_lang='en', secret_access_key=None, region=None, folder_id=None, **kwargs):
16+
def __init__(self, to_lang, from_lang=TRANSLATION_FROM_DEFAULT, secret_access_key=None, region=None, folder_id=None, **kwargs):
1517
self.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebit/535.19'
1618
'(KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19'}
1719
self.from_lang = from_lang

translate/providers/yandex.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import requests
2+
import json
23

34
from .base import BaseProvider
45
from ..constants import TRANSLATION_FROM_DEFAULT
@@ -12,48 +13,55 @@ class YandexProvider(BaseProvider):
1213
'''
1314
name = "Yandex"
1415
base_url = "https://translate.api.cloud.yandex.net/translate/v2/translate"
15-
session = None
1616

17-
def __init__(self, to_lang, from_lang=None, secret_access_key=None, base_url=None, folder_id=None, is_iam=False, **kwargs):
18-
super().__init__(to_lang)
19-
self.from_lang = from_lang
20-
self.base_url = base_url
21-
self.api = secret_access_key
22-
self.folder_id = folder_id # Folders used by Yandex API
23-
self.is_iam = is_iam # Yandex can authorise us using API or IAM tokens
17+
def __init__(self, to_lang, from_lang=TRANSLATION_FROM_DEFAULT, secret_access_key=None, base_url=None, folder_id=None, is_iam=False, **kwargs):
18+
super().__init__(to_lang, from_lang=from_lang, secret_access_key=secret_access_key, folder_id=folder_id, **kwargs)
19+
if base_url:
20+
self.base_url = base_url
21+
self.folder_id = folder_id
22+
self.is_iam = is_iam
2423

25-
def get_translation(self, text):
26-
is_autodetect = False
27-
if self.from_lang in ('autodetect', None):
28-
self.from_lang = None
29-
is_autodetect = True # We can send Yandex nothing if we want it to detect language automatically
24+
def _make_request(self, text):
25+
body = {
26+
"targetLanguageCode": self.to_lang,
27+
"texts": [text], # Yandex API expects texts as an array
28+
}
3029

31-
try:
32-
body = {
33-
"targetLanguageCode": self.to_lang,
34-
"texts": text,
35-
"folderId": self.folder_id,
36-
}
30+
if self.folder_id:
31+
body["folderId"] = self.folder_id
3732

38-
if not is_autodetect:
39-
body["sourceLanguageCode"] = self.from_lang # Inserting source language if we're not going to autodetect it
33+
if self.from_lang != TRANSLATION_FROM_DEFAULT:
34+
body["sourceLanguageCode"] = self.from_lang
4035

41-
headers = {
42-
"Content-Type": "application/json",
43-
}
36+
headers = {
37+
"Content-Type": "application/json",
38+
}
4439

45-
if self.is_iam:
46-
headers["Authorization"] = "Bearer {0}".format(self.api) # Passing to Yandex our IAM-token
47-
else:
48-
headers["Authorization"] = "Api-Key {0}".format(self.api) # Passing to Yandex our API-token
40+
if self.is_iam:
41+
headers["Authorization"] = "Bearer {0}".format(self.secret_access_key)
42+
else:
43+
headers["Authorization"] = "Api-Key {0}".format(self.secret_access_key)
4944

50-
response = self.session.post(
51-
self.base_url,
52-
json=body,
53-
headers=headers,
54-
)
45+
response = self.session.post(
46+
self.base_url,
47+
json=body,
48+
headers=headers,
49+
)
50+
response.raise_for_status()
51+
return json.loads(response.text)
5552

56-
return response.text
53+
def get_translation(self, text):
54+
try:
55+
data = self._make_request(text)
56+
57+
if "translations" in data and len(data["translations"]) > 0:
58+
return data["translations"][0]["text"]
59+
else:
60+
raise TranslationError("No translation found in response")
61+
except requests.HTTPError as e:
62+
raise TranslationError("HTTP error occurred: {0}".format(str(e)))
63+
except (KeyError, IndexError) as e:
64+
raise TranslationError("Invalid response format: {0}".format(str(e)))
5765
except Exception as e:
58-
raise TranslationError(e)
66+
raise TranslationError(str(e))
5967

0 commit comments

Comments
 (0)