Skip to content

Commit cd7ec4e

Browse files
Mount Telegram and Naver as sub-resources on Datamaxi / AsyncDatamaxi (#186)
* feat: mount telegram/naver as sub-resources on Datamaxi Telegram/Naver accept shared api= and reuse the client's one Session; standalone Telegram(api_key=)/Naver(api_key=) still build own transport. Refs #184 * feat: mount telegram/naver on AsyncDatamaxi AsyncTelegram/AsyncNaver accept shared api= for mounting; standalone still supported. Refs #184 * test: mounted telegram/naver reuse shared session (sync+async) Refs #184 * docs: show mounted maxi.telegram/maxi.naver form Refs #184 * chore: drop comment on telegram/naver mount * chore: drop section-header comment in telegram/naver tests * docs: drop standalone telegram/naver table rows + mount notes * docs: drop async telegram/naver mount note
1 parent 366f14a commit cd7ec4e

10 files changed

Lines changed: 117 additions & 41 deletions

File tree

README.md

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,16 @@ the others.
100100
<details markdown="1"><summary>Sync</summary>
101101

102102
```python
103-
from datamaxi import Datamaxi, Telegram, Naver
103+
from datamaxi import Datamaxi
104104

105-
# Clients read DATAMAXI_API_KEY from the environment automatically.
106-
# Alternatively, pass api_key="your_api_key" explicitly to each client.
105+
# The client reads DATAMAXI_API_KEY from the environment automatically.
106+
# Alternatively, pass api_key="your_api_key" explicitly.
107107
maxi = Datamaxi()
108-
telegram = Telegram()
109-
naver = Naver()
108+
109+
# Telegram and Naver are mounted as `maxi.telegram` / `maxi.naver`
110+
# (standalone `Telegram` / `Naver` clients remain available too).
111+
channels, _ = maxi.telegram.channels()
112+
trend = maxi.naver.trend(symbol="BTC")
110113

111114
# Fetch CEX candle data (returns pandas DataFrame)
112115
df = maxi.cex.candle(
@@ -188,14 +191,14 @@ The package ships these clients, all configured the same way (see
188191

189192
| Client | Import | Purpose |
190193
| ----------------- | --------------------------------------------- | ---------------------------------------------------------- |
191-
| `Datamaxi` | `from datamaxi import Datamaxi` | Synchronous client for all crypto REST data. |
192-
| `Telegram` | `from datamaxi import Telegram` | Telegram channel messages and metadata. |
193-
| `Naver` | `from datamaxi import Naver` | Naver search-trend data (South Korea). |
194+
| `Datamaxi` | `from datamaxi import Datamaxi` | Synchronous client for all REST data, incl. `maxi.telegram` / `maxi.naver`. |
194195
| `AsyncDatamaxi` | `from datamaxi.aio import AsyncDatamaxi` | Async twin of `Datamaxi` (needs the `[async]` extra). |
195196
| `AsyncDatamaxiWS` | `from datamaxi.aio.ws import AsyncDatamaxiWS` | Async WebSocket streaming (needs the `[ws]` extra). |
196197

197-
`AsyncTelegram` and `AsyncNaver` are the async twins of `Telegram` / `Naver`,
198-
also imported from `datamaxi.aio`.
198+
Telegram and Naver are mounted on `Datamaxi` / `AsyncDatamaxi` (`maxi.telegram`,
199+
`maxi.naver`) so they reuse the client's shared session. The standalone
200+
`Telegram` / `Naver` (and their async twins `AsyncTelegram` / `AsyncNaver`,
201+
imported from `datamaxi.aio`) remain available for independent use.
199202

200203
## REST API Reference
201204

@@ -495,12 +498,8 @@ Off-exchange signals: Telegram channels and Naver search trends.
495498
Fetch Telegram channel messages and metadata.
496499

497500
```python
498-
# Initialize Telegram client
499-
from datamaxi import Telegram
500-
telegram = Telegram(api_key=api_key)
501-
502501
# Fetch channels
503-
data, next_request = telegram.channels(
502+
data, next_request = maxi.telegram.channels(
504503
page=1, # Optional: page number
505504
limit=1000, # Optional: items per page
506505
category=None, # Optional: filter by category
@@ -509,7 +508,7 @@ data, next_request = telegram.channels(
509508
)
510509

511510
# Fetch messages
512-
data, next_request = telegram.messages(
511+
data, next_request = maxi.telegram.messages(
513512
channel_name=None, # Optional: filter by channel
514513
page=1, # Optional: page number
515514
limit=1000, # Optional: items per page
@@ -524,15 +523,11 @@ data, next_request = telegram.messages(
524523
Fetch Naver search trend data (South Korea).
525524

526525
```python
527-
# Initialize Naver client
528-
from datamaxi import Naver
529-
naver = Naver(api_key=api_key)
530-
531526
# Get supported symbols
532-
symbols = naver.symbols()
527+
symbols = maxi.naver.symbols()
533528

534529
# Fetch trend data
535-
data = naver.trend(
530+
data = maxi.naver.trend(
536531
symbol="BTC", # Required: symbol to search
537532
pandas=True # Optional: return DataFrame or list
538533
)
@@ -756,8 +751,7 @@ asyncio.run(main())
756751
Use `AsyncDatamaxi` as an async context manager (shown above) or call
757752
`await client.aclose()` yourself. Paginated endpoints return an async
758753
`next_request``await` it too
759-
(`data, next_request = await client.cex.announcement(...)`). Telegram and Naver
760-
have standalone `AsyncTelegram` / `AsyncNaver` clients.
754+
(`data, next_request = await client.cex.announcement(...)`).
761755

762756
Every endpoint in the [REST API Reference](#rest-api-reference) works the same
763757
under the async client — see the [docs](https://datamaxi.readthedocs.io/) where

datamaxi/aio/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
1717
Mirrors the full sync surface (``cex.*``, ``funding_rate``, ``forex``,
1818
``premium``, ``liquidation``, ``open_interest``, ``margin_borrow``,
19-
``index_price``, plus standalone ``AsyncTelegram`` / ``AsyncNaver``). Reuses
19+
``index_price``, ``telegram``, ``naver``). The standalone
20+
``AsyncTelegram`` / ``AsyncNaver`` classes stay exported for back-compat.
21+
Reuses
2022
the sync client's endpoint resolution and error handling (``datamaxi._dispatch``)
2123
and the shared DataFrame / ResponseMeta helpers, so the two clients can't drift
2224
on request building or error semantics.
@@ -68,6 +70,8 @@ def __init__(self, api_key=None, **kwargs: Any):
6870
self.open_interest = AsyncOpenInterest(api)
6971
self.margin_borrow = AsyncMarginBorrow(api)
7072
self.index_price = AsyncIndexPrice(api)
73+
self.telegram = AsyncTelegram(api=api)
74+
self.naver = AsyncNaver(api=api)
7175

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

datamaxi/aio/naver.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
class AsyncNaver(AsyncResource):
2121
"""Client to fetch Naver trend data from DataMaxi+ API (async)."""
2222

23-
def __init__(self, api_key=None, **kwargs: Any):
24-
if "base_url" not in kwargs:
25-
kwargs["base_url"] = BASE_URL
26-
super().__init__(AsyncAPI(api_key, **kwargs))
23+
def __init__(self, api_key=None, api=None, **kwargs: Any):
24+
if api is None:
25+
if "base_url" not in kwargs:
26+
kwargs["base_url"] = BASE_URL
27+
api = AsyncAPI(api_key, **kwargs)
28+
super().__init__(api)
2729

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

datamaxi/aio/telegram.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
class AsyncTelegram(AsyncResource):
1818
"""Client to fetch Telegram data from DataMaxi+ API (async)."""
1919

20-
def __init__(self, api_key=None, **kwargs: Any):
21-
if "base_url" not in kwargs:
22-
kwargs["base_url"] = BASE_URL
23-
super().__init__(AsyncAPI(api_key, **kwargs))
20+
def __init__(self, api_key=None, api=None, **kwargs: Any):
21+
if api is None:
22+
if "base_url" not in kwargs:
23+
kwargs["base_url"] = BASE_URL
24+
api = AsyncAPI(api_key, **kwargs)
25+
super().__init__(api)
2426

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

datamaxi/naver/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
class Naver(Resource):
1414
"""Client to fetch Naver trend data from DataMaxi+ API."""
1515

16-
def __init__(self, api_key=None, **kwargs: Any):
16+
def __init__(self, api_key=None, api=None, **kwargs: Any):
1717
"""Initialize the object.
1818
1919
Args:
2020
api_key (str): The DataMaxi+ API key
21+
api (API): Shared transport to reuse when mounted on
22+
``Datamaxi`` (``maxi.naver``); when omitted this builds its
23+
own ``API`` for standalone use.
2124
**kwargs: Keyword arguments used by `datamaxi.api.API`.
2225
"""
23-
if "base_url" not in kwargs:
26+
if api is None and "base_url" not in kwargs:
2427
kwargs["base_url"] = BASE_URL
25-
super().__init__(api_key, **kwargs)
28+
super().__init__(api_key, api=api, **kwargs)
2629

2730
def symbols(self) -> List[str]:
2831
"""Get Naver trend supported token symbols

datamaxi/resources/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from datamaxi.resources.cex_symbol import ( # used in documentation # noqa:F401
2929
CexSymbol,
3030
)
31+
from datamaxi.telegram import Telegram
32+
from datamaxi.naver import Naver
3133

3234

3335
class Datamaxi:
@@ -67,6 +69,8 @@ def __init__(self, api_key=None, **kwargs: Any):
6769
self.open_interest = OpenInterest(api=api)
6870
self.margin_borrow = MarginBorrow(api=api)
6971
self.index_price = IndexPrice(api=api)
72+
self.telegram = Telegram(api=api)
73+
self.naver = Naver(api=api)
7074

7175
def close(self):
7276
self._api.close()

datamaxi/telegram/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@
1010
class Telegram(Resource):
1111
"""Client to fetch Telegram data from DataMaxi+ API."""
1212

13-
def __init__(self, api_key=None, **kwargs: Any):
13+
def __init__(self, api_key=None, api=None, **kwargs: Any):
1414
"""Initialize the object.
1515
1616
Args:
1717
api_key (str): The DataMaxi+ API key
18+
api (API): Shared transport to reuse when mounted on
19+
``Datamaxi`` (``maxi.telegram``); when omitted this builds
20+
its own ``API`` for standalone use.
1821
**kwargs: Keyword arguments used by `datamaxi.api.API`.
1922
"""
20-
if "base_url" not in kwargs:
23+
if api is None and "base_url" not in kwargs:
2124
kwargs["base_url"] = BASE_URL
22-
super().__init__(api_key, **kwargs)
25+
super().__init__(api_key, api=api, **kwargs)
2326

2427
def channels(
2528
self,

tests/test_async_resources.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,30 @@ async def run():
171171
assert _run(run()) == _CHANNELS
172172

173173

174+
def test_async_telegram_naver_mounted_reuse_shared_session():
175+
c = _dm()
176+
assert isinstance(c.telegram, AsyncTelegram)
177+
assert isinstance(c.naver, AsyncNaver)
178+
# Same shared AsyncAPI/transport as every other sub-resource.
179+
assert c.telegram._api is c._api
180+
assert c.naver._api is c._api
181+
assert c.telegram._api is c.cex._api
182+
183+
184+
def test_async_mounted_telegram_and_naver_work():
185+
async def run():
186+
async with _dm() as c:
187+
channels, _ = await c.telegram.channels()
188+
messages, _ = await c.telegram.messages(channel_name="alpha")
189+
trend = await c.naver.trend("BTC", pandas=False)
190+
return channels, messages, trend
191+
192+
channels, messages, trend = _run(run())
193+
assert channels == _CHANNELS
194+
assert messages == _MESSAGES
195+
assert trend == _TREND
196+
197+
174198
def test_async_funding_history_and_latest():
175199
async def run():
176200
async with _dm() as c:

tests/test_naver.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77
from urllib.parse import urlparse, parse_qs
88

9-
from datamaxi.naver import Naver
9+
from datamaxi import Datamaxi, Naver
1010
from datamaxi.error import ClientError, ServerError
1111
from tests.util import mock_http_response
1212

@@ -68,3 +68,19 @@ def test_naver_trend_client_error():
6868
def test_naver_trend_server_error():
6969
with pytest.raises(ServerError):
7070
_client().trend("BTC")
71+
72+
73+
def test_naver_mounted_reuses_shared_session():
74+
maxi = Datamaxi(api_key="key", base_url=BASE_URL)
75+
assert isinstance(maxi.naver, Naver)
76+
# Same shared API/transport as every other sub-resource.
77+
assert maxi.naver._api is maxi._api
78+
assert maxi.naver._api is maxi.cex._api
79+
80+
81+
@mock_http_response(responses.GET, "/api/v1/naver-trend", _TREND)
82+
def test_naver_mounted_trend_works():
83+
maxi = Datamaxi(api_key="key", base_url=BASE_URL)
84+
df = maxi.naver.trend("BTC")
85+
assert isinstance(df, pd.DataFrame)
86+
assert len(df) == 2

tests/test_telegram.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66
from urllib.parse import urlparse, parse_qs
77

8-
from datamaxi.telegram import Telegram
8+
from datamaxi import Datamaxi, Telegram
99
from datamaxi.error import ClientError, ServerError
1010
from tests.util import mock_http_response
1111

@@ -101,3 +101,27 @@ def test_channels_client_error():
101101
def test_messages_server_error():
102102
with pytest.raises(ServerError):
103103
_client().messages(channel_name="alpha")
104+
105+
106+
def test_telegram_mounted_reuses_shared_session():
107+
maxi = Datamaxi(api_key="key", base_url=BASE_URL)
108+
assert isinstance(maxi.telegram, Telegram)
109+
# Same shared API/transport as every other sub-resource.
110+
assert maxi.telegram._api is maxi._api
111+
assert maxi.telegram._api is maxi.cex._api
112+
113+
114+
@mock_http_response(responses.GET, "/api/v1/telegram/channels", _CHANNELS)
115+
def test_telegram_mounted_channels_work():
116+
maxi = Datamaxi(api_key="key", base_url=BASE_URL)
117+
res, next_request = maxi.telegram.channels()
118+
assert res == _CHANNELS
119+
assert callable(next_request)
120+
121+
122+
@mock_http_response(responses.GET, "/api/v1/telegram/messages", _MESSAGES)
123+
def test_telegram_mounted_messages_work():
124+
maxi = Datamaxi(api_key="key", base_url=BASE_URL)
125+
res, next_request = maxi.telegram.messages(channel_name="alpha")
126+
assert res == _MESSAGES
127+
assert callable(next_request)

0 commit comments

Comments
 (0)