Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions datamaxi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ def _mount_retries(self, max_retries, retry_backoff, retry_statuses):
self.session.mount("https://", adapter)
self.session.mount("http://", adapter)

def __repr__(self):
return "{}(base_url={!r}, has_key={})".format(
type(self).__name__, self.base_url, bool(self.api_key)
)

def query(self, url_path, payload=None):
return self.send_request("GET", url_path, payload=payload)

Expand Down Expand Up @@ -283,6 +288,11 @@ class Resource(object):
def __init__(self, api_key=None, api=None, **kwargs):
self._api = api if api is not None else API(api_key, **kwargs)

def __repr__(self):
return "{}(base_url={!r}, has_key={})".format(
type(self).__name__, self._api.base_url, bool(self._api.api_key)
)

def request_endpoint(self, op_id, **params):
return self._api.request_endpoint(op_id, **params)

Expand Down
3 changes: 2 additions & 1 deletion datamaxi/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import List
from urllib.parse import urlencode
import pandas as pd
from functools import wraps
from datamaxi.error import ParameterRequiredError
from datamaxi.error import AtLeastOneParameterRequiredError
Expand Down Expand Up @@ -70,6 +69,8 @@ def encoded_string(query):


def convert_to_df(data, header: bool, index: str = None, apply_fn={}):
import pandas as pd

df = pd.DataFrame(data)

if header:
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/naver/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from typing import Any, List, Union
import pandas as pd
from __future__ import annotations

from typing import Any, List, Union, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.resources.responses import NaverTrendRow
from datamaxi.lib.utils import check_required_parameter
from datamaxi.lib.constants import BASE_URL

if TYPE_CHECKING:
import pandas as pd


class Naver(Resource):
"""Client to fetch Naver trend data from DataMaxi+ API."""
Expand Down Expand Up @@ -51,5 +55,7 @@ def trend(
check_required_parameter(symbol, "symbol")
res = self.request_endpoint("naver_trend", symbol=symbol)
if pandas:
import pandas as pd

return pd.DataFrame(res)
return res
6 changes: 6 additions & 0 deletions datamaxi/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(self, api_key=None, **kwargs: Any):
# pool threaded through every sub-client instead of each opening
# its own. Sub-clients receive it via `api=` and forward it down.
api = API(api_key, **kwargs)
self._api = api

self.cex = Cex(api=api)
self.funding_rate = FundingRate(api=api)
Expand All @@ -62,3 +63,8 @@ def __init__(self, api_key=None, **kwargs: Any):
self.open_interest = OpenInterest(api=api)
self.margin_borrow = MarginBorrow(api=api)
self.index_price = IndexPrice(api=api)

def __repr__(self):
return "Datamaxi(base_url={!r}, has_key={})".format(
self._api.base_url, bool(self._api.api_key)
)
8 changes: 6 additions & 2 deletions datamaxi/resources/cex_candle.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from typing import Any, List, Dict, Union, Optional
import pandas as pd
from __future__ import annotations

from typing import Any, List, Dict, Union, Optional, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.lib.utils import check_required_parameter
from datamaxi.lib.utils import check_required_parameters
from datamaxi.resources.utils import convert_data_to_data_frame
from datamaxi.resources.responses import CandleResponse
from datamaxi.lib.constants import SPOT, FUTURES, INTERVAL_1D, USD, Market, Interval

if TYPE_CHECKING:
import pandas as pd


class CexCandle(Resource):
"""Client to fetch CEX candle data from DataMaxi+ API."""
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/cex_ticker.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from typing import Any, List, Union
import pandas as pd
from __future__ import annotations

from typing import Any, List, Union, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.lib.utils import check_required_parameters
from datamaxi.resources.responses import TickerResponse
from datamaxi.lib.constants import SPOT, FUTURES, Market

if TYPE_CHECKING:
import pandas as pd


class CexTicker(Resource):
"""Client to fetch ticker data from DataMaxi+ API."""
Expand Down Expand Up @@ -70,6 +74,8 @@ def get(
)

if pandas:
import pandas as pd

df = pd.DataFrame([res["data"]])
df = df.set_index("d")
return df
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/cex_wallet_status.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from typing import Any, List, Union
import pandas as pd
from __future__ import annotations

from typing import Any, List, Union, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.resources.responses import WalletStatusRow
from datamaxi.lib.utils import check_required_parameters
from datamaxi.lib.utils import check_required_parameter

if TYPE_CHECKING:
import pandas as pd


class CexWalletStatus(Resource):
"""Client to fetch transfer status data from DataMaxi+ API."""
Expand Down Expand Up @@ -47,6 +51,8 @@ def __call__(

res = self.request_endpoint("wallet_status", exchange=exchange, asset=asset)
if pandas:
import pandas as pd

df = pd.DataFrame(res)
df = df.set_index("network")
return df
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/forex.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from typing import Any, List, Union
import pandas as pd
from __future__ import annotations

from typing import Any, List, Union, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.resources.responses import ForexRow
from datamaxi.lib.utils import check_required_parameter

if TYPE_CHECKING:
import pandas as pd


class Forex(Resource):
"""Client to fetch forex data from DataMaxi+ API."""
Expand Down Expand Up @@ -43,6 +47,8 @@ def __call__(
res = self.request_endpoint("forex", symbol=symbol)

if pandas:
import pandas as pd

return pd.DataFrame([res])
else:
return res
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/funding_rate.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from typing import Any, Callable, Tuple, List, Union
import pandas as pd
from __future__ import annotations

from typing import Any, Callable, Tuple, List, Union, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.lib.utils import check_required_parameter
from datamaxi.lib.utils import check_required_parameters
from datamaxi.resources.utils import convert_data_to_data_frame
from datamaxi.resources.responses import FundingHistoryResponse, LatestFundingRate
from datamaxi.lib.constants import ASC, DESC, SortOrder

if TYPE_CHECKING:
import pandas as pd


class FundingRate(Resource):
"""Client to fetch funding rate data from DataMaxi+ API."""
Expand Down Expand Up @@ -126,6 +130,8 @@ def latest(
)

if pandas:
import pandas as pd

df = pd.DataFrame([res])
df = df.set_index("d")
return df
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/premium.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from typing import Any, List, Union, Optional
import pandas as pd
from __future__ import annotations

from typing import Any, List, Union, Optional, TYPE_CHECKING
from datamaxi.api import Resource
from datamaxi.resources.responses import PremiumResponse
from datamaxi.lib.constants import Market, SortOrder

if TYPE_CHECKING:
import pandas as pd


class Premium(Resource):
"""Client to fetch premium data from DataMaxi+ API."""
Expand Down Expand Up @@ -148,6 +152,8 @@ def __call__( # noqa: C901
raise ValueError("no data found")

if pandas:
import pandas as pd

df = pd.DataFrame(
[
{
Expand Down
10 changes: 8 additions & 2 deletions datamaxi/resources/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from typing import List
import pandas as pd
from __future__ import annotations

from typing import List, TYPE_CHECKING

if TYPE_CHECKING:
import pandas as pd


def convert_data_to_data_frame(
data: List,
columns_to_replace: List[str] = [],
) -> pd.DataFrame:
import pandas as pd

df = pd.DataFrame(data)
df = df.set_index("d")

Expand Down
42 changes: 42 additions & 0 deletions tests/test_repr_and_lazy_pandas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Tests for the #143 polish: __repr__ and lazy pandas import."""

import subprocess
import sys

from datamaxi import Datamaxi
from datamaxi.api import API

BASE_URL = "https://api.datamaxiplus.com"


def test_api_repr():
r = repr(API(api_key="secret", base_url=BASE_URL))
assert r == "API(base_url='https://api.datamaxiplus.com', has_key=True)"
assert "secret" not in r


def test_resource_repr_uses_class_name():
c = Datamaxi(api_key="secret", base_url=BASE_URL)
assert repr(c.cex) == "Cex(base_url='https://api.datamaxiplus.com', has_key=True)"
assert (
repr(c.cex.candle)
== "CexCandle(base_url='https://api.datamaxiplus.com', has_key=True)"
)
assert "secret" not in repr(c.cex.candle)


def test_datamaxi_repr_and_no_key_leak(monkeypatch):
c = Datamaxi(api_key="secret", base_url=BASE_URL)
assert repr(c) == "Datamaxi(base_url='https://api.datamaxiplus.com', has_key=True)"
assert "secret" not in repr(c)
# has_key=False only holds with no key from arg *or* environment
monkeypatch.delenv("DATAMAXI_API_KEY", raising=False)
assert "has_key=False" in repr(Datamaxi(base_url=BASE_URL))


def test_importing_datamaxi_does_not_load_pandas():
# Isolated subprocess: other tests in this session load pandas, so a
# same-process sys.modules check would be unreliable.
code = "import sys, datamaxi; " "sys.exit(0 if 'pandas' not in sys.modules else 1)"
result = subprocess.run([sys.executable, "-c", code])
assert result.returncode == 0, "importing datamaxi should not import pandas"
Loading