Skip to content

Commit

Permalink
refactor: Optimized the request functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
bookfere committed Jul 2, 2024
1 parent 9702e84 commit e8afa13
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 52 deletions.
9 changes: 3 additions & 6 deletions engines/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,20 +167,17 @@ def _is_auto_lang(self):

def translate(self, text):
try:
result = ''
response = request(
self.get_endpoint(), self.get_body(text), self.get_headers(),
self.method, self.request_timeout, self.proxy_uri)
if not self.stream:
response = result = response.read().decode('utf-8').strip()
self.method, self.request_timeout, self.proxy_uri, self.stream)
return self.get_result(response)
except Exception as e:
# Combine the error messages for investigation.
error_message = traceback_error()
if isinstance(e, HTTPError):
error_message += '\n\n' + e.read().decode('utf-8')
elif result != '':
error_message += '\n\n' + result
elif not self.stream and 'response' in locals():
error_message += '\n\n' + response
# Swap a valid API key if necessary.
if self.need_swap_api_key(error_message) and self.swap_api_key():
return self.translate(text)
Expand Down
2 changes: 1 addition & 1 deletion engines/deepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get_usage(self):
headers = {'Authorization': 'DeepL-Auth-Key %s' % self.api_key}
try:
response = request(self.usage_endpoint, headers=headers)
usage = json.loads(response.read().decode('utf-8').strip())
usage = json.loads(response)
except Exception:
return None
total = usage.get('character_limit')
Expand Down
3 changes: 1 addition & 2 deletions engines/microsoft.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def _parse_jwt(self, token):
def _get_app_key(self):
if not self.access_info or datetime.now() > self.access_info['Expire']:
auth_url = 'https://edge.microsoft.com/translate/auth'
response = request(auth_url, method='GET')
app_key = response.read().decode('utf-8').strip()
app_key = request(auth_url, method='GET')
self.access_info = self._parse_jwt(app_key)
else:
app_key = self.access_info['Token']
Expand Down
18 changes: 14 additions & 4 deletions lib/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ def add_translation(self, translation=None):
text_elements = (
'a', 'em', 'strong', 'small', 's', 'cite', 'q', 'time', 'samp',
'i', 'b', 'u', 'mark', 'span', 'data', 'del', 'ins')

is_text_element = element_name in text_elements

# Add translation for left or right position.
Expand All @@ -345,17 +344,28 @@ def add_translation(self, translation=None):
is_table_descendant = parent_element is not None and \
get_name(parent_element) in group_elements

if self.position == 'only':
self.element.addnext(new_element)
self._safe_remove(self.element)
return

# new_element.tag = 'span'
if self.position in ('left', 'above'):
self.element.addprevious(new_element)
# # Added translation at the start of the element.
# new_element.tail = self.element.text
# self.element.text = None
# self.element.insert(0, etree.SubElement(self.element, 'br'))
# self.element.insert(0, new_element)
if is_text_element and is_table_descendant:
new_element.addnext(etree.SubElement(self.element, 'br'))
elif is_text_element:
new_element.tail = ' '
else:
self.element.addnext(new_element)
if self.position == 'only':
self._safe_remove(self.element)
return
# # Added translation at the end of the element.
# self.element.append(etree.SubElement(self.element, 'br'))
# self.element.append(new_element)
if is_text_element and is_table_descendant:
self.element.addnext(etree.SubElement(self.element, 'br'))
elif is_text_element:
Expand Down
6 changes: 4 additions & 2 deletions lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,7 @@ def request(
except Exception:
request = Request(url, data, headers=headers, timeout=timeout)
br.open(request)

return br.response()
response = br.response()
if stream:
return response
return response.read().decode('utf-8').strip()
62 changes: 26 additions & 36 deletions tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,15 @@ def test_get_external_program(self, mock_os_path_isfile):
@patch(module_name + '.base.request')
def test_translate(self, mock_request):
self.translator.stream = False
mock_request.return_value.read.return_value.decode.return_value\
.strip.return_value = '{"text": "你好世界"}'
mock_request.return_value = '{"text": "你好世界"}'

self.assertEqual(
'{"text": "你好世界"}', self.translator.translate('Hello World'))

mock_request.assert_called_once_with(
'https://example.com/api', '{"text": "Hello World"}',
{'Authorization': 'Bearer a', 'Content-Type': 'application/json'},
'POST', 10.0, None)
mock_request().assert_has_calls([
call.read(), call.read().decode('utf-8'),
call.read().decode().strip()])
'POST', 10.0, None, False)

