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
4 changes: 0 additions & 4 deletions datamaxi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from datamaxi.resources import Datamaxi # noqa: F401
from datamaxi.telegram import Telegram # noqa: F401
from datamaxi.naver import Naver # noqa: F401
from datamaxi.lib.constants import ( # noqa: F401
SPOT,
FUTURES,
Expand Down Expand Up @@ -46,8 +44,6 @@

__all__ = [
"Datamaxi",
"Telegram",
"Naver",
"SPOT",
"FUTURES",
"USD",
Expand Down
76 changes: 14 additions & 62 deletions datamaxi/aio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,15 @@

Mirrors the full sync surface (``cex.*``, ``funding_rate``, ``forex``,
``premium``, ``liquidation``, ``open_interest``, ``margin_borrow``,
``index_price``, ``telegram``, ``naver``). The standalone
``AsyncTelegram`` / ``AsyncNaver`` classes stay exported for back-compat.
Reuses
the sync client's endpoint resolution and error handling (``datamaxi._dispatch``)
and the shared DataFrame / ResponseMeta helpers, so the two clients can't drift
on request building or error semantics.
``index_price``, ``telegram``, ``naver``). Reuses the sync client's endpoint
resolution and error handling (``datamaxi._dispatch``) and the shared DataFrame
/ ResponseMeta helpers, so the two clients can't drift on request building or
error semantics.
"""

from typing import Any

from datamaxi.lib.constants import BASE_URL
from datamaxi.aio._core import AsyncAPI, AsyncResource
from datamaxi.aio.cex import (
from datamaxi.aio._client import AsyncDatamaxi # noqa: F401
from datamaxi.aio._core import AsyncAPI, AsyncResource # noqa: F401
from datamaxi.aio.cex import ( # noqa: F401
AsyncCex,
AsyncCexCandle,
AsyncCexTicker,
Expand All @@ -38,60 +34,16 @@
AsyncCexToken,
AsyncCexSymbol,
)
from datamaxi.aio.funding_rate import AsyncFundingRate
from datamaxi.aio.forex import AsyncForex
from datamaxi.aio.premium import AsyncPremium
from datamaxi.aio.liquidation import AsyncLiquidation
from datamaxi.aio.open_interest import AsyncOpenInterest
from datamaxi.aio.margin_borrow import AsyncMarginBorrow
from datamaxi.aio.index_price import AsyncIndexPrice
from datamaxi.aio.telegram import AsyncTelegram
from datamaxi.aio.naver import AsyncNaver


class AsyncDatamaxi:
"""Async entrypoint — full mirror of the sync :class:`datamaxi.Datamaxi`.

Use as an async context manager so the underlying ``httpx`` client is
closed, or call :meth:`aclose` explicitly.
"""

def __init__(self, api_key=None, **kwargs: Any):
if "base_url" not in kwargs:
kwargs["base_url"] = BASE_URL
api = AsyncAPI(api_key, **kwargs)
self._api = api

self.cex = AsyncCex(api)
self.funding_rate = AsyncFundingRate(api)
self.forex = AsyncForex(api)
self.premium = AsyncPremium(api)
self.liquidation = AsyncLiquidation(api)
self.open_interest = AsyncOpenInterest(api)
self.margin_borrow = AsyncMarginBorrow(api)
self.index_price = AsyncIndexPrice(api)
self.telegram = AsyncTelegram(api=api)
self.naver = AsyncNaver(api=api)

async def aclose(self):
await self._api.aclose()

async def __aenter__(self):
return self

async def __aexit__(self, *exc):
await self.aclose()

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

from datamaxi.aio.funding_rate import AsyncFundingRate # noqa: F401
from datamaxi.aio.forex import AsyncForex # noqa: F401
from datamaxi.aio.premium import AsyncPremium # noqa: F401
from datamaxi.aio.liquidation import AsyncLiquidation # noqa: F401
from datamaxi.aio.open_interest import AsyncOpenInterest # noqa: F401
from datamaxi.aio.margin_borrow import AsyncMarginBorrow # noqa: F401
from datamaxi.aio.index_price import AsyncIndexPrice # noqa: F401

__all__ = [
"AsyncDatamaxi",
"AsyncTelegram",
"AsyncNaver",
"AsyncAPI",
"AsyncResource",
"AsyncCex",
Expand Down
53 changes: 53 additions & 0 deletions datamaxi/aio/_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from typing import Any

from datamaxi.lib.constants import BASE_URL
from datamaxi.aio._core import AsyncAPI
from datamaxi.aio.cex import AsyncCex
from datamaxi.aio.funding_rate import AsyncFundingRate
from datamaxi.aio.forex import AsyncForex
from datamaxi.aio.premium import AsyncPremium
from datamaxi.aio.liquidation import AsyncLiquidation
from datamaxi.aio.open_interest import AsyncOpenInterest
from datamaxi.aio.margin_borrow import AsyncMarginBorrow
from datamaxi.aio.index_price import AsyncIndexPrice
from datamaxi.aio.telegram import AsyncTelegram
from datamaxi.aio.naver import AsyncNaver


class AsyncDatamaxi:
"""Async entrypoint — full mirror of the sync :class:`datamaxi.Datamaxi`.

Use as an async context manager so the underlying ``httpx`` client is
closed, or call :meth:`aclose` explicitly.
"""

def __init__(self, api_key=None, **kwargs: Any):
if "base_url" not in kwargs:
kwargs["base_url"] = BASE_URL
api = AsyncAPI(api_key, **kwargs)
self._api = api

self.cex = AsyncCex(api)
self.funding_rate = AsyncFundingRate(api)
self.forex = AsyncForex(api)
self.premium = AsyncPremium(api)
self.liquidation = AsyncLiquidation(api)
self.open_interest = AsyncOpenInterest(api)
self.margin_borrow = AsyncMarginBorrow(api)
self.index_price = AsyncIndexPrice(api)
self.telegram = AsyncTelegram(api=api)
self.naver = AsyncNaver(api=api)

async def aclose(self):
await self._api.aclose()

async def __aenter__(self):
return self

async def __aexit__(self, *exc):
await self.aclose()

def __repr__(self):
return "AsyncDatamaxi(base_url={!r}, has_key={})".format(
self._api.base_url, bool(self._api.api_key)
)
7 changes: 3 additions & 4 deletions docs/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@ finally:

- Every data and discovery method is a coroutine — `await` it (e.g.
`await client.cex.candle.exchanges(market="spot")`).
- Telegram and Naver have standalone async clients, `AsyncTelegram` and
`AsyncNaver`, also imported from `datamaxi.aio` and used the same way (async
context managers, awaited methods). See the [Telegram](telegram.md) and
[Naver Trend](naver-trend.md) pages for tabbed examples.
- Telegram and Naver are reached via `client.telegram` and `client.naver`. See
the [Telegram](telegram.md) and [Naver Trend](naver-trend.md) pages for tabbed
examples.

## Pagination

Expand Down
12 changes: 6 additions & 6 deletions docs/naver-trend.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Search trend data for South Korea via Naver.
<details markdown="1"><summary>Sync</summary>

```python
from datamaxi import Naver
from datamaxi import Datamaxi

naver = Naver(api_key="YOUR_API_KEY")
naver = Datamaxi(api_key="YOUR_API_KEY").naver

symbols = naver.symbols()
trend = naver.trend(symbol="BTC")
Expand All @@ -21,13 +21,13 @@ trend = naver.trend(symbol="BTC")

```python
import asyncio
from datamaxi.aio import AsyncNaver
from datamaxi.aio import AsyncDatamaxi


async def main():
async with AsyncNaver(api_key="YOUR_API_KEY") as naver:
symbols = await naver.symbols()
trend = await naver.trend(symbol="BTC")
async with AsyncDatamaxi(api_key="YOUR_API_KEY") as client:
symbols = await client.naver.symbols()
trend = await client.naver.trend(symbol="BTC")


asyncio.run(main())
Expand Down
12 changes: 6 additions & 6 deletions docs/telegram.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Telegram channel metadata and message history.
<details markdown="1"><summary>Sync</summary>

```python
from datamaxi import Telegram
from datamaxi import Datamaxi

telegram = Telegram(api_key="YOUR_API_KEY")
telegram = Datamaxi(api_key="YOUR_API_KEY").telegram

channels, _ = telegram.channels(category="korean", limit=50)
messages, next_request = telegram.messages(channel_name="yunlog_announcement", limit=50)
Expand All @@ -23,13 +23,13 @@ more_messages, _ = next_request()

```python
import asyncio
from datamaxi.aio import AsyncTelegram
from datamaxi.aio import AsyncDatamaxi


async def main():
async with AsyncTelegram(api_key="YOUR_API_KEY") as telegram:
channels, _ = await telegram.channels(category="korean", limit=50)
messages, next_request = await telegram.messages(
async with AsyncDatamaxi(api_key="YOUR_API_KEY") as client:
channels, _ = await client.telegram.channels(category="korean", limit=50)
messages, next_request = await client.telegram.messages(
channel_name="yunlog_announcement", limit=50
)

Expand Down
10 changes: 5 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pytest

from datamaxi import Datamaxi, Telegram, Naver
from datamaxi import Datamaxi
from datamaxi.api import API
from datamaxi.error import ServerError

Expand Down Expand Up @@ -76,11 +76,11 @@ def datamaxi():

@pytest.fixture(scope="module")
def telegram():
"""Create Telegram client for live tests."""
return Telegram(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT)
"""Telegram resource (mounted) for live tests."""
return Datamaxi(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT).telegram


@pytest.fixture(scope="module")
def naver():
"""Create Naver client for live tests."""
return Naver(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT)
"""Naver resource (mounted) for live tests."""
return Datamaxi(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT).naver
11 changes: 10 additions & 1 deletion tests/test_async_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

httpx = pytest.importorskip("httpx")

from datamaxi.aio import AsyncDatamaxi, AsyncTelegram, AsyncNaver # noqa: E402
from datamaxi.aio import AsyncDatamaxi # noqa: E402
from datamaxi.aio.telegram import AsyncTelegram # noqa: E402
from datamaxi.aio.naver import AsyncNaver # noqa: E402

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

Expand Down Expand Up @@ -171,6 +173,13 @@ async def run():
assert _run(run()) == _CHANNELS


def test_standalone_async_clients_not_top_level_importable():
import datamaxi.aio

assert not hasattr(datamaxi.aio, "AsyncTelegram")
assert not hasattr(datamaxi.aio, "AsyncNaver")


def test_async_telegram_naver_mounted_reuse_shared_session():
c = _dm()
assert isinstance(c.telegram, AsyncTelegram)
Expand Down
9 changes: 8 additions & 1 deletion tests/test_naver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import pytest
from urllib.parse import urlparse, parse_qs

from datamaxi import Datamaxi, Naver
from datamaxi import Datamaxi
from datamaxi.naver import Naver
from datamaxi.error import ClientError, ServerError
from tests.util import mock_http_response

Expand Down Expand Up @@ -84,3 +85,9 @@ def test_naver_mounted_trend_works():
df = maxi.naver.trend("BTC")
assert isinstance(df, pd.DataFrame)
assert len(df) == 2


def test_standalone_naver_not_top_level_importable():
import datamaxi

assert not hasattr(datamaxi, "Naver")
9 changes: 8 additions & 1 deletion tests/test_telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import pytest
from urllib.parse import urlparse, parse_qs

from datamaxi import Datamaxi, Telegram
from datamaxi import Datamaxi
from datamaxi.telegram import Telegram
from datamaxi.error import ClientError, ServerError
from tests.util import mock_http_response

Expand Down Expand Up @@ -125,3 +126,9 @@ def test_telegram_mounted_messages_work():
res, next_request = maxi.telegram.messages(channel_name="alpha")
assert res == _MESSAGES
assert callable(next_request)


def test_standalone_telegram_not_top_level_importable():
import datamaxi

assert not hasattr(datamaxi, "Telegram")
Loading