Source code for acdcli.api.client

import configparser
import logging
import os
import json
import requests
import time

from acdcli.utils.conf import get_conf

from . import oauth
from .backoff_req import BackOffRequest
from .common import *
from .account import AccountMixin
from .content import ContentMixin
from .metadata import MetadataMixin
from .trash import TrashMixin

logger = logging.getLogger(__name__)

_EXP_TIME_KEY = 'exp_time'
_AMZ_ENDPOINT_REQ_URL = 'https://drive.amazonaws.com/drive/v1/account/endpoint'

_SETTINGS_FILENAME = 'acd_client.ini'

_def_conf = configparser.ConfigParser()
_def_conf['endpoints'] = dict(filename='endpoint_data', validity_duration=259200)
_def_conf['transfer'] = dict(fs_chunk_size=128 * 1024, dl_chunk_size=500 * 1024 ** 2,
                             chunk_retries=1, connection_timeout=30, idle_timeout=60)
_def_conf['proxies'] = dict()


[docs]class ACDClient(AccountMixin, ContentMixin, MetadataMixin, TrashMixin): """Provides a client to the Amazon Cloud Drive RESTful interface."""
[docs] def __init__(self, cache_path='', settings_path=''): """Initializes OAuth and endpoints.""" self._conf = get_conf(settings_path, _SETTINGS_FILENAME, _def_conf) self.cache_path = cache_path logger.info('Initializing ACD with path "%s".' % cache_path) self.handler = oauth.create_handler(cache_path) self._endpoint_data = {} self._load_endpoints() requests_timeout = (self._conf.getint('transfer', 'connection_timeout'), self._conf.getint('transfer', 'idle_timeout')) proxies = dict(self._conf['proxies']) self.BOReq = BackOffRequest(self.handler, requests_timeout, proxies)
@property def _endpoint_data_path(self): return os.path.join(self.cache_path, self._conf['endpoints']['filename']) def _load_endpoints(self): """Tries to load endpoints from file and calls :meth:`_get_endpoints` on failure or if they are outdated.""" if not os.path.isfile(self._endpoint_data_path): self._endpoint_data = self._get_endpoints() else: with open(self._endpoint_data_path) as ep: self._endpoint_data = json.load(ep) if time.time() > self._endpoint_data[_EXP_TIME_KEY]: logger.info('Endpoint data expired.') self._endpoint_data = self._get_endpoints() def _get_endpoints(self) -> dict: """Retrieves Amazon endpoints and saves them on success. :raises: ValueError if requests returned invalid JSON :raises: KeyError if endpoint data does not include expected keys""" r = requests.get(_AMZ_ENDPOINT_REQ_URL, auth=self.handler) if r.status_code not in OK_CODES: logger.critical('Error getting endpoint data. Response: %s' % r.text) raise Exception try: e = r.json() except ValueError as e: logger.critical('Invalid JSON: "%s"' % r.text) raise e e[_EXP_TIME_KEY] = time.time() + self._conf.getint('endpoints', 'validity_duration') self._endpoint_data = e try: self.metadata_url self.content_url except KeyError as e: logger.critical('Received invalid endpoint data.') raise e self._save_endpoint_data() return e def _save_endpoint_data(self): f = open(self._endpoint_data_path, 'w') json.dump(self._endpoint_data, f, indent=4, sort_keys=True) f.flush() os.fsync(f.fileno()) f.close() @property def metadata_url(self): return self._endpoint_data['metadataUrl'] @property def content_url(self): return self._endpoint_data['contentUrl']