Skip to content

Commit ff41a18

Browse files
TexasCodingclaude
andcommitted
review(#135 pass 2): CHANGELOG + async docstring + trim unbounded test
Per second-pass bot review on PR #135: - CHANGELOG.md [Unreleased]: added Added entry for max_pages kwarg + RateLimit export, and Changed entry calling out that *_all() is now unbounded by default (was silently capped at 1000 pages). - _base.py async _list_all docstring: restored the "Raises KalshiError on repeated cursor; see sync docstring." note that got trimmed in the previous refactor. - TestMaxPagesNoneIsUnbounded: 1100 → 1010. Still exceeds the old 1000-cap by enough to prove unboundedness; 90% fewer mock requests. Skipped: - nonlocal-vs-dict counter style nit — bot called this take-or-leave; the existing pattern is consistent across the test file. uv run ruff check . clean. uv run pytest tests/test_base_helpers.py::TestMaxPagesNoneIsUnbounded passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d4b6d0c commit ff41a18

3 files changed

Lines changed: 27 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ All notable changes to kalshi-sdk will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- **`max_pages: int | None` kwarg on every public `*_all()` method** — sync and
10+
async (19 + 19 method signatures). Bounded iteration without manual pagination.
11+
`None` (default) iterates until the server returns no cursor; the existing
12+
cursor-repeat guard remains the safety net against infinite loops (#98).
13+
- **`RateLimit` model** exposed via `kalshi.RateLimit` — represents the per-
14+
direction token-bucket structure on `AccountApiLimits.read` / `.write`.
15+
16+
### Changed
17+
18+
- **`*_all()` methods are now unbounded by default.** Previously, internal
19+
`_list_all` had an invisible 1000-page safety cap that silently truncated
20+
callers iterating beyond ~100k items. The cap is gone; the cursor-repeat
21+
guard (`KalshiError` on a repeating cursor) provides the runaway protection
22+
it always did. Callers who want a cap pass `max_pages=N` explicitly. No
23+
user-visible change for anyone iterating <1000 pages (#98).
24+
725
### Fixed
826

927
- **`/account/limits` response now parses against the live server.** The

kalshi/resources/_base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ async def _list_all(
286286
max_pages: int | None = None,
287287
cursor_key: str = "cursor",
288288
) -> AsyncIterator[T]:
289-
"""Async counterpart of :meth:`SyncResource._list_all`."""
289+
"""Async counterpart of :meth:`SyncResource._list_all`.
290+
291+
Raises ``KalshiError`` on repeated cursor; see sync docstring.
292+
"""
290293
current_params = dict(params) if params else {}
291294
seen_cursors: set[str] = set()
292295
pages_fetched = 0

tests/test_base_helpers.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,18 +421,20 @@ class TestMaxPagesNoneIsUnbounded:
421421
def test_sync_iterates_past_1000_pages(
422422
self, test_auth: KalshiAuth, test_config: KalshiConfig,
423423
) -> None:
424-
# Page N returns cursor str(N+1) for the first 1100 pages, then empty.
424+
# 1010 just exceeds the old 1000 cap — proves the cap is gone without
425+
# making the test 100x slower than a normal pagination test.
426+
total = 1010
425427
call_counter = {"n": 0}
426428

427429
def responder(request: httpx.Request) -> httpx.Response:
428430
call_counter["n"] += 1
429431
n = call_counter["n"]
430-
cursor = str(n) if n < 1100 else ""
432+
cursor = str(n) if n < total else ""
431433
return httpx.Response(
432434
200, json={"items": [{"id": f"i{n}"}], "cursor": cursor},
433435
)
434436

435437
respx.get("https://test.kalshi.com/trade-api/v2/things").mock(side_effect=responder)
436438
resource = SyncResource(SyncTransport(test_auth, test_config))
437439
items = list(resource._list_all("/things", _Item, "items"))
438-
assert len(items) == 1100, f"Expected 1100 items, got {len(items)} (cap leaked?)"
440+
assert len(items) == total, f"Expected {total} items, got {len(items)} (cap leaked?)"

0 commit comments

Comments
 (0)