plesty.server.model.job
Long-running work on the bench as jobs: a record and a log.
An install, an update, a field test or an arbitrary command in a device’s
environment takes seconds to minutes — too long for one request/reply and too
interesting to run blind. A Job is the record of such a run: its
command, pid, timestamps and exit code live in <home>/jobs/<id>.json and
its stdout+stderr in <home>/logs/jobs/<id>.log. JobStore owns
both files — creating, updating, listing records and reading logs by byte
offset — so a job started by the agent is visible to the CLI and the GUI.
Starting and killing the process is not the model’s business: whoever launches
it calls JobStore.create() before and JobStore.finish() after, and
JobStore.refresh() settles a still-running record from the two facts
only the process layer knows — is the pid alive, and what exit code was left.
Attributes
Exceptions
A job record does not exist or cannot be read. |
Classes
One unit of long-running work. |
|
Job records and logs under a |
Functions
|
Read the log at path from byte offset. |
|
The end of the log at path: at most its last limit bytes, from a line start. |
|
The part of the log at path before byte end: at most limit bytes, from a line start. |
|
The last lines lines of path, read from at most its last limit bytes. |
|
Drop the partial line a read from the middle of a file begins with. |
|
Module Contents
- plesty.server.model.job.LOG_CHUNK = 65536
- plesty.server.model.job.LOG_END = 262144
- plesty.server.model.job.LOST_EXIT = -1
- plesty.server.model.job.CANCELLED_EXIT = -9
- exception plesty.server.model.job.JobError
Bases:
RuntimeErrorA job record does not exist or cannot be read.
Initialize self. See help(type(self)) for accurate signature.
- class plesty.server.model.job.Job
One unit of long-running work.
- Variables:
id –
<name>-<kind>-<timestamp>, unique per bench.name – Device instance the job belongs to.
kind –
install,update,field-test,command…command – The command line.
cwd – Working directory.
pid – Process id while it runs.
started_utc – ISO timestamp of the launch.
finished_utc – ISO timestamp of the end, or
Nonewhile running.returncode – Exit code once finished (
Nonewhile running;LOST_EXITwhen the process vanished without one).log – Path of the log file.
detail – Kind-specific data (e.g. the reports a field test wrote).
- id: str
- name: str
- kind: str
- command: list[str]
- cwd: str
- pid: int | None
- started_utc: str
- finished_utc: str | None = None
- returncode: int | None = None
- log: str = ''
- detail: dict[str, Any]
- property running: bool
Whether the job has not finished yet.
- Return type:
bool
- property state: str
running,done(exit 0),failed(any other exit).- Return type:
str
- to_dict() dict[str, Any]
JSON form, with the derived
stateincluded.- Return type:
dict[str, Any]
- class plesty.server.model.job.JobStore(home: plesty.server.model.home.Home)
Job records and logs under a
Home.Bind to a home.
- Parameters:
home (plesty.server.model.home.Home) – The bench home.
- home
- create(name: str, kind: str, command: list[str], cwd: str | os.PathLike[str], pid: int | None = None, detail: collections.abc.Mapping[str, Any] | None = None) Job
Open a new record and return it (state
running).- 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.
pid (int | None) – Process id when already known.
detail (collections.abc.Mapping[str, Any] | None) – Kind-specific data stored on the record.
- Return type:
- save(job: Job) pathlib.Path
Write job to its record file; returns the path.
- Parameters:
job (Job)
- Return type:
pathlib.Path
- get(job_id: str) Job | None
The record of job_id, or
Nonewhen unknown or unreadable.- Parameters:
job_id (str)
- Return type:
Job | None
- require(job_id: str) Job
Like
get()but raisesJobErrorwhen unknown.- Parameters:
job_id (str)
- Return type:
- list(name: str | None = None, kind: str | None = None) list[Job]
Jobs on record, newest first, optionally filtered by device and kind.
- Parameters:
name (str | None)
kind (str | None)
- Return type:
list[Job]
- finish(job_id: str, returncode: int) Job
Close the record of job_id with returncode; returns it.
- Parameters:
job_id (str)
returncode (int)
- Return type:
- refresh(job: Job, alive: collections.abc.Callable[[int], bool], exit_code: collections.abc.Callable[[str], int | None]) Job
Settle a
runningrecord against what the process layer knows.- Parameters:
job (Job) – The record to bring up to date (mutated and saved when it ends).
alive (collections.abc.Callable[[int], bool]) – Whether a pid is still running.
exit_code (collections.abc.Callable[[str], int | None]) – The exit code left for a job id, or
Nonewhen none.
- Returns:
job, finished when an exit code exists or the pid is gone (
LOST_EXITthen), unchanged while it still runs.- Return type:
- log(job_id: str, offset: int = 0, limit: int = LOG_CHUNK) tuple[str, int]
Read the job log from byte offset.
- Parameters:
job_id (str) – The job.
offset (int) – Where the previous read stopped (0 for the start).
limit (int) – Maximum bytes to return.
- Returns:
The text read and the offset to continue from. Decoding is lenient — a chunk may cut a multi-byte character.
- Return type:
tuple[str, int]
- log_end(job_id: str, limit: int = LOG_END) tuple[str, int, int]
The end of the job log; see
log_end().- Parameters:
job_id (str)
limit (int)
- Return type:
tuple[str, int, int]
- log_before(job_id: str, end: int, limit: int = LOG_END) tuple[str, int]
The part of the job log before byte end; see
log_before().- Parameters:
job_id (str)
end (int)
limit (int)
- Return type:
tuple[str, int]
- tail(job_id: str, lines: int = 50) str
The last lines lines of the job log (empty when there is none yet).
- Parameters:
job_id (str)
lines (int)
- Return type:
str
- plesty.server.model.job.read_log(path: pathlib.Path, offset: int = 0, limit: int = LOG_CHUNK) tuple[str, int]
Read the log at path from byte offset.
A job’s log and the copy a run keeps of it (#33) are read the same way.
- Parameters:
path (pathlib.Path) – The log file.
offset (int) – Where the previous read stopped (0 for the start).
limit (int) – Maximum bytes to return.
- Returns:
The text read and the offset to continue from. Decoding is lenient — a chunk may cut a multi-byte character.
- Return type:
tuple[str, int]
- plesty.server.model.job.log_end(path: pathlib.Path, limit: int = LOG_END) tuple[str, int, int]
The end of the log at path: at most its last limit bytes, from a line start.
What a log view opens on, so a long log is not read and painted from its first byte to reach the last (#28).
- Parameters:
path (pathlib.Path) – The log file.
limit (int) – Most bytes to return.
- Returns:
The text, the byte it starts at (
0when it is the whole log), and the byte it ends at — where reading on continues.- Return type:
tuple[str, int, int]
- plesty.server.model.job.log_before(path: pathlib.Path, end: int, limit: int = LOG_END) tuple[str, int]
The part of the log at path before byte end: at most limit bytes, from a line start.
- Parameters:
path (pathlib.Path) – The log file.
end (int) – Where the part already shown begins.
limit (int) – Most bytes to return.
- Returns:
The text and the byte it starts at;
("", 0)at the top.- Return type:
tuple[str, int]
- plesty.server.model.job.TAIL_LIMIT = 262144
- plesty.server.model.job.tail_file(path: pathlib.Path, lines: int, limit: int = TAIL_LIMIT) str
The last lines lines of path, read from at most its last limit bytes.
One read of a bounded span. The loop this replaced prepended each 8 KB block to everything read so far until enough newlines turned up — on a file with none (a truncated log a server kept writing at its old offset fills with NULs) that is quadratic in the file’s size, and an 11.7 MB log hung the console, the agent and every reader with it (#23). A line longer than limit is shown from its last limit bytes.
- Parameters:
path (pathlib.Path)
lines (int)
limit (int)
- Return type:
str
- plesty.server.model.job._from_line_start(start: int, data: bytes) tuple[int, bytes]
Drop the partial line a read from the middle of a file begins with.
A read from byte 0 starts on a line already. One whose span holds no line break at all is kept whole: a line longer than the span still shows.
- Parameters:
start (int)
data (bytes)
- Return type:
tuple[int, bytes]
- plesty.server.model.job._now() str
- Return type:
str