-
Notifications
You must be signed in to change notification settings - Fork 4
Add subprocess.Popen example #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add tabbed interface showing both subprocess.run and subprocess.Popen approaches - Add "Understanding subprocess.Popen" info section with Python docs reference - Link to OpenHands Software Agent SDK as production example
|
Thanks! but what advantage does that have over |
|
You're right that if we just call The advantages of 1. Agent with parallel executionimport subprocess
from subprocess import PIPE
def execute_action(command: str) -> str:
"""Execute action, supports parallel commands with 'parallel:' prefix"""
# Parallel execution: parallel:npm install && eslint .
if command.startswith("parallel:"):
commands = command[9:].split(" && ")
processes = []
# Start all processes
for cmd in commands:
proc = subprocess.Popen(
cmd.strip(),
shell=True,
stdout=PIPE,
stderr=subprocess.STDOUT,
text=True
)
processes.append((cmd.strip(), proc))
# Wait for all and collect output
results = []
for cmd, proc in processes:
output, _ = proc.communicate()
results.append(f"[{cmd}]\n{output}")
return "\n---\n".join(results)
# Normal blocking command
result = subprocess.run(
command,
shell=True,
text=True,
stdout=PIPE,
stderr=subprocess.STDOUT,
timeout=30,
)
return result.stdoutThe agent could run Output: 2. Agent with persistent shell sessionimport subprocess
from subprocess import PIPE
class PersistentShell:
def __init__(self):
self.proc = subprocess.Popen(
["bash"],
stdin=PIPE,
stdout=PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1
)
def execute(self, command: str) -> str:
end_marker = f"__END_{id(command)}__"
self.proc.stdin.write(f"{command}\n")
self.proc.stdin.write(f"echo '{end_marker}'\n")
self.proc.stdin.flush()
output = []
for line in self.proc.stdout:
if end_marker in line:
break
output.append(line)
return "".join(output)
shell = PersistentShell()
def execute_action(command: str) -> str:
return shell.execute(command)
# cd and env vars persist between commands3. Agent with streaming output (see shell output)import subprocess
from subprocess import PIPE
def execute_action(command: str) -> str:
proc = subprocess.Popen(
command,
shell=True,
stdout=PIPE,
stderr=subprocess.STDOUT,
text=True
)
output_lines = []
for line in proc.stdout:
output_lines.append(line)
print(f"[live] {line}", end="") # Agent sees output as it happens
# React to specific patterns
if "error" in line.lower():
proc.kill()
output_lines.append("\n[Agent: Detected error, stopping process]")
break
if len(output_lines) > 500:
proc.kill()
output_lines.append("\n[Agent: Output too long, stopping]")
break
proc.wait()
return "".join(output_lines)The agent sees For the current minimal agent, subprocess.run() is indeed simpler and sufficient. Popen would make sense if we wanted to add features mentioned above. |
Summary
subprocess.runandsubprocess.Popenapproaches