plesty.server.services
Services: the model’s side effects on the host.
Everything that touches the operating system — uv and git for
installing, subprocesses for running servers and jobs — lives here. Services
take model objects in and hand model objects back; presenters drive them and
never call the tools directly, so a service is the one place a tool’s quirks
are handled and the one seam tests replace.
Submodules
- plesty.server.services.agent
- plesty.server.services.catalogue
- plesty.server.services.control
- plesty.server.services.experiments
- plesty.server.services.gitbin
- plesty.server.services.installer
- plesty.server.services.jobs
- plesty.server.services.launch
- plesty.server.services.nodes
- plesty.server.services.problem_report
- plesty.server.services.run_report
- plesty.server.services.supervisor
- plesty.server.services.updates
Attributes
Exceptions
No |
|
|
|
The catalogue could not be fetched or read. |
|
Raised when an experiment cannot be read, started or driven. |
|
A node could not be reached, or refused what it was asked. |
|
|
|
|
|
|
|
A start or stop request could not be carried out. |
Classes
Install, update and remove device environments; records them in the manager. |
|
What a repository on disk says it is, in its |
|
Serve |
|
The hub catalogue at one index URL. |
|
A lazy |
|
Start, observe and cancel jobs under a |
|
One device of a node, on a connection this handle owns. |
|
This master's link to one node's agent. |
|
Every node a master drives, one |
|
Launch and control device servers under a |
Functions
|
Where the bench keeps its own git (whether or not one is there yet). |
|
A usable |
|
Unpack a portable git into |
|
Run cmd, streaming its merged output to echo as it arrives. |
|
Read what the repository at root is, and refuse what a bench cannot run. |
|
Run agent on a daemon thread; returns the thread and its stop event. |
|
GET url with the standard library (the bench may have no |
|
The newest release of package on PyPI, or |
|
Every release of package on PyPI, newest first; none when it has none or PyPI is out. |
|
A sort key for release versions: a snapshot or pre-release before its release. |
|
The release tags ( |
|
Amend a run's control file and return what it now asks for. |
|
Return what a run is currently being asked for. |
|
Remove a run's control file, so a resumed run is asked for nothing. |
|
Return text if it is usable as a run directory name, else raise. |
|
Append a note to the run's |
|
Name a run before it is started, recording what it will be run with. |
|
Return what the console recorded when the run was created, if anything. |
|
Return the run directory run_id names, or the newest one. |
|
The run's notes, oldest first: each |
|
Whether the run has started: its configuration is frozen into its plan then. |
|
Copy a job's log into the run it ran, under |
|
The newest job log run_dir keeps, or |
|
Return the newest run of run_root, or |
|
Return what the step in flight last said about itself. |
|
Ask the module which devices of its |
|
Ask the module what it takes, and return what it said. |
|
Replace the configuration of a run that has not started. |
|
Return the newest committed step records. |
|
Give a run that has not started another name, which is its directory's. |
|
The run's own report, or |
|
Return the id of the newest run that could be continued, if any. |
|
Build the environment a run gets. |
|
Return this experiment's run directories, newest first. |
|
Return what a run's journal says about it, by replaying the journal here. |
|
Return the plan's steps, so a status can name the one in flight. |
|
Ask the server on port to describe itself. |
|
Send one request to the server on port and return its reply. |
Package Contents
Bases:
RuntimeErrorNo
giton the bench, and none could be installed.Initialize self. See help(type(self)) for accurate signature.
- plesty.server.services.bench_git(home: plesty.server.model.home.Home) pathlib.Path
Where the bench keeps its own git (whether or not one is there yet).
- Parameters:
- Return type:
pathlib.Path
- plesty.server.services.find_git(home: plesty.server.model.home.Home) str | None
A usable
git:PATHfirst, then the usual installs, then the bench’s own.- Parameters:
home (plesty.server.model.home.Home) – The bench home, which may hold a git of its own.
- Returns:
The executable, or
Nonewhen the bench has none.- Return type:
str | None
- plesty.server.services.install_git(home: plesty.server.model.home.Home, echo: Echo | None = None, fetch: Fetcher | None = None) str
Unpack a portable git into
<home>/tools/git; returns the executable.Windows only: it is the platform where a bench routinely has no git and no way to install one. The archive is verified against the pinned digest before anything is unpacked — a mismatch is an error, not a warning.
- Parameters:
home (plesty.server.model.home.Home) – The bench home to install into.
echo (Echo | None) – Where progress lines go.
fetch (Fetcher | None) – Reads the archive URL; injected by tests.
- Raises:
GitUnavailable – Not Windows, unknown architecture, download failed, digest mismatch, or the archive held no
git.- Return type:
str
- plesty.server.services.CHECKOUT_VENV = '.venv'
- plesty.server.services.LOADABLE_TYPES
- plesty.server.services.PYTHON_SPEC = '>=3.12'
- plesty.server.services.Echo
- class plesty.server.services.Installer(home: plesty.server.model.home.Home, manager: plesty.server.model.device_manager.DeviceManager | None = None, uv: str | None = None, git: str | None = None, runner: Runner | None = None, echo: Echo | None = None)
Install, update and remove device environments; records them in the manager.
Bind the installer to a home.
- Parameters:
home (plesty.server.model.home.Home) – The bench home.
manager (plesty.server.model.device_manager.DeviceManager | None) – The inventory to record into; a fresh one on home by default.
uv (str | None) – Path of the
uvexecutable; looked up onPATHby default.git (str | None) – Path of the
gitexecutable; found (or installed) by default.runner (Runner | None) – Executes a command line — injected by tests.
echo (Echo | None) – Where each line
uvandgitwrite goes while they run. Defaults to stdout, which is the job log when run as a job.
- home
- manager
- _uv = None
- _git = None
- _run
- property uv: str
The
uvexecutable; raisesInstallErrorwhen the bench has none.- Return type:
str
- _find_uv() str
PATHfirst, then where uv installs itself (find_uv()).- Return type:
str
- property git: str
The
gitexecutable, installing one for the bench when it has none.- Raises:
InstallError – When the bench has no git and cannot be given one.
- Return type:
str
- _on_path(git: str) str
Put git’s directory on
PATHfor this process and its children.Calling git by absolute path is enough for the installer’s own clone and checkout — and not enough for the install. A module’s dependencies may be
git+httpsURLs, and theuvthis spawns to resolve them looks for git onPATHand nowhere else: on the very bench this feature exists for, one with no git at all, the install would fetch a portable git, clone with it, and then fail inuv syncwith “Git executable not found”. The same holds for a git found under%ProgramFiles%but absent fromPATH.- Parameters:
git (str)
- Return type:
str
- _find_or_install_git() str
Locate git, or fetch a portable one (Windows); explains itself either way.
- Return type:
str
- local_branches(root: pathlib.Path) list[str]
The local branches of the repository at root; empty when it has none or no git.
- Parameters:
root (pathlib.Path)
- Return type:
list[str]
- local_branch(root: pathlib.Path) str
The branch the repository at root is on;
""when detached or not git.- Parameters:
root (pathlib.Path)
- Return type:
str
- python(installed: plesty.server.model.device_manager.InstalledVersion) pathlib.Path
The interpreter that runs installed.
- Parameters:
installed (plesty.server.model.device_manager.InstalledVersion)
- Return type:
pathlib.Path
- load_local(folder: str | os.PathLike[str], ref: str | None = None) plesty.server.model.device_manager.InstalledVersion
Put a repository already on disk onto the bench, where it lives.
Nothing is copied or moved: the bench records the folder, what the module is, the version its environment holds and the commit it is on, under
<home>/packages/<package>/, and runs it with the folder’s own.venv— the one the developer’suv runuses. When there is none yet,uv syncmakes it; one that is there is never re-synced, so the developer’s tools in it stay.Loading the same folder again refreshes the record: a new commit, a bumped version.
- Parameters:
folder (str | os.PathLike[str]) – The repository’s root, holding its
pyproject.toml.ref (str | None) – A branch to check out in the folder first; the one it is on when
None. Refused over uncommitted changes, which a switch would carry onto the other branch.
- Returns:
The record.
- Raises:
InstallError – When the folder is not a module this bench can run, the branch cannot be checked out, or the environment cannot be made.
- Return type:
- _seeded(installed: plesty.server.model.device_manager.InstalledVersion) plesty.server.model.device_manager.InstalledVersion
Give the checkout of installed a
.envline for each variable its setup names.An experiment’s
setup.yamlnames the variables holding where each bench is, its token and where it sees the shared disk; the.envbeside it holds their values, set in the console. A value already there is kept. A module with no setup is left as it is.- Parameters:
installed (plesty.server.model.device_manager.InstalledVersion)
- Return type:
- install(spec: plesty.server.model.device.DeviceSpec, force: bool = False, job_id: str | None = None) plesty.server.model.device_manager.InstalledVersion
Make sure an environment serving spec exists; returns its record.
- Parameters:
spec (plesty.server.model.device.DeviceSpec) – What to install: package plus a pinned version (PyPI) or a repository and ref (git checkout).
force (bool) – Reinstall even when a satisfying environment is on record.
job_id (str | None) – The job this runs as, stored on the record.
- Raises:
InstallError – When
uv/gitfail or the package did not end up installed.- Return type:
- _install_the_tag_instead(spec: plesty.server.model.device.DeviceSpec, job_id: str | None, failure: InstallError) plesty.server.model.device_manager.InstalledVersion
After a failed release install, install the same version from the repository.
A module is on the hub before it is on PyPI: the registry knows the release from its tag, and the package reaches the index only once the release job has run — so “that release is not on the index” is a routine state of a young module, not a broken bench. The repository holds the same version under
v<version>, which is the ref a spec with a version already resolves to.- Raises:
InstallError – When no repository is known for the package, or the repository has no such tag either — carrying both reasons.
- Parameters:
job_id (str | None)
failure (InstallError)
- Return type:
- update(installed: plesty.server.model.device_manager.InstalledVersion, ref: str | None = None, job_id: str | None = None) plesty.server.model.device_manager.InstalledVersion
Bring installed up to date; returns the (possibly new) record.
Git checkout: fetch, check out ref (default: the recorded ref), fast-forward,
uv sync. PyPI: reinstall the same version. Local repository: read it again — it is the developer’s to pull and sync.- Parameters:
installed (plesty.server.model.device_manager.InstalledVersion) – The environment to refresh.
ref (str | None) – Branch, tag or commit to move a checkout to.
job_id (str | None) – The job this runs as.
- Return type:
- uninstall(installed: plesty.server.model.device_manager.InstalledVersion) bool
Delete the environment of installed and forget it.
- Returns:
Truewhen a directory was removed.- Parameters:
installed (plesty.server.model.device_manager.InstalledVersion)
- Return type:
bool
- _install_release(spec: plesty.server.model.device.DeviceSpec, job_id: str | None) plesty.server.model.device_manager.InstalledVersion
- Parameters:
job_id (str | None)
- Return type:
- _install_checkout(spec: plesty.server.model.device.DeviceSpec, job_id: str | None) plesty.server.model.device_manager.InstalledVersion
- Parameters:
job_id (str | None)
- Return type:
- _uv_call(args: list[str], what: str, quiet: bool = False, venv: pathlib.Path | None = None, creates: pathlib.Path | None = None) subprocess.CompletedProcess[str]
Run
uv, waiting out a scanner that is holding a file it just wrote.uv writes every console script as an
.exeand reopens it at once to patch its resources. An on-access scanner opening that new executable first makes the reopen fail, and uv reports the whole wheel as uninstallable — so an install dies on whichever dependency ships the most scripts, which is why the wheel named in the error moves around (PySide6 on one bench, Sphinx on another) and why it is nothing to do with the module being installed.The wait is the fix: nothing here can make the scanner let go, and the file is fine once it does. Only this failure is retried, and only a few times — a scanner that never releases is a bench problem, and saying so beats retrying until the job looks hung.
A command that builds venv and was interrupted can leave it without
VENV_CONFIG; the retry then failed “not a valid Python environment” every time (#45). Such a.venvis removed before each retry, as_sync_checkout()removes one before its first attempt.- Parameters:
args (list[str])
what (str)
quiet (bool)
venv (pathlib.Path | None)
creates (pathlib.Path | None)
- Return type:
subprocess.CompletedProcess[str]
- _clear_bench_reports(checkout: pathlib.Path) None
Move the bench’s own
reports/files out of the way of the commits a pull brings.A field test writes its report into the checkout, and the procedure then commits that same file to the module. The next pull refused to overwrite the bench’s copy — untracked, or changed by a later run — so the device kept its old code until someone deleted the file by hand on the bench PC (#37). A copy identical to the incoming one is dropped; any other is kept beside it as
<name>.bench-<time><suffix>, said in the log, so no result of a run is lost.Only
reports/is touched — the folder the bench writes. Anything else in the way is the developer’s and still stops the pull.- Parameters:
checkout (pathlib.Path)
- Return type:
None
- _sync_checkout(checkout: pathlib.Path, package: str) None
uv sync --no-devcheckout, rebuilding its.venvif that stops it.The module itself is rebuilt every time (
--reinstall-package). It is installed editable, so its code follows the pull anyway, but uv keeps the version metadata of the first build: after an update the bench,listand a field-test report all named the commit it was first installed from (#36).The environment holds what the device needs to run and nothing else.
tests/field_test.pyis unaffected — it importsplesty.liband the module, never pytest. What no longer works in a fresh checkout is the authoring toolchain:plesty check,plesty initandpytestin the Terminal tab have nothing to run, becauseplesty-sdklives in the dev group. Someone who wants them canuv syncin the checkout by hand and get them back.uv refuses a
.venvthat exists without an interpreter in it rather than replacing it, and nothing removes it, so every later sync fails the same way: one interrupted install wedges the module until someone deletes the directory by hand — which is what a bench operator has no console for, and what the message does not tell them to do. The interruption can be almost anything (a stop mid-install, a scanner taking the interpreter, an uv-managed Python that went away), so this recovers from the state rather than from any one cause.Deleting it is safe: uv owns the directory and rebuilds it from the lock. Only this failure is retried — any other sync error is the module’s own and is raised unchanged.
A
.venvwithoutVENV_CONFIGis removed before the sync, and one still without it afterwards fails the install: its interpreter is the shared one, and an install that “succeeds” into it is worse than one that stops.- Parameters:
checkout (pathlib.Path)
package (str)
- Return type:
None
- _uv_resolve(args: list[str], what: str, venv: pathlib.Path | None = None) subprocess.CompletedProcess[str]
Run a resolving
uvcommand, retrying once with pre-releases allowed.A hub module under development depends on a
.devrelease of plesty-lib, and uv considers only stable versions unless it is told otherwise — it fails with a hint naming the pre-release. Allowing them from the start would pull a pre-release into every install that has a stable answer, so it is the retry, not the rule.- Parameters:
args (list[str])
what (str)
venv (pathlib.Path | None)
- Return type:
subprocess.CompletedProcess[str]
- _git_call(args: list[str], what: str, quiet: bool = False) subprocess.CompletedProcess[str]
- Parameters:
args (list[str])
what (str)
quiet (bool)
- Return type:
subprocess.CompletedProcess[str]
- _call(cmd: list[str], what: str, quiet: bool = False) subprocess.CompletedProcess[str]
Run cmd; quiet keeps a query’s output out of the log (it is read, not watched).
- Parameters:
cmd (list[str])
what (str)
quiet (bool)
- Return type:
subprocess.CompletedProcess[str]
- _installed_version(python: pathlib.Path, package: str) str | None
- Parameters:
python (pathlib.Path)
package (str)
- Return type:
str | None
- _python_version(python: pathlib.Path) str
- Parameters:
python (pathlib.Path)
- Return type:
str
- exception plesty.server.services.InstallError
Bases:
RuntimeErroruv/gitfailed, or the environment is not in the shape expected.Initialize self. See help(type(self)) for accurate signature.
- class plesty.server.services.LocalModule
What a repository on disk says it is, in its
pyproject.toml.- Variables:
package –
[project] name.module_type –
[tool.plesty] module_type—device,experiment, …description –
[project] description.
- package: str
- module_type: str
- description: str = ''
- property category: str
devicesorexperiments.- Type:
The catalogue category it belongs to
- Return type:
str
- plesty.server.services.Runner
- plesty.server.services.default_runner(cmd: list[str], echo: Echo | None = None) subprocess.CompletedProcess[str]
Run cmd, streaming its merged output to echo as it arrives.
stderr is folded into stdout so the order the tool wrote them in survives; the text is returned as well, so a failure still carries its own detail.
- Parameters:
cmd (list[str])
echo (Echo | None)
- Return type:
subprocess.CompletedProcess[str]
- plesty.server.services.local_module(root: pathlib.Path) LocalModule
Read what the repository at root is, and refuse what a bench cannot run.
- Raises:
InstallError – When there is no
pyproject.toml, it names no project, or it is not a device or experiment module.- Parameters:
root (pathlib.Path)
- Return type:
- class plesty.server.services.Agent(home: plesty.server.model.home.Home, fleet_path: str | os.PathLike[str] | None = None, bind: str = DEFAULT_BIND, token: str | None = None, workers: int = 4)
Serve
plesty.benchclients for one bench.Configure the agent.
- Parameters:
home (plesty.server.model.home.Home) – The bench home every command runs against.
fleet_path (str | os.PathLike[str] | None) – Fleet file passed to every command (
-f), orNone.bind (str) – ZMQ endpoint to bind.
token (str | None) – Shared secret;
NonereadsPLESTY_BENCH_TOKEN; empty means no check (a bench on a trusted network).workers (int) – Concurrent requests.
- home
- fleet_path
- bind = 'tcp://*:5550'
- token
- workers = 4
- bound: str | None = None
- served = 0
- device_timeout_ms = 15000
- handle(message: Any) dict[str, Any]
Answer one decoded request (pure; no socket involved).
- Parameters:
message (Any)
- Return type:
dict[str, Any]
- description() dict[str, Any]
What
describereturns.- Return type:
dict[str, Any]
- exec(argv: collections.abc.Sequence[Any]) dict[str, Any]
Run
plesty-server <argv>in-process against this bench; capture everything.- Parameters:
argv (collections.abc.Sequence[Any])
- Return type:
dict[str, Any]
- device(name: str, request: Any) dict[str, Any]
Relay one device-server request to the server of name, and its reply back.
Why a request type of its own, when the rule is “the CLI is the API”: a control panel is a long-lived session of many small round trips — a poll per parameter per second — and
execwould spawn the click application for each one. This carries the device wire itself, so the panel a console opens locally is the same panel driven from a laptop.Why relayed at all, when device servers bind
tcp://*and could be reached directly: they check no token. The agent does, so the bench keeps one authenticated door instead of one per instrument.The reply is passed through as the device server framed it, so an error from the instrument reaches the caller as its own error, not as a relay failure.
- Parameters:
name (str) – The declared device whose server to reach.
request (Any) – The device-server request (
query,write,call,describe,help) to forward verbatim.
- Returns:
The reply envelope to send back to the client.
- Raises:
ValueError – When the name or the request is missing or malformed.
- Return type:
dict[str, Any]
- fetch(path: str) dict[str, Any]
Read a file under the bench home; path is relative to it.
- Parameters:
path (str)
- Return type:
dict[str, Any]
- serve(stop: threading.Event, poll_ms: int = 100, max_replies: int | None = None) int
Bind and answer requests until stop is set; returns the number answered.
- Parameters:
stop (threading.Event) – Set to end the loop.
poll_ms (int) – Socket poll interval.
max_replies (int | None) – Stop once this many replies went out (tests).
- Return type:
int
- plesty.server.services.serve_in_thread(agent: Agent, stop: threading.Event | None = None) tuple[threading.Thread, threading.Event]
Run agent on a daemon thread; returns the thread and its stop event.
- Parameters:
agent (Agent)
stop (threading.Event | None)
- Return type:
tuple[threading.Thread, threading.Event]
- class plesty.server.services.Catalogue(index_url: str | None = None, fetch: Fetcher | None = None)
The hub catalogue at one index URL.
Bind to an index.
- Parameters:
index_url (str | None) –
/api/modules/endpoint; the public hub by default.fetch (Fetcher | None) – Reads a URL; injected by tests.
- index_url = 'https://hub.plesty.net/api/modules/'
- _fetch
- _cache: list[plesty.server.model.catalogue.CatalogueEntry] | None = None
- entries(refresh: bool = False) list[plesty.server.model.catalogue.CatalogueEntry]
Every module the index lists (fetched once, then cached).
- Raises:
CatalogueError – When the index cannot be fetched or is not JSON.
- Parameters:
refresh (bool)
- Return type:
- search(query: str = '', vendor: str = '', category: str = 'devices', standard: str = REQUIRED_STANDARD, refresh: bool = False) list[plesty.server.model.catalogue.CatalogueEntry]
Entries matching query, filtered by vendor, category and standard (
""= any).refresh asks the index again instead of answering from the cache — what a module published while the bench runs needs to be seen.
- Parameters:
query (str)
vendor (str)
category (str)
standard (str)
refresh (bool)
- Return type:
- vendors(category: str = 'devices') list[str]
The vendor groups present in category.
- Parameters:
category (str)
- Return type:
list[str]
- lookup(package: str) plesty.server.model.catalogue.CatalogueEntry | None
The entry named package (any category, any standard), or
None.- Parameters:
package (str)
- Return type:
- exception plesty.server.services.CatalogueError
Bases:
RuntimeErrorThe catalogue could not be fetched or read.
Initialize self. See help(type(self)) for accurate signature.
- plesty.server.services.http_fetch(url: str, timeout: float = 15.0) bytes
GET url with the standard library (the bench may have no
requests).- Parameters:
url (str)
timeout (float)
- Return type:
bytes
- plesty.server.services.pypi_release(package: str, fetch: Fetcher | None = None) str | None
The newest release of package on PyPI, or
Nonewhen it has none there.- Parameters:
package (str) – Distribution name.
fetch (Fetcher | None) – Reads a URL; injected by tests.
- Return type:
str | None
- plesty.server.services.pypi_releases(package: str, fetch: Fetcher | None = None) tuple[str, Ellipsis]
Every release of package on PyPI, newest first; none when it has none or PyPI is out.
A release whose files were all yanked is left out: uv would refuse to install it unpinned, and offering it pinned offers what its author withdrew.
- Parameters:
package (str) – Distribution name.
fetch (Fetcher | None) – Reads a URL; injected by tests.
- Return type:
tuple[str, Ellipsis]
- plesty.server.services.release_order(version: str) tuple[tuple[int, Ellipsis], int, int]
A sort key for release versions: a snapshot or pre-release before its release.
Plain string or chunk-wise order puts
0.5.0.dev6above0.5.0and0.10.0below0.9.0. Trailing zeros are dropped, so1.0and1.0.0are the same release. Anything unparseable sorts last.- Parameters:
version (str)
- Return type:
tuple[tuple[int, Ellipsis], int, int]
- plesty.server.services.remote_tags(git: str, url: str, timeout: float = 20.0) tuple[str, Ellipsis]
The release tags (
v0.3.1) of the repository at url, newest first.One
git ls-remote; none when it fails or takes longer than timeout. Git is told never to prompt: a repository that wants credentials is one whose tags are not listed, not a console that hangs on a hidden prompt.- Parameters:
git (str) – The git executable.
url (str) – Clone URL.
timeout (float) – Seconds before giving up.
- Return type:
tuple[str, Ellipsis]
- plesty.server.services.DEVICE_TIMEOUT_MS = 15000
- class plesty.server.services.DeviceLink(name: str, address: str, timeout_ms: int = DEVICE_TIMEOUT_MS, resolve: collections.abc.Callable[[], str | None] | None = None)
A lazy
DeviceHandleover one device server.Prepare a link; nothing is connected yet.
- Parameters:
name (str) – The device’s fleet name, for messages.
address (str) – ZMQ endpoint of its server,
tcp://host:port.timeout_ms (int) – Reply timeout for one call.
resolve (collections.abc.Callable[[], str | None] | None) – Returns where the server is now, or
Nonewhen it is not running; raises when that is not known. A restarted server can come back on another port; a link that kept the first address drove a port nobody listened on, and the panel’s controls silently did nothing (#22).
- name
- address
- timeout_ms = 15000
- _resolve = None
- _client: Any = None
- _held = False
- hold() None
Refuse every call until
release(): the server is being stopped.A call sent to a server that is shutting down, or to the port it left, gets no reply and waits out the whole timeout — and every call after it waits behind it on the panel’s one worker. Restarting on a new port left the panel dead for up to fifteen seconds that way (#22). Held, a call fails at once and sends nothing.
- Return type:
None
- _follow() None
Move to the server’s current address, dropping a client on the old one.
- Raises:
ConnectionError – When the server is not running, rather than send a call to an address nothing answers on.
- Return type:
None
- client() Any
Return the connected client, building it on first use.
- Returns:
The
DeviceTCPIPClient.- Raises:
ConnectionError – When the link is held or the server is not running.
- Return type:
Any
- describe() dict[str, Any]
Return the device’s methods and its serialized doc model.
- Return type:
dict[str, Any]
- query(key: str) Any
Read configuration parameter key.
- Parameters:
key (str)
- Return type:
Any
- write(key: str, value: Any) Any
Write value to configuration parameter key.
- Parameters:
key (str)
value (Any)
- Return type:
Any
- call(op: str, **kwargs: Any) Any
Run operation op.
- Parameters:
op (str)
kwargs (Any)
- Return type:
Any
- close() None
Release the connection; a later call reconnects.
Never raises: this runs when a panel closes and a window is going away, and a server that already died must not be what stops it.
- Return type:
None
- plesty.server.services.ABORT = 'abort'
- plesty.server.services.CANCEL = 'cancel'
- plesty.server.services.CONTROL_FILE = 'control.json'
- plesty.server.services.EXPERIMENT_KIND = 'experiment'
- plesty.server.services.RUN_CONFIG_FILE = 'config.yaml'
- plesty.server.services.RUN_LOGS_DIR = 'job-logs'
- plesty.server.services.RUN_REPORT_FILE = 'report.html'
- plesty.server.services.VIEWER_KIND = 'viewer'
- exception plesty.server.services.ExperimentError
Bases:
RuntimeErrorRaised when an experiment cannot be read, started or driven.
Initialize self. See help(type(self)) for accurate signature.
- plesty.server.services.ask(run_dir: str | pathlib.Path, **changes: Any) dict[str, Any]
Amend a run’s control file and return what it now asks for.
The file is replaced rather than rewritten, so a run reading it sees either the old document or the new one and never half of each.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
**changes (Any) – Fields of the control document to set.
- Returns:
The control document as it now stands.
- Raises:
ExperimentError – If the file cannot be written.
- Return type:
dict[str, Any]
- plesty.server.services.asked(run_dir: str | pathlib.Path) dict[str, Any]
Return what a run is currently being asked for.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
The control document; empty when nothing is being asked.
- Return type:
dict[str, Any]
- plesty.server.services.clear(run_dir: str | pathlib.Path) None
Remove a run’s control file, so a resumed run is asked for nothing.
A run resumed while its control file still says
cancelwould stop at its first step, which reads as a resume that did nothing.- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Return type:
None
- plesty.server.services.check_run_id(text: str) str
Return text if it is usable as a run directory name, else raise.
Deliberately the console’s own check rather than plesty-lib’s, for the same reason the journal is replayed here: the rule is about what a directory name may be, and the console must apply it on any plesty-lib — including one too old to carry the check at all. It is the same rule, and a short one.
- Parameters:
text (str) – The proposed run id.
- Returns:
The same string, trimmed.
- Raises:
ExperimentError – If it is empty, a path, or holds a character that does not belong in a directory name.
- Return type:
str
- plesty.server.services.add_note(run_dir: str | pathlib.Path, text: str, by: str, when: Any = None) None
Append a note to the run’s
NOTES_FILE, while it runs or after it ended.- Parameters:
run_dir (str | pathlib.Path) – The run directory.
text (str) – The note; blank lines inside it are kept.
by (str) – Who wrote it.
when (Any) – A
datetime; now by default (tests pass one).
- Raises:
ExperimentError – When the note is empty or cannot be written.
- Return type:
None
- plesty.server.services.create_run(run_dir: str | pathlib.Path, config_text: str | None = None, **fields: Any) dict[str, Any]
Name a run before it is started, recording what it will be run with.
- Parameters:
run_dir (str | pathlib.Path) – The directory the run will use — its name is the run id.
config_text (str | None) – The run’s own configuration, written as its
RUN_CONFIG_FILE; none is written whenNone.**fields (Any) – What to record: the experiment, the configuration file the run’s copy came from, and whatever else the run needs to be started later.
- Returns:
The document written.
- Raises:
ExperimentError – If the directory already holds a run, or cannot be written. Naming a second run over a started one would interleave two journals into one directory.
- Return type:
dict[str, Any]
- plesty.server.services.created_of(run_dir: str | pathlib.Path) dict[str, Any]
Return what the console recorded when the run was created, if anything.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
The document, or
{}for a run that was started directly.- Return type:
dict[str, Any]
- plesty.server.services.find_run(run_root: str | pathlib.Path, run_id: str | None) pathlib.Path | None
Return the run directory run_id names, or the newest one.
- Parameters:
run_root (str | pathlib.Path) – The experiment’s run root.
run_id (str | None) – The run to find, or
Nonefor the newest.
- Returns:
The directory, or
Nonewhen it does not exist. A run created but not yet started counts: it is the one an operator is about to run.- Return type:
pathlib.Path | None
- plesty.server.services.notes_of(run_dir: str | pathlib.Path) list[dict[str, str]]
The run’s notes, oldest first: each
{"at", "by", "text"}; none without a file.- Parameters:
run_dir (str | pathlib.Path)
- Return type:
list[dict[str, str]]
- plesty.server.services.has_started(run_dir: str | pathlib.Path) bool
Whether the run has started: its configuration is frozen into its plan then.
- Parameters:
run_dir (str | pathlib.Path)
- Return type:
bool
- plesty.server.services.keep_log(run_dir: str | pathlib.Path, log: str | pathlib.Path) pathlib.Path
Copy a job’s log into the run it ran, under
RUN_LOGS_DIR.Copied when the job ends, not written there from the start: a run the module names itself has no directory until it has begun, and the job’s log is already being written by then.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
log (str | pathlib.Path) – The job’s log, named after the job.
- Returns:
The copy.
- Raises:
OSError – When the log cannot be read or the copy written.
- Return type:
pathlib.Path
- plesty.server.services.kept_log(run_dir: str | pathlib.Path) pathlib.Path | None
The newest job log run_dir keeps, or
Nonewhen it keeps none.Job ids end in their start time, so the newest is the last by name — the one a resumed run was finished by.
- Parameters:
run_dir (str | pathlib.Path)
- Return type:
pathlib.Path | None
- plesty.server.services.LOCAL_HOST_NAME = 'local'
- plesty.server.services.latest(run_root: str | pathlib.Path) pathlib.Path | None
Return the newest run of run_root, or
Nonewhen there is none.- Parameters:
run_root (str | pathlib.Path)
- Return type:
pathlib.Path | None
- plesty.server.services.progress_of(run_dir: str | pathlib.Path) dict[str, Any] | None
Return what the step in flight last said about itself.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
The reported progress, or
Nonewhen the step reports none. A step that says nothing is not an error — most are short enough not to.- Return type:
dict[str, Any] | None
- plesty.server.services.read_devices(spec: plesty.server.model.experiment.ExperimentSpec, python: str | pathlib.Path, cwd: str | pathlib.Path, config: str, env: Mapping[str, str] | None = None, timeout: float = MANIFEST_TIMEOUT) list[str] | None
Ask the module which devices of its
setup.yamla run of config uses.Only for a module whose manifest says
chooses_devices(plesty-lib#62).- Parameters:
spec (plesty.server.model.experiment.ExperimentSpec) – The declared experiment.
python (str | pathlib.Path) – Interpreter of its installed environment.
cwd (str | pathlib.Path) – The checkout.
config (str) – The configuration file the run will read.
env (Mapping[str, str] | None) – Environment for the call.
timeout (float) – Seconds to wait.
- Returns:
The device names, or
Nonefor all of them.- Raises:
ExperimentError – If the command fails, times out, or prints no list.
- Return type:
list[str] | None
- plesty.server.services.read_manifest(spec: plesty.server.model.experiment.ExperimentSpec, python: str | pathlib.Path, cwd: str | pathlib.Path, env: Mapping[str, str] | None = None, timeout: float = MANIFEST_TIMEOUT) plesty.server.model.experiment.ExperimentManifest
Ask the module what it takes, and return what it said.
- Parameters:
spec (plesty.server.model.experiment.ExperimentSpec) – The declared experiment.
python (str | pathlib.Path) – Interpreter of its installed environment.
cwd (str | pathlib.Path) – Where to run it — the checkout, so a relative configuration path in the answer means what it says.
env (Mapping[str, str] | None) – Environment for the call; the manifest needs none, but a module that reads its own package data may.
timeout (float) – Seconds to wait.
- Returns:
The parsed manifest.
- Raises:
ExperimentError – If the command fails, times out, or prints something that is not a manifest.
- Return type:
- plesty.server.services.reconfigure_run(run_dir: str | pathlib.Path, config_text: str, config: str) pathlib.Path
Replace the configuration of a run that has not started.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
config_text (str) – The run’s configuration, as it is to be run.
config (str) – The module’s file it was taken from (
config/demo.yaml), recorded inCREATED_FILEso the run table names it.
- Returns:
The run’s configuration file.
- Raises:
ExperimentError – If the run has started — its plan was built from the configuration it had, and resuming checks it against that — or the files cannot be written.
- Return type:
pathlib.Path
- plesty.server.services.records_of(run_dir: str | pathlib.Path, limit: int = 200) list[dict[str, Any]]
Return the newest committed step records.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
limit (int) – How many to return, newest last.
- Returns:
The records in write order. A record is the durable result of its step, so this is the raw data a run has produced so far.
- Return type:
list[dict[str, Any]]
- plesty.server.services.rename_run(run_dir: str | pathlib.Path, new_name: str) pathlib.Path
Give a run that has not started another name, which is its directory’s.
The directory moves with everything in it — the run’s configuration and its
CREATED_FILE— and a duplicate recorded as a copy of it follows the new name, so it stays beside it in the table (#27).- Parameters:
run_dir (str | pathlib.Path) – The run directory.
new_name (str) – What to call it; checked as
check_run_id()checks.
- Returns:
The run’s directory under its new name.
- Raises:
ExperimentError – If the name is unusable or taken, the run has started — its journal and data are written under the name it has — or the directory cannot be moved.
- Return type:
pathlib.Path
- plesty.server.services.report_of(run_dir: str | pathlib.Path) dict[str, Any] | None
The run’s own report, or
Nonewhen it has none.- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
Its
name,path,bytesandwritten(epoch seconds), orNonewhen no report has been written — a run that has not ended, or one that ended before this bench wrote them.- Return type:
dict[str, Any] | None
- plesty.server.services.resumable(run_root: str | pathlib.Path) str | None
Return the id of the newest run that could be continued, if any.
A run is resumable when it stopped without finishing: canceled or aborted, or left running by a process that is no longer there. A completed run is not, and neither is one that never wrote a journal.
- Parameters:
run_root (str | pathlib.Path) – The experiment’s run root.
- Returns:
The run id, or
None.- Return type:
str | None
- plesty.server.services.run_environment(spec: plesty.server.model.experiment.ExperimentSpec, base: Mapping[str, str], dotenv: Mapping[str, str]) dict[str, str]
Build the environment a run gets.
The bench’s own environment first, then the module’s
.env, then what the fleet declares — so the declaration is what an operator edits to point one experiment at a different stage, and the.envstays the module’s own deployment file.- Parameters:
spec (plesty.server.model.experiment.ExperimentSpec) – The declared experiment.
base (Mapping[str, str]) – The bench’s environment.
dotenv (Mapping[str, str]) – The module’s
.env, already parsed.
- Returns:
The environment for the run.
- Return type:
dict[str, str]
- plesty.server.services.runs_of(run_root: str | pathlib.Path) list[pathlib.Path]
Return this experiment’s run directories, newest first.
A run directory is one holding a
journal.jsonl— or acreated.json, for a run named and configured but not yet started. The run root may also hold whatever the module wrote beside its runs, and a directory that is neither is not a run anything can report on.- Parameters:
run_root (str | pathlib.Path) – The experiment’s run root.
- Returns:
The run directories, newest first by name — run ids carry a date, so name order is close enough to time order to read as it.
- Return type:
list[pathlib.Path]
- plesty.server.services.status_of(run_dir: str | pathlib.Path) dict[str, Any]
Return what a run’s journal says about it, by replaying the journal here.
Deliberately not
plesty.lib.experiment.Journal.status(), though it computes the same thing. The run was written by an experiment module, whose plesty-lib has nothing to do with the one this console happens to have installed: delegating would make the same run read differently on two benches, and a run written by a newer module would lose its paused state on an older console. The journal is an append-only document with five event names, and reading it is cheaper than the coupling.- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
state,completed,failed,reports,started,ended,provenanceand the last event. A run that was named but not yet started iscreated, which is distinct fromnot_started: one is a run waiting to go, the other an experiment that has never had one.- Return type:
The status mapping
- plesty.server.services.steps_of(run_dir: str | pathlib.Path) list[dict[str, Any]]
Return the plan’s steps, so a status can name the one in flight.
- Parameters:
run_dir (str | pathlib.Path) – The run directory.
- Returns:
One
{id, op, params}per step; empty when the plan is unreadable.- Return type:
list[dict[str, Any]]
- class plesty.server.services.JobRunner(home: plesty.server.model.home.Home, store: plesty.server.model.job.JobStore | None = None)
Start, observe and cancel jobs under a
Home.Bind to a home.
- Parameters:
home (plesty.server.model.home.Home) – The bench home.
store (plesty.server.model.job.JobStore | None) – The record store; a fresh one on home by default.
- home
- store
- exit_file(job_id: str) pathlib.Path
Where the launcher records the exit code of job_id.
- Parameters:
job_id (str)
- Return type:
pathlib.Path
- start(name: str, kind: str, command: list[str], cwd: str | os.PathLike[str], env: collections.abc.Mapping[str, str] | None = None, detail: collections.abc.Mapping[str, Any] | None = None) plesty.server.model.job.Job
Launch command as a job and return its record.
- Parameters:
name (str) – Device instance the job belongs to.
kind (str) – Job kind, part of the id.
command (list[str]) – The command line.
cwd (str | os.PathLike[str]) – Working directory.
env (collections.abc.Mapping[str, str] | None) – Environment on top of the bench’s own.
detail (collections.abc.Mapping[str, Any] | None) – Kind-specific data stored on the record.
- Raises:
JobError – When the command cannot be launched.
- Return type:
- get(job_id: str) plesty.server.model.job.Job | None
The job job_id with its state brought up to date, or
None.- Parameters:
job_id (str)
- Return type:
plesty.server.model.job.Job | None
- require(job_id: str) plesty.server.model.job.Job
Like
get()but raisesJobErrorwhen unknown.- Parameters:
job_id (str)
- Return type:
- list(name: str | None = None, kind: str | None = None) list[plesty.server.model.job.Job]
Jobs on record, newest first, brought up to date.
- Parameters:
name (str | None)
kind (str | None)
- Return type:
- wait(job_id: str, timeout: float | None = None, poll: float = 0.25) plesty.server.model.job.Job
Block until the job finishes (or timeout seconds pass); returns it.
- Parameters:
job_id (str)
timeout (float | None)
poll (float)
- Return type:
- cancel(job_id: str) bool
Kill a running job (its whole process tree);
Truewhen it was running.- Parameters:
job_id (str)
- Return type:
bool
- plesty.server.services.ACTION_TIMEOUT_MS = 120000
- plesty.server.services.POLL_TIMEOUT_MS = 3000
- class plesty.server.services.NodeDevice(peer: plesty.server.model.Peer, name: str, connect: Connect)
One device of a node, on a connection this handle owns.
What a control panel drives. It is the four calls of plesty-lib’s
DeviceHandle—describe,query,write,call— delegated to aplesty.bench.RemoteDevice, plus the one thing that handle cannot offer: aclosethat actually releases something.plesty.bench.RemoteDevicecloses nothing by design, because its connection belongs to theBenchClientthat made it. A panel needs the opposite — a connection of its own, released when the panel goes — so that the console’s poll and the panel never share a socket, and so that the console’s_releasehas something to close.The client connects lazily, so the socket is made by whichever thread makes the first call. That is the panel’s worker, which is the thread that must own it.
Open a handle on name at peer.
- Parameters:
peer (plesty.server.model.Peer) – The node the device runs on.
name (str) – The declared device.
connect (Connect) – Builds the client; injected by the tests.
- name
- peer
- _client
- _device: Any
- describe() dict[str, Any]
The device’s methods and its serialized doc model.
- Return type:
dict[str, Any]
- query(key: str) Any
Read configuration parameter key.
- Parameters:
key (str)
- Return type:
Any
- write(key: str, value: Any) Any
Write value to configuration parameter key.
- Parameters:
key (str)
value (Any)
- Return type:
Any
- call(op: str, **kwargs: Any) Any
Run operation op.
- Parameters:
op (str)
kwargs (Any)
- Return type:
Any
- close() None
Release this handle’s own connection.
- Return type:
None
- exception plesty.server.services.NodeError
Bases:
RuntimeErrorA node could not be reached, or refused what it was asked.
Initialize self. See help(type(self)) for accurate signature.
- class plesty.server.services.NodeLink(peer: plesty.server.model.Peer, connect: Connect | None = None)
This master’s link to one node’s agent.
Bind to peer; the socket opens on the first request.
- Parameters:
peer (plesty.server.model.Peer) – The node’s name, address and secret.
connect (Connect | None) – Builds the client; injected by the tests.
- peer
- _connect
- _client: plesty.bench.BenchClient | None = None
- _lock
- _trouble: str = ''
- property name: str
The node’s name — what the Host column shows.
- Return type:
str
- property trouble: str
Why the last request failed, or empty after one that worked.
- Return type:
str
- property reachable: bool
Whether the last request got an answer.
- Return type:
bool
- close() None
Drop the connection; a later request opens a new one.
- Return type:
None
- run(argv: collections.abc.Sequence[str], timeout_ms: int = ACTION_TIMEOUT_MS) str
Run
plesty-server <argv>on the node and return its output.- Parameters:
argv (collections.abc.Sequence[str]) – The command line, as the local CLI would take it.
timeout_ms (int) – Reply timeout.
- Returns:
Standard output, stripped.
- Raises:
NodeError – When the node did not answer, or the command failed — carrying what the node said, since it is the node that knows.
- Return type:
str
- devices() list[dict[str, Any]]
Every device the node declares, as
list --jsonreports them.The same document the local
list --jsonprints, so one call carries the declaration, the installed version, the source line and the state — everything a row needs, in one round trip per node per refresh.- Raises:
NodeError – When the node did not answer or gave no JSON.
- Return type:
list[dict[str, Any]]
- device(name: str) NodeDevice
A handle on one running device of the node, for a control panel.
On a connection of its own, not the one this link polls with. A panel drives its handle from its own worker thread while the device table keeps polling on ours, and a ZMQ socket belongs to one thread at a time: sharing one would let a panel’s reply answer a poll, and every request after that would return the one before it.
- Parameters:
name (str)
- Return type:
- class plesty.server.services.Nodes(peers: collections.abc.Iterable[plesty.server.model.Peer] = (), connect: Connect | None = None)
Every node a master drives, one
NodeLinkeach.Hold a link per peer.
- Parameters:
peers (collections.abc.Iterable[plesty.server.model.Peer]) – The declared nodes.
connect (Connect | None) – Builds each client; injected by the tests.
- _connect = None
- refresh(peers: collections.abc.Iterable[plesty.server.model.Peer]) None
Make the links match peers, keeping the connections still wanted.
A peer whose address or secret changed is reconnected; one that is simply still there keeps the socket it has, so editing the list does not cost every other node its connection.
- Parameters:
peers (collections.abc.Iterable[plesty.server.model.Peer])
- Return type:
None
- property names: list[str]
The node names.
- Return type:
list[str]
- close() None
Drop every connection.
- Return type:
None
- plesty.server.services.PROBE_HOST = '127.0.0.1'
- exception plesty.server.services.AlreadyRunning
Bases:
SupervisorErrorstartwas asked for a device whose server is already up.Initialize self. See help(type(self)) for accurate signature.
- exception plesty.server.services.NotRunning
Bases:
SupervisorErrorstopwas asked for a device that has no live server.Initialize self. See help(type(self)) for accurate signature.
- exception plesty.server.services.PortBusy
Bases:
SupervisorErrorstartfound the device’s port already answering — another server holds it.Initialize self. See help(type(self)) for accurate signature.
- class plesty.server.services.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:
- 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,
Nonewhile it runs.LOST_EXITwhen 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
describeto tellrunningfromstarting; skipping it is cheaper for bulk listings.
- Return type:
- wait_ready(name: str, timeout: float | None = None) bool
Poll until the server of name answers a probe.
- Returns:
Trueonce it answers;Falseon 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 —
runningwhen it came up,startingwhen wait is off or it has not answered yet,exitedwhen it died before answering (detailcarries the log tail).- Raises:
AlreadyRunning – When a live server of that name exists.
SupervisorError – When the interpreter cannot be executed.
- Return type:
- _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:
python (pathlib.Path)
wait (bool)
timeout (float | None)
package (str)
version (str)
- Return type:
- stop(name: str, timeout: float | None = None, note: collections.abc.Callable[[str], None] | None = None) bool
Interrupt the server of name, escalating to a kill if it lingers.
Interrupt → wait timeout → terminate → wait 3 s → kill tree.
note, when given, receives one line about how the server ended, in the cases where that is worth saying — today, a server the stop killed before its shutdown ran, whose device is therefore still held. It has to be read here:
_drop_record()removes the exit file and the record, andstatus()returns early once the record is gone, so after a stop there is nothing left for anyone to observe. That made the note unreachable on exactly the path an operator takes — press Stop, then ask why the device never disconnected.- Parameters:
name (str) – Device instance name.
timeout (float | None) – Seconds to wait before escalating.
note (collections.abc.Callable[[str], None] | None) – Receives a line about an ending worth reporting.
- Returns:
Truewhen a process was stopped,Falsewhen only a stale record was cleared.- Raises:
NotRunning – When nothing (not even a stale record) is known.
- 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:
python (pathlib.Path)
wait (bool)
timeout (float | None)
- Return type:
- 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:
wait (bool)
- Return type:
- 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
- clear_log(name: str) int
Start a new, empty log for name; returns the size of the one set aside.
Nothing is truncated or deleted. Truncating a file a running server writes left it writing at its old offset: the gap filled with NULs, and an 11.7 MB log with no line breaks hung the console, the agent and every reader (#23). The old file stays beside the new one.
A running server’s waiter is asked to write to the new file (
request_switch()). One started by an older bench hands the server the file itself and cannot switch, so it is refused with what to do instead.- Parameters:
name (str) – The device whose log to clear.
- Returns:
The size of the log set aside;
0when there was none.- Raises:
SupervisorError – When the server is running and cannot switch, or did not take the new file.
- Return type:
int
- _wait_exit(name: str, timeout: float) bool
- Parameters:
name (str)
timeout (float)
- Return type:
bool
- exception plesty.server.services.SupervisorError
Bases:
RuntimeErrorA start or stop request could not be carried out.
Initialize self. See help(type(self)) for accurate signature.
- plesty.server.services.probe(port: int, timeout_ms: int = 2000, host_addr: str = PROBE_HOST) bool
Ask the server on port to describe itself.
- Returns:
Truewhen a plesty-lib server answeredstatus: okin time.- Parameters:
port (int)
timeout_ms (int)
host_addr (str)
- Return type:
bool
- plesty.server.services.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]