Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c8157a2
feat(python): expose TCP client configuration to Python
ethanlin01x Jul 28, 2026
4d5f97c
test(python): cover the TCP client configuration surface
ethanlin01x Jul 28, 2026
44e02e2
docs(python): add a client configuration example
ethanlin01x Jul 28, 2026
76ab557
docs(python): document client configuration in the SDK README
ethanlin01x Jul 28, 2026
921cd8f
fix(python): validate durations and derive config defaults from Rust
ethanlin01x Jul 28, 2026
a82351c
test(python): make the unit marker selectable and pin duration edges
ethanlin01x Jul 28, 2026
b3190f4
docs(python): make the README configuration snippet runnable
ethanlin01x Jul 28, 2026
4fb0ff3
docs(python): fold the configuration example into getting-started
ethanlin01x Jul 30, 2026
12da3bc
docs(python): show the optional TcpConfig fields inline in the README
ethanlin01x Jul 30, 2026
1e5561d
docs(python): name Python exceptions and drop Rust types from docs
ethanlin01x Jul 30, 2026
42ad9ab
docs(python): describe results in Python terms instead of Ok(())
ethanlin01x Aug 1, 2026
92048cb
fix(python): keep full microsecond precision when reading durations
ethanlin01x Aug 1, 2026
dc180e5
test(python): cover negative durations on the pre-existing surface
ethanlin01x Aug 1, 2026
ab97c0b
docs(python): state the cost of disabling certificate validation
ethanlin01x Aug 1, 2026
6278728
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 1, 2026
fef08b4
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 1, 2026
94a659d
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 4, 2026
85c0708
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 5, 2026
acd01f6
fix(python): reject zero durations that spin the client
ethanlin01x Aug 5, 2026
54e80df
docs(python): name the failure modes the configuration can reach
ethanlin01x Aug 5, 2026
72025b3
fix(python): print the whole configuration and fail the examples cleanly
ethanlin01x Aug 5, 2026
7ae1f00
test(python): say what the duration and equivalence tests prove
ethanlin01x Aug 5, 2026
71e3936
docs(python): point the readme CA path at the file it means
ethanlin01x Aug 5, 2026
9150053
fix(python): report an out-of-range retry count as a value error
ethanlin01x Aug 5, 2026
f80d1f0
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 7, 2026
752a4b9
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 7, 2026
b4c3584
Merge branch 'master' into feat/python-tcp-config
hubcio Aug 7, 2026
b63f701
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 8, 2026
9ee182d
Merge branch 'master' into feat/python-tcp-config
ethanlin01x Aug 8, 2026
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
24 changes: 12 additions & 12 deletions core/sdk/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,18 @@ pub use iggy_common::{
Aes256GcmEncryptor, Args, ArgsOptional, AutoLogin, CacheMetrics, CacheMetricsKey, ClientError,
ClientInfoDetails, ClusterMetadata, ClusterNode, ClusterNodeRole, ClusterNodeStatus,
CompressionAlgorithm, Consumer, ConsumerGroup, ConsumerGroupDetails, ConsumerGroupMember,
ConsumerKind, EncryptorKind, GlobalPermissions, HeaderField, HeaderKey, HeaderKind,
HeaderValue, HttpClientConfig, HttpClientConfigBuilder, HttpMethod, IdKind, Identifier,
IdentityInfo, IggyByteSize, IggyDuration, IggyError, IggyExpiry, IggyIndexView, IggyMessage,
IggyMessageHeader, IggyMessageHeaderView, IggyMessageView, IggyMessageViewIterator,
IggyTimestamp, MaxTopicSize, Partition, Partitioner, Partitioning, Permissions,
PersonalAccessTokenExpiry, PollMessages, PolledMessages, PollingKind, PollingStrategy,
QuicClientConfig, QuicClientConfigBuilder, QuicClientReconnectionConfig, SendMessages,
SendMessagesConfirmationResponse, SendMessagesResponse, Sizeable, SnapshotCompression, Stats,
Stream, StreamDetails, StreamPermissions, SystemSnapshotType, TcpClientConfig,
TcpClientConfigBuilder, TcpClientReconnectionConfig, Topic, TopicDetails, TopicPermissions,
TransportEndpoints, TransportProtocol, UserId, UserInfo, UserInfoDetails, UserStatus,
Validatable, WebSocketClientConfig, WebSocketClientConfigBuilder,
ConsumerKind, Credentials, EncryptorKind, GlobalPermissions, HeaderField, HeaderKey,
HeaderKind, HeaderValue, HttpClientConfig, HttpClientConfigBuilder, HttpMethod, IdKind,
Identifier, IdentityInfo, IggyByteSize, IggyDuration, IggyError, IggyExpiry, IggyIndexView,
IggyMessage, IggyMessageHeader, IggyMessageHeaderView, IggyMessageView,
IggyMessageViewIterator, IggyTimestamp, MaxTopicSize, Partition, Partitioner, Partitioning,
Permissions, PersonalAccessTokenExpiry, PollMessages, PolledMessages, PollingKind,
PollingStrategy, QuicClientConfig, QuicClientConfigBuilder, QuicClientReconnectionConfig,
SendMessages, SendMessagesConfirmationResponse, SendMessagesResponse, Sizeable,
SnapshotCompression, Stats, Stream, StreamDetails, StreamPermissions, SystemSnapshotType,
TcpClientConfig, TcpClientConfigBuilder, TcpClientReconnectionConfig, Topic, TopicDetails,
TopicPermissions, TransportEndpoints, TransportProtocol, UserId, UserInfo, UserInfoDetails,
UserStatus, Validatable, WebSocketClientConfig, WebSocketClientConfigBuilder,
WebSocketClientReconnectionConfig, defaults, locking,
};
pub use iggy_common::{
Expand Down
52 changes: 30 additions & 22 deletions examples/python/getting-started/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@
import asyncio
import typing
import urllib.parse

from apache_iggy import IggyClient, PollingStrategy, ReceiveMessage
from datetime import timedelta

from apache_iggy import (
AutoLogin,
IggyClient,
PollingStrategy,
ReceiveMessage,
TcpConfig,
TcpReconnectionConfig,
)
from loguru import logger

STREAM_NAME = "sample-stream"
Expand Down Expand Up @@ -91,34 +99,34 @@ def parse_args() -> ArgNamespace:
return ArgNamespace(**vars(args))


def build_connection_string(args) -> str:
"""Build a connection string with TLS support."""

conn_str = f"iggy://{args.username}:{args.password}@{args.tcp_server_address}"

if args.tls:
# Extract domain from server address (host:port -> host)
host = args.tcp_server_address.split(":")[0]
query_params = ["tls=true", f"tls_domain={host}"]

# Add CA file if provided
if args.tls_ca_file:
query_params.append(f"tls_ca_file={args.tls_ca_file}")
conn_str += "?" + "&".join(query_params)
def build_config(args: ArgNamespace) -> TcpConfig:
"""Build a TCP client configuration with auto-login and reconnection."""

return conn_str
return TcpConfig(
server_address=args.tcp_server_address,
auto_login=AutoLogin.username_password(args.username, args.password),
reconnection=TcpReconnectionConfig(
enabled=True,
interval=timedelta(seconds=1),
),
tls_enabled=args.tls,
tls_ca_file=args.tls_ca_file or None,
)


async def main():
args: ArgNamespace = parse_args()
try:
config = build_config(args)
except ValueError as error:
logger.error(f"Invalid client configuration: {error}")
return
logger.info(f"Connecting to {args.tcp_server_address} (TLS: {args.tls})")

# Build connection string with TLS support
connection_string = build_connection_string(args)
logger.info(f"Connection string: {connection_string}")

client = IggyClient.from_connection_string(connection_string)
client = IggyClient(config)
try:
logger.info("Connecting to IggyClient...")
# No login_user() call: auto_login replays the credentials on every connect.
await client.connect()
logger.info("Connected.")
await consume_messages(client)
Expand Down
50 changes: 29 additions & 21 deletions examples/python/getting-started/producer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@
import asyncio
import typing
import urllib.parse

from apache_iggy import IggyClient, StreamDetails, TopicDetails
from datetime import timedelta

from apache_iggy import (
AutoLogin,
IggyClient,
StreamDetails,
TcpConfig,
TcpReconnectionConfig,
TopicDetails,
)
from apache_iggy import SendMessage as Message
from loguru import logger

Expand Down Expand Up @@ -92,33 +100,33 @@ def parse_args() -> ArgNamespace:
return ArgNamespace(**vars(args))


def build_connection_string(args) -> str:
"""Build a connection string with TLS support."""

conn_str = f"iggy://{args.username}:{args.password}@{args.tcp_server_address}"

if args.tls:
# Extract domain from server address (host:port -> host)
host = args.tcp_server_address.split(":")[0]
query_params = ["tls=true", f"tls_domain={host}"]
def build_config(args: ArgNamespace) -> TcpConfig:
"""Build a TCP client configuration with auto-login and reconnection."""

# Add CA file if provided
if args.tls_ca_file:
query_params.append(f"tls_ca_file={args.tls_ca_file}")
conn_str += "?" + "&".join(query_params)

return conn_str
return TcpConfig(
server_address=args.tcp_server_address,
auto_login=AutoLogin.username_password(args.username, args.password),
reconnection=TcpReconnectionConfig(
enabled=True,
interval=timedelta(seconds=1),
),
tls_enabled=args.tls,
tls_ca_file=args.tls_ca_file or None,
)


async def main():
args: ArgNamespace = parse_args()
# Build connection string with TLS support
connection_string = build_connection_string(args)
logger.info(f"Connection string: {connection_string}")
try:
config = build_config(args)
except ValueError as error:
logger.error(f"Invalid client configuration: {error}")
return
logger.info(f"Connecting to {args.tcp_server_address} (TLS: {args.tls})")

client = IggyClient.from_connection_string(connection_string)
client = IggyClient(config)
logger.info("Connecting to IggyClient")
# No login_user() call: auto_login replays the credentials on every connect.
await client.connect()
logger.info("Connected.")
await init_system(client)
Expand Down
1 change: 1 addition & 0 deletions foreign/python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ pyo3-async-runtimes = { version = "0.29.0", features = [
"tokio-runtime",
] }
pyo3-stub-gen = "0.23.0"
secrecy = "0.10"
Comment thread
ethanlin01x marked this conversation as resolved.
tokio = "1.53.1"
36 changes: 36 additions & 0 deletions foreign/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,42 @@ running prek / committing / pushing. This list is not exhaustive and other hook
./scripts/ci/markdownlint.sh --fix foreign/python/README.md # read the diff after applying this, sometimes it gives unwanted results, e.g. messing up enumerations
```

## Client Configuration

`IggyClient` takes either a server address or a `TcpConfig`:

```python
import asyncio
from datetime import timedelta

from apache_iggy import AutoLogin, IggyClient, TcpConfig, TcpReconnectionConfig


async def main():
client = IggyClient(
TcpConfig(
server_address="127.0.0.1:8090",
auto_login=AutoLogin.username_password("iggy", "iggy"),
reconnection=TcpReconnectionConfig(
enabled=True,
max_retries=10,
interval=timedelta(seconds=2),
reestablish_after=timedelta(seconds=30),
),
heartbeat_interval=timedelta(seconds=5),
# tls_enabled=True,
# tls_domain="localhost",
# tls_ca_file="../../core/certs/iggy_ca_cert.pem",
# tls_validate_certificate=True,
# nodelay=True,
)
)
await client.connect()


asyncio.run(main())
```

## Examples

Refer to the [examples/python/](https://github.com/apache/iggy/tree/master/examples/python) directory for usage examples.
Expand Down
Loading
Loading