Skip to content

BREAKING(v3): CommunicationsResource sub-namespaces (rfqs/quotes) + standardize list_all_<noun> naming#377

Merged
TexasCoding merged 2 commits into
mainfrom
v3/W4-A
May 22, 2026
Merged

BREAKING(v3): CommunicationsResource sub-namespaces (rfqs/quotes) + standardize list_all_<noun> naming#377
TexasCoding merged 2 commits into
mainfrom
v3/W4-A

Conversation

@TexasCoding

Copy link
Copy Markdown
Owner

Summary

Two breaking surface renames targeting v3.0.0:

  1. CommunicationsResource uses prefix-style method names (get_rfq/get_quote) inconsistent with every other resource #348CommunicationsResource reorganized into sub-namespaces matching
    the OpenAPI tag structure: client.communications.rfqs.* and
    client.communications.quotes.*. The misc get_id() endpoint stays at
    the top level because it has no sub-noun.
  2. list_trades_all vs list_all_rfqs — two *_all naming conventions coexist in the same SDK #349MarketsResource.list_trades_all renamed to
    list_all_trades, aligning with the SDK-wide list_all_<noun> convention
    already used by communications, subaccounts, etc.

Every renamed method is kept as a @typing_extensions.deprecated thin
forwarder that delegates to the new implementation and emits exactly one
DeprecationWarning per call. Both names work in v3.0.0; the deprecated
aliases are scheduled for removal in a future release.

Issues closed

Migration — BEFORE / AFTER

