plesty.server.services.supervisor

Start, stop, probe and log the device servers of one bench.

A device server is <env python> -m plesty.<module> <args> — the entry point every hub device ships. The supervisor launches it detached through plesty.server.services.launch (so no plesty-server process ever owns it as a child) with its stdout and stderr appended to <home>/logs/<name>.log, remembers pid and port in <home>/state/<name>.json so a later plesty-server invocation, the agent or the GUI finds it, and judges liveness the way experiments do: a describe request over ZMQ answered with status: ok.

Stopping is graceful first — the interrupt lets plesty-lib close the device session ([SERVER] Shutting down...) — and escalates to a kill only after stop_timeout seconds. Every platform difference on the way (how to interrupt, how to kill, how to tell a pid is alive) is a call into plesty.server.host.

Attributes

PROBE_HOST

_START_LOCKS

_LOCKS_GUARD

Exceptions

SupervisorError

A start or stop request could not be carried out.

AlreadyRunning

start was asked for a device whose server is already up.

NotRunning

stop was asked for a device that has no live server.

PortBusy

start found the device's port already answering — another server holds it.

Classes

Supervisor

Launch and control device servers under a Home.

Functions

_start_lock(→ threading.Lock)

probe(→ bool)

Ask the server on port to describe itself.

request(→ dict[str, Any])

Send one request to the server on port and return its reply.

_now(→ str)

Module Contents

plesty.server.services.supervisor.PROBE_HOST = '127.0.0.1'
exception plesty.server.services.supervisor.SupervisorError

Bases: RuntimeError

A start or stop request could not be carried out.

Initialize self. See help(type(self)) for accurate signature.

exception plesty.server.services.supervisor.AlreadyRunning

Bases: SupervisorError

start was asked for a device whose server is already up.

Initialize self. See help(type(self)) for accurate signature.

exception plesty.server.services.supervisor.NotRunning

Bases: SupervisorError

stop was asked for a device that has no live server.

Initialize self. See help(type(self)) for accurate signature.

exception plesty.server.services.supervisor.PortBusy

Bases: SupervisorError

start found the device’s port already answering — another server holds it.

Initialize self. See help(type(self)) for accurate signature.

plesty.server.services.supervisor._START_LOCKS: dict[str, threading.Lock]
plesty.server.services.supervisor._LOCKS_GUARD
plesty.server.services.supervisor._start_lock(name: str) threading.Lock
Parameters:

name (str)

Return type:

threading.Lock

plesty.server.services.supervisor.probe(port: int, timeout_ms: int = 2000, host_addr: str = PROBE_HOST) bool

Ask the server on port to describe itself.

Returns:

True when a plesty-lib server answered status: ok in time.

Parameters:
  • port (int)

  • timeout_ms (int)

  • host_addr (str)

Return type:

bool

plesty.server.services.supervisor.request(port: int, message: dict[str, Any], timeout_ms: int = 5000, host_addr: str = PROBE_HOST) dict[str, Any]

Send one request to the server on port and return its reply.

The same DEALER socket probe() uses, with the message left to the caller: this is how a developer asks a running device server anything its plesty-lib command solver understands, without a client library.

Raises:

SupervisorError – When nobody answers in time, or the answer is not JSON the wire format allows.

Parameters:
  • port (int)

  • message (dict[str, Any])

  • timeout_ms (int)

  • host_addr (str)

Return type:

dict[str, Any]

class plesty.server.services.supervisor.Supervisor(home: plesty.server.model.home.Home, ready_timeout: float | None = None, stop_timeout: float | None = None, probe_timeout_ms: int | None = None)

Launch and control device servers under a Home.

Bind the supervisor to a home.

