Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions foreign/python/apache_iggy.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ __all__ = [
"PollingStrategy",
"ReceiveMessage",
"SendMessage",
"Stream",
"StreamDetails",
"Topic",
"TopicDetails",
Expand Down Expand Up @@ -352,6 +353,32 @@ class IggyClient:
Gets stream by id.
Returns Option of stream details or a PyRuntimeError on failure.
"""
def get_streams(self) -> collections.abc.Awaitable[list[Stream]]:
r"""
Gets all streams.
Returns a list of streams or a PyRuntimeError on failure.
"""
def update_stream(
self, stream_id: builtins.str | builtins.int, name: builtins.str
) -> collections.abc.Awaitable[None]:
r"""
Updates a stream's name by id.
Returns Ok(()) on successful update or a PyRuntimeError on failure.
"""
def delete_stream(
self, stream_id: builtins.str | builtins.int
) -> collections.abc.Awaitable[None]:
r"""
Deletes a stream by id.
Returns Ok(()) on successful deletion or a PyRuntimeError on failure.
"""
def purge_stream(
self, stream_id: builtins.str | builtins.int
) -> collections.abc.Awaitable[None]:
r"""
Purges all messages from a stream by id.
Returns Ok(()) on successful purge or a PyRuntimeError on failure.
"""
def create_topic(
self,
stream: builtins.str | builtins.int,
Expand Down Expand Up @@ -796,6 +823,17 @@ class SendMessage:
directly from Python using the provided string or bytes data.
"""

@typing.final
class Stream:
@property
def id(self) -> builtins.int: ...
@property
def name(self) -> builtins.str: ...
@property
def messages_count(self) -> builtins.int: ...
@property
def topics_count(self) -> builtins.int: ...

@typing.final
class StreamDetails:
@property
Expand Down
74 changes: 73 additions & 1 deletion foreign/python/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::consumer::{
use crate::identifier::PyIdentifier;
use crate::receive_message::{PollingStrategy, ReceiveMessage};
use crate::send_message::SendMessage;
use crate::stream::StreamDetails;
use crate::stream::{Stream, StreamDetails};
use crate::topic::{Topic, TopicDetails};
use tokio::sync::Mutex;

Expand Down Expand Up @@ -168,6 +168,78 @@ impl IggyClient {
})
}

/// Gets all streams.
/// Returns a list of streams or a PyRuntimeError on failure.
Comment on lines +171 to +172

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.

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.

#[gen_stub(override_return_type(type_repr="collections.abc.Awaitable[list[Stream]]", imports=("collections.abc")))]
fn get_streams<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let inner = self.inner.clone();
future_into_py(py, async move {
let streams = inner
.get_streams()
.await
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
Ok(streams.into_iter().map(Stream::from).collect::<Vec<_>>())
})
}

/// Updates a stream's name by id.
/// Returns Ok(()) on successful update or a PyRuntimeError on failure.
Comment on lines +184 to +186

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.

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.

#[gen_stub(override_return_type(type_repr="collections.abc.Awaitable[None]", imports=("collections.abc")))]
fn update_stream<'a>(
&self,
py: Python<'a>,
stream_id: PyIdentifier,
name: String,
) -> PyResult<Bound<'a, PyAny>> {
let stream_id = Identifier::try_from(stream_id)?;
let inner = self.inner.clone();
future_into_py(py, async move {
inner
.update_stream(&stream_id, &name)
.await
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
Ok(())
})
}

/// Deletes a stream by id.
/// Returns Ok(()) on successful deletion or a PyRuntimeError on failure.
Comment on lines +205 to +206

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.

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.

#[gen_stub(override_return_type(type_repr="collections.abc.Awaitable[None]", imports=("collections.abc")))]
fn delete_stream<'a>(
&self,
py: Python<'a>,
stream_id: PyIdentifier,
) -> PyResult<Bound<'a, PyAny>> {
let stream_id = Identifier::try_from(stream_id)?;
let inner = self.inner.clone();
future_into_py(py, async move {
inner
.delete_stream(&stream_id)
.await
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
Ok(())
})
}

/// Purges all messages from a stream by id.
/// Returns Ok(()) on successful purge or a PyRuntimeError on failure.
Comment on lines +224 to +225

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.

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.

#[gen_stub(override_return_type(type_repr="collections.abc.Awaitable[None]", imports=("collections.abc")))]
fn purge_stream<'a>(
&self,
py: Python<'a>,
stream_id: PyIdentifier,
) -> PyResult<Bound<'a, PyAny>> {
let stream_id = Identifier::try_from(stream_id)?;
let inner = self.inner.clone();
future_into_py(py, async move {
inner
.purge_stream(&stream_id)
.await
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;
Ok(())
})
}

/// Creates a new topic with the given parameters.
/// Returns Ok(()) on successful topic creation or a PyRuntimeError on failure.
#[pyo3(
Expand Down
3 changes: 2 additions & 1 deletion foreign/python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use consumer::{
use pyo3::prelude::*;
use receive_message::{PollingStrategy, ReceiveMessage};
use send_message::SendMessage;
use stream::StreamDetails;
use stream::{Stream, StreamDetails};
use topic::{Topic, TopicDetails};

/// A Python module implemented in Rust.
Expand All @@ -41,6 +41,7 @@ fn apache_iggy(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<ReceiveMessage>()?;
m.add_class::<IggyClient>()?;
m.add_class::<StreamDetails>()?;
m.add_class::<Stream>()?;
m.add_class::<Topic>()?;
m.add_class::<TopicDetails>()?;
m.add_class::<ConsumerGroup>()?;
Expand Down
38 changes: 37 additions & 1 deletion foreign/python/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

use iggy::prelude::StreamDetails as RustStreamDetails;
use iggy::prelude::{Stream as RustStream, StreamDetails as RustStreamDetails};
use pyo3::prelude::*;
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};

Expand Down Expand Up @@ -56,3 +56,39 @@ impl StreamDetails {
self.inner.topics_count
}
}

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.

Add the following doc:

/// Summary information returned by `IggyClient.get_streams()`.
///
/// `created_at` is Unix time in microseconds. `size_bytes` is the stream's
/// current stored size in bytes.

#[gen_stub_pyclass]
#[pyclass]
pub struct Stream {
pub(crate) inner: RustStream,
}

impl From<RustStream> for Stream {
fn from(stream: RustStream) -> Self {
Self { inner: stream }
}
}

#[gen_stub_pymethods]
#[pymethods]
impl Stream {

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 getters for created_at and size as well.

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.

Also write their docs as well.

#[getter]
pub fn id(&self) -> u32 {

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.

Doc: /// Numeric stream identifier.

self.inner.id
}

#[getter]
pub fn name(&self) -> String {

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.

Doc: /// Unique stream name.

self.inner.name.to_string()
}

#[getter]
pub fn messages_count(&self) -> u64 {

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.

Doc: /// Total messages across all topics in the stream.

self.inner.messages_count
}

#[getter]
pub fn topics_count(&self) -> u32 {

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.

Doc: /// Number of topics in the stream.

self.inner.topics_count
}
}
Loading
Loading