Skip to content

feat(python): expose client disconnect and shutdown - #4288

Merged
slbotbm merged 7 commits into
apache:masterfrom
TomPlanche:feat/python-client-disconnect-shutdown
Sep 29, 2026
Merged

slbotbm merged 7 commits into
apache:masterfrom
TomPlanche:feat/python-client-disconnect-shutdown

Conversation

@TomPlanche

@TomPlanche TomPlanche commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

edit: add prek tests.
edit2: replace RuntimeError("Client shutdown") by RuntimeError, and run the lifecycle tests on every transport.
edit3: describe the lifecycle limits that the automated review found, and check the HTTP session in the test.

Which issue does this PR address?

Closes #4163

Rationale

The Rust Client trait has disconnect() and shutdown(), but the Python IggyClient only had connect(). Thus a Python caller cannot close a connection to connect again later, and cannot release the client without dropping it.

What changed?

Now IggyClient has the async methods disconnect() and shutdown(). Both methods call the Rust client directly, and repeated calls are safe. The behavior depends on the transport:

  • disconnect() closes the connection, and the client can connect again. The sign-in from login_user() is lost, thus the caller must log in again after connect(). A client with auto-login credentials logs in again on connect().
  • shutdown() closes the connection and releases the client. After shutdown(), requests fail with RuntimeError.
  • Over HTTP, both methods do nothing, because HTTP has no connection to close.

The Rust SDK has lifecycle problems that also affect these methods. They are tracked in #4287, and this PR does not change the Rust SDK:

  • With auto-login, the heartbeat connects the client again and signs in within one heartbeat interval after disconnect(). The disconnect() docstring describes this behavior, and the tests use clients without auto-login.
  • A WebSocket client can connect again after shutdown(). The test for connect() after shutdown() marks WebSocket xfail(strict=True), thus the test fails when the fix for Rust SDK: the heartbeat undoes disconnect(), and a WebSocket client connects after shutdown() #4287 lands and the mark can go.
  • disconnect() after shutdown() makes the client reusable on every transport. For this reason, the shutdown() docstring does not say that the call is terminal.

Local Execution