@patch(module_name + '.base.request')
def test_translate_with_stream(self, mock_request):
Expand All @@ -209,7 +205,7 @@ def test_translate_with_stream(self, mock_request):
mock_request.assert_called_once_with(
'https://example.com/api', '{"text": "Hello World"}',
{'Authorization': 'Bearer a', 'Content-Type': 'application/json'},
'POST', 10.0, None)
'POST', 10.0, None, True)

@patch(module_name + '.base.request')
def test_translate_with_http_error(self, mock_request):
Expand Down Expand Up @@ -238,8 +234,7 @@ def test_translate_with_http_stream_parse_error(self, mock_request):
@patch(module_name + '.base.request')
def test_translate_with_http_parse_error(self, mock_request):
self.translator.stream = False
mock_request.return_value.read.return_value.decode.return_value \
.strip.return_value = 'any unexpected result'
mock_request.return_value = 'any unexpected result'

with patch.object(self.translator, 'get_result') as mock_get_result:
with self.assertRaises(Exception) as cm:
Expand Down Expand Up @@ -353,27 +348,24 @@ def setUp(self):

@patch(module_name + '.deepl.request')
def test_get_usage(self, mock_request):
result = mock_request.return_value.read.return_value.decode \
.return_value.strip
result.return_value = '{"character_count": 30, "character_limit": 100}'
mock_request.return_value = \
'{"character_count": 30, "character_limit": 100}'

self.assertEqual(
_('{} total, {} used, {} left').format(100, 30, 70),
self.translator.get_usage(),)

result.return_value = '<dummy info>'
mock_request.return_value = '<dummy info>'
self.assertIsNone(self.translator.get_usage())

@patch(module_name + '.base.request')
def test_translate(self, mock_request):
result = mock_request.return_value.read.return_value.decode \
.return_value.strip
result.return_value = '{"translations":[{' \
mock_request.return_value = '{"translations":[{' \
'"detected_source_language":"EN","text":"你好世界!"}]}'

self.assertEqual('你好世界!', self.translator.translate('Hello World!'))

result.return_value = '<dummy info>'
mock_request.return_value = '<dummy info>'
error = re.compile(
_('Can not parse returned response. Raw data: {}')
.format('\n\nTraceback.*\n\n<dummy info>'), re.S)
Expand Down Expand Up @@ -445,16 +437,14 @@ def test_translate_stream(self, mock_request, mock_et):
'model': 'gpt-3.5-turbo',
'messages': [
{'role': 'system', 'content': prompt},
{'role': 'user', 'content': 'Hello World!'}
],
{'role': 'user', 'content': 'Hello World!'}],
'stream': True,
'temperature': 1.0})
mock_et.__version__ = '1.0.0'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer a',
'User-Agent': 'Ebook-Translator/1.0.0'}

template = b'data: {"choices":[{"delta":{"content":"%b"}}]}'
mock_response = Mock()
mock_response.readline.side_effect = [
Expand All @@ -463,14 +453,14 @@ def test_translate_stream(self, mock_request, mock_et):
mock_request.return_value = mock_response
result = self.translator.translate('Hello World!')

mock_request.assert_called_with(url, data, headers, 'POST', 30.0, None)
mock_request.assert_called_with(
url, data, headers, 'POST', 30.0, None, True)
self.assertIsInstance(result, GeneratorType)
self.assertEqual('你好世界!', ''.join(result))

@patch(module_name + '.base.request')
def test_translate_normal(self, mock_request):
result = mock_request.return_value.read.return_value.decode. \
return_value.strip.return_value = \
mock_request.return_value = \
'{"choices": [{"message": {"content": "你好世界!"}}]}'
self.translator.stream = False
result = self.translator.translate('Hello World!')
Expand Down Expand Up @@ -793,7 +783,8 @@ def test_translate_stream(self, mock_request):
'gpt-35-turbo/chat/completions?api-version=2023-05-15')
self.translator.endpoint = url
result = self.translator.translate('Hello World!')
mock_request.assert_called_with(url, data, headers, 'POST', 30.0, None)
mock_request.assert_called_with(
url, data, headers, 'POST', 30.0, None, True)
self.assertIsInstance(result, GeneratorType)
self.assertEqual('你好世界!', ''.join(result))

