Validate spawn arguments and return the resolved (processes, process_id).
(
*,
threads: int,
processes: int | None,
first_port: int,
addresses: str | None,
process_id: int | None,
)
| 50 | |
| 51 | |
| 52 | def validate_and_resolve_spawn_args( |
| 53 | *, |
| 54 | threads: int, |
| 55 | processes: int | None, |
| 56 | first_port: int, |
| 57 | addresses: str | None, |
| 58 | process_id: int | None, |
| 59 | ) -> tuple[int, int]: |
| 60 | """Validate spawn arguments and return the resolved (processes, process_id).""" |
| 61 | if threads < 1: |
| 62 | raise click.UsageError("--threads must be at least 1") |
| 63 | if processes is not None and processes < 1: |
| 64 | raise click.UsageError("--processes must be at least 1") |
| 65 | if addresses is not None and processes is not None: |
| 66 | raise click.UsageError( |
| 67 | "--processes and --addresses are mutually exclusive: " |
| 68 | "when --addresses is set, the process count is deduced from the address list" |
| 69 | ) |
| 70 | if addresses is not None: |
| 71 | if process_id is None: |
| 72 | raise click.UsageError("--process-id is required when --addresses is set") |
| 73 | address_list = addresses.split(",") |
| 74 | for addr in address_list: |
| 75 | parts = addr.split(":") |
| 76 | if len(parts) != 2 or not parts[1].isdigit(): |
| 77 | raise click.UsageError( |
| 78 | f"invalid address {addr!r} in --addresses: expected host:port format" |
| 79 | ) |
| 80 | port = int(parts[1]) |
| 81 | if not (1 <= port <= MAX_PORT): |
| 82 | raise click.UsageError( |
| 83 | f"invalid port {port} in --addresses entry {addr!r}: must be in range 1..{MAX_PORT}" |
| 84 | ) |
| 85 | if len(address_list) != len(set(address_list)): |
| 86 | raise click.UsageError("--addresses contains duplicate entries") |
| 87 | processes = len(address_list) |
| 88 | if not (0 <= process_id < processes): |
| 89 | raise click.UsageError( |
| 90 | f"--process-id {process_id} is out of range: " |
| 91 | f"--addresses defines {processes} process(es), valid range is 0..{processes - 1}" |
| 92 | ) |
| 93 | else: |
| 94 | if process_id is not None: |
| 95 | raise click.UsageError("--process-id requires --addresses") |
| 96 | process_id = 0 |
| 97 | processes = processes or 1 |
| 98 | last_port = first_port + processes - 1 |
| 99 | if last_port > MAX_PORT: |
| 100 | raise click.UsageError( |
| 101 | f"--first-port {first_port} with --processes {processes} requires " |
| 102 | f"port {last_port}, which exceeds the maximum of {MAX_PORT}" |
| 103 | ) |
| 104 | return processes, process_id |
| 105 | |
| 106 | |
| 107 | def create_process_handles( |