Skip to content

feat: add WebSocketClient.request_close() - #24

Draft
chipkent wants to merge 1 commit into
feat/ws-reconnect-optionsfrom
feat/ws-request-close
Draft

chipkent wants to merge 1 commit into
feat/ws-reconnect-optionsfrom
feat/ws-request-close

Conversation

@chipkent

@chipkent chipkent commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Preview only. This is a draft in the fork showing the exact text of the PR planned for massive-com/client-python. It will be closed, not merged, once the upstream PR is opened.

Fixes #<issue: F2-close>

Builds on #23.

close() is a coroutine that must run on connect()'s event loop. run() creates that loop itself, so another thread cannot stop a client started with run(): asyncio.run(client.close()) from another thread fails with "attached to a different loop".

New request_close(), safe to call from any thread, and it does not wait:

  • It sets a flag and, while connect() runs, closes the connection through loop.call_soon_threadsafe().
  • connect() checks the flag before it reconnects and after a wait before reconnecting, and returns instead, also with reconnect_on_close.
  • An attempt to connect, or a wait before reconnecting, is not interrupted: connect() returns once it ends, without opening another connection.
  • If connect() is not running, the next call returns at once.

close() called while connect() runs now sets the same flag. Before, a close() during the wait before a reconnect (#23's reconnect_backoff), or during a connect or reconnect handshake, found no open connection to close, and connect() connected anyway. A close() before connect() starts is unchanged: the next connect() proceeds. With the default reconnect_backoff=0 there is no wait, so the only difference is a close() made during a connect or reconnect handshake.

threading.Thread(target=client.run, args=(handle_msg,)).start()
...
client.request_close()  # from any thread

Tests

  • New test_request_close.py: stops run() from another thread, stops reconnecting after a drop, makes a request before connect() return without connecting, checks that a close() before connect() starts still lets it connect, and checks that request_close() or close() during the wait before a reconnect opens no new connection (the wait is patched, not timed).
  • make test and make lint pass on 3.10 and 3.14.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

close() is a coroutine that must run on connect()'s event loop. run()
creates that loop itself, so another thread cannot stop a client
started with run(): asyncio.run(client.close()) from another thread
fails with "attached to a different loop".

request_close() can be called from any thread and does not wait. It
sets a flag and, while connect() runs, closes the connection through
loop.call_soon_threadsafe(). connect() checks the flag before it
reconnects and after a wait before reconnecting, so it returns instead,
also with reconnect_on_close.

An attempt to connect, or a wait before reconnecting, is not
interrupted: connect() returns once it ends, without opening another
connection. If connect() is not running, the next call returns at once.

close() called while connect() runs now sets the same flag. Before, a
close() during the wait before a reconnect (reconnect_backoff > 0) or
during a connect or reconnect handshake found no open connection to
close, and connect() went on to connect. A close() before connect()
starts is unchanged: the next connect() proceeds. With the default
reconnect_backoff of 0 there is no wait, so the only difference is a
close() made during a connect or reconnect handshake.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant