tcgapi 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,140 @@
1
+ """BulkResource — /bulk/* endpoints. Auto-chunks oversized lists."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ from ..models import BulkCard, BulkPriceRow, PriceHistoryPoint, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+ BULK_PRICES_MAX = 500
15
+ BULK_CARDS_MAX = 100
16
+ BULK_HISTORY_MAX = 50
17
+
18
+ RangeLiteral = Literal["month", "quarter", "year", "all"]
19
+
20
+
21
+ def _ids(ids: list[int]) -> str:
22
+ if not ids:
23
+ raise ValueError("At least one card ID required")
24
+ return ",".join(str(i) for i in ids)
25
+
26
+
27
+ class BulkResource:
28
+ def __init__(self, client: "TCGApi") -> None:
29
+ self._client = client
30
+
31
+ def prices(self, ids: list[int]) -> Response[list[BulkPriceRow]]:
32
+ if len(ids) <= BULK_PRICES_MAX:
33
+ body = self._client._request("GET", "/bulk/prices", {"ids": _ids(ids)})
34
+ return parse_response(list[BulkPriceRow], body)
35
+ rows: list[BulkPriceRow] = []
36
+ last: Response[list[BulkPriceRow]] | None = None
37
+ for i in range(0, len(ids), BULK_PRICES_MAX):
38
+ chunk = ids[i : i + BULK_PRICES_MAX]
39
+ body = self._client._request("GET", "/bulk/prices", {"ids": _ids(chunk)})
40
+ resp: Response[list[BulkPriceRow]] = parse_response(list[BulkPriceRow], body)
41
+ rows.extend(resp.data)
42
+ last = resp
43
+ return Response[list[BulkPriceRow]](data=rows, rate_limit=last.rate_limit if last else None)
44
+
45
+ def cards(self, ids: list[int]) -> Response[list[BulkCard]]:
46
+ if len(ids) <= BULK_CARDS_MAX:
47
+ body = self._client._request("GET", "/bulk/cards", {"ids": _ids(ids)})
48
+ return parse_response(list[BulkCard], body)
49
+ rows: list[BulkCard] = []
50
+ last: Response[list[BulkCard]] | None = None
51
+ for i in range(0, len(ids), BULK_CARDS_MAX):
52
+ chunk = ids[i : i + BULK_CARDS_MAX]
53
+ body = self._client._request("GET", "/bulk/cards", {"ids": _ids(chunk)})
54
+ resp: Response[list[BulkCard]] = parse_response(list[BulkCard], body)
55
+ rows.extend(resp.data)
56
+ last = resp
57
+ return Response[list[BulkCard]](data=rows, rate_limit=last.rate_limit if last else None)
58
+
59
+ def history(
60
+ self,
61
+ ids: list[int],
62
+ *,
63
+ range: RangeLiteral | None = None,
64
+ printing: str | None = None,
65
+ ) -> Response[dict[str, list[PriceHistoryPoint]]]:
66
+ params: dict[str, object | None] = {"range": range, "printing": printing}
67
+ if len(ids) <= BULK_HISTORY_MAX:
68
+ body = self._client._request("GET", "/bulk/history", {**params, "ids": _ids(ids)})
69
+ return parse_response(dict[str, list[PriceHistoryPoint]], body)
70
+ merged: dict[str, list[PriceHistoryPoint]] = {}
71
+ last: Response[dict[str, list[PriceHistoryPoint]]] | None = None
72
+ for i in range(0, len(ids), BULK_HISTORY_MAX):
73
+ chunk = ids[i : i + BULK_HISTORY_MAX]
74
+ body = self._client._request("GET", "/bulk/history", {**params, "ids": _ids(chunk)})
75
+ resp: Response[dict[str, list[PriceHistoryPoint]]] = parse_response(
76
+ dict[str, list[PriceHistoryPoint]], body
77
+ )
78
+ merged.update(resp.data)
79
+ last = resp
80
+ return Response[dict[str, list[PriceHistoryPoint]]](
81
+ data=merged, rate_limit=last.rate_limit if last else None
82
+ )
83
+
84
+
85
+ class AsyncBulkResource:
86
+ def __init__(self, client: "AsyncTCGApi") -> None:
87
+ self._client = client
88
+
89
+ async def prices(self, ids: list[int]) -> Response[list[BulkPriceRow]]:
90
+ if len(ids) <= BULK_PRICES_MAX:
91
+ body = await self._client._request("GET", "/bulk/prices", {"ids": _ids(ids)})
92
+ return parse_response(list[BulkPriceRow], body)
93
+ rows: list[BulkPriceRow] = []
94
+ last: Response[list[BulkPriceRow]] | None = None
95
+ for i in range(0, len(ids), BULK_PRICES_MAX):
96
+ chunk = ids[i : i + BULK_PRICES_MAX]
97
+ body = await self._client._request("GET", "/bulk/prices", {"ids": _ids(chunk)})
98
+ resp: Response[list[BulkPriceRow]] = parse_response(list[BulkPriceRow], body)
99
+ rows.extend(resp.data)
100
+ last = resp
101
+ return Response[list[BulkPriceRow]](data=rows, rate_limit=last.rate_limit if last else None)
102
+
103
+ async def cards(self, ids: list[int]) -> Response[list[BulkCard]]:
104
+ if len(ids) <= BULK_CARDS_MAX:
105
+ body = await self._client._request("GET", "/bulk/cards", {"ids": _ids(ids)})
106
+ return parse_response(list[BulkCard], body)
107
+ rows: list[BulkCard] = []
108
+ last: Response[list[BulkCard]] | None = None
109
+ for i in range(0, len(ids), BULK_CARDS_MAX):
110
+ chunk = ids[i : i + BULK_CARDS_MAX]
111
+ body = await self._client._request("GET", "/bulk/cards", {"ids": _ids(chunk)})
112
+ resp: Response[list[BulkCard]] = parse_response(list[BulkCard], body)
113
+ rows.extend(resp.data)
114
+ last = resp
115
+ return Response[list[BulkCard]](data=rows, rate_limit=last.rate_limit if last else None)
116
+
117
+ async def history(
118
+ self,
119
+ ids: list[int],
120
+ *,
121
+ range: RangeLiteral | None = None,
122
+ printing: str | None = None,
123
+ ) -> Response[dict[str, list[PriceHistoryPoint]]]:
124
+ params: dict[str, object | None] = {"range": range, "printing": printing}
125
+ if len(ids) <= BULK_HISTORY_MAX:
126
+ body = await self._client._request("GET", "/bulk/history", {**params, "ids": _ids(ids)})
127
+ return parse_response(dict[str, list[PriceHistoryPoint]], body)
128
+ merged: dict[str, list[PriceHistoryPoint]] = {}
129
+ last: Response[dict[str, list[PriceHistoryPoint]]] | None = None
130
+ for i in range(0, len(ids), BULK_HISTORY_MAX):
131
+ chunk = ids[i : i + BULK_HISTORY_MAX]
132
+ body = await self._client._request("GET", "/bulk/history", {**params, "ids": _ids(chunk)})
133
+ resp: Response[dict[str, list[PriceHistoryPoint]]] = parse_response(
134
+ dict[str, list[PriceHistoryPoint]], body
135
+ )
136
+ merged.update(resp.data)
137
+ last = resp
138
+ return Response[dict[str, list[PriceHistoryPoint]]](
139
+ data=merged, rate_limit=last.rate_limit if last else None
140
+ )
@@ -0,0 +1,88 @@
1
+ """CardsResource — /cards endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ from ..models import BulkCard, Card, Price, PriceHistoryPoint, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+ RangeLiteral = Literal["month", "quarter", "year", "all"]
15
+
16
+
17
+ class CardsResource:
18
+ def __init__(self, client: "TCGApi") -> None:
19
+ self._client = client
20
+
21
+ def get(self, card_id: int) -> Response[Card]:
22
+ body = self._client._request("GET", f"/cards/{card_id}")
23
+ return parse_response(Card, body)
24
+
25
+ def prices(self, card_id: int, *, printing: str | None = None) -> Response[list[Price]]:
26
+ body = self._client._request("GET", f"/cards/{card_id}/prices", {"printing": printing})
27
+ return parse_response(list[Price], body)
28
+
29
+ def by_tcgplayer_id(self, tcgplayer_id: int) -> Response[BulkCard]:
30
+ body = self._client._request("GET", f"/cards/tcgplayer/{tcgplayer_id}")
31
+ return parse_response(BulkCard, body)
32
+
33
+ def history(
34
+ self,
35
+ card_id: int,
36
+ *,
37
+ range: RangeLiteral | None = None,
38
+ printing: str | None = None,
39
+ ) -> Response[list[PriceHistoryPoint]]:
40
+ body = self._client._request(
41
+ "GET", f"/cards/{card_id}/history", {"range": range, "printing": printing}
42
+ )
43
+ return parse_response(list[PriceHistoryPoint], body)
44
+
45
+ def history_detailed(
46
+ self, card_id: int, *, printing: str | None = None
47
+ ) -> Response[list[PriceHistoryPoint]]:
48
+ body = self._client._request(
49
+ "GET", f"/cards/{card_id}/history/detailed", {"printing": printing}
50
+ )
51
+ return parse_response(list[PriceHistoryPoint], body)
52
+
53
+
54
+ class AsyncCardsResource:
55
+ def __init__(self, client: "AsyncTCGApi") -> None:
56
+ self._client = client
57
+
58
+ async def get(self, card_id: int) -> Response[Card]:
59
+ body = await self._client._request("GET", f"/cards/{card_id}")
60
+ return parse_response(Card, body)
61
+
62
+ async def prices(self, card_id: int, *, printing: str | None = None) -> Response[list[Price]]:
63
+ body = await self._client._request("GET", f"/cards/{card_id}/prices", {"printing": printing})
64
+ return parse_response(list[Price], body)
65
+
66
+ async def by_tcgplayer_id(self, tcgplayer_id: int) -> Response[BulkCard]:
67
+ body = await self._client._request("GET", f"/cards/tcgplayer/{tcgplayer_id}")
68
+ return parse_response(BulkCard, body)
69
+
70
+ async def history(
71
+ self,
72
+ card_id: int,
73
+ *,
74
+ range: RangeLiteral | None = None,
75
+ printing: str | None = None,
76
+ ) -> Response[list[PriceHistoryPoint]]:
77
+ body = await self._client._request(
78
+ "GET", f"/cards/{card_id}/history", {"range": range, "printing": printing}
79
+ )
80
+ return parse_response(list[PriceHistoryPoint], body)
81
+
82
+ async def history_detailed(
83
+ self, card_id: int, *, printing: str | None = None
84
+ ) -> Response[list[PriceHistoryPoint]]:
85
+ body = await self._client._request(
86
+ "GET", f"/cards/{card_id}/history/detailed", {"printing": printing}
87
+ )
88
+ return parse_response(list[PriceHistoryPoint], body)
@@ -0,0 +1,36 @@
1
+ """ExportResource — /export/set/{id}."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ..models import BulkPriceRow, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+
15
+ class ExportResource:
16
+ def __init__(self, client: "TCGApi") -> None:
17
+ self._client = client
18
+
19
+ def set_json(self, set_id: int) -> Response[list[BulkPriceRow]]:
20
+ body = self._client._request("GET", f"/export/set/{set_id}", {"format": "json"})
21
+ return parse_response(list[BulkPriceRow], body)
22
+
23
+ def set_csv(self, set_id: int) -> str:
24
+ return self._client._request_raw("GET", f"/export/set/{set_id}", {"format": "csv"})
25
+
26
+
27
+ class AsyncExportResource:
28
+ def __init__(self, client: "AsyncTCGApi") -> None:
29
+ self._client = client
30
+
31
+ async def set_json(self, set_id: int) -> Response[list[BulkPriceRow]]:
32
+ body = await self._client._request("GET", f"/export/set/{set_id}", {"format": "json"})
33
+ return parse_response(list[BulkPriceRow], body)
34
+
35
+ async def set_csv(self, set_id: int) -> str:
36
+ return await self._client._request_raw("GET", f"/export/set/{set_id}", {"format": "csv"})
@@ -0,0 +1,48 @@
1
+ """GamesResource — /games endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ..models import Game, Response, Set
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+
15
+ class GamesResource:
16
+ def __init__(self, client: "TCGApi") -> None:
17
+ self._client = client
18
+
19
+ def list(self, *, page: int | None = None, per_page: int | None = None) -> Response[list[Game]]:
20
+ body = self._client._request("GET", "/games", {"page": page, "per_page": per_page})
21
+ return parse_response(list[Game], body)
22
+
23
+ def get(self, slug: str | int) -> Response[Game]:
24
+ body = self._client._request("GET", f"/games/{slug}")
25
+ return parse_response(Game, body)
26
+
27
+ def sets(self, slug: str | int, *, page: int | None = None, per_page: int | None = None) -> Response[list[Set]]:
28
+ body = self._client._request("GET", f"/games/{slug}/sets", {"page": page, "per_page": per_page})
29
+ return parse_response(list[Set], body)
30
+
31
+
32
+ class AsyncGamesResource:
33
+ def __init__(self, client: "AsyncTCGApi") -> None:
34
+ self._client = client
35
+
36
+ async def list(self, *, page: int | None = None, per_page: int | None = None) -> Response[list[Game]]:
37
+ body = await self._client._request("GET", "/games", {"page": page, "per_page": per_page})
38
+ return parse_response(list[Game], body)
39
+
40
+ async def get(self, slug: str | int) -> Response[Game]:
41
+ body = await self._client._request("GET", f"/games/{slug}")
42
+ return parse_response(Game, body)
43
+
44
+ async def sets(
45
+ self, slug: str | int, *, page: int | None = None, per_page: int | None = None
46
+ ) -> Response[list[Set]]:
47
+ body = await self._client._request("GET", f"/games/{slug}/sets", {"page": page, "per_page": per_page})
48
+ return parse_response(list[Set], body)
@@ -0,0 +1,30 @@
1
+ """KeysResource — /keys (session-cookie auth, mostly used from the dashboard)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ..models import ApiKeySummary, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+
15
+ class KeysResource:
16
+ def __init__(self, client: "TCGApi") -> None:
17
+ self._client = client
18
+
19
+ def list(self) -> Response[list[ApiKeySummary]]:
20
+ body = self._client._request("GET", "/keys")
21
+ return parse_response(list[ApiKeySummary], body)
22
+
23
+
24
+ class AsyncKeysResource:
25
+ def __init__(self, client: "AsyncTCGApi") -> None:
26
+ self._client = client
27
+
28
+ async def list(self) -> Response[list[ApiKeySummary]]:
29
+ body = await self._client._request("GET", "/keys")
30
+ return parse_response(list[ApiKeySummary], body)
@@ -0,0 +1,74 @@
1
+ """PricesResource — /prices/top-movers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Literal
6
+
7
+ from ..models import PriceMover, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+ DirectionLiteral = Literal["up", "down"]
15
+ PeriodLiteral = Literal["24h", "7d", "30d"]
16
+ TypeLiteral = Literal["Cards", "Sealed Products"]
17
+
18
+
19
+ class PricesResource:
20
+ def __init__(self, client: "TCGApi") -> None:
21
+ self._client = client
22
+
23
+ def top_movers(
24
+ self,
25
+ *,
26
+ game: str | None = None,
27
+ direction: DirectionLiteral | None = None,
28
+ period: PeriodLiteral | None = None,
29
+ printing: str | None = None,
30
+ type: TypeLiteral | None = None,
31
+ limit: int | None = None,
32
+ ) -> Response[list[PriceMover]]:
33
+ body = self._client._request(
34
+ "GET",
35
+ "/prices/top-movers",
36
+ {
37
+ "game": game,
38
+ "direction": direction,
39
+ "period": period,
40
+ "printing": printing,
41
+ "type": type,
42
+ "limit": limit,
43
+ },
44
+ )
45
+ return parse_response(list[PriceMover], body)
46
+
47
+
48
+ class AsyncPricesResource:
49
+ def __init__(self, client: "AsyncTCGApi") -> None:
50
+ self._client = client
51
+
52
+ async def top_movers(
53
+ self,
54
+ *,
55
+ game: str | None = None,
56
+ direction: DirectionLiteral | None = None,
57
+ period: PeriodLiteral | None = None,
58
+ printing: str | None = None,
59
+ type: TypeLiteral | None = None,
60
+ limit: int | None = None,
61
+ ) -> Response[list[PriceMover]]:
62
+ body = await self._client._request(
63
+ "GET",
64
+ "/prices/top-movers",
65
+ {
66
+ "game": game,
67
+ "direction": direction,
68
+ "period": period,
69
+ "printing": printing,
70
+ "type": type,
71
+ "limit": limit,
72
+ },
73
+ )
74
+ return parse_response(list[PriceMover], body)
@@ -0,0 +1,118 @@
1
+ """SearchResource — /search."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, AsyncIterator, Iterator, Literal
6
+
7
+ from ..models import CardWithPrice, Response
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+ SortLiteral = Literal["relevance", "price_asc", "price_desc", "name"]
15
+ TypeLiteral = Literal["Cards", "Sealed Products"]
16
+
17
+
18
+ def _params(
19
+ q: str,
20
+ game: str | None,
21
+ set_id: int | None,
22
+ rarity: str | None,
23
+ type: TypeLiteral | None,
24
+ printing: str | None,
25
+ min_price: float | None,
26
+ max_price: float | None,
27
+ sort: SortLiteral | None,
28
+ page: int | None,
29
+ per_page: int | None,
30
+ ) -> dict[str, object | None]:
31
+ return {
32
+ "q": q,
33
+ "game": game,
34
+ "set_id": set_id,
35
+ "rarity": rarity,
36
+ "type": type,
37
+ "printing": printing,
38
+ "min_price": min_price,
39
+ "max_price": max_price,
40
+ "sort": sort,
41
+ "page": page,
42
+ "per_page": per_page,
43
+ }
44
+
45
+
46
+ class SearchResource:
47
+ def __init__(self, client: "TCGApi") -> None:
48
+ self._client = client
49
+
50
+ def cards(
51
+ self,
52
+ q: str,
53
+ *,
54
+ game: str | None = None,
55
+ set_id: int | None = None,
56
+ rarity: str | None = None,
57
+ type: TypeLiteral | None = None,
58
+ printing: str | None = None,
59
+ min_price: float | None = None,
60
+ max_price: float | None = None,
61
+ sort: SortLiteral | None = None,
62
+ page: int | None = None,
63
+ per_page: int | None = None,
64
+ ) -> Response[list[CardWithPrice]]:
65
+ body = self._client._request(
66
+ "GET",
67
+ "/search",
68
+ _params(q, game, set_id, rarity, type, printing, min_price, max_price, sort, page, per_page),
69
+ )
70
+ return parse_response(list[CardWithPrice], body)
71
+
72
+ def iter(self, q: str, **kwargs: object) -> Iterator[CardWithPrice]:
73
+ page = int(kwargs.pop("page", 1) or 1) # type: ignore[arg-type]
74
+ per_page = min(200, int(kwargs.pop("per_page", 200) or 200)) # type: ignore[arg-type]
75
+ while True:
76
+ resp = self.cards(q, page=page, per_page=per_page, **kwargs) # type: ignore[arg-type]
77
+ yield from resp.data
78
+ if not resp.meta or not resp.meta.has_more:
79
+ return
80
+ page += 1
81
+
82
+
83
+ class AsyncSearchResource:
84
+ def __init__(self, client: "AsyncTCGApi") -> None:
85
+ self._client = client
86
+
87
+ async def cards(
88
+ self,
89
+ q: str,
90
+ *,
91
+ game: str | None = None,
92
+ set_id: int | None = None,
93
+ rarity: str | None = None,
94
+ type: TypeLiteral | None = None,
95
+ printing: str | None = None,
96
+ min_price: float | None = None,
97
+ max_price: float | None = None,
98
+ sort: SortLiteral | None = None,
99
+ page: int | None = None,
100
+ per_page: int | None = None,
101
+ ) -> Response[list[CardWithPrice]]:
102
+ body = await self._client._request(
103
+ "GET",
104
+ "/search",
105
+ _params(q, game, set_id, rarity, type, printing, min_price, max_price, sort, page, per_page),
106
+ )
107
+ return parse_response(list[CardWithPrice], body)
108
+
109
+ async def iter(self, q: str, **kwargs: object) -> AsyncIterator[CardWithPrice]:
110
+ page = int(kwargs.pop("page", 1) or 1) # type: ignore[arg-type]
111
+ per_page = min(200, int(kwargs.pop("per_page", 200) or 200)) # type: ignore[arg-type]
112
+ while True:
113
+ resp = await self.cards(q, page=page, per_page=per_page, **kwargs) # type: ignore[arg-type]
114
+ for card in resp.data:
115
+ yield card
116
+ if not resp.meta or not resp.meta.has_more:
117
+ return
118
+ page += 1
@@ -0,0 +1,115 @@
1
+ """SetsResource — /sets endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, AsyncIterator, Iterator, Literal
6
+
7
+ from ..models import BulkPriceRow, CardWithPrice, Response, Set
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+ SortLiteral = Literal["number", "price_asc", "price_desc", "name"]
15
+ TypeLiteral = Literal["Cards", "Sealed Products"]
16
+
17
+
18
+ class SetsResource:
19
+ def __init__(self, client: "TCGApi") -> None:
20
+ self._client = client
21
+
22
+ def list(
23
+ self,
24
+ *,
25
+ game: str | None = None,
26
+ page: int | None = None,
27
+ per_page: int | None = None,
28
+ ) -> Response[list[Set]]:
29
+ body = self._client._request("GET", "/sets", {"game": game, "page": page, "per_page": per_page})
30
+ return parse_response(list[Set], body)
31
+
32
+ def get(self, set_id: int) -> Response[Set]:
33
+ body = self._client._request("GET", f"/sets/{set_id}")
34
+ return parse_response(Set, body)
35
+
36
+ def cards(
37
+ self,
38
+ set_id: int,
39
+ *,
40
+ type: TypeLiteral | None = None,
41
+ sort: SortLiteral | None = None,
42
+ page: int | None = None,
43
+ per_page: int | None = None,
44
+ ) -> Response[list[CardWithPrice]]:
45
+ body = self._client._request(
46
+ "GET",
47
+ f"/sets/{set_id}/cards",
48
+ {"type": type, "sort": sort, "page": page, "per_page": per_page},
49
+ )
50
+ return parse_response(list[CardWithPrice], body)
51
+
52
+ def prices(self, set_id: int) -> Response[list[BulkPriceRow]]:
53
+ body = self._client._request("GET", f"/sets/{set_id}/prices")
54
+ return parse_response(list[BulkPriceRow], body)
55
+
56
+ def iter_cards(self, set_id: int, **params: object) -> Iterator[CardWithPrice]:
57
+ """Paginate through every card in a set, yielding one at a time."""
58
+ page = int(params.pop("page", 1) or 1) # type: ignore[arg-type]
59
+ per_page = min(200, int(params.pop("per_page", 200) or 200)) # type: ignore[arg-type]
60
+ while True:
61
+ resp = self.cards(set_id, page=page, per_page=per_page, **params) # type: ignore[arg-type]
62
+ yield from resp.data
63
+ if not resp.meta or not resp.meta.has_more:
64
+ return
65
+ page += 1
66
+
67
+
68
+ class AsyncSetsResource:
69
+ def __init__(self, client: "AsyncTCGApi") -> None:
70
+ self._client = client
71
+
72
+ async def list(
73
+ self,
74
+ *,
75
+ game: str | None = None,
76
+ page: int | None = None,
77
+ per_page: int | None = None,
78
+ ) -> Response[list[Set]]:
79
+ body = await self._client._request("GET", "/sets", {"game": game, "page": page, "per_page": per_page})
80
+ return parse_response(list[Set], body)
81
+
82
+ async def get(self, set_id: int) -> Response[Set]:
83
+ body = await self._client._request("GET", f"/sets/{set_id}")
84
+ return parse_response(Set, body)
85
+
86
+ async def cards(
87
+ self,
88
+ set_id: int,
89
+ *,
90
+ type: TypeLiteral | None = None,
91
+ sort: SortLiteral | None = None,
92
+ page: int | None = None,
93
+ per_page: int | None = None,
94
+ ) -> Response[list[CardWithPrice]]:
95
+ body = await self._client._request(
96
+ "GET",
97
+ f"/sets/{set_id}/cards",
98
+ {"type": type, "sort": sort, "page": page, "per_page": per_page},
99
+ )
100
+ return parse_response(list[CardWithPrice], body)
101
+
102
+ async def prices(self, set_id: int) -> Response[list[BulkPriceRow]]:
103
+ body = await self._client._request("GET", f"/sets/{set_id}/prices")
104
+ return parse_response(list[BulkPriceRow], body)
105
+
106
+ async def iter_cards(self, set_id: int, **params: object) -> AsyncIterator[CardWithPrice]:
107
+ page = int(params.pop("page", 1) or 1) # type: ignore[arg-type]
108
+ per_page = min(200, int(params.pop("per_page", 200) or 200)) # type: ignore[arg-type]
109
+ while True:
110
+ resp = await self.cards(set_id, page=page, per_page=per_page, **params) # type: ignore[arg-type]
111
+ for card in resp.data:
112
+ yield card
113
+ if not resp.meta or not resp.meta.has_more:
114
+ return
115
+ page += 1
@@ -0,0 +1,30 @@
1
+ """UsageResource — /usage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ..models import Response, UsageResponse
8
+ from ._base import parse_response
9
+
10
+ if TYPE_CHECKING:
11
+ from ..async_client import AsyncTCGApi
12
+ from ..client import TCGApi
13
+
14
+
15
+ class UsageResource:
16
+ def __init__(self, client: "TCGApi") -> None:
17
+ self._client = client
18
+
19
+ def get(self) -> Response[UsageResponse]:
20
+ body = self._client._request("GET", "/usage")
21
+ return parse_response(UsageResponse, body)
22
+
23
+
24
+ class AsyncUsageResource:
25
+ def __init__(self, client: "AsyncTCGApi") -> None:
26
+ self._client = client
27
+
28
+ async def get(self) -> Response[UsageResponse]:
29
+ body = await self._client._request("GET", "/usage")
30
+ return parse_response(UsageResponse, body)