Expand Down Expand Up @@ -851,14 +842,14 @@ def test_translate(self, mock_request, mock_et):
}
}
"""
mock_response = Mock()
mock_response.read.return_value = data_sample.encode()
mock_request.return_value = mock_response
mock_request.return_value = data_sample.encode()
url = 'https://api.anthropic.com/v1/messages'
self.translator.endpoint = url
self.translator.stream = False
result = self.translator.translate('Hello World!')
mock_request.assert_called_with(url, data, headers, 'POST', 30.0, None)

mock_request.assert_called_with(
url, data, headers, 'POST', 30.0, None, False)
self.assertEqual('你好世界!', result)

@patch(module_name + '.anthropic.EbookTranslator')
Expand Down Expand Up @@ -924,7 +915,8 @@ def test_translate_stream(self, mock_request, mock_et):
url = 'https://api.anthropic.com/v1/messages'
self.translator.endpoint = url
result = self.translator.translate('Hello World!')
mock_request.assert_called_with(url, data, headers, 'POST', 30.0, None)
mock_request.assert_called_with(
url, data, headers, 'POST', 30.0, None, True)
self.assertIsInstance(result, GeneratorType)
self.assertEqual('你好世界!', ''.join(result))

Expand Down Expand Up @@ -1058,21 +1050,20 @@ def test_translate(self, mock_request):
translator = CustomTranslate()
translator.set_source_lang('English')
translator.set_target_lang('Chinese')
request = mock_request.return_value.read.return_value.decode
# JSON response
request.return_value = '{"text": "你好世界"}'
mock_request.return_value = '{"text": "你好世界"}'
self.assertEqual('你好世界', translator.translate('Hello "World"'))
mock_request.assert_called_with(
'https://example.api',
b'{"source": "en", "target": "zh", "text": "Hello \\"World\\""}',
{'Content-Type': 'application/json'}, 'POST', 10.0, None)
{'Content-Type': 'application/json'}, 'POST', 10.0, None, False)
# XML response
translator.response = 'response.text'
request.return_value = '<test>你好世界</test>'
mock_request.return_value = '<test>你好世界</test>'
self.assertEqual('你好世界', translator.translate('Hello World'))
# Plain response
translator.response = 'response'
request.return_value = '你好世界'
mock_request.return_value = '你好世界'
self.assertEqual('你好世界', translator.translate('Hello World'))

@patch(module_name + '.base.request')
Expand All @@ -1082,7 +1073,6 @@ def test_translate_urlencoded(self, mock_request):
del translator.request['headers']
translator.set_source_lang('English')
translator.set_target_lang('Chinese')
mock_request.return_value.read.return_value.decode \
.return_value = '{"text": "\\"你好\\"\\n世界"}'
mock_request.return_value = '{"text": "\\"你好\\"\\n世界"}'
self.assertEqual(
'\"你好\"\n世界', translator.translate('\"Hello\"\nWorld'))
24 changes: 23 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ def test_request(self, mock_browser, mock_request, mock_ssl):

self.assertIs(
request('https://example.com/api', 'test data'),
browser.response().read().decode('utf-8').strip())

browser.set_handle_robots.assert_called_once_with(False)
mock_ssl._create_unverified_context.assert_called_once_with(
cert_reqs=mock_ssl.CERT_NONE)
browser.set_ca_data.assert_called_once_with(
context=mock_ssl._create_unverified_context())
browser.set_proxies.assert_not_called()

mock_request.assert_called_once_with(
'https://example.com/api', 'test data', headers={}, timeout=30,
method='GET')
browser.open.assert_called_once_with(mock_request())

@patch(module_name + '.ssl')
@patch(module_name + '.Request')
@patch(module_name + '.Browser')
def test_request_with_stream(self, mock_browser, mock_request, mock_ssl):
browser = mock_browser()

self.assertIs(
request('https://example.com/api', 'test data', stream=True),
browser.response())

browser.set_handle_robots.assert_called_once_with(False)
Expand All @@ -109,7 +131,7 @@ def test_request_with_proxy(self, mock_browser, mock_request, mock_ssl):
'https://example.com/api', 'test data',
headers={'User-Agent': 'Test/Agent'}, method='POST',
timeout=30, proxy_uri='http://127.0.0.1:1234'),
browser.response())
browser.response().read().decode('utf-8').strip())

browser.set_handle_robots.assert_called_once_with(False)
mock_ssl._create_unverified_context.assert_called_once_with(
Expand Down

0 comments on commit e8afa13

Please sign in to comment.