Skip to content

polish(resources): communications status Literal + V2 batch bytes-fast-path#373

Merged
TexasCoding merged 2 commits into
mainfrom
r3/W3-D
May 22, 2026
Merged

polish(resources): communications status Literal + V2 batch bytes-fast-path#373
TexasCoding merged 2 commits into
mainfrom
r3/W3-D

Conversation

@TexasCoding

Copy link
Copy Markdown
Owner

Summary

Two LOW-severity follow-ups against the v2.4 / v3.18 surfaces: narrow the
status= kwarg on CommunicationsResource.list_rfqs / list_quotes (sync

Issues closed

Behavioral changes

  • CommunicationsResource.list_rfqs(status=...) and list_quotes(status=...)
    (sync + async, including list_all_*) now declare a closed Literal
    set. Runtime semantics are unchanged — Python does not enforce Literal
    at runtime — but typos like status="accpeted" are now flagged by mypy
    / IDEs at the call site instead of round-tripping to the server. Matches
    the project-wide pattern already used by OrdersResource.list(status=),
    incentive_programs.list(status=), etc.

Tests

  • tests/test_communications.py::TestIssue324CommunicationsStatusLiteralNarrowing
    — pins the closed get_args(RfqStatusLiteral) / get_args(QuoteStatusLiteral)
    sets against the spec, asserts re-export from kalshi.models + kalshi,
    and verifies valid status values flow through to the query string.
  • tests/test_orders.py::TestIssue329BatchV2BytesFastPath and
    tests/test_async_orders.py::TestIssue329AsyncBatchV2BytesFastPath
    patch OrdersResource / AsyncOrdersResource to assert batch_create_v2
    routes through _post_json(content=<bytes>) (never _post(json=...))
    and batch_cancel_v2 routes through _delete_with_body_json(content=<bytes>)
    (never _delete_with_body(json=...)).

Source

Round-3 independent audit closure plan, wave W3 (LOW polish / perf).

…-path

#324: CommunicationsResource.list_rfqs / list_quotes (sync + async, plus
list_all variants) declared 'status: str | None', the only paginated list
on the SDK that did not narrow its status filter. The spec defines closed
enums (RFQ.status: open|closed; Quote.status: open|accepted|confirmed|
executed|cancelled) and the README advertises 'Literal types on fixed-enum
kwargs' as a headline feature. Add RfqStatusLiteral / QuoteStatusLiteral
to kalshi.models.communications, re-export from kalshi.models + kalshi,
and narrow the eight status kwargs. Pure type-system fence, no runtime
behavior change — typos now surface at the call site under mypy / IDE
autocomplete instead of silently round-tripping.

#329: batch_create_v2 / batch_cancel_v2 (sync + async) still serialized
via request.model_dump(mode='json') -> _post(json=dict) /
_delete_with_body(json=dict), paying the dict-walk pass that httpx's
'json=' encoder repeats — exactly the cost #223 measured and removed for
V1 batch_create / batch_cancel. Add _build_batch_create_v2_body /
_build_batch_cancel_v2_body next to the V1 builders (model_dump_json +
.encode()) and route the V2 methods through _post_json /
_delete_with_body_json with content=<bytes>. V2 is the spec-recommended
surface for new code, so leaving the perf regression on it would
regress #223's own contract.

Closes #324, #329
@TexasCoding TexasCoding added polish Code-quality and DX improvements; non-functional spec-drift Upstream OpenAPI/AsyncAPI spec changed since last sync performance Performance / hot-path concern labels May 22, 2026
@claude

claude Bot commented May 22, 2026

Copy link
Copy Markdown

Code Review — PR #373

Both changes are correct and well-motivated. A few focused notes below.


Overview

Issue #324 (Literal narrowing): RfqStatusLiteral / QuoteStatusLiteral close the open str on all four status= parameters (sync + async, list_* + list_all_*). The pattern is already used project-wide (OrdersResource.list, incentive_programs.list, etc.), so this is clean spec-alignment.

