loaders: Add LoaderChain.
This commit is contained in:
parent
10b0a818d7
commit
1927a18120
4 changed files with 115 additions and 5 deletions
|
@ -1,4 +1,5 @@
|
||||||
import cgi
|
import cgi
|
||||||
|
import functools
|
||||||
import io
|
import io
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
@ -19,6 +20,10 @@ class LoaderSourceError(LoaderError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoLoadersError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FileCache:
|
class FileCache:
|
||||||
def __init__(self, dir_path, filename_pattern):
|
def __init__(self, dir_path, filename_pattern):
|
||||||
self.dir_path = dir_path
|
self.dir_path = dir_path
|
||||||
|
@ -75,3 +80,32 @@ class OXRAPIRequest:
|
||||||
'historical/{}.json'.format(date.isoformat()),
|
'historical/{}.json'.format(date.isoformat()),
|
||||||
{'app_id': self.app_id, 'base': base},
|
{'app_id': self.app_id, 'base': base},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LoaderChain:
|
||||||
|
def __init__(self):
|
||||||
|
self.loaders = []
|
||||||
|
|
||||||
|
def add_loader(self, loader):
|
||||||
|
self.loaders.append(loader)
|
||||||
|
|
||||||
|
def _wrap_load_method(orig_func):
|
||||||
|
@functools.wraps(orig_func)
|
||||||
|
def load_wrapper(self, *args, **kwargs):
|
||||||
|
self.used_loader = None
|
||||||
|
error = None
|
||||||
|
for loader in self.loaders:
|
||||||
|
try:
|
||||||
|
response = getattr(loader, orig_func.__name__)(*args, **kwargs)
|
||||||
|
except LoaderError as this_error:
|
||||||
|
error = this_error
|
||||||
|
else:
|
||||||
|
self.used_loader = loader
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
raise NoLoadersError() if error is None else error
|
||||||
|
return load_wrapper
|
||||||
|
|
||||||
|
@_wrap_load_method
|
||||||
|
def historical(self, date, base):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
|
import datetime
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import random
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
TEST_DIR = pathlib.Path(__file__).parent
|
TEST_DIR = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
def relpath(*parts):
|
def relpath(*parts):
|
||||||
return TEST_DIR / pathlib.Path(*parts)
|
return TEST_DIR / pathlib.Path(*parts)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def any_date():
|
||||||
|
return datetime.date.today() - datetime.timedelta(days=730 - random.randint(0, 365))
|
||||||
|
|
71
tests/test_LoaderChain.py
Normal file
71
tests/test_LoaderChain.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import io
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import oxrlib.loaders
|
||||||
|
|
||||||
|
from . import any_date
|
||||||
|
|
||||||
|
SUCCESS_S = '"success"\n'
|
||||||
|
ERROR = oxrlib.loaders.LoaderNoDataError("test")
|
||||||
|
|
||||||
|
class FakeLoader:
|
||||||
|
def __init__(self, result):
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
def _respond(self, *args, **kwargs):
|
||||||
|
return io.StringIO(self.result)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self._respond
|
||||||
|
|
||||||
|
|
||||||
|
class FakeErrorLoader(FakeLoader):
|
||||||
|
def __init__(self, error):
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
def _respond(self, *args, **kwargs):
|
||||||
|
raise self.error
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def lchain():
|
||||||
|
return oxrlib.loaders.LoaderChain()
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def good_loader():
|
||||||
|
return FakeLoader(SUCCESS_S)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def error_loader():
|
||||||
|
return FakeErrorLoader(ERROR)
|
||||||
|
|
||||||
|
def test_no_loaders(lchain, any_date):
|
||||||
|
try:
|
||||||
|
lchain.historical(any_date, 'USD')
|
||||||
|
except oxrlib.loaders.NoLoadersError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert False, "expected NoLoadersError not raised"
|
||||||
|
|
||||||
|
def test_one_with_success(lchain, any_date, good_loader):
|
||||||
|
lchain.add_loader(good_loader)
|
||||||
|
response = lchain.historical(any_date, 'USD')
|
||||||
|
assert response.read(32) == SUCCESS_S
|
||||||
|
|
||||||
|
def test_two_with_success(lchain, any_date, good_loader, error_loader):
|
||||||
|
lchain.add_loader(error_loader)
|
||||||
|
lchain.add_loader(good_loader)
|
||||||
|
response = lchain.historical(any_date, 'USD')
|
||||||
|
assert response.read(32) == SUCCESS_S
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('count', [1, 2])
|
||||||
|
def test_no_success(lchain, any_date, error_loader, count):
|
||||||
|
for _ in range(count):
|
||||||
|
lchain.add_loader(error_loader)
|
||||||
|
try:
|
||||||
|
lchain.historical(any_date, 'USD')
|
||||||
|
except type(ERROR) as error:
|
||||||
|
assert error is ERROR
|
||||||
|
else:
|
||||||
|
assert False, "{} not raised".format(type(ERROR).__name__)
|
|
@ -1,4 +1,3 @@
|
||||||
import datetime
|
|
||||||
import http.client
|
import http.client
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
@ -10,6 +9,8 @@ import urllib.parse
|
||||||
import pytest
|
import pytest
|
||||||
import oxrlib.loaders
|
import oxrlib.loaders
|
||||||
|
|
||||||
|
from . import any_date
|
||||||
|
|
||||||
APPID_CHARS = string.ascii_letters + string.digits
|
APPID_CHARS = string.ascii_letters + string.digits
|
||||||
RANDOM_APPID = ''.join(random.choice(APPID_CHARS) for _ in range(32))
|
RANDOM_APPID = ''.join(random.choice(APPID_CHARS) for _ in range(32))
|
||||||
API_ROOT = 'http://[100::]/oxrlibtest/'
|
API_ROOT = 'http://[100::]/oxrlibtest/'
|
||||||
|
@ -67,10 +68,6 @@ class FakeOpener:
|
||||||
def api_client():
|
def api_client():
|
||||||
return oxrlib.loaders.OXRAPIRequest(RANDOM_APPID, API_ROOT)
|
return oxrlib.loaders.OXRAPIRequest(RANDOM_APPID, API_ROOT)
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def any_date():
|
|
||||||
return datetime.date.today() - datetime.timedelta(days=730 - random.randint(0, 365))
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('base', ['USD', 'JPY'])
|
@pytest.mark.parametrize('base', ['USD', 'JPY'])
|
||||||
def test_success(api_client, any_date, base):
|
def test_success(api_client, any_date, base):
|
||||||
body = "Good Test"
|
body = "Good Test"
|
||||||
|
|
Loading…
Reference in a new issue