plesty.server.services.launch
Detached process launch: no zombies, and an exit code every process can read.
Several plesty-server processes look at the same servers and jobs — the
CLI, the agent, fleet run, the GUI. A child that its parent never reaps
is a zombie: kill(pid, 0) still succeeds, so a different process takes
it for alive and waits a full stop escalation for nothing. And a plain
detached child loses its exit code for everyone but its parent.
So nothing here is a direct child. launch() spawns a small waiter
(python -m plesty.server.services.launch) that detaches itself
(plesty.server.host.detach()), starts the real command with its output
appended to the log, forwards the interrupt it receives, waits, and writes
the exit code to exit_file. Callers track the waiter’s pid: alive means the
command runs; the exit file appears when it is over.
Attributes
Classes
Copy a child's output into the current log, and move to another on request. |
Functions
|
The file that asks the waiter of exit_file to stop its command. |
|
The file that asks the waiter of exit_file to write to another log. |
|
Ask a relaying waiter to write to log from now on; |
|
executable as an absolute path, found on the |
|
Start command detached; return the pid of the waiter that owns it. |
|
The exit code recorded in exit_file, or |
|
The waiter: run the command, forward the interrupt, record the exit code. |
|
|
|
|
|
Entry point of the waiter (spec as JSON on stdin). |
Module Contents
- plesty.server.services.launch.stop_file(exit_file: str | os.PathLike[str]) pathlib.Path
The file that asks the waiter of exit_file to stop its command.
Alongside the exit file, so anything that knows where a command reports its end also knows how to ask it to end (see
plesty.server.host.request_stop()).- Parameters:
exit_file (str | os.PathLike[str])
- Return type:
pathlib.Path
- plesty.server.services.launch.switch_file(exit_file: str | os.PathLike[str]) pathlib.Path
The file that asks the waiter of exit_file to write to another log.
Its content is the path of the new log. The waiter opens it, writes there from then on, and removes this file to say it has (see
request_switch()).- Parameters:
exit_file (str | os.PathLike[str])
- Return type:
pathlib.Path
- plesty.server.services.launch.SWITCH_POLL_S = 0.2
- plesty.server.services.launch.request_switch(exit_file: str | os.PathLike[str], log: str | os.PathLike[str], timeout: float = 5.0) bool
Ask a relaying waiter to write to log from now on;
Trueonce it has.A file rather than a signal, for the reason the stop is one: on Windows nothing outside the waiter’s console can signal it.
- Parameters:
exit_file (str | os.PathLike[str])
log (str | os.PathLike[str])
timeout (float)
- Return type:
bool
- plesty.server.services.launch._resolve(executable: str, env: collections.abc.Mapping[str, str]) str
executable as an absolute path, found on the
PATHenv carries.A bare name has to be resolved here because Windows will not do it against the environment the child is given:
CreateProcesssearches the calling process’sPATH. Handing it an absolute path settles the question on both platforms and removes the difference rather than branching on it.A name that resolves to nothing is returned unchanged, so the failure is the same
OSErrorfrom the launcher that it always was, naming the command the operator typed rather than a path they never wrote.- Parameters:
executable (str) –
command[0]— a bare name, or already a path.env (collections.abc.Mapping[str, str]) – The environment the command will run with.
- Return type:
str
- plesty.server.services.launch.launch(command: collections.abc.Sequence[str], cwd: str | os.PathLike[str], env: collections.abc.Mapping[str, str], log: str | os.PathLike[str], exit_file: str | os.PathLike[str], banner: str = '', relay: bool = False) int
Start command detached; return the pid of the waiter that owns it.
- Parameters:
command (collections.abc.Sequence[str]) – The command line.
cwd (str | os.PathLike[str]) – Working directory.
env (collections.abc.Mapping[str, str]) – Full environment for the command.
log (str | os.PathLike[str]) – File its stdout+stderr are appended to.
exit_file (str | os.PathLike[str]) – File the exit code is written to when it ends (removed first, so its presence means “finished”).
banner (str) – A line written to the log before this function returns, so a caller may read the log on the pid it is handed.
relay (bool) – The waiter copies the command’s output into the log rather than handing the command the file, so the log can be switched to another file while it runs (
request_switch()). For a device server, whose log is cleared while it runs; a job’s log is never switched and keeps the direct handle.
- Raises:
OSError – When the waiter itself cannot be started.
RuntimeError – When the waiter did not report a pid.
- Return type:
int
- plesty.server.services.launch.READ_EXIT_TRIES = 5
- plesty.server.services.launch.READ_EXIT_PAUSE_S = 0.02
- plesty.server.services.launch.read_exit(exit_file: str | os.PathLike[str]) int | None
The exit code recorded in exit_file, or
Nonewhile it cannot be read yet.The waiter writes the code to a temporary file and moves it into place (
_write_exit()). On Windows, opening the file while that move is under way is refused withPermissionError— a supervisor checking a stopping device at that moment raised it instead of answering (found on the project’s own Windows runner). The move takes milliseconds, so the read is tried again briefly, and a file still refused is reported as not there yet: every caller polls again.- Parameters:
exit_file (str | os.PathLike[str])
- Return type:
int | None
- plesty.server.services.launch._wait(spec: dict[str, Any]) int
The waiter: run the command, forward the interrupt, record the exit code.
- Parameters:
spec (dict[str, Any])
- Return type:
int
- class plesty.server.services.launch._Relay(log: str, child: subprocess.Popen[bytes], request: pathlib.Path)
Copy a child’s output into the current log, and move to another on request.
The log used to be the child’s own stdout. Clearing it then had to truncate a file the server kept writing at its old offset: the gap filled with NULs, and an 11.7 MB log with no line breaks hung every reader (#23). Here the waiter holds the file, so it can close one and open the next between two reads, and nothing is ever truncated.
- Parameters:
log (str)
child (subprocess.Popen[bytes])
request (pathlib.Path)
- path
- _file
- _lock
- _done
- _request
- _reader
- _copy(child: subprocess.Popen[bytes]) None
- Parameters:
child (subprocess.Popen[bytes])
- Return type:
None
- _watch() None
- Return type:
None
- finish() str
Drain what the child left, close the file, and return the log in use.
- Return type:
str
- plesty.server.services.launch._write_exit(exit_file: str, code: int) None
- Parameters:
exit_file (str)
code (int)
- Return type:
None
- plesty.server.services.launch._report_pid(pid: int) None
- Parameters:
pid (int)
- Return type:
None
- plesty.server.services.launch.main() int
Entry point of the waiter (spec as JSON on stdin).
Reached as
python -m plesty.server.services.launchfrom a source checkout and asplesty-server _launchfrom a bundle.- Return type:
int