Skip to content

Commit

Permalink
Change raise_error() to decorator error_handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Writtic committed Feb 27, 2022
1 parent b207c2c commit 204f2f9
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 94 deletions.
59 changes: 33 additions & 26 deletions pyupbit/errors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from requests import Response
from typing import Any
from typing import Dict, Any

__all__ = [
"CreateAskError",
Expand All @@ -19,7 +19,7 @@
"TooManyRequests",
"RemainingReqParsingError",
"InValidAccessKey",
"raise_error", # raise_error must be in place of last in this list
"error_handler", # raise_error must be in place of last in this list
]


Expand Down Expand Up @@ -158,27 +158,34 @@ class InValidAccessKey(UpbitUnauthorizedError):
TOO_MANY_REQ = [eval(err) for err in __all__[:-1] if eval(err).code == 429]


def raise_error(resp: Response) -> None:
message, name = "", ""

error = resp.json().get("error", {})
if bool(error):
message = error.get("message")
name = error.get("name")
code = resp.status_code

if code == 400:
for err in BAD_REQUESTS:
if err.name == name:
raise err
elif code == 401:
for err in UNAUTHORIZED:
if err.name == name:
raise err
elif code == 429:
for err in TOO_MANY_REQ:
# too_many_request doesn't use json response but text
if err.name == resp.text:
raise TooManyRequests
else:
raise UpbitError(name=name, code=code, msg=message)
def error_handler(func):
def wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Response:
message, name = "", ""

resp = func(*args, **kwargs)
code = resp.status_code
if 200 <= code < 300:
return resp

error = resp.json().get("error", {})
if bool(error):
message = error.get("message")
name = error.get("name")

if code == 400:
for err in BAD_REQUESTS:
if err.name == name:
raise err
elif code == 401:
for err in UNAUTHORIZED:
if err.name == name:
raise err
elif code == 429:
for err in TOO_MANY_REQ:
# too_many_request doesn't use json response but text
if err.name == resp.text:
raise TooManyRequests
else:
raise UpbitError(name=name, code=code, msg=message)
return resp
return wrapper
4 changes: 0 additions & 4 deletions pyupbit/quotation_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@

import datetime
import pandas as pd
import sys
import time
from pyupbit.request_api import _call_public_api
from pyupbit.errors import UpbitError, TooManyRequests, raise_error
import requests
import re


def get_tickers(fiat="", is_details=False, limit_info=False, verbose=False):
Expand Down
138 changes: 82 additions & 56 deletions pyupbit/request_api.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import re
import requests
from requests import Response
from typing import Any, Tuple, Dict, Optional
from .errors import raise_error, RemainingReqParsingError
from .errors import error_handler, RemainingReqParsingError

HTTP_RESP_CODE_START = 200
HTTP_RESP_CODE_END = 400
HTTP_RESP_CODE_END = 400


def _parse_remaining_req(remaining_req: str) -> Dict[str, Any]:
"""parse the request limit data of the Upbit API
def _parse(remaining_req: str) -> Dict[str, Any]:
"""Parse the number of remaining requests info for Upbit API
Args:
remaining_req (str): "group=market; min=573; sec=9"
remaining_req (str): String of the number of remaining requests info
like "group=market; min=573; sec=9"
Returns:
dict: {'group': 'market', 'min': 573, 'sec': 2}
Parsed dictionary of the number of remaining requests info
like {'group': 'market', 'min': 573, 'sec': 2}
Raises:
RemainingReqParsingError: If the input can not be parsed.
"""
try:
pattern = re.compile(r"group=([a-z\-]+); min=([0-9]+); sec=([0-9]+)")
Expand All @@ -32,67 +36,89 @@ def _parse_remaining_req(remaining_req: str) -> Dict[str, Any]:
raise RemainingReqParsingError


def _call_public_api(
url: str, **params: Any
) -> Optional[Tuple[Any, Dict[str, Any]]]:
"""call get type api
@error_handler
def _call_get(url: str, **kwargs: Any) -> Response:
return requests.get(url, **kwargs)


@error_handler
def _call_post(url: str, **kwargs: Any) -> Response:
return requests.post(url, **kwargs)


@error_handler
def _call_delete(url: str, **kwargs: Any) -> Response:
return requests.delete(url, **kwargs)


def _call_public_api(url: str, **params: Any) -> Tuple[Any, Dict[str, Any]]:
"""Call Upbit public api
Args:
url (str): REST API url
params (any): GET method parameters
Returns:
tuple: (data, req_limit_info)
The contents of requested url, parsed remaining requests count info
"""
resp = requests.get(url, params=params)
if HTTP_RESP_CODE_START <= resp.status_code < HTTP_RESP_CODE_END:
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse_remaining_req(remaining_req)
data = resp.json()
return data, limit
else:
raise_error(resp)
resp = _call_get(url, params=params)
data = resp.json()
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse(remaining_req)
return data, limit