Issue #329 (V2 bytes fast-path): batch_create_v2 / batch_cancel_v2 (sync + async) are migrated from model_dump(mode='json') + _post(json=…) to model_dump_json().encode() + _post_json(content=…), matching the V1 pattern shipped in #223.


Issues

1. Trivial wrappers with no logic (_build_batch_create_v2_body / _build_batch_cancel_v2_body)

Both functions contain exactly one line of logic:

return request.model_dump_json(exclude_none=True, by_alias=True).encode()

The V1 builders justify their existence — they normalise orders sequences, validate exclusivity, and build the request model. The V2 builders do none of that; they just rename the one-liner. Per the project's Simplicity First guideline ("no abstractions for single-use code"), these should be inlined at the call site:

def batch_create_v2(self, *, request: BatchCreateOrdersV2Request, …) -> …:
    self._require_auth()
    data = self._post_json(
        "/portfolio/events/orders/batched",
        content=request.model_dump_json(exclude_none=True, by_alias=True).encode(),
        extra_headers=extra_headers,
    )
    return BatchCreateOrdersV2Response.model_validate(data)

That also removes the four test assertions that patch _build_batch_create_v2_body / _build_batch_cancel_v2_body indirectly — the tests can just assert on _post_json / _delete_with_body_json directly, which they already do.

2. Multi-paragraph docstrings and issue-number references

CLAUDE.md is explicit: "Never write multi-paragraph docstrings or multi-line comment blocks — one short line max" and "Don't reference the current task, fix, or callers… those belong in the PR description."

_build_batch_create_v2_body has a three-sentence docstring citing #223, v2.4, and #329. _build_batch_cancel_v2_body has a two-line docstring citing #329. Both violate both rules. If the wrappers are kept, trim them to a single sentence with no issue numbers.

3. Missing async status flow-through tests

TestIssue324CommunicationsStatusLiteralNarrowing tests that valid status values reach the query string — but only for the sync resource. AsyncCommunicationsResource has the same narrowed signatures and uses the same _list_rfqs_params / _list_quotes_params helpers, so the async path is almost certainly correct, but it has no dedicated coverage. Two @pytest.mark.asyncio tests mirroring the sync ones would close this gap.


Minor nit

_build_batch_cancel_v2_body's docstring wraps mid-sentence:

"""Serialize the V2 batch-cancel body directly to JSON bytes. See
:func:`_build_batch_create_v2_body` for the perf rationale (#329)."""

That's an unusual split for what the convention says should be one line.


No issues found

  • Literal values match the OpenAPI spec enums exactly.
  • Re-exports in kalshi/models/__init__.py and kalshi/__init__.py are correct; __all__ is updated.
  • Fast-path bytes are identical to model_dump(mode='json') output — the test round-trips through json.loads to confirm.
  • No security concerns; no retry-safety issues (POST/DELETE batch endpoints retain their no-retry guarantee through the transport layer).
  • Test structure (patch at the transport-helper layer, assert content=bytes / no json=) is the right level of abstraction.

Summary: #324 is clean to merge as-is. For #329 I'd inline the trivial V2 helpers and trim the docstrings before merging, plus add the two async status flow-through tests for completeness.

@TexasCoding

Copy link
Copy Markdown
Owner Author

