Skip to content

Commit

Permalink
use tenacity, retry on 409 conflict, fix #31
Browse files Browse the repository at this point in the history
  • Loading branch information
honzajavorek committed Dec 28, 2024
1 parent 86949a5 commit 7ffbdec
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 16 deletions.
21 changes: 16 additions & 5 deletions fiobank.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from decimal import Decimal

import requests
from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)


__all__ = ("FioBank", "ThrottlingError")
Expand Down Expand Up @@ -35,10 +41,10 @@ def sanitize_value(value, convert=None):


class ThrottlingError(Exception):
"""Throttling error raised when api is being used too fast."""
"""Throttling error raised when the API is being used too fast."""

def __str__(self):
return "Token should be used only once per 30s."
return "Token can be used only once per 30s."


class FioBank(object):
Expand Down Expand Up @@ -102,14 +108,19 @@ def __init__(self, token, decimal=False):
"closingbalance": ("balance", self.float_type),
}

@retry(
retry=retry_if_exception_type(ThrottlingError),
reraise=True,
stop=stop_after_attempt(3),
wait=wait_random_exponential(max=2 * 60),
)
def _request(self, action, **params):
template = self.base_url + self.actions[action]
url = template.format(token=self.token, **params)
url_template = self.base_url + self.actions[action]
url = url_template.format(token=self.token, **params)

response = requests.get(url)
if response.status_code == requests.codes["conflict"]:
raise ThrottlingError()

response.raise_for_status()

if response.content:
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license = {text = "ICS"}
requires-python = "<4.0,>=3.9"
dependencies = [
"requests<3.0.0,>=2.28.2",
"tenacity>=9.0.0",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down
34 changes: 24 additions & 10 deletions tests/test_fiobank.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,30 @@
import pytest
import requests
import responses
from responses.registries import OrderedRegistry

from fiobank import FioBank


@pytest.fixture()
def token():
def token() -> uuid.UUID:
return uuid.uuid4()


@pytest.fixture()
def transactions_text():
def transactions_text() -> str:
with open((os.path.dirname(__file__) + "/transactions.json")) as f:
return f.read()


@pytest.fixture()
def transactions_json():
def transactions_json() -> dict:
with open((os.path.dirname(__file__) + "/transactions.json")) as f:
return json.load(f)


@pytest.fixture()
def client_float(token, transactions_text):
def client_float(token: uuid.UUID, transactions_text: str):
with responses.RequestsMock(assert_all_requests_are_fired=False) as resps:
url = re.compile(
re.escape(FioBank.base_url)
Expand All @@ -49,7 +50,7 @@ def client_float(token, transactions_text):


@pytest.fixture()
def client_decimal(token, transactions_text):
def client_decimal(token: uuid.UUID, transactions_text: str):
with responses.RequestsMock(assert_all_requests_are_fired=False) as resps:
url = re.compile(
re.escape(FioBank.base_url)
Expand All @@ -66,7 +67,7 @@ def client_decimal(token, transactions_text):
yield FioBank(token, decimal=True)


def test_client_decimal(client_decimal):
def test_client_decimal(client_decimal: FioBank):
transaction = next(client_decimal.last())
info = client_decimal.info()

Expand All @@ -75,7 +76,7 @@ def test_client_decimal(client_decimal):
assert info["balance"] == Decimal("2060.52")


def test_info_integration(client_float):
def test_info_integration(client_float: FioBank):
assert frozenset(client_float.info().keys()) == frozenset(
[
"account_number_full",
Expand All @@ -89,12 +90,11 @@ def test_info_integration(client_float):
)


def test_info_uses_today(transactions_json):
def test_info_uses_today(transactions_json: dict):
client = FioBank("...")
today = date.today()

options = {"return_value": transactions_json}
with mock.patch.object(client, "_request", **options) as stub:
with mock.patch.object(client, "_request", return_value=transactions_json) as stub:
client.info()
stub.assert_called_once_with("periods", from_date=today, to_date=today)

Expand Down Expand Up @@ -445,3 +445,17 @@ def test_transactions_parse_no_account_number_full(transactions_json):
sdk_transaction = list(client._parse_transactions(transactions_json))[0]

assert sdk_transaction["account_number_full"] is None


def test_409_conflict(token: uuid.UUID, transactions_text: str):
with responses.RequestsMock(registry=OrderedRegistry) as resps:
url = re.compile(
re.escape(FioBank.base_url)
+ r"[^/]+/{token}/([^/]+/)*transactions\.json".format(token=token)
)
resps.add(responses.GET, url, status=409)
resps.add(responses.GET, url, body=transactions_text)
client = FioBank(token, decimal=True)
transaction = next(client.last())

assert transaction["amount"] == Decimal("-130.0")
15 changes: 14 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7ffbdec

Please sign in to comment.