When a request fails due to EAGAIN response, retry with exp backoff up to 20 seconds.

master
Richard O'Dwyer 2019-11-05 21:21:53 +00:00
parent e9985e7831
commit b87694e72d
3 changed files with 19 additions and 21 deletions

View File

@ -6,7 +6,7 @@ Release History
1.0.1 (unreleased) 1.0.1 (unreleased)
------------------ ------------------
- Nothing changed yet. - When a request fails due to EAGAIN response, retry with exp backoff up to 20 seconds.
1.0.0 (2019-10-31) 1.0.0 (2019-10-31)

View File

@ -1,4 +1,4 @@
requests>=0.10 requests>=0.10
pycrypto pycrypto
pathlib==1.0.1 pathlib==1.0.1
python2-secrets==1.0.5 tenacity>=5.1.5,<6.0.0

View File

@ -1,5 +1,4 @@
import re import re
import time
import json import json
import logging import logging
import secrets import secrets
@ -15,6 +14,7 @@ import tempfile
import shutil import shutil
import requests import requests
from tenacity import retry, wait_exponential, retry_if_exception_type
from .errors import ValidationError, RequestError from .errors import ValidationError, RequestError
from .crypto import ( from .crypto import (
@ -139,6 +139,10 @@ class Mega:
sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid) sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid)
self.sid = base64_url_encode(sid[:43]) self.sid = base64_url_encode(sid[:43])
@retry(
retry=retry_if_exception_type(RuntimeError),
wait=wait_exponential(multiplier=1, min=1, max=20)
)
def _api_request(self, data): def _api_request(self, data):
params = {'id': self.sequence_num} params = {'id': self.sequence_num}
self.sequence_num += 1 self.sequence_num += 1
@ -160,9 +164,9 @@ class Mega:
json_resp = json.loads(req.text) json_resp = json.loads(req.text)
if isinstance(json_resp, int): if isinstance(json_resp, int):
if json_resp == -3: if json_resp == -3:
logger.info('Request failed, retrying...') msg = 'Request failed, retrying'
time.sleep(10) logger.info(msg)
return self._api_request(data=data) raise RuntimeError(msg)
raise RequestError(json_resp) raise RequestError(json_resp)
return json_resp[0] return json_resp[0]
@ -810,7 +814,9 @@ class Mega:
timeout=self.timeout timeout=self.timeout
) )
completion_file_handle = output_file.text completion_file_handle = output_file.text
logger.info('%s of %s uploaded', upload_progress, file_size) logger.info(
'%s of %s uploaded', upload_progress, file_size
)
else: else:
output_file = requests.post( output_file = requests.post(
ul_url + "/0", data='', timeout=self.timeout ul_url + "/0", data='', timeout=self.timeout
@ -843,6 +849,7 @@ class Mega:
{ {
'a': 'p', 'a': 'p',
't': dest, 't': dest,
'i': self.request_id,
'n': [ 'n': [
{ {
'h': completion_file_handle, 'h': completion_file_handle,
@ -919,9 +926,8 @@ class Mega:
encrypted_key = a32_to_base64( encrypted_key = a32_to_base64(
encrypt_key(file['key'], self.master_key) encrypt_key(file['key'], self.master_key)
) )
# update attributes # update attributes
data = self._api_request( return self._api_request(
[ [
{ {
'a': 'a', 'a': 'a',
@ -932,7 +938,6 @@ class Mega:
} }
] ]
) )
return data
def move(self, file_id, target): def move(self, file_id, target):
""" """
@ -957,7 +962,7 @@ class Mega:
# determine target_node_id # determine target_node_id
if type(target) == int: if type(target) == int:
target_node_id = str(self.get_node_by_type(target)[0]) target_node_id = str(self.get_node_by_type(target)[0])
elif type(target) in (str, str): elif type(target) in (str, ):
target_node_id = target target_node_id = target
else: else:
file = target[1] file = target[1]
@ -1042,9 +1047,7 @@ class Mega:
unencrypted_attrs = decrypt_attr(base64_url_decode(data['at']), k) unencrypted_attrs = decrypt_attr(base64_url_decode(data['at']), k)
if not unencrypted_attrs: if not unencrypted_attrs:
return None return None
result = {'size': size, 'name': unencrypted_attrs['n']} result = {'size': size, 'name': unencrypted_attrs['n']}
return result return result
def import_public_file( def import_public_file(
@ -1053,7 +1056,6 @@ class Mega:
""" """
Import the public file into user account Import the public file into user account
""" """
# Providing dest_node spare an API call to retrieve it. # Providing dest_node spare an API call to retrieve it.
if dest_node is None: if dest_node is None:
# Get '/Cloud Drive' folder no dest node specified # Get '/Cloud Drive' folder no dest node specified
@ -1071,13 +1073,10 @@ class Mega:
encrypted_key = a32_to_base64(encrypt_key(key, self.master_key)) encrypted_key = a32_to_base64(encrypt_key(key, self.master_key))
encrypted_name = base64_url_encode(encrypt_attr({'n': dest_name}, k)) encrypted_name = base64_url_encode(encrypt_attr({'n': dest_name}, k))
return self._api_request(
data = self._api_request(
{ {
'a': 'a': 'p',
'p', 't': dest_node['h'],
't':
dest_node['h'],
'n': [ 'n': [
{ {
'ph': file_handle, 'ph': file_handle,
@ -1088,4 +1087,3 @@ class Mega:
] ]
} }
) )
return data