Round-2 review addressed in 4c7ea5c:

  • Inlined trivial V2 helpers (batch_create_v2 / batch_cancel_v2 missed the v2.4 #223 bytes-fast-path optimization #329): removed _build_batch_create_v2_body and _build_batch_cancel_v2_body — each was a single request.model_dump_json(exclude_none=True, by_alias=True).encode() expression with no meaningful logic beyond the V1 helpers' validation/normalization. Inlined at all four call sites (sync + async batch_create_v2 / batch_cancel_v2). The helper docstrings (which were the only docstrings on this surface) are gone with the helpers, satisfying the one-short-line ask.
  • Async status flow-through tests (communications.list_rfqs / list_quotes accept status: str | None — should be Literal-narrowed #324): added test_issue_324_valid_rfq_status_flows_through_to_query_async and test_issue_324_valid_quote_status_flows_through_to_query_async in tests/test_communications.py, mirroring the sync versions with @pytest.mark.asyncio + async_comms fixture + respx_mock router.

Verification: ruff check clean; mypy kalshi/resources/orders.py clean; pytest -k 'issue_324 or issue_329' → 10/10 pass.

@claude

claude Bot commented May 22, 2026

Copy link
Copy Markdown

Code Review — PR #373

Overview

Two clean, well-scoped follow-ups: narrowing status= params to closed Literal types on the communications resource, and routing V2 batch endpoints through the bytes fast-path (parity with V1 #223). Both changes are minimal, correct, and match established project patterns.


Issue #324 — Status Literal Narrowing

Positives:

  • Covers all four public methods (list_rfqs, list_all_rfqs, list_quotes, list_all_quotes) and the private _list_rfqs_params / _list_quotes_params builder functions — no inconsistency left behind.
  • Pattern matches the existing project-wide convention (OrdersResource.list(status=), etc.).
  • Literals are properly exported through kalshi/models/__init__.pykalshi/__init__.py with __all__ entries.

Nit — imports inside test methods:

get_args, kalshi, and kalshi.models are imported inside individual test method bodies rather than at module level. Every other import in test_communications.py is at the top. Hoisting these keeps the file consistent and avoids misleading readers into thinking there's a deliberate deferred-import reason.

# Current (inside test body)
def test_issue_324_communications_status_literal_narrowing(self) -> None:
    from typing import get_args
    from kalshi.models.communications import QuoteStatusLiteral, RfqStatusLiteral
    ...

# Preferred (module top)
from typing import get_args
from kalshi.models.communications import QuoteStatusLiteral, RfqStatusLiteral

Minor concern — spec confidence for QuoteStatusLiteral:

The QuoteStatusLiteral set is {"open", "accepted", "confirmed", "executed", "cancelled"}. The test pins this against itself (via get_args), not against the raw spec YAML. If the spec ever adds a "rejected" or "expired" state, the Literal will silently lag. This is inherent to the approach (the V1 OrderStatusLiteral has the same property), so no blocker — just worth a note that drift would surface in a spec-sync audit, not in these tests.


Issue #329 — V2 Batch Bytes Fast-Path

Positives:

  • The change is a faithful port of the V1 pattern: model_dump_json(...).encode() + _post_json(content=...) / _delete_with_body_json(content=...). No new logic introduced.
  • Both sync and async paths updated.
  • Tests patch at the correct layer (transport helpers) and assert both the positive path (_post_json called once with content=bytes) and the negative path (_post / _delete_with_body never called).

Nit — spurious double blank line:

The diff adds a blank line at orders.py:167 between _build_batch_cancel_body and _build_amend_body, producing two consecutive blank lines. PEP 8 calls for exactly two between top-level definitions; the extra blank is a no-op but slightly untidy.

Observation — test roundtrip assertion:

The fast-path tests confirm json.loads(body) == request.model_dump(exclude_none=True, by_alias=True, mode="json"). This is a solid correctness check that the bytes output is semantically equivalent to the old dict-walk path — good pattern worth keeping.


No Issues Found With

  • Type annotation consistency (sync ↔ async, public ↔ private)
  • Re-export hygiene (__all__ entries added in both kalshi/models/__init__.py and kalshi/__init__.py)
  • None guard on batch_cancel_v2 response (unchanged, still present)
  • _require_auth() call placement (unchanged)
  • Test hermetics: respx mock boundaries are correct, async tests use AsyncMock appropriately

Summary

Two small nits (in-method imports, extra blank line) — neither is a blocker. The implementation is correct, minimal, and consistent with the project's established patterns. Ready to merge after the nits are addressed if desired, or as-is if they're considered acceptable for the scale of the change.

@TexasCoding
TexasCoding merged commit 0a2de0d into main May 22, 2026
5 checks passed
@TexasCoding
TexasCoding deleted the r3/W3-D branch May 22, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance / hot-path concern polish Code-quality and DX improvements; non-functional spec-drift Upstream OpenAPI/AsyncAPI spec changed since last sync

Projects

None yet

1 participant