|
| 1 | +#!/usr/bin/env -S uv run python |
| 2 | +""" |
| 3 | +Async Runloop SDK Example - Concurrent Devbox Operations |
| 4 | +
|
| 5 | +This example demonstrates the asynchronous capabilities of the Runloop SDK: |
| 6 | +- Creating and managing devboxes asynchronously |
| 7 | +- Concurrent command execution across multiple devboxes |
| 8 | +- Async file operations |
| 9 | +- Async command streaming |
| 10 | +""" |
| 11 | + |
| 12 | +import asyncio |
| 13 | +import os |
| 14 | +from runloop_api_client import AsyncRunloopSDK |
| 15 | + |
| 16 | + |
| 17 | +async def demonstrate_basic_async(): |
| 18 | + """Demonstrate basic async devbox operations.""" |
| 19 | + print("=== Basic Async Operations ===") |
| 20 | + |
| 21 | + sdk = AsyncRunloopSDK() |
| 22 | + |
| 23 | + # Create a devbox with async context manager |
| 24 | + async with sdk.devbox.create(name="async-example-devbox") as devbox: |
| 25 | + print(f"Created devbox: {devbox.id}") |
| 26 | + |
| 27 | + # Execute command asynchronously |
| 28 | + result = await devbox.cmd.exec("echo 'Hello from async devbox!'") |
| 29 | + output = await result.stdout() |
| 30 | + print(f"Command output: {output.strip()}") |
| 31 | + |
| 32 | + # File operations |
| 33 | + await devbox.file.write( |
| 34 | + path="/home/user/async_test.txt", |
| 35 | + contents="Hello from async operations!\n", |
| 36 | + ) |
| 37 | + content = await devbox.file.read(path="/home/user/async_test.txt") |
| 38 | + print(f"File content: {content.strip()}") |
| 39 | + |
| 40 | + print("Devbox automatically shutdown\n") |
| 41 | + |
| 42 | + |
| 43 | +async def demonstrate_concurrent_commands(): |
| 44 | + """Execute multiple commands concurrently on the same devbox.""" |
| 45 | + print("=== Concurrent Command Execution ===") |
| 46 | + |
| 47 | + sdk = AsyncRunloopSDK() |
| 48 | + |
| 49 | + async with sdk.devbox.create(name="concurrent-commands-devbox") as devbox: |
| 50 | + print(f"Created devbox: {devbox.id}") |
| 51 | + |
| 52 | + # Execute multiple commands concurrently |
| 53 | + async def run_command(cmd: str, label: str): |
| 54 | + print(f"Starting: {label}") |
| 55 | + result = await devbox.cmd.exec(cmd) |
| 56 | + output = await result.stdout() |
| 57 | + print(f"{label} completed: {output.strip()}") |
| 58 | + return output |
| 59 | + |
| 60 | + # Run multiple commands in parallel |
| 61 | + results = await asyncio.gather( |
| 62 | + run_command("echo 'Task 1' && sleep 1", "Task 1"), |
| 63 | + run_command("echo 'Task 2' && sleep 1", "Task 2"), |
| 64 | + run_command("echo 'Task 3' && sleep 1", "Task 3"), |
| 65 | + ) |
| 66 | + |
| 67 | + print(f"All {len(results)} tasks completed\n") |
| 68 | + |
| 69 | + |
| 70 | +async def demonstrate_multiple_devboxes(): |
| 71 | + """Create and manage multiple devboxes concurrently.""" |
| 72 | + print("=== Managing Multiple Devboxes ===") |
| 73 | + |
| 74 | + sdk = AsyncRunloopSDK() |
| 75 | + |
| 76 | + async def create_and_use_devbox(name: str, number: int): |
| 77 | + """Create a devbox, run a command, and return the result.""" |
| 78 | + async with sdk.devbox.create(name=name) as devbox: |
| 79 | + print(f"Devbox {number} ({devbox.id}): Created") |
| 80 | + |
| 81 | + # Run a command |
| 82 | + result = await devbox.cmd.exec(f"echo 'Hello from devbox {number}'") |
| 83 | + output = await result.stdout() |
| 84 | + print(f"Devbox {number}: {output.strip()}") |
| 85 | + |
| 86 | + return output |
| 87 | + |
| 88 | + # Create and use multiple devboxes concurrently |
| 89 | + results = await asyncio.gather( |
| 90 | + create_and_use_devbox("multi-devbox-1", 1), |
| 91 | + create_and_use_devbox("multi-devbox-2", 2), |
| 92 | + create_and_use_devbox("multi-devbox-3", 3), |
| 93 | + ) |
| 94 | + |
| 95 | + print(f"All {len(results)} devboxes completed and shutdown\n") |
| 96 | + |
| 97 | + |
| 98 | +async def demonstrate_async_streaming(): |
| 99 | + """Demonstrate real-time command output streaming with async callbacks.""" |
| 100 | + print("=== Async Command Streaming ===") |
| 101 | + |
| 102 | + sdk = AsyncRunloopSDK() |
| 103 | + |
| 104 | + async with sdk.devbox.create(name="streaming-devbox") as devbox: |
| 105 | + print(f"Created devbox: {devbox.id}") |
| 106 | + |
| 107 | + # Async callback to capture output |
| 108 | + output_lines = [] |
| 109 | + |
| 110 | + async def capture_output(line: str): |
| 111 | + print(f"[STREAM] {line.strip()}") |
| 112 | + output_lines.append(line) |
| 113 | + |
| 114 | + # Execute command with streaming output |
| 115 | + print("\nStreaming command output:") |
| 116 | + await devbox.cmd.exec( |
| 117 | + "for i in 1 2 3 4 5; do echo \"Line $i\"; sleep 0.2; done", |
| 118 | + stdout=capture_output, |
| 119 | + ) |
| 120 | + |
| 121 | + print(f"\nCaptured {len(output_lines)} lines of output\n") |
| 122 | + |
| 123 | + |
| 124 | +async def demonstrate_async_execution(): |
| 125 | + """Demonstrate async execution management.""" |
| 126 | + print("=== Async Execution Management ===") |
| 127 | + |
| 128 | + sdk = AsyncRunloopSDK() |
| 129 | + |
| 130 | + async with sdk.devbox.create(name="async-exec-devbox") as devbox: |
| 131 | + print(f"Created devbox: {devbox.id}") |
| 132 | + |
| 133 | + # Start an async execution |
| 134 | + execution = await devbox.cmd.exec_async( |
| 135 | + "echo 'Starting...'; sleep 2; echo 'Finished!'" |
| 136 | + ) |
| 137 | + print(f"Started execution: {execution.execution_id}") |
| 138 | + |
| 139 | + # Poll execution state |
| 140 | + state = await execution.get_state() |
| 141 | + print(f"Initial status: {state.status}") |
| 142 | + |
| 143 | + # Wait for completion |
| 144 | + print("Waiting for completion...") |
| 145 | + result = await execution.result() |
| 146 | + print(f"Exit code: {result.exit_code}") |
| 147 | + output = await result.stdout() |
| 148 | + print(f"Output:\n{output}") |
| 149 | + |
| 150 | + # Start another execution and kill it |
| 151 | + print("\nStarting long-running process...") |
| 152 | + long_execution = await devbox.cmd.exec_async("sleep 30") |
| 153 | + print(f"Execution ID: {long_execution.execution_id}") |
| 154 | + |
| 155 | + # Wait a bit then kill it |
| 156 | + await asyncio.sleep(1) |
| 157 | + print("Killing execution...") |
| 158 | + await long_execution.kill() |
| 159 | + print("Execution killed\n") |
| 160 | + |
| 161 | + |
| 162 | +async def main(): |
| 163 | + """Run all async demonstrations.""" |
| 164 | + print("Initialized Async Runloop SDK\n") |
| 165 | + |
| 166 | + # Run demonstrations |
| 167 | + await demonstrate_basic_async() |
| 168 | + await demonstrate_concurrent_commands() |
| 169 | + await demonstrate_multiple_devboxes() |
| 170 | + await demonstrate_async_streaming() |
| 171 | + await demonstrate_async_execution() |
| 172 | + |
| 173 | + print("All async demonstrations completed!") |
| 174 | + |
| 175 | + |
| 176 | +if __name__ == "__main__": |
| 177 | + # Ensure API key is set |
| 178 | + if not os.getenv("RUNLOOP_API_KEY"): |
| 179 | + print("Error: RUNLOOP_API_KEY environment variable is not set") |
| 180 | + print("Please set it to your Runloop API key:") |
| 181 | + print(" export RUNLOOP_API_KEY=your-api-key") |
| 182 | + exit(1) |
| 183 | + |
| 184 | + try: |
| 185 | + asyncio.run(main()) |
| 186 | + except Exception as e: |
| 187 | + print(f"\nError: {e}") |
| 188 | + raise |
| 189 | + |
0 commit comments