I used Zed in devcontainer mode. The server and the tests ran in the devcontainer, and prek ran on macOS.

  • Passed: cargo fmt and cargo clippy --all-targets --all-features -- -D warnings in foreign/python.

  • Passed: cargo run --bin stub_gen, then ruff format and ruff check --fix. The stub diff contains only the two new methods and their docstrings.

  • Passed: the lifecycle tests against a local iggy-server built from this branch, with 18 passed and 1 expected failure:

    $ uv run --no-sync pytest tests/test_connectivity.py -v -k Lifecycle
    collected 54 items / 35 deselected / 19 selected
    
    test_disconnect_is_idempotent_and_rejects_requests[tcp] PASSED
    test_disconnect_is_idempotent_and_rejects_requests[websocket] PASSED
    test_disconnect_is_idempotent_and_rejects_requests[quic] PASSED
    test_disconnected_client_connects_again[tcp] PASSED
    test_disconnected_client_connects_again[websocket] PASSED
    test_disconnected_client_connects_again[quic] PASSED
    test_disconnected_client_reconnects_and_logs_in_again[tcp] PASSED
    test_disconnected_client_reconnects_and_logs_in_again[websocket] PASSED
    test_disconnected_client_reconnects_and_logs_in_again[quic] PASSED
    test_disconnected_client_with_auto_login_signs_in_on_reconnect[tcp] PASSED
    test_disconnected_client_with_auto_login_signs_in_on_reconnect[websocket] PASSED
    test_disconnected_client_with_auto_login_signs_in_on_reconnect[quic] PASSED
    test_shutdown_is_idempotent_and_rejects_requests[tcp] PASSED
    test_shutdown_is_idempotent_and_rejects_requests[websocket] PASSED
    test_shutdown_is_idempotent_and_rejects_requests[quic] PASSED
    test_shutdown_client_cannot_connect_again[tcp] PASSED
    test_shutdown_client_cannot_connect_again[websocket] XFAIL (WebSocketClient connects again after shutdown: #4287)
    test_shutdown_client_cannot_connect_again[quic] PASSED
    test_http_disconnect_and_shutdown_do_nothing PASSED
    
    18 passed, 35 deselected, 1 xfailed in 30.96s
    
  • Passed: pre-commit hooks with SKIP=web-lint prek run --all-files. Every hook passed. I skipped web-lint because this PR does not change web/.

AI Usage

  1. No ^^

@github-actions

Copy link
Copy Markdown

Thanks for the PR. It is labeled S-waiting-on-review and queued for review.

Slash commands (own line, regular comment) move it around the queue:

  • /ready - back to S-waiting-on-review after addressing feedback
  • /author - flip to S-waiting-on-author while you finish changes
  • /request-review @user-or-team - request a reviewer
  • /pin - exempt the PR from the stale bot, /unpin to undo

See CONTRIBUTING.md for details.

@jiengup jiengup left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
covered all the acceptance criterias stated in the original issue.

@codecov

codecov Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.76%. Comparing base (917612d) to head (24444e2).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #4288   +/-   ##
=========================================
  Coverage     87.75%   87.76%           
+ Complexity     1576     1575    -1     
=========================================
  Files          1289     1290    +1     
  Lines        227123   227156   +33     
  Branches     190575   190544   -31     
=========================================
+ Hits         199323   199364   +41     
+ Misses        23096    23087    -9     
- Partials       4704     4705    +1     
Components Coverage Δ
Rust Core 88.87% <ø> (+<0.01%) ⬆️
Java SDK 68.68% <ø> (-0.02%) ⬇️
C# SDK 77.40% <ø> (ø)
Python SDK 91.00% <100.00%> (+0.03%) ⬆️
PHP SDK 85.67% <ø> (ø)
Node SDK 96.54% <ø> (-0.01%) ⬇️
Go SDK 70.05% <ø> (-0.12%) ⬇️
Files with missing lines Coverage Δ
foreign/python/src/client.rs 99.77% <100.00%> (+<0.01%) ⬆️

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@slbotbm slbotbm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add the following tests:

  • IggyClient(addr) → disconnect() → disconnect() → connect() → ping() works.
  • shutdown() → shutdown() → connect() must fail

Otherwise looks good

Comment thread foreign/python/src/client.rs Outdated
Comment thread foreign/python/tests/test_connectivity.py Outdated
@github-actions github-actions Bot added S-waiting-on-author PR is waiting on author response and removed S-waiting-on-review PR is waiting on a reviewer labels Sep 25, 2026
@TomPlanche

Copy link
Copy Markdown
Contributor Author

/ready

@github-actions github-actions Bot added S-waiting-on-review PR is waiting on a reviewer and removed S-waiting-on-author PR is waiting on author response labels Sep 26, 2026
@hubcio

hubcio commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

/skill team-review-slim

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary: PR 4288 adds disconnect() and shutdown() to the Python client with docstrings and lifecycle tests. The wrappers and the tests track the SDK, but two documented contracts do not hold: an auto-login client reconnects on its own after disconnect(), and a later disconnect() makes shutdown() non-terminal.

Counts: critical 0, warning 2, nit 1, simplification 0


This review was generated by Claude Code 2.1.284 on deepseek-flash[1m]. Review the output before you act on it.

Comment thread foreign/python/src/client.rs
Comment thread foreign/python/src/client.rs Outdated
Comment thread foreign/python/tests/test_connectivity.py Outdated
@github-actions github-actions Bot added S-waiting-on-author PR is waiting on author response and removed S-waiting-on-review PR is waiting on a reviewer labels Sep 28, 2026
@TomPlanche

Copy link
Copy Markdown
Contributor Author

/ready

@github-actions github-actions Bot added S-waiting-on-review PR is waiting on a reviewer and removed S-waiting-on-author PR is waiting on author response labels Sep 28, 2026
@TomPlanche

Copy link
Copy Markdown
Contributor Author

/request-review @slbotbm

@github-actions
github-actions Bot requested a review from slbotbm September 28, 2026 21:20
@hubcio
hubcio dismissed slbotbm’s stale review September 28, 2026 22:52

committer fixed all issues

@hubcio hubcio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not blocking, and all pre-existing in the rust sdk - #4287 does not cover these yet:

  • warning: a reconnect already running when shutdown() is called sets Connected over Shutdown and signs in again once the server is back (core/sdk/src/tcp/tcp_client.rs:705). QUIC and websocket do the same with auto-login.
  • warning: disconnect() returns Ok while a connect is in flight, and that connect later leaves the client connected, even without auto-login (core/sdk/src/tcp/tcp_client.rs:1281). same root cause as the one above.
  • warning: over TLS, a failed close after a peer reset returns before set_state(Shutdown), so the heartbeat brings the client back (core/sdk/src/tcp/tcp_client.rs:1308). websocket ignores that error and always sets Shutdown.
  • warning: a redial during endpoint.wait_idle() keeps QUIC disconnect() blocked for as long as the new connection lives (core/sdk/src/quic/quic_client.rs:980).

separate from this PR, and worth its own issue: TCP, QUIC and websocket clients keep an event receiver nobody reads, so publish_event blocks forever at event 1001 (core/sdk/src/tcp/tcp_client.rs:574). set_overflow(true) on the sender fixes it.

Comment thread foreign/python/src/client.rs Outdated
Comment thread foreign/python/src/client.rs Outdated
Comment thread foreign/python/tests/test_connectivity.py
@github-actions github-actions Bot removed the S-waiting-on-review PR is waiting on a reviewer label Sep 28, 2026
@github-actions github-actions Bot added the S-waiting-on-author PR is waiting on author response label Sep 28, 2026
hubcio
hubcio previously approved these changes Sep 28, 2026

@hubcio hubcio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prev "Request changes" was by mistake.

@TomPlanche

Copy link
Copy Markdown
Contributor Author

/ready

@github-actions github-actions Bot added S-waiting-on-review PR is waiting on a reviewer and removed S-waiting-on-author PR is waiting on author response labels Sep 29, 2026
@slbotbm
slbotbm merged commit 81c26fc into apache:master Sep 29, 2026
56 checks passed
@github-actions github-actions Bot removed the S-waiting-on-review PR is waiting on a reviewer label Sep 29, 2026
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.

Python SDK: expose client disconnect and shutdown lifecycle methods

6 participants