feat(python): add stream listing, update, delete, and purge#3701
feat(python): add stream listing, update, delete, and purge#3701felixfaisal wants to merge 1 commit into
Conversation
Signed-off-by: Faisal Ahmed <faisalahmedfarooq46@gmail.com>
|
Thanks for the PR. It is labeled Slash commands (own line, regular comment) move it around the queue:
See CONTRIBUTING.md for details. |
|
/request-review @slbotbm |
|
/request-review @hubcio |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3701 +/- ##
============================================
+ Coverage 73.99% 74.00% +0.01%
Complexity 937 937
============================================
Files 1301 1301
Lines 147393 147466 +73
Branches 122945 122945
============================================
+ Hits 109063 109138 +75
+ Misses 34858 34856 -2
Partials 3472 3472
🚀 New features to boost your workflow:
|
ryankert01
left a comment
There was a problem hiding this comment.
Overall lg to me, a comment.
| mine = [by_name[name] for name in created] | ||
| assert [stream.id for stream in mine] == sorted(stream.id for stream in mine) |
There was a problem hiding this comment.
It occurs to me that it's not testing get_streams() ordering since it's constructed based on created ordering. Although I'm also not quite sure whether if iggy wants to ensure the ordering or not in the distributed future, kafka for example, don't.
|
|
||
| #[gen_stub_pymethods] | ||
| #[pymethods] | ||
| impl Stream { |
There was a problem hiding this comment.
Let's add getters for created_at and size as well.
There was a problem hiding this comment.
Also write their docs as well.
| /// Gets all streams. | ||
| /// Returns a list of streams or a PyRuntimeError on failure. |
There was a problem hiding this comment.
Change to
/// Return all streams visible to the authenticated user.
///
/// The result is ordered by ascending numeric stream ID.
///
/// Returns:
/// A list of `Stream` summaries.
///
/// Raises:
/// RuntimeError: If the client is not authenticated, the user lacks global
/// `read_streams` or `manage_streams` permission, or the request fails.
|
|
||
| /// Updates a stream's name by id. | ||
| /// Returns Ok(()) on successful update or a PyRuntimeError on failure. |
There was a problem hiding this comment.
Change to
/// Rename a stream selected by name or numeric ID.
///
/// `stream_id` accepts a stream name as `str` or numeric ID as `int`. A
/// decimal-only string is interpreted as a numeric ID. `name` must be unique
/// and contain between 1 and 255 UTF-8 bytes. Renaming a stream to its current
/// name succeeds without changing it.
///
/// Returns:
/// None.
///
/// Raises:
/// TypeError: If `stream_id` is neither `str` nor `int`, or `name` is not
/// `str`.
/// OverflowError: If an integer identifier is outside `0..=2**32 - 1`.
/// ValueError: If a string identifier is empty or exceeds 255 UTF-8 bytes.
/// RuntimeError: If the client is not authenticated, the user lacks global
/// `manage_streams` or per-stream `manage_stream` permission, the
/// stream does not exist, the new name is invalid or already used, or
/// the request fails.
| /// Purges all messages from a stream by id. | ||
| /// Returns Ok(()) on successful purge or a PyRuntimeError on failure. |
There was a problem hiding this comment.
Change to
/// Delete all messages from every topic in a stream.
///
/// The stream, topics, and partitions remain available. Repeated purges of an
/// existing empty stream succeed. `stream_id` accepts a stream name as `str`
/// or numeric ID as `int`. A decimal-only string is interpreted as a numeric
/// ID.
///
/// Returns:
/// None.
///
/// Raises:
/// TypeError: If `stream_id` is neither `str` nor `int`.
/// OverflowError: If an integer identifier is outside `0..=2**32 - 1`.
/// ValueError: If a string identifier is empty or exceeds 255 UTF-8 bytes.
/// RuntimeError: If the client is not authenticated, the user lacks global
/// `manage_streams` or per-stream `manage_stream` permission, the
/// stream does not exist, or the request fails.
| /// Deletes a stream by id. | ||
| /// Returns Ok(()) on successful deletion or a PyRuntimeError on failure. |
There was a problem hiding this comment.
Change to
/// Delete a stream selected by name or numeric ID.
///
/// Deletion removes the stream and all of its topics, partitions, and messages.
/// `stream_id` accepts a stream name as `str` or numeric ID as `int`. A
/// decimal-only string is interpreted as a numeric ID.
///
/// Returns:
/// None.
///
/// Raises:
/// TypeError: If `stream_id` is neither `str` nor `int`.
/// OverflowError: If an integer identifier is outside `0..=2**32 - 1`.
/// ValueError: If a string identifier is empty or exceeds 255 UTF-8 bytes.
/// RuntimeError: If the client is not authenticated, the user lacks global
/// `manage_streams` or per-stream `manage_stream` permission, the
/// stream does not exist, or the request fails.
| } | ||
|
|
||
| #[getter] | ||
| pub fn topics_count(&self) -> u32 { |
There was a problem hiding this comment.
Doc: /// Number of topics in the stream.
| async def test_get_streams_before_connect_fails(self): | ||
| """Test get_streams requires an established connection.""" | ||
| host, port = get_server_config() | ||
| client = IggyClient(f"{host}:{port}") | ||
|
|
||
| with pytest.raises(RuntimeError): | ||
| await client.get_streams() | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_get_streams_before_login_fails(self): | ||
| """Test get_streams requires authentication.""" |
|
|
||
| old = await iggy_client.get_stream(stream_name) | ||
| assert old is None | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_update_stream_preserves_id( |
There was a problem hiding this comment.
These two tests can be combined into one. No need for another test just to assert stream id remains same.
| with pytest.raises(RuntimeError): | ||
| await iggy_client.update_stream(stream_id=second_stream, name=first_stream) |
There was a problem hiding this comment.
After the failure, assert that none of the metadata of the stream has changed.
| await client.get_streams() | ||
|
|
||
|
|
||
| class TestUpdateStream: |
There was a problem hiding this comment.
Add the following tests for update_stream:
- it accepts exact 1-byte and 255-byte names and rejects empty and 256-byte names, including UTF-8 byte boundaries.
|
Please do not force push. Instead, create new commits with your work. This makes it easier for me to understand the changes /author |
Which issue does this PR address?
Closes #3520
Rationale
The Python SDK exposed only create_stream and get_stream, so Python users
could not list, rename, delete, or purge streams — operations the Rust SDK
already supports. This closes that parity gap.
What changed?
Added get_streams, update_stream, delete_stream, and purge_stream to the
Python client by wrapping the Rust StreamClient methods via PyO3, plus a new
Stream wrapper class for the list result. Extended test_stream.py with
end-to-end coverage and regenerated apache_iggy.pyi.
Local Execution
AI Usage
If AI tools were used, please answer:
Yup