A Python wrapper for NATS that provides an API without requiring asyncio, built with Rust and PyO3.
Rant: This is the real pain of asyncio - libraries need to be implemented twice. WHY? We have gevent, which does not force to reimplement everything.
This driver uses Rust async_nats under the hood. The Tokio runtime is executed in a seperate (not python aware) thread. Syncronization/Notification between python and Tokio is done via eventfd
Even with this overhead performance is still decent.
By using eventfd this driver can also be used with gevent. Simply use the gevent_connect function, which provides a gevent friendly NATS connection.
- publish on pypi
- Implement a
.close()method - Implement error callback on the Client level
- Go to
Actions - Select the latest green build
- Download the .zip file according to you architecture
- unzip the file
pip install ./no_asyncio_nats-....whl
import no_asyncio_nats
import datetime
# Connect to NATS
nc = no_asyncio_nats.connect("nats://localhost:4222")
# Subscribe to a subject
sub = nc.subscribe('my.subject')
# Publish a message
nc.publish('my.subject', b'Hello, World!')
# Receive a message
message = sub.recv_msg()
print(f"Received: {message['payload']}")
# Close the connection
sub.unsubscribe()Connects to a NATS server and returns a Client instance.
Parameters:
address(str): The NATS server address (e.g., "nats://localhost:4222")options(dict, optional): Connection configuration options
Returns:
Client: A client instance for interacting with NATS
Connection Options:
The options parameter is a dictionary that can contain the following keys:
Authentication Options:
token(str): Authentication tokenuser_and_password(tuple): Username and password as(username, password)nkey(str): Nkey seed for authenticationcredentials_file(str): Path to credentials filecredentials(str): Credentials as a string
TLS Options:
tls_root_certificates(str): Path to root certificate filetls_client_cert(str): Path to client certificate filetls_client_key(str): Path to client private key filerequire_tls(bool): Whether TLS is requiredtls_first(bool): Whether to establish TLS connection before handshake
Timeout and Performance Options:
ping_interval(datetime.timedelta): Ping intervalconnection_timeout(datetime.timedelta): Connection timeoutrequest_timeout(datetime.timedelta): Request timeoutsubscription_capacity(int): Maximum number of subscriptionsread_buffer_capacity(int): Read buffer size
Connection Behavior Options:
no_echo(bool): Disable message echocustom_inbox_prefix(str): Custom inbox prefixname(str): Client nameretry_on_initial_connect(bool): Retry on initial connection failuremax_reconnects(int): Maximum number of reconnection attemptsignore_discovered_servers(bool): Ignore server discoveryretain_servers_order(bool): Retain server connection order
The main client class for interacting with NATS.
Publishes a message to a subject.
Parameters:
subject(str): The subject to publish todata(bytes): The message payload as bytesreply(str, optional): Reply subject for responsesheaders(dict, optional): Message headers as key-value pairs
Returns:
- None
Flushes all pending messages to the server.
Parameters:
- None
Returns:
- None
Subscribes to a subject and returns a Subscriber instance.
Parameters:
subject(str): The subject to subscribe to
Returns:
Subscriber: A subscriber instance for receiving messages
Subscribes to a subject with queue group semantics.
Parameters:
subject(str): The subject to subscribe toqueue_group(str): The queue group name
Returns:
Subscriber: A subscriber instance for receiving messages
Performs a request-response pattern.
Parameters:
subject(str): The subject to send the request todata(bytes): The request payload as bytesheaders(dict, optional): Request headers as key-value pairs
Returns:
- dict: Response message containing:
subject(str): Response subjectpayload(bytes): Response payloadreply(str, optional): Reply subjectheaders(dict, optional): Response headers
Creates a new unique inbox name.
Parameters:
- None
Returns:
- str: A unique inbox name
Represents a subscription to a NATS subject.
Drains the subscription, allowing in-flight messages to be processed.
Parameters:
- None
Returns:
- None
Unsubscribes from the subject.
Parameters:
- None
Returns:
- None
Unsubscribes after receiving a specific number of messages.
Parameters:
count(int): Number of messages to receive before unsubscribing
Returns:
- None
Receives a message from the subscription.
Parameters:
timeout(datetime.timedelta, optional): Timeout. If None, blocks indefinitely.
Returns:
- dict or None: Message dictionary or None if timeout occurs. Message contains:
subject(str): Message subjectpayload(bytes): Message payloadreply(str, optional): Reply subjectheaders(dict, optional): Message headers
import no_asyncio_nats
# Connect to NATS
nc = no_asyncio_nats.connect("nats://localhost:4222")
# Subscribe to a subject
sub = nc.subscribe('updates')
# Publish a message
nc.publish('updates', b'Hello, World!')
# Receive a message
message = sub.recv_msg()
print(f"Received: {message['payload']}")
# Clean up
sub.unsubscribe()import no_asyncio_nats
nc = no_asyncio_nats.connect("nats://localhost:4222")
# Send a request
response = nc.request('service.hello', b'Hello, Service!')
print(f"Response: {response['payload']}")
# Handle multiple requests
def handle_request(message):
print(f"Received request: {message['payload']}")
response_data = b"Hello, Client!"
nc.publish(message['reply'], response_data)
# Set up request handler
sub = nc.subscribe('service.hello')
while True:
msg = sub.recv_msg()
handle_request(msg)import no_asyncio_nats
import threading
def worker(worker_id):
nc = no_asyncio_nats.connect("nats://localhost:4222")
sub = nc.queue_subscribe('tasks', 'worker-group')
while True:
msg = sub.recv_msg()
print(f"Worker {worker_id} processing: {msg['payload']}")
# Start multiple workers
for i in range(3):
threading.Thread(target=worker, args=(i,)).start()import no_asyncio_nats
import datetime
# Connect with authentication and TLS
options = {
"name": "my-client",
"user_and_password": ("username", "password"),
"tls_root_certificates": "/path/to/ca.crt",
"tls_client_cert": "/path/to/client.crt",
"tls_client_key": "/path/to/client.key",
"connection_timeout": datetime.timedelta(seconds=10), # Use timedelta for time durations
"ping_interval": datetime.timedelta(seconds=30), # Use timedelta for time durations
"max_reconnects": 5
}
nc = no_asyncio_nats.connect("nats://localhost:4222", options)The API uses exceptions to handle errors. Common exceptions include:
ConnectionError: When connection to NATS failsTimeoutError: When operations timeoutValueError: When invalid parameters are provided
Always handle exceptions appropriately:
try:
nc = no_asyncio_nats.connect("nats://localhost:4222")
response = nc.request('service.hello', b'Hello')
except ConnectionError as e:
print(f"Connection failed: {e}")
except TimeoutError as e:
print(f"Request timed out: {e}")