Skip to content

[Python] Fix wait_for_offset regression; release 1.6.1 - #728

Merged
elenagaljak-db merged 2 commits into
mainfrom
effort/zerobus-sdk
Aug 12, 2026
Merged

[Python] Fix wait_for_offset regression; release 1.6.1#728
elenagaljak-db merged 2 commits into
mainfrom
effort/zerobus-sdk

Conversation

@elenagaljak-db

Copy link
Copy Markdown
Collaborator

Summary

Fixes #726ZerobusStream.wait_for_offset(offset) raised TypeError: missing 1 required positional argument: 'timeout_sec' on every call through the public sync API on 1.5.0 and 1.6.0.

Root cause

The PyO3 0.20 → 0.29 upgrade (#652) dropped the rule where an Option<T> argument implicitly defaults to None. Without an explicit #[pyo3(signature = ...)], timeout_sec silently became a required argument in the generated Python signature — with no change to any source line:

Version PyO3 Binding signature
1.4.0 0.20.3 (offset, timeout_sec=None)
1.5.0 / 1.6.0 0.29.0 (offset, timeout_sec)

The SDK's own wrapper calls self._inner.wait_for_offset(offset), so it could never satisfy the second signature.

Fix

Restore the optional defaults with #[pyo3(signature = ...)] on the four affected sync bindings:

  • ZerobusStream.wait_for_offset (the crash)
  • RecordAcknowledgment.wait_for_ack
  • ZerobusSdk.create_stream
  • ZerobusSdk.create_stream_with_headers_provider

The async surface already carried the attribute and was unaffected. The Rust bodies are untouched (wait_for_offset already discards timeout_sec), so this is a pure signature restoration back to 1.4.0 behavior — additive, non-breaking.

Why it shipped unnoticed

No example exercised the broken combination (sync record stream + explicit wait_for_offset). The JSON/proto examples use flush(); the Arrow examples call wait_for_offset on the Arrow stream, whose binding never had a timeout_sec argument. tests/test_signatures.py closes that coverage gap — 8 tests asserting the optional defaults survive, needing neither network nor credentials. Verified they fail on the unfixed binding and pass with the fix.

Release v1.6.1

Patch on top of the current released line (1.6.0). Bumped the version in rust/Cargo.toml, Cargo.lock, and zerobus/__init__.py; moved NEXT_CHANGELOG.md into CHANGELOG.md and reset the template.

Note on versioning: 1.6.1 (not 1.5.1) because this branch is on the already-released 1.6.0 codebase; a 1.5.1 would sort below 1.6.0 and pip would never hand it to users on the latest release. A 1.5.x backport, if wanted, is a separate branch off the python/v1.5.0 tag.

Test plan

  • python -m unittest tests.test_signatures tests.test_smoke → 30 passing.
  • Negative control: reverted the binding fix, rebuilt → the 5 sync signature tests fail with the exact reported TypeError; the 3 async tests stay green.

This pull request and its description were written by Isaac.

The PyO3 0.20 -> 0.29 upgrade (#652) dropped the implicit `None` default
for `Option<T>` arguments, so `timeout_sec` silently became required on
`ZerobusStream.wait_for_offset`. The SDK's own wrapper calls it as
`wait_for_offset(offset)`, so it raised `TypeError` on every call through
the public API on 1.5.0 and 1.6.0 (issue #726).

Restore the optional defaults with `#[pyo3(signature = ...)]` on the four
affected sync bindings: `wait_for_offset`, `RecordAcknowledgment.wait_for_ack`,
`ZerobusSdk.create_stream`, and `create_stream_with_headers_provider`. The
async surface already carried the attribute and was unaffected.

Add `tests/test_signatures.py` locking down the optional defaults so this
class of regression is caught without network or credentials — no example
exercised the sync record-stream `wait_for_offset` path, which is why it
shipped unnoticed.

Release v1.6.1: bump version, move NEXT_CHANGELOG into CHANGELOG.

Co-authored-by: Isaac
Signed-off-by: elenagaljak-db <elena.galjak@databricks.com>
Co-authored-by: Isaac
Signed-off-by: elenagaljak-db <elena.galjak@databricks.com>
impl RecordAcknowledgment {
/// Wait for the acknowledgment and return the offset ID.
/// This method can only be called once.
#[pyo3(signature = (_timeout_sec = None))]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here we expose _timeout_sec, but the stub and example document timeout_sec. Thus ack.wait_for_ack(timeout_sec=30) still raises TypeError. The new test incorrectly locks in _timeout_sec at python/tests/test_signatures.py:36. Rename the Rust parameter and test the documented keyword.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As discussed offline, this would be a breaking change.

impl RecordAcknowledgment {
/// Wait for the acknowledgment and return the offset ID.
/// This method can only be called once.
#[pyo3(signature = (_timeout_sec = None))]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As discussed offline, this would be a breaking change.

@elenagaljak-db
elenagaljak-db added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 43bc824 Aug 12, 2026
27 checks passed
@elenagaljak-db
elenagaljak-db deleted the effort/zerobus-sdk branch August 12, 2026 16:03
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] wait_for_offset broken in 1.5.0+. PyO3 0.29 upgrade made timeout_sec required

2 participants