| 1 | #!/usr/bin/env python3
|
| 2 | """
|
| 3 | Testing child process APIs on Windows
|
| 4 | """
|
| 5 |
|
| 6 | import asyncio
|
| 7 | import sys
|
| 8 | import os
|
| 9 | from typing import List
|
| 10 |
|
| 11 |
|
| 12 | async def run_pipeline_async(commands: List[List[str]]) -> List[int]:
|
| 13 | """Run a pipeline of commands, letting stdout flow naturally, and return exit codes."""
|
| 14 | if not commands:
|
| 15 | return []
|
| 16 |
|
| 17 | exit_codes = []
|
| 18 | stdin = None # Initial stdin is None (inherited)
|
| 19 |
|
| 20 | # Create pipes for connecting processes
|
| 21 | processes = []
|
| 22 |
|
| 23 | # Set up all processes in the pipeline
|
| 24 | for i, cmd in enumerate(commands):
|
| 25 | # For all processes except the last one, create a pipe for stdout
|
| 26 | if i < len(commands) - 1:
|
| 27 | # Create a pipe for this process's stdout to the next process's stdin
|
| 28 | read_fd, write_fd = os.pipe()
|
| 29 | stdout = write_fd
|
| 30 | next_stdin = read_fd
|
| 31 | else:
|
| 32 | # Last process inherits stdout (terminal or parent process stdout)
|
| 33 | stdout = None
|
| 34 | next_stdin = None
|
| 35 |
|
| 36 | # Create the process
|
| 37 | proc = await asyncio.create_subprocess_exec(
|
| 38 | *cmd,
|
| 39 | stdin=stdin,
|
| 40 | stdout=stdout,
|
| 41 | stderr=None # Inherit stderr
|
| 42 | )
|
| 43 |
|
| 44 | processes.append(proc)
|
| 45 |
|
| 46 | # Close write end of pipe in parent process after spawning child
|
| 47 | if i < len(commands) - 1:
|
| 48 | os.close(write_fd)
|
| 49 |
|
| 50 | # Set up for next iteration
|
| 51 | stdin = next_stdin
|
| 52 |
|
| 53 | # Wait for all processes to complete and collect exit codes
|
| 54 | for proc in processes:
|
| 55 | await proc.wait()
|
| 56 | exit_codes.append(proc.returncode)
|
| 57 |
|
| 58 | return exit_codes
|
| 59 |
|
| 60 |
|
| 61 | def RunPipeline(*commands) -> List[int]:
|
| 62 | """Run a pipeline of commands with natural output flow and return exit codes."""
|
| 63 | return asyncio.run(run_pipeline_async(commands))
|
| 64 |
|
| 65 |
|
| 66 | def SubprocessDemo():
|
| 67 | if sys.platform == 'win32':
|
| 68 | a_list = [['dir', 'build'], ['find', 'sh'], ['find', 'o']]
|
| 69 |
|
| 70 | a_list2 = []
|
| 71 | for a in a_list:
|
| 72 | a_list2.append(['cmd.exe', '/c'] + a)
|
| 73 | print(a_list2)
|
| 74 |
|
| 75 | #codes = RunPipeline(a_list2[0], a_list2[1], a_list2[2])
|
| 76 | exit_codes = RunPipeline(*a_list2)
|
| 77 | print(f"Exit codes: {exit_codes}")
|
| 78 | return
|
| 79 |
|
| 80 | # ls build | grep sh | wc -l
|
| 81 | exit_codes = RunPipeline(['ls', 'build'], ['grep', 'sh'], ['grep', 'o'])
|
| 82 | print(f"Exit codes: {exit_codes}")
|
| 83 |
|
| 84 | exit_codes = RunPipeline(['ls', 'build'], ['sh', '-c', 'grep py; exit 42'],
|
| 85 | ['wc', '-l'])
|
| 86 | print(f"Exit codes: {exit_codes}")
|
| 87 |
|
| 88 |
|
| 89 | # Example usage
|
| 90 | if __name__ == "__main__":
|
| 91 | SubprocessDemo()
|