From 1927a1812033c164e4d4bf3a2543ec009223b691 2017-05-12 11:40:27 From: Brett Smith Date: 2017-05-12 11:40:27 Subject: [PATCH] loaders: Add LoaderChain. --- diff --git a/oxrlib/loaders.py b/oxrlib/loaders.py index e839dddcbb9c18748fc020fdae6f2cd04a373b06..5213a40df3b28e6172015d89f62a2ee4b624249d 100644 --- a/oxrlib/loaders.py +++ b/oxrlib/loaders.py @@ -1,4 +1,5 @@ import cgi +import functools import io import urllib.request import urllib.parse @@ -19,6 +20,10 @@ class LoaderSourceError(LoaderError): pass +class NoLoadersError(Exception): + pass + + class FileCache: def __init__(self, dir_path, filename_pattern): self.dir_path = dir_path @@ -75,3 +80,32 @@ class OXRAPIRequest: 'historical/{}.json'.format(date.isoformat()), {'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 diff --git a/tests/__init__.py b/tests/__init__.py index 98b8c22b7d673d59f255948f1022f87135263725..98cfa519c320355fbef71071ba04837dd58cfef2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,14 @@ +import datetime import pathlib +import random + +import pytest TEST_DIR = pathlib.Path(__file__).parent def relpath(*parts): return TEST_DIR / pathlib.Path(*parts) + +@pytest.fixture +def any_date(): + return datetime.date.today() - datetime.timedelta(days=730 - random.randint(0, 365)) diff --git a/tests/test_LoaderChain.py b/tests/test_LoaderChain.py new file mode 100644 index 0000000000000000000000000000000000000000..80dcdb2e828bb4b85b2f420a7b4ef3b08f46bfb9 --- /dev/null +++ b/tests/test_LoaderChain.py @@ -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__) diff --git a/tests/test_OXRAPIRequest.py b/tests/test_OXRAPIRequest.py index 147af8a11f327b3e9e038117397ee30feb0bea48..fbfdc50ba95229c29e53918ba23f008664c2f1c4 100644 --- a/tests/test_OXRAPIRequest.py +++ b/tests/test_OXRAPIRequest.py @@ -1,4 +1,3 @@ -import datetime import http.client import io import json @@ -10,6 +9,8 @@ import urllib.parse import pytest import oxrlib.loaders +from . import any_date + APPID_CHARS = string.ascii_letters + string.digits RANDOM_APPID = ''.join(random.choice(APPID_CHARS) for _ in range(32)) API_ROOT = 'http://[100::]/oxrlibtest/' @@ -67,10 +68,6 @@ class FakeOpener: def api_client(): 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']) def test_success(api_client, any_date, base): body = "Good Test"