pyrsolace


Namepyrsolace JSON
Version 0.3.2 PyPI version JSON
download
home_pageNone
SummaryPython bindings for rsolace
upload_time2025-07-13 07:04:20
maintainerNone
docs_urlNone
authorNone
requires_python>=3.7
licenseNone
keywords solace python rsolace rust
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pyrsolace

[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Python](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org)
[![PyPI](https://img.shields.io/pypi/v/pyrsolace.svg)](https://pypi.org/project/pyrsolace/)

Python bindings for rsolace with **full async/await support** and proper GIL release.

## ✨ Key Features

- 🐍 **Full asyncio Support**: Native async/await patterns with asyncio
- 🔓 **GIL Release**: Properly releases GIL during blocking operations
- 🔄 **Sync + Async**: Choose the best pattern for your use case
- 📦 **Complete API**: Pub/Sub, Request/Reply, Message Caching, Events
- ⚡ **High Performance**: Zero-copy message handling from Rust
- 🛡️ **Type Safe**: Complete type hints with `.pyi` files

## 🚀 Installation

```bash
# Using pip
pip install pyrsolace

# Using uv (recommended)
uv add pyrsolace
```

## 🔥 Quick Start

### Async Example (NEW!)

```python
import asyncio
import pyrsolace

async def main():
    # Initialize client
    client = pyrsolace.Client()
    
    # Connect to Solace broker
    connected = client.connect(
        host="tcp://localhost:55555",
        vpn="default",
        username="admin",
        password="admin",
        compression_level=5
    )
    
    if not connected:
        print("Failed to connect")
        return
    
    # Subscribe to topics
    client.subscribe_ext("test/topic/*", pyrsolace.SubscribeFlag.RequestConfirm)
    
    # Get async receivers
    async_msg_receiver = client.get_async_msg_receiver()
    async_event_receiver = client.get_async_event_receiver()
    
    # Handle messages asynchronously
    async def message_handler():
        while True:
            try:
                msg = await async_msg_receiver.recv()
                print(f"Async received: {msg.topic} - {msg.data}")
            except Exception as e:
                print(f"Message handler error: {e}")
                break
    
    # Handle events asynchronously
    async def event_handler():
        while True:
            try:
                event = await async_event_receiver.recv()
                print(f"Event: {event.session_event}")
            except Exception as e:
                print(f"Event handler error: {e}")
                break
    
    # Start async handlers
    msg_task = asyncio.create_task(message_handler())
    event_task = asyncio.create_task(event_handler())
    
    # Send some test messages
    for i in range(5):
        msg = pyrsolace.Msg(
            topic="test/topic/async",
            data=f"Async message {i}".encode()
        )
        client.send_msg(msg)
        await asyncio.sleep(1)
    
    # Send async request (true async, no blocking!)
    try:
        request_msg = pyrsolace.Msg(
            topic="test/request", 
            data=b"Hello async world!", 
            corr_id="req123"
        )
        
        response = await client.send_request_async(request_msg)
        print(f"Async response: {response.data}")
    except Exception as e:
        print(f"Async request failed: {e}")
    
    # Cleanup
    await asyncio.sleep(2)
    msg_task.cancel()
    event_task.cancel()
    client.disconnect()

if __name__ == "__main__":
    asyncio.run(main())
```

### Sync Example (Enhanced with GIL Release)

```python
import pyrsolace
import threading
import time

def message_handler(receiver, name):
    """Handle messages synchronously with proper GIL release."""
    while True:
        try:
            # This properly releases GIL, allowing other threads to run
            msg = receiver.recv()
            print(f"{name} received: {msg.topic} - {msg.data}")
        except Exception as e:
            print(f"{name} handler error: {e}")
            break

def main():
    client = pyrsolace.Client()
    
    # Connect
    connected = client.connect(
        host="tcp://localhost:55555",
        vpn="default",
        username="admin", 
        password="admin"
    )
    
    if not connected:
        print("Failed to connect")
        return
    
    # Subscribe
    client.subscribe("test/topic/*")
    
    # Get receivers
    msg_receiver = client.get_msg_receiver()
    event_receiver = client.get_event_receiver()
    
    # Start background threads (GIL is properly released)
    msg_thread = threading.Thread(
        target=message_handler, 
        args=(msg_receiver, "Messages")
    )
    event_thread = threading.Thread(
        target=message_handler, 
        args=(event_receiver, "Events")
    )
    
    msg_thread.start()
    event_thread.start()
    
    # Send messages
    for i in range(5):
        msg = pyrsolace.Msg(
            topic="test/topic/sync",
            data=f"Sync message {i}".encode()
        )
        client.send_msg(msg)
        time.sleep(1)
    
    client.disconnect()

if __name__ == "__main__":
    main()
```

### Callback-based Example

```python
import pyrsolace
import time

def on_message(msg):
    """Message callback function."""
    print(f"Callback received: {msg.topic} - {msg.data}")

def on_event(event):
    """Event callback function."""
    print(f"Event: {event.session_event} - {event.info}")

def main():
    client = pyrsolace.Client()
    
    # Set callbacks
    client.set_msg_callback(on_message)
    client.set_event_callback(on_event)
    
    # Connect and subscribe
    client.connect(
        host="tcp://localhost:55555",
        vpn="default",
        username="admin",
        password="admin"
    )
    
    client.subscribe("test/topic/*")
    
    # Send messages
    for i in range(5):
        msg = pyrsolace.Msg(
            topic="test/topic/callback",
            data=f"Callback message {i}".encode()
        )
        client.send_msg(msg)
        time.sleep(1)
    
    client.disconnect()

if __name__ == "__main__":
    main()
```

## 🔄 Sync vs Async

| Pattern | Best For | Usage | GIL Behavior |
|---------|----------|-------|--------------|
| **Callbacks** | Simple event handling | `client.set_msg_callback(fn)` | Released during callback |
| **Sync Receivers** | Threading, blocking I/O | `receiver.recv()` | Released during recv |
| **Async Receivers** | High concurrency | `await async_receiver.recv()` | N/A (async) |

### Migration from Sync to Async

```python
# Before: Sync only
receiver = client.get_msg_receiver()
msg = receiver.recv()  # Blocks thread (but releases GIL)

# After: True async
async_receiver = client.get_async_msg_receiver()
msg = await async_receiver.recv()  # Non-blocking, async

# Mixed: Use both in same application
sync_receiver = client.get_msg_receiver()     # For background threads
async_receiver = client.get_async_msg_receiver()  # For async tasks
```

## 📋 API Reference

### Client Class

```python
class Client:
    def connect(self, host: str, vpn: str, username: str, password: str, ...) -> bool
    def disconnect(self) -> None
    def subscribe(self, topic: str) -> ReturnCode
    def subscribe_ext(self, topic: str, flag: SubscribeFlag) -> ReturnCode
    
    # Message sending
    def send_msg(self, msg: Msg) -> ReturnCode
    def send_reply(self, rx_msg: Msg, reply_msg: Msg) -> ReturnCode
    
    # Sync receivers (with GIL release)
    def get_msg_receiver(self) -> MsgReceiver
    def get_request_receiver(self) -> MsgReceiver
    def get_p2p_receiver(self) -> MsgReceiver
    def get_event_receiver(self) -> EventReceiver
    
    # Async receivers (NEW!)
    def get_async_msg_receiver(self) -> AsyncMsgReceiver
    def get_async_request_receiver(self) -> AsyncMsgReceiver
    def get_async_p2p_receiver(self) -> AsyncMsgReceiver
    def get_async_event_receiver(self) -> AsyncEventReceiver
    
    # Request/Reply
    def send_request(self, msg: Msg, timeout: int) -> MsgReceiver
    async def send_request_async(self, msg: Msg) -> Msg  # NEW!
    
    # Callbacks
    def set_msg_callback(self, callback: Callable[[Msg], None]) -> None
    def set_event_callback(self, callback: Callable[[Event], None]) -> None
```

### Message Class

```python
class Msg:
    def __init__(self, topic: str = None, data: bytes = None, ...) -> None
    
    # Properties
    topic: str
    data: bytes
    corr_id: str
    reply_topic: str
    delivery_mode: DeliveryMode
    
    # Methods
    def set_user_prop(self, key: str, value: str) -> None
    def get_user_prop(self, key: str) -> str
    def dump(self) -> str
```

### Receiver Classes

```python
class MsgReceiver:
    def recv(self) -> Msg  # Releases GIL

class AsyncMsgReceiver:
    async def recv(self) -> Msg  # True async

class EventReceiver:
    def recv(self) -> Event  # Releases GIL

class AsyncEventReceiver:
    async def recv(self) -> Event  # True async
```

## 🛠️ Development

### Building from Source

```bash
# Clone repository
git clone https://github.com/Yvictor/rsolace.git
cd rsolace/pyrsolace

# Using uv (recommended)
uv build
uv pip install -e .

# Using maturin
pip install maturin
maturin develop --release
```

### Running Tests

```bash
# Run tests
uv run pytest tests/

# Run specific tests
uv run pytest tests/test_msg.py -v
```

## 🔧 Configuration

### Connection Parameters

```python
client.connect(
    host="tcp://broker:55555",          # Broker URL
    vpn="vpn_name",                     # VPN name
    username="user",                    # Username
    password="pass",                    # Password
    client_name="my_client",            # Client identifier
    compression_level=5,                # 1-9 (higher = more compression)
    connect_timeout_ms=30000,           # Connection timeout
    connect_retries=3,                  # Retry attempts
    reconnect_retries=10,               # Auto-reconnect attempts
    keep_alive_ms=3000,                 # Keep-alive interval
    reapply_subscriptions=True,         # Restore subs on reconnect
    generate_sender_id=True,            # Add sender ID to messages
    generate_timestamps=True,           # Add timestamps
)
```

### Message Properties

```python
msg = pyrsolace.Msg(
    topic="my/topic",
    data=b"payload",
    corr_id="request-123",
    reply_topic="reply/topic",
    delivery_mode=pyrsolace.DeliveryMode.Persistent
)

# User properties
msg.set_user_prop("priority", "high")
msg.set_user_prop("version", "1.0")
```

## 🎯 Advanced Examples

### Async Producer-Consumer Pattern

```python
import asyncio
from asyncio import Queue

async def producer(client, queue):
    """Produce messages to queue."""
    for i in range(100):
        msg = pyrsolace.Msg(
            topic=f"data/stream/{i % 10}",
            data=f"Data packet {i}".encode()
        )
        await queue.put(msg)
        await asyncio.sleep(0.1)

async def consumer(client, queue):
    """Consume messages from queue."""
    while True:
        msg = await queue.get()
        client.send_msg(msg)
        queue.task_done()

async def message_processor(client):
    """Process incoming messages."""
    receiver = client.get_async_msg_receiver()
    while True:
        msg = await receiver.recv()
        # Process message asynchronously
        await process_message(msg)

async def main():
    client = pyrsolace.Client()
    client.connect(...)
    
    queue = Queue(maxsize=100)
    
    # Start producer, consumer, and processor
    await asyncio.gather(
        producer(client, queue),
        consumer(client, queue),
        message_processor(client)
    )
```

### Request/Reply Service

```python
async def request_handler(client):
    """Handle incoming requests asynchronously."""
    receiver = client.get_async_request_receiver()
    
    while True:
        request = await receiver.recv()
        
        # Process request
        response_data = await process_request(request.data)
        
        # Send reply
        reply = pyrsolace.Msg(
            topic=request.reply_topic,
            data=response_data,
            corr_id=request.corr_id
        )
        client.send_reply(request, reply)
```

## 🚀 Performance Tips

### Async Best Practices

1. **Use Semaphores**: Limit concurrent operations
```python
semaphore = asyncio.Semaphore(10)
async with semaphore:
    await process_message(msg)
```

2. **Batch Operations**: Group related operations
```python
messages = []
async for msg in message_stream():
    messages.append(msg)
    if len(messages) >= 100:
        await process_batch(messages)
        messages.clear()
```

3. **Graceful Shutdown**: Cancel tasks properly
```python
try:
    await main_task
except asyncio.CancelledError:
    await cleanup()
```

## 📚 Documentation

- **Main Project**: See [root README](../README.md) for complete documentation
- **Rust Library**: Check [rsolace](../rsolace/README.md) for Rust-specific features
- **Type Hints**: Complete API in [`pyrsolace.pyi`](pyrsolace.pyi)
- **Examples**: More examples in [`tests/`](tests/) directory

## 🤝 Contributing

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Run tests: `uv run pytest`
5. Submit a pull request

## 📄 License

GPL-3.0-only License - see [LICENSE](../LICENSE) for details.

---

**Powered by rsolace** ⚡ - High-performance Rust Solace bindings


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pyrsolace",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "solace, python, rsolace, rust",
    "author": null,
    "author_email": "yvictor <yvictor3141@gmail.com>",
    "download_url": null,
    "platform": null,
    "description": "# pyrsolace\n\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n[![Python](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org)\n[![PyPI](https://img.shields.io/pypi/v/pyrsolace.svg)](https://pypi.org/project/pyrsolace/)\n\nPython bindings for rsolace with **full async/await support** and proper GIL release.\n\n## \u2728 Key Features\n\n- \ud83d\udc0d **Full asyncio Support**: Native async/await patterns with asyncio\n- \ud83d\udd13 **GIL Release**: Properly releases GIL during blocking operations\n- \ud83d\udd04 **Sync + Async**: Choose the best pattern for your use case\n- \ud83d\udce6 **Complete API**: Pub/Sub, Request/Reply, Message Caching, Events\n- \u26a1 **High Performance**: Zero-copy message handling from Rust\n- \ud83d\udee1\ufe0f **Type Safe**: Complete type hints with `.pyi` files\n\n## \ud83d\ude80 Installation\n\n```bash\n# Using pip\npip install pyrsolace\n\n# Using uv (recommended)\nuv add pyrsolace\n```\n\n## \ud83d\udd25 Quick Start\n\n### Async Example (NEW!)\n\n```python\nimport asyncio\nimport pyrsolace\n\nasync def main():\n    # Initialize client\n    client = pyrsolace.Client()\n    \n    # Connect to Solace broker\n    connected = client.connect(\n        host=\"tcp://localhost:55555\",\n        vpn=\"default\",\n        username=\"admin\",\n        password=\"admin\",\n        compression_level=5\n    )\n    \n    if not connected:\n        print(\"Failed to connect\")\n        return\n    \n    # Subscribe to topics\n    client.subscribe_ext(\"test/topic/*\", pyrsolace.SubscribeFlag.RequestConfirm)\n    \n    # Get async receivers\n    async_msg_receiver = client.get_async_msg_receiver()\n    async_event_receiver = client.get_async_event_receiver()\n    \n    # Handle messages asynchronously\n    async def message_handler():\n        while True:\n            try:\n                msg = await async_msg_receiver.recv()\n                print(f\"Async received: {msg.topic} - {msg.data}\")\n            except Exception as e:\n                print(f\"Message handler error: {e}\")\n                break\n    \n    # Handle events asynchronously\n    async def event_handler():\n        while True:\n            try:\n                event = await async_event_receiver.recv()\n                print(f\"Event: {event.session_event}\")\n            except Exception as e:\n                print(f\"Event handler error: {e}\")\n                break\n    \n    # Start async handlers\n    msg_task = asyncio.create_task(message_handler())\n    event_task = asyncio.create_task(event_handler())\n    \n    # Send some test messages\n    for i in range(5):\n        msg = pyrsolace.Msg(\n            topic=\"test/topic/async\",\n            data=f\"Async message {i}\".encode()\n        )\n        client.send_msg(msg)\n        await asyncio.sleep(1)\n    \n    # Send async request (true async, no blocking!)\n    try:\n        request_msg = pyrsolace.Msg(\n            topic=\"test/request\", \n            data=b\"Hello async world!\", \n            corr_id=\"req123\"\n        )\n        \n        response = await client.send_request_async(request_msg)\n        print(f\"Async response: {response.data}\")\n    except Exception as e:\n        print(f\"Async request failed: {e}\")\n    \n    # Cleanup\n    await asyncio.sleep(2)\n    msg_task.cancel()\n    event_task.cancel()\n    client.disconnect()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n### Sync Example (Enhanced with GIL Release)\n\n```python\nimport pyrsolace\nimport threading\nimport time\n\ndef message_handler(receiver, name):\n    \"\"\"Handle messages synchronously with proper GIL release.\"\"\"\n    while True:\n        try:\n            # This properly releases GIL, allowing other threads to run\n            msg = receiver.recv()\n            print(f\"{name} received: {msg.topic} - {msg.data}\")\n        except Exception as e:\n            print(f\"{name} handler error: {e}\")\n            break\n\ndef main():\n    client = pyrsolace.Client()\n    \n    # Connect\n    connected = client.connect(\n        host=\"tcp://localhost:55555\",\n        vpn=\"default\",\n        username=\"admin\", \n        password=\"admin\"\n    )\n    \n    if not connected:\n        print(\"Failed to connect\")\n        return\n    \n    # Subscribe\n    client.subscribe(\"test/topic/*\")\n    \n    # Get receivers\n    msg_receiver = client.get_msg_receiver()\n    event_receiver = client.get_event_receiver()\n    \n    # Start background threads (GIL is properly released)\n    msg_thread = threading.Thread(\n        target=message_handler, \n        args=(msg_receiver, \"Messages\")\n    )\n    event_thread = threading.Thread(\n        target=message_handler, \n        args=(event_receiver, \"Events\")\n    )\n    \n    msg_thread.start()\n    event_thread.start()\n    \n    # Send messages\n    for i in range(5):\n        msg = pyrsolace.Msg(\n            topic=\"test/topic/sync\",\n            data=f\"Sync message {i}\".encode()\n        )\n        client.send_msg(msg)\n        time.sleep(1)\n    \n    client.disconnect()\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Callback-based Example\n\n```python\nimport pyrsolace\nimport time\n\ndef on_message(msg):\n    \"\"\"Message callback function.\"\"\"\n    print(f\"Callback received: {msg.topic} - {msg.data}\")\n\ndef on_event(event):\n    \"\"\"Event callback function.\"\"\"\n    print(f\"Event: {event.session_event} - {event.info}\")\n\ndef main():\n    client = pyrsolace.Client()\n    \n    # Set callbacks\n    client.set_msg_callback(on_message)\n    client.set_event_callback(on_event)\n    \n    # Connect and subscribe\n    client.connect(\n        host=\"tcp://localhost:55555\",\n        vpn=\"default\",\n        username=\"admin\",\n        password=\"admin\"\n    )\n    \n    client.subscribe(\"test/topic/*\")\n    \n    # Send messages\n    for i in range(5):\n        msg = pyrsolace.Msg(\n            topic=\"test/topic/callback\",\n            data=f\"Callback message {i}\".encode()\n        )\n        client.send_msg(msg)\n        time.sleep(1)\n    \n    client.disconnect()\n\nif __name__ == \"__main__\":\n    main()\n```\n\n## \ud83d\udd04 Sync vs Async\n\n| Pattern | Best For | Usage | GIL Behavior |\n|---------|----------|-------|--------------|\n| **Callbacks** | Simple event handling | `client.set_msg_callback(fn)` | Released during callback |\n| **Sync Receivers** | Threading, blocking I/O | `receiver.recv()` | Released during recv |\n| **Async Receivers** | High concurrency | `await async_receiver.recv()` | N/A (async) |\n\n### Migration from Sync to Async\n\n```python\n# Before: Sync only\nreceiver = client.get_msg_receiver()\nmsg = receiver.recv()  # Blocks thread (but releases GIL)\n\n# After: True async\nasync_receiver = client.get_async_msg_receiver()\nmsg = await async_receiver.recv()  # Non-blocking, async\n\n# Mixed: Use both in same application\nsync_receiver = client.get_msg_receiver()     # For background threads\nasync_receiver = client.get_async_msg_receiver()  # For async tasks\n```\n\n## \ud83d\udccb API Reference\n\n### Client Class\n\n```python\nclass Client:\n    def connect(self, host: str, vpn: str, username: str, password: str, ...) -> bool\n    def disconnect(self) -> None\n    def subscribe(self, topic: str) -> ReturnCode\n    def subscribe_ext(self, topic: str, flag: SubscribeFlag) -> ReturnCode\n    \n    # Message sending\n    def send_msg(self, msg: Msg) -> ReturnCode\n    def send_reply(self, rx_msg: Msg, reply_msg: Msg) -> ReturnCode\n    \n    # Sync receivers (with GIL release)\n    def get_msg_receiver(self) -> MsgReceiver\n    def get_request_receiver(self) -> MsgReceiver\n    def get_p2p_receiver(self) -> MsgReceiver\n    def get_event_receiver(self) -> EventReceiver\n    \n    # Async receivers (NEW!)\n    def get_async_msg_receiver(self) -> AsyncMsgReceiver\n    def get_async_request_receiver(self) -> AsyncMsgReceiver\n    def get_async_p2p_receiver(self) -> AsyncMsgReceiver\n    def get_async_event_receiver(self) -> AsyncEventReceiver\n    \n    # Request/Reply\n    def send_request(self, msg: Msg, timeout: int) -> MsgReceiver\n    async def send_request_async(self, msg: Msg) -> Msg  # NEW!\n    \n    # Callbacks\n    def set_msg_callback(self, callback: Callable[[Msg], None]) -> None\n    def set_event_callback(self, callback: Callable[[Event], None]) -> None\n```\n\n### Message Class\n\n```python\nclass Msg:\n    def __init__(self, topic: str = None, data: bytes = None, ...) -> None\n    \n    # Properties\n    topic: str\n    data: bytes\n    corr_id: str\n    reply_topic: str\n    delivery_mode: DeliveryMode\n    \n    # Methods\n    def set_user_prop(self, key: str, value: str) -> None\n    def get_user_prop(self, key: str) -> str\n    def dump(self) -> str\n```\n\n### Receiver Classes\n\n```python\nclass MsgReceiver:\n    def recv(self) -> Msg  # Releases GIL\n\nclass AsyncMsgReceiver:\n    async def recv(self) -> Msg  # True async\n\nclass EventReceiver:\n    def recv(self) -> Event  # Releases GIL\n\nclass AsyncEventReceiver:\n    async def recv(self) -> Event  # True async\n```\n\n## \ud83d\udee0\ufe0f Development\n\n### Building from Source\n\n```bash\n# Clone repository\ngit clone https://github.com/Yvictor/rsolace.git\ncd rsolace/pyrsolace\n\n# Using uv (recommended)\nuv build\nuv pip install -e .\n\n# Using maturin\npip install maturin\nmaturin develop --release\n```\n\n### Running Tests\n\n```bash\n# Run tests\nuv run pytest tests/\n\n# Run specific tests\nuv run pytest tests/test_msg.py -v\n```\n\n## \ud83d\udd27 Configuration\n\n### Connection Parameters\n\n```python\nclient.connect(\n    host=\"tcp://broker:55555\",          # Broker URL\n    vpn=\"vpn_name\",                     # VPN name\n    username=\"user\",                    # Username\n    password=\"pass\",                    # Password\n    client_name=\"my_client\",            # Client identifier\n    compression_level=5,                # 1-9 (higher = more compression)\n    connect_timeout_ms=30000,           # Connection timeout\n    connect_retries=3,                  # Retry attempts\n    reconnect_retries=10,               # Auto-reconnect attempts\n    keep_alive_ms=3000,                 # Keep-alive interval\n    reapply_subscriptions=True,         # Restore subs on reconnect\n    generate_sender_id=True,            # Add sender ID to messages\n    generate_timestamps=True,           # Add timestamps\n)\n```\n\n### Message Properties\n\n```python\nmsg = pyrsolace.Msg(\n    topic=\"my/topic\",\n    data=b\"payload\",\n    corr_id=\"request-123\",\n    reply_topic=\"reply/topic\",\n    delivery_mode=pyrsolace.DeliveryMode.Persistent\n)\n\n# User properties\nmsg.set_user_prop(\"priority\", \"high\")\nmsg.set_user_prop(\"version\", \"1.0\")\n```\n\n## \ud83c\udfaf Advanced Examples\n\n### Async Producer-Consumer Pattern\n\n```python\nimport asyncio\nfrom asyncio import Queue\n\nasync def producer(client, queue):\n    \"\"\"Produce messages to queue.\"\"\"\n    for i in range(100):\n        msg = pyrsolace.Msg(\n            topic=f\"data/stream/{i % 10}\",\n            data=f\"Data packet {i}\".encode()\n        )\n        await queue.put(msg)\n        await asyncio.sleep(0.1)\n\nasync def consumer(client, queue):\n    \"\"\"Consume messages from queue.\"\"\"\n    while True:\n        msg = await queue.get()\n        client.send_msg(msg)\n        queue.task_done()\n\nasync def message_processor(client):\n    \"\"\"Process incoming messages.\"\"\"\n    receiver = client.get_async_msg_receiver()\n    while True:\n        msg = await receiver.recv()\n        # Process message asynchronously\n        await process_message(msg)\n\nasync def main():\n    client = pyrsolace.Client()\n    client.connect(...)\n    \n    queue = Queue(maxsize=100)\n    \n    # Start producer, consumer, and processor\n    await asyncio.gather(\n        producer(client, queue),\n        consumer(client, queue),\n        message_processor(client)\n    )\n```\n\n### Request/Reply Service\n\n```python\nasync def request_handler(client):\n    \"\"\"Handle incoming requests asynchronously.\"\"\"\n    receiver = client.get_async_request_receiver()\n    \n    while True:\n        request = await receiver.recv()\n        \n        # Process request\n        response_data = await process_request(request.data)\n        \n        # Send reply\n        reply = pyrsolace.Msg(\n            topic=request.reply_topic,\n            data=response_data,\n            corr_id=request.corr_id\n        )\n        client.send_reply(request, reply)\n```\n\n## \ud83d\ude80 Performance Tips\n\n### Async Best Practices\n\n1. **Use Semaphores**: Limit concurrent operations\n```python\nsemaphore = asyncio.Semaphore(10)\nasync with semaphore:\n    await process_message(msg)\n```\n\n2. **Batch Operations**: Group related operations\n```python\nmessages = []\nasync for msg in message_stream():\n    messages.append(msg)\n    if len(messages) >= 100:\n        await process_batch(messages)\n        messages.clear()\n```\n\n3. **Graceful Shutdown**: Cancel tasks properly\n```python\ntry:\n    await main_task\nexcept asyncio.CancelledError:\n    await cleanup()\n```\n\n## \ud83d\udcda Documentation\n\n- **Main Project**: See [root README](../README.md) for complete documentation\n- **Rust Library**: Check [rsolace](../rsolace/README.md) for Rust-specific features\n- **Type Hints**: Complete API in [`pyrsolace.pyi`](pyrsolace.pyi)\n- **Examples**: More examples in [`tests/`](tests/) directory\n\n## \ud83e\udd1d Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Run tests: `uv run pytest`\n5. Submit a pull request\n\n## \ud83d\udcc4 License\n\nGPL-3.0-only License - see [LICENSE](../LICENSE) for details.\n\n---\n\n**Powered by rsolace** \u26a1 - High-performance Rust Solace bindings\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Python bindings for rsolace",
    "version": "0.3.2",
    "project_urls": null,
    "split_keywords": [
        "solace",
        " python",
        " rsolace",
        " rust"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4dabff30597a7de4de7a0816499245f8128132af7b0e51aa20a72b09801749bc",
                "md5": "472661ef6dc97017bc887af5ee89863d",
                "sha256": "1f52435c5a2fbfa71572f2343e352b1168912cdbf1b68fc045a083f1dd674238"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-macosx_10_12_x86_64.whl",
            "has_sig": false,
            "md5_digest": "472661ef6dc97017bc887af5ee89863d",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 2146057,
            "upload_time": "2025-07-13T07:04:20",
            "upload_time_iso_8601": "2025-07-13T07:04:20.252004Z",
            "url": "https://files.pythonhosted.org/packages/4d/ab/ff30597a7de4de7a0816499245f8128132af7b0e51aa20a72b09801749bc/pyrsolace-0.3.2-cp37-abi3-macosx_10_12_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "bdc5a4bcc0188aba1fa0b6267cc1101d5e7be3f9fb6daf6e9390ce552dd42413",
                "md5": "0ce9412e9071c100ce7030c3d014c955",
                "sha256": "db495600567a35f93275b4b136fb2ffa0ea928cc87e53fbddfa42d947d74d950"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "0ce9412e9071c100ce7030c3d014c955",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 1986064,
            "upload_time": "2025-07-13T07:04:21",
            "upload_time_iso_8601": "2025-07-13T07:04:21.649790Z",
            "url": "https://files.pythonhosted.org/packages/bd/c5/a4bcc0188aba1fa0b6267cc1101d5e7be3f9fb6daf6e9390ce552dd42413/pyrsolace-0.3.2-cp37-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8830194e797f34c5ec7b8660e8d20c94a5ceb28dba528b8f269fd1a3f8d4e667",
                "md5": "7364cd8a1f4a8c6c51309cb2a870bf0b",
                "sha256": "21a634c7564ef6f6cb53a0118790d2071e76e1f9a7c48c73809746c25bcd7e62"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-manylinux_2_34_aarch64.whl",
            "has_sig": false,
            "md5_digest": "7364cd8a1f4a8c6c51309cb2a870bf0b",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 1305551,
            "upload_time": "2025-07-13T07:04:23",
            "upload_time_iso_8601": "2025-07-13T07:04:23.286509Z",
            "url": "https://files.pythonhosted.org/packages/88/30/194e797f34c5ec7b8660e8d20c94a5ceb28dba528b8f269fd1a3f8d4e667/pyrsolace-0.3.2-cp37-abi3-manylinux_2_34_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b5b89e1f6f076b0a189b728b166ac430db98b2aa5e5b728557ad7a337d2cb229",
                "md5": "4f3ce87eecd830a515c8169423e22d06",
                "sha256": "34be8a672c0063b8750aa6de4e084c5208278d42e95796848978d2405a66a38e"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "4f3ce87eecd830a515c8169423e22d06",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 1308533,
            "upload_time": "2025-07-13T07:04:24",
            "upload_time_iso_8601": "2025-07-13T07:04:24.888337Z",
            "url": "https://files.pythonhosted.org/packages/b5/b8/9e1f6f076b0a189b728b166ac430db98b2aa5e5b728557ad7a337d2cb229/pyrsolace-0.3.2-cp37-abi3-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e46fa3422244ceb5743f4efee24fd0be832d6a2bf41397dc26b8b1afb97eca4a",
                "md5": "d12e48200d73ed9ab239441c26c6e546",
                "sha256": "736c62dded8357ee434f0d785f73cca6ce11d5ff29e19dd4a89d587903d4e171"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-win32.whl",
            "has_sig": false,
            "md5_digest": "d12e48200d73ed9ab239441c26c6e546",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 1662678,
            "upload_time": "2025-07-13T07:04:26",
            "upload_time_iso_8601": "2025-07-13T07:04:26.424683Z",
            "url": "https://files.pythonhosted.org/packages/e4/6f/a3422244ceb5743f4efee24fd0be832d6a2bf41397dc26b8b1afb97eca4a/pyrsolace-0.3.2-cp37-abi3-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "39d551453c90e59fb77eaa9eccc34b2b873af69de33d55290fe112ca010b8edc",
                "md5": "5a0c8119d07132ef04449e406a86e17e",
                "sha256": "71a53fc39a1803bc1c111ba64da1fcf37630acb45bb05c4dcb4fe85879990b5c"
            },
            "downloads": -1,
            "filename": "pyrsolace-0.3.2-cp37-abi3-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "5a0c8119d07132ef04449e406a86e17e",
            "packagetype": "bdist_wheel",
            "python_version": "cp37",
            "requires_python": ">=3.7",
            "size": 1955175,
            "upload_time": "2025-07-13T07:04:27",
            "upload_time_iso_8601": "2025-07-13T07:04:27.855590Z",
            "url": "https://files.pythonhosted.org/packages/39/d5/51453c90e59fb77eaa9eccc34b2b873af69de33d55290fe112ca010b8edc/pyrsolace-0.3.2-cp37-abi3-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-13 07:04:20",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "pyrsolace"
}
        
Elapsed time: 1.44769s