def _send_post_request(
url: str,
headers: Optional[Dict[str, str]] = None,
data: Optional[Dict[str, Any]] = None,
) -> Optional[Tuple[Any, Dict[str, Any]]]:
resp = requests.post(url, headers=headers, data=data)
if HTTP_RESP_CODE_START <= resp.status_code < HTTP_RESP_CODE_END:
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse_remaining_req(remaining_req)
contents = resp.json()
return contents, limit
else:
raise_error(resp)
url: str, headers: Dict[str, str], data: Dict[str, Any]
) -> Tuple[Any, Dict[str, Any]]:
"""Call POST method request for Upbit
Args:
url (str): REST API url
headers (dict[str, str]): HTTP headers
data (dict[str, any]): Data
Returns:
The contents of requested url, parsed remaining requests count info
"""
resp = _call_post(url, headers=headers, data=data)
data = resp.json()
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse(remaining_req)
return data, limit


def _send_get_request(
url: str,
headers: Optional[Dict[str, str]] = None,
data: Optional[Dict[str, Any]] = None,
) -> Optional[Tuple[Any, Dict[str, Any]]]:
resp = requests.get(url, headers=headers, data=data)
if HTTP_RESP_CODE_START <= resp.status_code < HTTP_RESP_CODE_END:
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse_remaining_req(remaining_req)
contents = resp.json()
return contents, limit
else:
raise_error(resp)
url: str, headers: Dict[str, str], data: Dict[str, Any]
) -> Tuple[Any, Dict[str, Any]]:
"""Call GET method request for Upbit
Args:
url (str): REST API url
headers (dict[str, str]): HTTP headers
data (dict[str, any]): Data
Returns:
The contents of requested url, parsed remaining requests count info
"""
resp = _call_get(url, headers=headers, data=data)
data = resp.json()
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse(remaining_req)
return data, limit


def _send_delete_request(
url: str,
headers: Optional[Dict[str, str]] = None,
data: Optional[Dict[str, Any]] = None,
url: str, headers: Dict[str, str], data: Dict[str, Any]
) -> Optional[Tuple[Any, Dict[str, Any]]]:
resp = requests.delete(url, headers=headers, data=data)
if HTTP_RESP_CODE_START <= resp.status_code < HTTP_RESP_CODE_END:
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse_remaining_req(remaining_req)
contents = resp.json()
return contents, limit
else:
raise_error(resp)
"""Call DELETE method request for Upbit
Args:
url (str): REST API url
headers (dict[str, str]): HTTP headers
data (dict[str, any]): Data
Returns:
The contents of requested url, parsed remaining requests count info
"""
resp = _call_delete(url, headers=headers, data=data)
data = resp.json()
remaining_req = resp.headers.get("Remaining-Req", "")
limit = _parse(remaining_req)
return data, limit
18 changes: 15 additions & 3 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@


def test_raise_error_with_bad_requests():
@error_handler
def func(resp):
return resp

responses = list()
for err_name in bad_requests:
mock = Mock(spec=Response)
Expand All @@ -29,7 +33,7 @@ def test_raise_error_with_bad_requests():

for response in responses:
with pytest.raises(UpbitErrorMixin) as exc:
raise_error(response)
func(response)

error = response.json()["error"]
assert exc.value.name == error["name"]
Expand All @@ -38,6 +42,10 @@ def test_raise_error_with_bad_requests():


def test_raise_error_with_unauthorized():
@error_handler
def func(resp):
return resp

responses = list()
for err_name in unauthorized:
mock = Mock(spec=Response)
Expand All @@ -52,7 +60,7 @@ def test_raise_error_with_unauthorized():

for response in responses:
with pytest.raises(UpbitErrorMixin) as exc:
raise_error(response)
func(response)

error = response.json()["error"]
assert exc.value.name == error["name"]
Expand All @@ -61,6 +69,10 @@ def test_raise_error_with_unauthorized():


def test_raise_error_with_too_many_req():
@error_handler
def func(resp):
return resp

responses = list()
for err_name in too_many_req:
mock = Mock(spec=Response)
Expand All @@ -70,7 +82,7 @@ def test_raise_error_with_too_many_req():

for response in responses:
with pytest.raises(UpbitErrorMixin) as exc:
raise_error(response)
func(response)

# too_many_request error doesn't use json response but text
assert exc.value.name == response.text
Expand Down
10 changes: 5 additions & 5 deletions tests/test_request_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from pyupbit.request_api import _parse_remaining_req
from pyupbit.request_api import _parse
from pyupbit.request_api import _call_public_api
from pyupbit.errors import RemainingReqParsingError

Expand All @@ -11,20 +11,20 @@ def test_parse_remaining_req_defaults():
'min': 573,
'sec': 9
}
ret = _parse_remaining_req(limit_info)
ret = _parse(limit_info)
assert ret == expected


def test_parse_remaining_req_raises():
"""_parse_remaining_req shold raise an exception with wrong parameter
"""
with pytest.raises(RemainingReqParsingError):
_parse_remaining_req("")
_parse("")


def test_call_public_api():
url = "https://api.upbit.com/v1/market/all"
querystring = {"isDetails":"false"}
data, limit = _call_public_api(url, **querystring)
querystring = {"isDetails": "false"}
_, limit = _call_public_api(url, **querystring)
assert isinstance(limit, dict)

0 comments on commit 204f2f9

Please sign in to comment.