Communications (#348)

BEFORE (v2.x — now deprecated) AFTER (v3.0.0 — canonical)
client.communications.list_rfqs(...) client.communications.rfqs.list(...)
client.communications.list_all_rfqs(...) client.communications.rfqs.list_all(...)
client.communications.get_rfq(rfq_id) client.communications.rfqs.get(rfq_id)
client.communications.create_rfq(...) client.communications.rfqs.create(...)
client.communications.delete_rfq(rfq_id) client.communications.rfqs.delete(rfq_id)
client.communications.list_quotes(...) client.communications.quotes.list(...)
client.communications.list_all_quotes(...) client.communications.quotes.list_all(...)
client.communications.get_quote(quote_id) client.communications.quotes.get(quote_id)
client.communications.create_quote(...) client.communications.quotes.create(...)
client.communications.delete_quote(quote_id) client.communications.quotes.delete(quote_id)
client.communications.accept_quote(quote_id, accepted_side="yes") client.communications.quotes.accept(quote_id, accepted_side="yes")
client.communications.confirm_quote(quote_id) client.communications.quotes.confirm(quote_id)

client.communications.get_id() is unchanged.

Markets (#349)

BEFORE (v2.x — now deprecated) AFTER (v3.0.0 — canonical)
client.markets.list_trades_all(...) client.markets.list_all_trades(...)

list_trades(...) (single-page paginator) is unchanged.

Behavioral changes

  • Calling any of the 13 renamed methods emits a DeprecationWarning once per
    call (via typing_extensions.deprecated, recognized by type checkers per
    PEP 702). The wire request is identical — forwarders delegate to the new
    implementation with zero behavioral divergence.
  • Public surface gains RFQsResource, QuotesResource, AsyncRFQsResource,
    AsyncQuotesResource classes (reachable via the parent's .rfqs / .quotes
    attributes; the parent's __init__ constructs them with the same transport).
  • No wire changes. No type narrowing. No new validation. Pure rename + alias.

Tests

  • tests/test_communications.py — new TestV3DeprecationAliases class:
    • test_issue_348_rfqs_sub_namespace_works — exercises rfqs.list/get/create.
    • test_issue_348_quotes_sub_namespace_works — exercises quotes.list/create/accept.
    • test_issue_348_async_rfqs_sub_namespace_classisinstance wiring on async.
    • test_issue_348_flat_names_still_work_emit_deprecation_warning — every old
      flat method (12 of them) under pytest.warns(DeprecationWarning) with
      response-shape assertions.
  • tests/test_markets.py:
    • test_issue_349_list_all_trades_works — new name returns the right shape.
    • test_issue_349_list_trades_all_emits_deprecation_warning — old name still
      works, emits one DeprecationWarning.
  • tests/test_async_markets.py:
    • Same two test_issue_349_* tests for the async resource.
  • tests/_contract_support.pyMETHOD_ENDPOINT_MAP extended with 13 new
    entries for the sub-namespace methods + list_all_trades; matching cursor
    exclusions and _MAX_PAGES_FQNS entries added so the existing
    test_every_public_method_has_entry and
    test_exclusions_bootstrap_has_cursor_entries contract guards remain green.

All existing tests that exercise the flat methods continue to pass; they now
emit DeprecationWarning (visible in the test report) without failing —
pyproject.toml's filterwarnings does not escalate to error.

Verification (run in worktree CWD):

uv run ruff check kalshi/ tests/       # clean
uv run mypy kalshi/                    # 76 source files, no issues
uv run pytest tests/test_communications.py tests/test_markets.py \
              tests/test_async_markets.py tests/test_contracts.py \
              tests/test_contract_support.py tests/test_base_helpers.py
# 717 passed, 59 deprecation warnings

Source

Round-3 independent audit closure plan, wave W4 (BREAKING — v3.0.0).

…dization

Reorganize `CommunicationsResource` (sync + async) into sub-namespaces
matching the OpenAPI v3.18.0 tag structure: `client.communications.rfqs.*`
and `client.communications.quotes.*`. The misc `get_id()` endpoint stays at
the top level because it has no sub-noun. Add `RFQsResource`,
`QuotesResource`, `AsyncRFQsResource`, `AsyncQuotesResource` constructed by
the parent's `__init__` against the same transport. The new shape gives
callers `rfqs.list/list_all/get/create/delete` and
`quotes.list/list_all/get/create/delete/accept/confirm` — twelve flat
methods collapse into two clean noun-scoped namespaces (#348).

Rename `MarketsResource.list_trades_all` to `list_all_trades` on both sync
and async, aligning with the `list_all_<noun>` form already used by
communications, subaccounts, etc. (#349).

Every renamed symbol is kept as a `typing_extensions.deprecated` thin
forwarder that delegates to the new implementation. Each call emits exactly
one `DeprecationWarning` (recognized by type checkers per PEP 702) and is
documented `.. deprecated:: 3.0.0` in its docstring. Both names work in
v3.0.0; the deprecated aliases are scheduled for removal in a future
release. Forwarders preserve zero behavioral divergence — they call the
underlying `_build_*_body` and transport methods through the new
sub-resource impls.

Update `tests/_contract_support.py` to register the 13 new public methods
in `METHOD_ENDPOINT_MAP` plus matching cursor exclusions and
`_MAX_PAGES_FQNS` entries, so `test_every_public_method_has_entry` and
`test_exclusions_bootstrap_has_cursor_entries` remain green. Add a
`TestV3DeprecationAliases` class to `tests/test_communications.py`
exercising both the new sub-namespace shape and each deprecated flat
forwarder under `pytest.warns(DeprecationWarning)`. Add
`test_issue_349_list_all_trades_works` and
`test_issue_349_list_trades_all_emits_deprecation_warning` to
`tests/test_markets.py` and `tests/test_async_markets.py`. Rewrite
`docs/resources/communications.md` around the new sub-namespace shape with
a BEFORE/AFTER migration table; mark `list_trades_all` deprecated in
`docs/resources/markets.md`.

Closes #348, #349
@TexasCoding TexasCoding added polish Code-quality and DX improvements; non-functional breaking Backwards-incompatible change labels May 22, 2026
@claude

claude Bot commented May 22, 2026

Copy link
Copy Markdown

Code Review — PR #377

Overview

Clean, well-scoped breaking-change stack. Two surface renames (#348 sub-namespaces, #349 naming convention) with deprecated thin forwarders, solid docs updates, and contract-support wiring. The approach is correct and the implementation is careful. A few items below are worth a look before merge.


Issues

1. Async deprecation warnings not exercised in tests (gap)

test_issue_348_async_rfqs_sub_namespace_class only does isinstance wiring checks. The PR description says async I/O is "exercised in TestAsyncCommunications via the deprecated forwarders" — but those existing tests do not assert that DeprecationWarning is emitted; they predate the deprecation and just call the old names. The sync side has test_issue_348_flat_names_still_work_emit_deprecation_warning covering all 12 forwarders. The async side has no equivalent.

This means a regression where the async @deprecated decorator is silently dropped would go undetected. Worth adding at least a spot-check async test, similar to test_issue_349_list_trades_all_emits_deprecation_warning in test_async_markets.py.

2. # type: ignore[call-overload, no-any-return, misc]misc is broad

The create_rfq and create_quote deprecated forwarders suppress misc in addition to call-overload and no-any-return:

return self.rfqs.create(  # type: ignore[call-overload, no-any-return, misc]

call-overload and no-any-return are expected (collapsed overload → single signature with None defaults). misc is a catch-all that can hide unrelated mypy errors. Worth checking exactly what mypy reports on these lines — if misc is needed, a comment explaining why would prevent confusion for future readers.

3. _MAX_PAGES_FQNS retains old list_trades_all entries without annotation

_contract_support.py adds list_all_trades entries alongside the existing list_trades_all entries. That's correct (the deprecated alias still exists and accepts max_pages), but the tuple now has both names for the same underlying method with no comment explaining the duplication. A brief inline note would prevent future cleanup confusion.

4. New sub-resource classes not exported from kalshi/__init__.py

RFQsResource, QuotesResource, AsyncRFQsResource, AsyncQuotesResource are importable from kalshi.resources.communications directly (and the tests do so). CLAUDE.md says new public classes should be exported from kalshi/__init__.py. If users are expected to type-annotate sub-resource attributes (e.g., comms: CommunicationsResource; comms.rfqs: RFQsResource), they'd need to import from the submodule.

Whether these belong in the top-level export is a judgment call — they're sub-resources, not top-level — but the decision should be explicit. If intentionally not exported, a note in the PR description or a comment in __init__.py would help.


Minor observations

  • Inconsistent @deprecated message style — Markets uses an inline string literal; communications uses _rfq_dep()/_quote_dep() helper functions. Both are fine, but the inconsistency is mildly surprising. The helpers reduce repetition (12 forwarders vs. 2), so the difference is justified; a comment on the helpers explaining this would help.

  • accept_quote forwarder suppresses fewer ignore codes than create_rfq/create_quote (call-overload only vs. call-overload, no-any-return, misc). If accept_quote truly needs fewer suppressions, that's good — but it's worth confirming it's intentional rather than an oversight.

  • list_all_rfqs on AsyncCommunicationsResource is plain def (not async def) returning AsyncIterator — this matches the sub-resource shape and is correctly annotated. The existing comment explains the intent. Good.


What's solid

  • Deprecation messages name the exact replacement, which is the most useful information for users.
  • The @deprecated decorator is recognized by type checkers per PEP 702 — IDEs will surface warnings at the call site.
  • Zero wire changes — all forwarders delegate without re-implementing logic.
  • test_issue_348_flat_names_still_work_emit_deprecation_warning comprehensively covers all 12 sync forwarders with response-shape assertions, not just warning checks.
  • Contract drift tests (METHOD_ENDPOINT_MAP, EXCLUSIONS, _MAX_PAGES_FQNS) are all updated correctly for the new names.
  • The docs migration table is clear and complete.

Summary: The implementation is correct and the approach is sound. The main gap worth addressing before merge is the missing async deprecation-warning test coverage. The type: ignore[misc] suppression and the export question are lower priority but worth a look.

@TexasCoding

Copy link
Copy Markdown
Owner Author

Round-2 review addressed in b187b97: added async deprecation-warning test covering 12 forwarders; narrowed type:ignore codes on create_rfq/create_quote forwarders (all three codes verified required via per-code mypy probe; misc annotated inline as mypy's "too many union combinations" overload limit); exported RFQsResource/QuotesResource/AsyncRFQsResource/AsyncQuotesResource from kalshi.init. Deferred: list_trades_all duplication annotation in _MAX_PAGES_FQNS (will resolve naturally when alias is removed in v3.1+); inconsistent @deprecated message style between markets (inline) vs communications (_rfq_dep/_quote_dep helpers) — helpers earn their cost at 12 forwarders.

@claude

claude Bot commented May 22, 2026

Copy link
Copy Markdown

Code Review — PR #377

Overview

Two clean v3.0.0 surface renames with backward-compatible deprecated forwarders:

  1. CommunicationsResource gains .rfqs / .quotes sub-namespaces matching the OpenAPI tag structure
  2. MarketsResource.list_trades_alllist_all_trades (consistent with list_all_<noun> SDK convention)

The architecture is sound: sub-resources share the same transport, logic lives in the canonical implementation only, and forwarders are pure pass-throughs. Nothing here changes wire behavior.


Strengths

  • PEP 702 deprecation via typing_extensions.deprecated is the right tool — type checkers see it, IDEs surface it at call sites.
  • Zero logic duplication — deprecated forwarders delegate entirely; there's no copy-pasted impl to drift.
  • Transport wiring is correct — sub-resources receive the same SyncTransport / AsyncTransport instance, so auth, retry, and header state are shared without any special plumbing.
  • Contract infrastructure updated properly — all 13 new methods are registered in METHOD_ENDPOINT_MAP, cursor exclusions added, _MAX_PAGES_FQNS extended for both sync and async paths.
  • Test coverage is solid — happy-path for new names, explicit pytest.warns(DeprecationWarning) for every deprecated alias, both sync and async, with response-shape assertions (not just "it didn't raise").

Issues / Suggestions

1. Possible missing cursor exclusion for AsyncMarketsResource.list_all_trades

The diff adds:

("kalshi.resources.markets.MarketsResource.list_all_trades", "cursor"): Exclusion(...)

but I don't see a corresponding entry for AsyncMarketsResource.list_all_trades. The old AsyncMarketsResource.list_trades_all would have needed one too. If that pre-existing exclusion was keyed on the old name, the contract test test_exclusions_bootstrap_has_cursor_entries should catch the missing entry for the new name. Since the PR reports 717 passing tests it may be a non-issue in practice, but worth a double-check — if the old list_trades_all async cursor exclusion key was removed somewhere not visible in this diff, there could be a latent gap.

2. Deprecation messages leave removal version open-ended

The current message is:

"deprecated since v3.0.0 and will be removed in a future release"

Giving a concrete target (e.g., "v4.0.0") is more actionable for users planning migrations. If the removal timeline isn't decided yet, even "v3.x" is better than "a future release". Minor, but users planning upgrades will appreciate the specificity.

3. Async sub-namespace happy-path test is wiring-only

test_issue_348_async_rfqs_sub_namespace_class just does isinstance(async_comms.rfqs, AsyncRFQsResource). The I/O path is exercised indirectly via test_issue_348_async_flat_names_emit_deprecation_warning (which routes through the sub-resources), so coverage is real — just indirect. Noting in case you want a direct await async_comms.rfqs.list(...) test for symmetry with the sync case.

4. type: ignore on overloaded forwarders

return self.rfqs.create(  # type: ignore[call-overload, no-any-return, misc]

The comment explaining "mypy's too many union combinations overload limit" is correct and sufficient. This is an acceptable trade-off for the compat layer — just flagging it's present and expected, not a concern.


No issues found with

  • Auth propagation to sub-resources
  • Retry/error mapping inheritance (comes from transport, not resource class)
  • @deprecated interaction with @overload — decorators apply to the implementation overload only, which is correct
  • list_all plain-def pattern for async iterators (fires deprecation + validation eagerly at call time, before iteration)
  • __init__.py exports — all four new classes (RFQsResource, QuotesResource, AsyncRFQsResource, AsyncQuotesResource) are exported and in __all__

Verdict

Approve with minor notes. The one item worth confirming before merge is the AsyncMarketsResource.list_all_trades cursor exclusion question (#1 above). Everything else is clean, well-tested, and follows SDK conventions.

@TexasCoding
TexasCoding merged commit 121fc26 into main May 22, 2026
5 checks passed
@TexasCoding
TexasCoding deleted the v3/W4-A 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

breaking Backwards-incompatible change polish Code-quality and DX improvements; non-functional

Projects

None yet

1 participant