Parameters:
  • home (plesty.server.model.home.Home) – The bench home.

  • ready_timeout (float | None) – Seconds start() waits for the first probe answer.

  • stop_timeout (float | None) – Seconds stop() waits before escalating.

  • probe_timeout_ms (int | None) – Milliseconds one probe waits for its reply.

  • running.) ((Defaults come from config.yaml)

home
ready_timeout
stop_timeout
probe_timeout_ms
record(name: str) plesty.server.model.process.ProcessRecord | None

The persisted launch record of name, or None.

Parameters:

name (str)

Return type:

plesty.server.model.process.ProcessRecord | None

exit_file(name: str) pathlib.Path

Where the launcher records the exit code of the server of name.

Parameters:

name (str)

Return type:

pathlib.Path

known() list[str]

Names that have a launch record (alive or not).

Return type:

list[str]

_drop_record(name: str) None
Parameters:

name (str)

Return type:

None

alive(name: str) bool

Whether the recorded server of name still runs.

Parameters:

name (str)

Return type:

bool

poll(name: str) int | None

Exit code of the server of name, None while it runs.

LOST_EXIT when the process is gone without a recorded exit (the bench rebooted, or someone killed the launcher) — and when nothing is known at all.

Parameters:

name (str)

Return type:

int | None

status(name: str, probe_port: bool = True) plesty.server.model.process.ProcessStatus

Observe name: record, process existence and (optionally) a probe.

Parameters:
  • name (str) – Device instance name.

  • probe_port (bool) – Whether to send a describe to tell running from starting; skipping it is cheaper for bulk listings.

Return type:

plesty.server.model.process.ProcessStatus

wait_ready(name: str, timeout: float | None = None) bool

Poll until the server of name answers a probe.

Returns:

True once it answers; False on timeout or when the process exits first.

Parameters:
  • name (str)

  • timeout (float | None)

Return type:

bool

start(spec: plesty.server.model.device.DeviceSpec, python: pathlib.Path, wait: bool = True, timeout: float | None = None, package: str = '', version: str = '') plesty.server.model.process.ProcessStatus

Launch the server of spec with the interpreter python.

Parameters:
  • spec (plesty.server.model.device.DeviceSpec) – Module, arguments, environment and port to run with.

  • python (pathlib.Path) – Interpreter of the environment the module is installed in.

  • wait (bool) – Block until the server answers a probe (or fails).

  • timeout (float | None) – Readiness wait in seconds; defaults to ready_timeout.

  • package (str) – Distribution name, stored on the record for listings.

  • version (str) – Installed version, likewise.

Returns:

The status after launch — running when it came up, starting when wait is off or it has not answered yet, exited when it died before answering (detail carries the log tail).

Raises:
Return type:

plesty.server.model.process.ProcessStatus

_start_locked(spec: plesty.server.model.device.DeviceSpec, python: pathlib.Path, wait: bool, timeout: float | None, package: str, version: str) plesty.server.model.process.ProcessStatus
Parameters:
Return type:

plesty.server.model.process.ProcessStatus

stop(name: str, timeout: float | None = None) bool

Interrupt the server of name, escalating to a kill if it lingers.

Interrupt → wait timeout → terminate → wait 3 s → kill tree.

Returns:

True when a process was stopped, False when only a stale record was cleared.

Raises:

NotRunning – When nothing (not even a stale record) is known.

Parameters:
  • name (str)

  • timeout (float | None)

Return type:

bool

restart(spec: plesty.server.model.device.DeviceSpec, python: pathlib.Path, wait: bool = True, timeout: float | None = None) plesty.server.model.process.ProcessStatus

Stop the server of spec if it runs, then start() it again.

Parameters:
Return type:

plesty.server.model.process.ProcessStatus

relaunch(record: plesty.server.model.process.ProcessRecord, wait: bool = True) plesty.server.model.process.ProcessStatus

Start the server again exactly as record says it was started.

Used after a host-tier field test: the interpreter, arguments and environment of the interrupted launch are what the bench expects back.

Parameters:
Return type:

plesty.server.model.process.ProcessStatus

stop_all(names: list[str] | None = None) list[str]

Stop every known (or the given) server; returns the names stopped.

Parameters:

names (list[str] | None)

Return type:

list[str]

tail(name: str, lines: int = 50) str

The last lines lines of the log of name ("" when no log).

Parameters:
  • name (str)

  • lines (int)

Return type:

str

_wait_exit(name: str, timeout: float) bool
Parameters:
  • name (str)

  • timeout (float)

Return type:

bool

plesty.server.services.supervisor._now() str
Return type:

str