Skip to content

polish(transport): make Sync/AsyncTransport.close() explicitly idempotent + test parity with KalshiAuth #301

Closed

Description

@TexasCoding

Summary

SyncTransport.close() (kalshi/_base_client.py:362-363) and AsyncTransport.aclose() (:550-551) delegate straight to self._client.close() / aclose() with no _closed flag and no test asserting idempotence. The rest of the SDK's lifecycle layer (KalshiAuth, KalshiClient, AsyncKalshiClient) tracks _closed explicitly — #267 even added a re-check-under-lock guard on the sign executor. The transport is the odd one out.

Today this is benign because httpx.Client.close() is itself idempotent, but:

  1. The contract is implicit — a future httpx change (or a custom httpx.BaseTransport injected via KalshiClient(transport=...)) could break it.
  2. The SDK already tests triple-close on KalshiAuth (tests/test_auth.py:290-296) but only single-close on the transport (tests/test_client.py:451, tests/test_async_client.py:284-285). The asymmetry leaves a regression hole.

Suggested fix

Add a _closed: bool = False to both transport __init__, return early from close() / aclose() if already closed, and document the contract:

class SyncTransport:
    def __init__(self, ...) -> None:
        ...
        self._closed = False

    def close(self) -> None:
        """Idempotent. Safe to call multiple times — subsequent calls no-op."""
        if self._closed:
            return
        self._closed = True
        self._client.close()

Mirror on AsyncTransport. Optionally raise RuntimeError from request() if _closed is True so a use-after-close fails loudly instead of dispatching against a closed httpx pool.

Add tests:

def test_sync_transport_close_is_idempotent(test_auth, config):
    t = SyncTransport(test_auth, config)
    t.close(); t.close(); t.close()  # no exception
    assert t._closed is True

async def test_async_transport_close_is_idempotent(test_auth, config):
    t = AsyncTransport(test_auth, config)
    await t.aclose(); await t.aclose(); await t.aclose()
    assert t._closed is True

Discovered by

Post-v2.5 multi-reviewer audit (HTTP transport + Testing surfaces). Polish-grade; brings the transport's lifecycle contract in line with the rest of the SDK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    polishCode-quality and DX improvements; non-functionaltestingTest coverage

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions