plesty.bench.client =================== .. py:module:: plesty.bench.client .. autoapi-nested-parse:: The bench client: one ZMQ DEALER socket, three request types. The wire is the plesty-lib device-server wire — JSON frames, ``status: ok`` or ``status: error`` replies — so a :func:`plesty.lib.service.build_client` or a ``describe`` probe understands the agent too. Requests: ``{"type": "describe"}`` → ``{"status": "ok", "result": {"kind": "plesty-server-agent", "version": …, "home": …, "commands": [...]}}`` ``{"type": "exec", "argv": [...], "token": "…"}`` → ``{"status": "ok", "result": {"code": 0, "stdout": "…", "stderr": "…"}}`` ``{"type": "fetch", "path": "relative/to/home", "token": "…"}`` → ``{"status": "ok", "result": {"path": …, "size": n, "data": ""}}`` Everything long-running is already a job on the bench: ask for it with ``--no-wait``, then :meth:`BenchClient.follow` the job log. Attributes ---------- .. autoapisummary:: plesty.bench.client.DEFAULT_PORT plesty.bench.client.ADDRESS_ENV plesty.bench.client.TOKEN_ENV Exceptions ---------- .. autoapisummary:: plesty.bench.client.BenchClientError Classes ------- .. autoapisummary:: plesty.bench.client.ExecResult plesty.bench.client.BenchClient Functions --------- .. autoapisummary:: plesty.bench.client.parse_address Module Contents --------------- .. py:data:: DEFAULT_PORT :value: 5550 .. py:data:: ADDRESS_ENV :value: 'PLESTY_BENCH' .. py:data:: TOKEN_ENV :value: 'PLESTY_BENCH_TOKEN' .. py:exception:: BenchClientError Bases: :py:obj:`RuntimeError` The bench did not answer, refused the request, or the command failed to run. Initialize self. See help(type(self)) for accurate signature. .. py:class:: ExecResult What one remote ``plesty-server`` invocation produced. :ivar argv: The arguments that were run. :ivar code: Exit code. :ivar stdout: Captured standard output. :ivar stderr: Captured standard error (progress lines). .. py:attribute:: argv :type: tuple[str, Ellipsis] .. py:attribute:: code :type: int .. py:attribute:: stdout :type: str .. py:attribute:: stderr :type: str .. py:property:: ok :type: bool Whether the command exited 0. .. py:method:: json() -> Any Parse stdout as JSON (for commands run with ``--json``). .. py:function:: parse_address(value: str | None) -> str Turn ``host``, ``host:port`` or ``tcp://host:port`` into a ZMQ endpoint. :param value: The address; ``None`` reads ``PLESTY_BENCH``, else ``localhost``. .. py:class:: BenchClient(address: str | None = None, token: str | None = None, timeout_ms: int = 10000, context: zmq.Context[Any] | None = None) A connection to one bench agent. Connect lazily to *address*. :param address: ``host``, ``host:port`` or ``tcp://host:port``; ``None`` reads ``PLESTY_BENCH``. :param token: Shared secret the agent was started with; ``None`` reads ``PLESTY_BENCH_TOKEN``. :param timeout_ms: Default reply timeout for :meth:`request`. :param context: ZMQ context to use; the global instance by default. .. py:attribute:: address .. py:attribute:: token .. py:attribute:: timeout_ms :value: 10000 .. py:attribute:: _context :value: None .. py:attribute:: _socket :type: zmq.Socket[Any] | None :value: None .. py:method:: __enter__() -> BenchClient Open the socket. .. py:method:: __exit__(*_exc: object) -> None Close the socket. .. py:method:: close() -> None Close the socket (a later request reconnects). .. py:method:: _connect() -> zmq.Socket[Any] .. py:method:: request(message: dict[str, Any], timeout_ms: int | None = None) -> dict[str, Any] Send one request and return the ``result`` of an ``ok`` reply. :raises BenchClientError: On no reply in time, a malformed reply, or an ``error`` reply (its message is the exception text). .. py:method:: describe(timeout_ms: int | None = None) -> dict[str, Any] The agent's self-description (kind, version, home, commands). .. py:method:: ping(timeout_ms: int = 2000) -> bool Whether a bench agent answers at the address. .. py:method:: exec(argv: collections.abc.Sequence[str], timeout_ms: int | None = None) -> ExecResult Run ``plesty-server `` on the bench and return what it produced. The result carries the exit code; it does not raise on a non-zero exit, only when the agent could not run the command at all. .. py:method:: fetch(path: str, timeout_ms: int | None = None) -> bytes Download a file from the bench home (``path`` relative to it). .. py:method:: follow(job_id: str, sink: collections.abc.Callable[[str], None], poll_s: float = 0.5, timeout_s: float | None = None) -> int Stream the log of *job_id* to *sink* until the job ends; returns its exit code. Uses ``jobs log --offset N --json`` on the bench, so nothing is held open between polls and a lost connection just resumes. :raises BenchClientError: When the job is unknown or the bench stops answering.