This guide covers all features of Hussh's asynchronous AsyncConnection class for use with Python's asyncio.
The AsyncConnection class supports the same authentication methods as the synchronous Connection class.
import asyncio
from hussh.aio import AsyncConnection
async def main():
# With username and password
async with AsyncConnection(host="my.test.server", username="user", password="pass") as conn:
result = await conn.execute("whoami")
print(result.stdout)
asyncio.run(main())async with AsyncConnection(host="my.test.server", private_key="~/.ssh/id_rsa") as conn:
result = await conn.execute("whoami")
# With password-protected key
async with AsyncConnection(
host="my.test.server",
private_key="~/.ssh/id_rsa",
password="keypass"
) as conn:
result = await conn.execute("whoami")async with AsyncConnection("my.test.server") as conn:
result = await conn.execute("whoami")The async context manager (async with) is the recommended pattern:
async with AsyncConnection(host="my.test.server", password="pass") as conn:
result = await conn.execute("ls")
# Connection is automatically closedFor manual management:
conn = AsyncConnection(host="my.test.server", password="pass")
await conn.connect()
try:
result = await conn.execute("ls")
finally:
await conn.close()import asyncio
from hussh.aio import AsyncConnection
async def main():
async with AsyncConnection(host="my.test.server", username="user", password="pass") as conn:
result = await conn.execute("ls")
print(result.stdout)
asyncio.run(main())Async connections support timeouts at both the connection level and per-command.
Set a default timeout (in seconds) for all operations:
async with AsyncConnection(host="my.test.server", timeout=10) as conn:
# All commands will timeout after 10 seconds by default
result = await conn.execute("sleep 5") # OK - completes in 5sOverride the default timeout for specific commands:
async with AsyncConnection(host="my.test.server", timeout=30) as conn:
# Use the default 30s timeout
await conn.execute("quick_command")
# Override with a shorter timeout
try:
await conn.execute("sleep 60", timeout=5)
except TimeoutError:
print("Command timed out!")
# Override with a longer timeout for slow operations
await conn.execute("long_running_backup", timeout=300)async with AsyncConnection(host="my.test.server", timeout=10) as conn:
try:
result = await conn.execute("sleep 20", timeout=5)
except TimeoutError:
print("The command took too long!")
# Handle the timeout - maybe retry or alertCommands are executed asynchronously using await:
async with AsyncConnection(host="my.test.server", password="pass") as conn:
result = await conn.execute("whoami")
print(result.stdout, result.stderr, result.status)One of the main benefits of async is running multiple commands concurrently:
import asyncio
async def check_server(host, password):
async with AsyncConnection(host=host, password=password) as conn:
result = await conn.execute("uptime")
return host, result.stdout
async def main():
hosts = ["server1", "server2", "server3"]
tasks = [check_server(host, "pass") for host in hosts]
results = await asyncio.gather(*tasks)
for host, uptime in results:
print(f"{host}: {uptime}")
asyncio.run(main())Async SFTP operations mirror the synchronous API with await.
async with AsyncConnection(host="my.test.server", password="pass") as conn:
# Write a local file to the remote server
await conn.sftp_write(local_path="/path/to/my/file", remote_path="/dest/path/file")
# Write string data directly to a remote file
await conn.sftp_write_data(data="Hello there!", remote_path="/dest/path/file")async with AsyncConnection(host="my.test.server", password="pass") as conn:
# Copy a remote file to a local destination
await conn.sftp_read(remote_path="/dest/path/file", local_path="/path/to/my/file")
# Read remote file contents directly into a string
contents = await conn.sftp_read(remote_path="/dest/path/file")
print(contents)async with AsyncConnection(host="my.test.server", password="pass") as conn:
files = await conn.sftp_list("/remote/path")
for file in files:
print(file)async with AsyncConnection(host="my.test.server", password="pass") as conn:
# Upload multiple files concurrently
await asyncio.gather(
conn.sftp_write("/local/file1.txt", "/remote/file1.txt"),
conn.sftp_write("/local/file2.txt", "/remote/file2.txt"),
conn.sftp_write("/local/file3.txt", "/remote/file3.txt"),
)The async interactive shell supports multiple read/write cycles, unlike the synchronous version.
async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with await conn.shell() as shell:
await shell.send("ls")
result = await shell.read()
print(result.stdout)One advantage of the async shell is the ability to read and write multiple times:
async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with await conn.shell() as shell:
# First command
await shell.send("whoami")
result = await shell.read()
print(f"User: {result.stdout.strip()}")
# Second command
await shell.send("pwd")
result = await shell.read()
print(f"Directory: {result.stdout.strip()}")
# Change directory and verify
await shell.send("cd /tmp")
await shell.send("pwd")
result = await shell.read()
print(f"New directory: {result.stdout.strip()}")async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with await conn.shell() as shell:
# Set up environment
await shell.send("export PATH=$PATH:/opt/myapp/bin")
await shell.read()
# Run application commands
await shell.send("myapp --version")
result = await shell.read()
print(f"App version: {result.stdout}")
await shell.send("myapp status")
result = await shell.read()
print(f"App status: {result.stdout}")Async file tailing allows you to monitor files while performing other async operations.
async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with conn.tail("/path/to/file.txt") as tf:
# Do something that writes to the file
await conn.execute("echo 'test' >> /path/to/file.txt")
# Read new content
content = await tf.read()
print(content)
# Access all contents after exiting
print(tf.contents)async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with conn.tail("/var/log/app.log") as tf:
# Restart a service
await conn.execute("systemctl restart myapp")
# Wait a bit for logs
await asyncio.sleep(2)
# Check startup logs
logs = await tf.read()
if "Started successfully" in logs:
print("Service started OK")
else:
print(f"Startup logs: {logs}")async with AsyncConnection(host="my.test.server", password="pass") as conn:
async with conn.tail("/var/log/syslog") as syslog:
async with conn.tail("/var/log/auth.log") as authlog:
# Perform some operation
await conn.execute("sudo -u testuser whoami")
await asyncio.sleep(1)
print("Syslog:", await syslog.read())
print("Auth log:", await authlog.read())