Skip to content

Commit 09529f1

Browse files
Remove standalone Telegram/Naver clients from public API (#189)
* Remove standalone Telegram/Naver clients from public API Drop top-level re-exports + __all__ entries for Telegram/Naver (datamaxi) and AsyncTelegram/AsyncNaver (datamaxi.aio); bind aio mount imports privately so the names aren't importable. Impl classes kept, reachable only via maxi.telegram / maxi.naver. Tests source the classes from their package modules / mounts. Closes #188 * Update docs to mounted surface; split top-level-import surface test docs/{telegram,naver-trend,async}.md showed the removed standalone imports (from datamaxi import Telegram, from datamaxi.aio import AsyncTelegram, ...) — repoint to maxi.telegram / client.telegram. Split the not-top-level-importable assertion per resource file. * refactor(aio): move AsyncDatamaxi to private _client submodule Mirrors sync layout (Datamaxi in resources/): aio/__init__.py is now a pure re-export file, so the impl classes import normally in _client.py instead of the _AsyncTelegram/_AsyncNaver alias hack. Telegram/Naver stay off the public aio surface; client.telegram/.naver unchanged. Closes #190
1 parent a5f6076 commit 09529f1

10 files changed

Lines changed: 113 additions & 90 deletions

File tree

datamaxi/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
from datamaxi.resources import Datamaxi # noqa: F401
2-
from datamaxi.telegram import Telegram # noqa: F401
3-
from datamaxi.naver import Naver # noqa: F401
42
from datamaxi.lib.constants import ( # noqa: F401
53
SPOT,
64
FUTURES,
@@ -46,8 +44,6 @@
4644

4745
__all__ = [
4846
"Datamaxi",
49-
"Telegram",
50-
"Naver",
5147
"SPOT",
5248
"FUTURES",
5349
"USD",

datamaxi/aio/__init__.py

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,15 @@
1616
1717
Mirrors the full sync surface (``cex.*``, ``funding_rate``, ``forex``,
1818
``premium``, ``liquidation``, ``open_interest``, ``margin_borrow``,
19-
``index_price``, ``telegram``, ``naver``). The standalone
20-
``AsyncTelegram`` / ``AsyncNaver`` classes stay exported for back-compat.
21-
Reuses
22-
the sync client's endpoint resolution and error handling (``datamaxi._dispatch``)
23-
and the shared DataFrame / ResponseMeta helpers, so the two clients can't drift
24-
on request building or error semantics.
19+
``index_price``, ``telegram``, ``naver``). Reuses the sync client's endpoint
20+
resolution and error handling (``datamaxi._dispatch``) and the shared DataFrame
21+
/ ResponseMeta helpers, so the two clients can't drift on request building or
22+
error semantics.
2523
"""
2624

27-
from typing import Any
28-
29-
from datamaxi.lib.constants import BASE_URL
30-
from datamaxi.aio._core import AsyncAPI, AsyncResource
31-
from datamaxi.aio.cex import (
25+
from datamaxi.aio._client import AsyncDatamaxi # noqa: F401
26+
from datamaxi.aio._core import AsyncAPI, AsyncResource # noqa: F401
27+
from datamaxi.aio.cex import ( # noqa: F401
3228
AsyncCex,
3329
AsyncCexCandle,
3430
AsyncCexTicker,
@@ -38,60 +34,16 @@
3834
AsyncCexToken,
3935
AsyncCexSymbol,
4036
)
41-
from datamaxi.aio.funding_rate import AsyncFundingRate
42-
from datamaxi.aio.forex import AsyncForex
43-
from datamaxi.aio.premium import AsyncPremium
44-
from datamaxi.aio.liquidation import AsyncLiquidation
45-
from datamaxi.aio.open_interest import AsyncOpenInterest
46-
from datamaxi.aio.margin_borrow import AsyncMarginBorrow
47-
from datamaxi.aio.index_price import AsyncIndexPrice
48-
from datamaxi.aio.telegram import AsyncTelegram
49-
from datamaxi.aio.naver import AsyncNaver
50-
51-
52-
class AsyncDatamaxi:
53-
"""Async entrypoint — full mirror of the sync :class:`datamaxi.Datamaxi`.
54-
55-
Use as an async context manager so the underlying ``httpx`` client is
56-
closed, or call :meth:`aclose` explicitly.
57-
"""
58-
59-
def __init__(self, api_key=None, **kwargs: Any):
60-
if "base_url" not in kwargs:
61-
kwargs["base_url"] = BASE_URL
62-
api = AsyncAPI(api_key, **kwargs)
63-
self._api = api
64-
65-
self.cex = AsyncCex(api)
66-
self.funding_rate = AsyncFundingRate(api)
67-
self.forex = AsyncForex(api)
68-
self.premium = AsyncPremium(api)
69-
self.liquidation = AsyncLiquidation(api)
70-
self.open_interest = AsyncOpenInterest(api)
71-
self.margin_borrow = AsyncMarginBorrow(api)
72-
self.index_price = AsyncIndexPrice(api)
73-
self.telegram = AsyncTelegram(api=api)
74-
self.naver = AsyncNaver(api=api)
75-
76-
async def aclose(self):
77-
await self._api.aclose()
78-
79-
async def __aenter__(self):
80-
return self
81-
82-
async def __aexit__(self, *exc):
83-
await self.aclose()
84-
85-
def __repr__(self):
86-
return "AsyncDatamaxi(base_url={!r}, has_key={})".format(
87-
self._api.base_url, bool(self._api.api_key)
88-
)
89-
37+
from datamaxi.aio.funding_rate import AsyncFundingRate # noqa: F401
38+
from datamaxi.aio.forex import AsyncForex # noqa: F401
39+
from datamaxi.aio.premium import AsyncPremium # noqa: F401
40+
from datamaxi.aio.liquidation import AsyncLiquidation # noqa: F401
41+
from datamaxi.aio.open_interest import AsyncOpenInterest # noqa: F401
42+
from datamaxi.aio.margin_borrow import AsyncMarginBorrow # noqa: F401
43+
from datamaxi.aio.index_price import AsyncIndexPrice # noqa: F401
9044

9145
__all__ = [
9246
"AsyncDatamaxi",
93-
"AsyncTelegram",
94-
"AsyncNaver",
9547
"AsyncAPI",
9648
"AsyncResource",
9749
"AsyncCex",

datamaxi/aio/_client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from typing import Any
2+
3+
from datamaxi.lib.constants import BASE_URL
4+
from datamaxi.aio._core import AsyncAPI
5+
from datamaxi.aio.cex import AsyncCex
6+
from datamaxi.aio.funding_rate import AsyncFundingRate
7+
from datamaxi.aio.forex import AsyncForex
8+
from datamaxi.aio.premium import AsyncPremium
9+
from datamaxi.aio.liquidation import AsyncLiquidation
10+
from datamaxi.aio.open_interest import AsyncOpenInterest
11+
from datamaxi.aio.margin_borrow import AsyncMarginBorrow
12+
from datamaxi.aio.index_price import AsyncIndexPrice
13+
from datamaxi.aio.telegram import AsyncTelegram
14+
from datamaxi.aio.naver import AsyncNaver
15+
16+
17+
class AsyncDatamaxi:
18+
"""Async entrypoint — full mirror of the sync :class:`datamaxi.Datamaxi`.
19+
20+
Use as an async context manager so the underlying ``httpx`` client is
21+
closed, or call :meth:`aclose` explicitly.
22+
"""
23+
24+
def __init__(self, api_key=None, **kwargs: Any):
25+
if "base_url" not in kwargs:
26+
kwargs["base_url"] = BASE_URL
27+
api = AsyncAPI(api_key, **kwargs)
28+
self._api = api
29+
30+
self.cex = AsyncCex(api)
31+
self.funding_rate = AsyncFundingRate(api)
32+
self.forex = AsyncForex(api)
33+
self.premium = AsyncPremium(api)
34+
self.liquidation = AsyncLiquidation(api)
35+
self.open_interest = AsyncOpenInterest(api)
36+
self.margin_borrow = AsyncMarginBorrow(api)
37+
self.index_price = AsyncIndexPrice(api)
38+
self.telegram = AsyncTelegram(api=api)
39+
self.naver = AsyncNaver(api=api)
40+
41+
async def aclose(self):
42+
await self._api.aclose()
43+
44+
async def __aenter__(self):
45+
return self
46+
47+
async def __aexit__(self, *exc):
48+
await self.aclose()
49+
50+
def __repr__(self):
51+
return "AsyncDatamaxi(base_url={!r}, has_key={})".format(
52+
self._api.base_url, bool(self._api.api_key)
53+
)

docs/async.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ finally:
5555

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

6362
## Pagination
6463

docs/naver-trend.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Search trend data for South Korea via Naver.
77
<details markdown="1"><summary>Sync</summary>
88

99
```python
10-
from datamaxi import Naver
10+
from datamaxi import Datamaxi
1111

12-
naver = Naver(api_key="YOUR_API_KEY")
12+
naver = Datamaxi(api_key="YOUR_API_KEY").naver
1313

1414
symbols = naver.symbols()
1515
trend = naver.trend(symbol="BTC")
@@ -21,13 +21,13 @@ trend = naver.trend(symbol="BTC")
2121

2222
```python
2323
import asyncio
24-
from datamaxi.aio import AsyncNaver
24+
from datamaxi.aio import AsyncDatamaxi
2525

2626

2727
async def main():
28-
async with AsyncNaver(api_key="YOUR_API_KEY") as naver:
29-
symbols = await naver.symbols()
30-
trend = await naver.trend(symbol="BTC")
28+
async with AsyncDatamaxi(api_key="YOUR_API_KEY") as client:
29+
symbols = await client.naver.symbols()
30+
trend = await client.naver.trend(symbol="BTC")
3131

3232

3333
asyncio.run(main())

docs/telegram.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ Telegram channel metadata and message history.
77
<details markdown="1"><summary>Sync</summary>
88

99
```python
10-
from datamaxi import Telegram
10+
from datamaxi import Datamaxi
1111

12-
telegram = Telegram(api_key="YOUR_API_KEY")
12+
telegram = Datamaxi(api_key="YOUR_API_KEY").telegram
1313

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

2424
```python
2525
import asyncio
26-
from datamaxi.aio import AsyncTelegram
26+
from datamaxi.aio import AsyncDatamaxi
2727

2828

2929
async def main():
30-
async with AsyncTelegram(api_key="YOUR_API_KEY") as telegram:
31-
channels, _ = await telegram.channels(category="korean", limit=50)
32-
messages, next_request = await telegram.messages(
30+
async with AsyncDatamaxi(api_key="YOUR_API_KEY") as client:
31+
channels, _ = await client.telegram.channels(category="korean", limit=50)
32+
messages, next_request = await client.telegram.messages(
3333
channel_name="yunlog_announcement", limit=50
3434
)
3535

tests/conftest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import pytest
1111

12-
from datamaxi import Datamaxi, Telegram, Naver
12+
from datamaxi import Datamaxi
1313
from datamaxi.api import API
1414
from datamaxi.error import ServerError
1515

@@ -76,11 +76,11 @@ def datamaxi():
7676

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

8282

8383
@pytest.fixture(scope="module")
8484
def naver():
85-
"""Create Naver client for live tests."""
86-
return Naver(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT)
85+
"""Naver resource (mounted) for live tests."""
86+
return Datamaxi(api_key=API_KEY, base_url=BASE_URL, timeout=TIMEOUT).naver

tests/test_async_resources.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
httpx = pytest.importorskip("httpx")
1313

14-
from datamaxi.aio import AsyncDatamaxi, AsyncTelegram, AsyncNaver # noqa: E402
14+
from datamaxi.aio import AsyncDatamaxi # noqa: E402
15+
from datamaxi.aio.telegram import AsyncTelegram # noqa: E402
16+
from datamaxi.aio.naver import AsyncNaver # noqa: E402
1517

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

@@ -171,6 +173,13 @@ async def run():
171173
assert _run(run()) == _CHANNELS
172174

173175

176+
def test_standalone_async_clients_not_top_level_importable():
177+
import datamaxi.aio
178+
179+
assert not hasattr(datamaxi.aio, "AsyncTelegram")
180+
assert not hasattr(datamaxi.aio, "AsyncNaver")
181+
182+
174183
def test_async_telegram_naver_mounted_reuse_shared_session():
175184
c = _dm()
176185
assert isinstance(c.telegram, AsyncTelegram)

tests/test_naver.py

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

9-
from datamaxi import Datamaxi, Naver
9+
from datamaxi import Datamaxi
10+
from datamaxi.naver import Naver
1011
from datamaxi.error import ClientError, ServerError
1112
from tests.util import mock_http_response
1213

@@ -84,3 +85,9 @@ def test_naver_mounted_trend_works():
8485
df = maxi.naver.trend("BTC")
8586
assert isinstance(df, pd.DataFrame)
8687
assert len(df) == 2
88+
89+
90+
def test_standalone_naver_not_top_level_importable():
91+
import datamaxi
92+
93+
assert not hasattr(datamaxi, "Naver")

tests/test_telegram.py

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

8-
from datamaxi import Datamaxi, Telegram
8+
from datamaxi import Datamaxi
9+
from datamaxi.telegram import Telegram
910
from datamaxi.error import ClientError, ServerError
1011
from tests.util import mock_http_response
1112

@@ -125,3 +126,9 @@ def test_telegram_mounted_messages_work():
125126
res, next_request = maxi.telegram.messages(channel_name="alpha")
126127
assert res == _MESSAGES
127128
assert callable(next_request)
129+
130+
131+
def test_standalone_telegram_not_top_level_importable():
132+
import datamaxi
133+
134+
assert not hasattr(datamaxi, "Telegram")

0 commit comments

Comments
 (0)