API reference¶
The reference below is generated from the Python sources. Names beginning with an underscore are implementation details.
Workspace¶
Workspace
¶
Workspace(*, app_id: str, host_namespace: str | None = None, project_resources=None, app_settings=None)
Bases: WorkspaceJobApiMixin, WorkspaceProjectRuntimeMixin
Main core facade.
- Lightweight global bootstrap.
- Per-process persistent runtime.
- Swappable active project.
Methods:
| Name | Description |
|---|---|
app_setting_specs |
|
get_app_setting |
|
update_app_setting |
|
get_project_resource_contract |
|
project_resource_specs |
|
list_project_resources |
|
get_project_resource |
|
get_project_resource_path |
|
get_project_resource_map |
|
list_projects |
|
open_project |
|
create_project |
|
find_project |
|
create_or_open_project |
|
register_task_canceller |
|
get_app_logger |
|
get_executor_logger |
|
get_project_logger |
|
get_entity_loader_context |
|
get_project_data_context |
|
refresh_master_db |
|
close_project |
Close the active project. Its jobs that have not ended are interrupted, not lost: |
get_project_activity |
What is still going on in a project: its jobs that have not ended, and the external |
delete_projects |
|
shutdown |
|
executors |
One row for each executor kept: |
activate |
Start the executor kept as |
deactivate |
Stop the executor active as |
save_host |
|
remove_host |
|
save_executor |
|
remove_executor |
|
submit |
Run a |
origin |
What the job recorded under |
resume |
Submit again the job recorded under |
move |
Continue on |
remote_jobs |
What the executor of that name has on another machine, whichever session or project |
remove_remote_job |
Take one of |
jobs |
The jobs of the active project, oldest first; with |
job_tasks |
The tasks in flight of a job this session runs, by its |
job_failures |
|
get_project_store |
|
Attributes:
| Name | Type | Description |
|---|---|---|
app_id |
|
|
host_namespace |
|
|
settings_manager |
|
|
logging_manager |
|
|
app_logger |
|
|
master_db |
|
|
project_manager |
|
|
project_db |
ProjectStore | None
|
|
project_store |
ProjectStore | None
|
|
active_context |
Optional[ProjectContext]
|
|
project_logger |
|
|
advanced |
WorkspaceAdvancedAccess
|
Explicit access to advanced surfaces outside the public happy path. |
artifacts |
ArtifactRegistry
|
|
host_env |
|
|
engine |
The engine behind |
Source code in src/ms_flow/main.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
host_namespace
instance-attribute
¶
host_namespace = str(host_namespace or '').strip() or self.app_id
logging_manager
instance-attribute
¶
logging_manager = LoggingManager(global_log_dir=self.settings_manager.settings.projects_db.parent / 'logs', max_bytes=logging_cfg.max_file_size_mb * 1024 * 1024, backup_count=logging_cfg.backup_count, retention_days=logging_cfg.retention_days, queue_size=logging_cfg.queue_size, app_level=logging_cfg.app_level, executor_level=logging_cfg.executor_level, project_level=logging_cfg.project_level, console_level=logging_cfg.console_level, root_namespace=self.host_namespace)
project_manager
instance-attribute
¶
project_manager = ProjectManager(self.master_db, app_id_filter=self.app_id)
advanced
property
¶
advanced: WorkspaceAdvancedAccess
Explicit access to advanced surfaces outside the public happy path.
engine
property
¶
engine
The engine behind submit, built at first use: the one that shares out this machine,
with its threads as thread and its pool of processes as local.
Raises ms_flow.engine.Busy when another process of this user already runs one.
app_setting_specs
¶
app_setting_specs()
Source code in src/ms_flow/main.py
85 86 | |
get_app_setting
¶
get_app_setting(key: str)
Source code in src/ms_flow/main.py
88 89 | |
update_app_setting
¶
update_app_setting(key: str, value, *, save_global_too: bool = False) -> None
Source code in src/ms_flow/main.py
91 92 93 94 95 96 97 | |
get_project_resource_contract
¶
get_project_resource_contract()
Source code in src/ms_flow/_api_impl/project_runtime.py
16 17 | |
project_resource_specs
¶
project_resource_specs()
Source code in src/ms_flow/_api_impl/project_runtime.py
19 20 | |
list_project_resources
¶
list_project_resources(project_id: str | UUID | None = None) -> dict[str, ProjectResource]
Source code in src/ms_flow/_api_impl/project_runtime.py
30 31 32 33 34 | |
get_project_resource
¶
get_project_resource(key: str, project_id: str | UUID | None = None) -> ProjectResource
Source code in src/ms_flow/_api_impl/project_runtime.py
36 37 38 39 40 | |
get_project_resource_path
¶
get_project_resource_path(key: str, *parts: str | Path, project_id: str | UUID | None = None, create_parent: bool = False) -> Path
Source code in src/ms_flow/_api_impl/project_runtime.py
42 43 44 45 46 47 48 49 50 51 52 53 | |
get_project_resource_map
¶
get_project_resource_map(project_id: str | UUID | None = None) -> dict[str, dict[str, str]]
Source code in src/ms_flow/_api_impl/project_runtime.py
55 56 57 58 59 | |
list_projects
¶
list_projects(page: int = 1, items_per_page: int = 20) -> list[Project]
Source code in src/ms_flow/_api_impl/project_runtime.py
102 103 | |
open_project
¶
open_project(project_id: str | UUID, *, cancel_running_tasks_on_switch: bool = True, touch_master: bool = True, extra_dirs: list[str] | None = None) -> ProjectContext
Source code in src/ms_flow/_api_impl/project_runtime.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
create_project
¶
create_project(name: str, folder: Path | str, description: str = '', tags: list[str] | None = None, base_settings: SettingsProfile = 'global', scope: str = 'full', activate: bool = True, extra_dirs: list[str] | None = None) -> ProjectContext
Source code in src/ms_flow/_api_impl/project_runtime.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | |
find_project
¶
find_project(*, name: str | None = None, folder: Path | str | None = None) -> Project | None
Source code in src/ms_flow/_api_impl/project_runtime.py
176 177 178 179 180 181 182 | |
create_or_open_project
¶
create_or_open_project(*, name: str, folder: Path | str, description: str = '', tags: list[str] | None = None, base_settings: SettingsProfile = 'global', scope: str = 'full', cancel_running_tasks_on_switch: bool = True, activate: bool = True, extra_dirs: list[str] | None = None) -> ProjectContext
Source code in src/ms_flow/_api_impl/project_runtime.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
register_task_canceller
¶
register_task_canceller(project_id: str | UUID, canceller: Callable[[], None])
Source code in src/ms_flow/_api_impl/project_runtime.py
243 244 245 246 247 248 | |
get_app_logger
¶
get_app_logger(name: str = 'core')
Source code in src/ms_flow/_api_impl/project_runtime.py
250 251 | |
get_executor_logger
¶
get_executor_logger(name: str = 'core')
Source code in src/ms_flow/_api_impl/project_runtime.py
253 254 | |
get_project_logger
¶
get_project_logger(name: str = 'core')
Source code in src/ms_flow/_api_impl/project_runtime.py
256 257 | |
get_entity_loader_context
¶
get_entity_loader_context() -> ProjectDataContext
Source code in src/ms_flow/_api_impl/project_runtime.py
259 260 | |
get_project_data_context
¶
get_project_data_context() -> ProjectDataContext
Source code in src/ms_flow/_api_impl/project_runtime.py
262 263 | |
refresh_master_db
¶
refresh_master_db()
Source code in src/ms_flow/_api_impl/project_runtime.py
265 266 267 | |
close_project
¶
close_project(cancel_running_tasks: bool = True)
Close the active project. Its jobs that have not ended are interrupted, not lost: submitted again under the same key in a later session, each continues where it stopped.
With cancel_running_tasks=False, raises instead when one is still going on.
Source code in src/ms_flow/_api_impl/project_runtime.py
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | |
get_project_activity
¶
get_project_activity(project_id: str | UUID | None = None) -> dict[str, Any]
What is still going on in a project: its jobs that have not ended, and the external
tasks registered with register_task_canceller. Only the active project has any.
Source code in src/ms_flow/_api_impl/project_runtime.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |
delete_projects
¶
delete_projects(project_ids: list[str | UUID], delete_files: bool = True)
Source code in src/ms_flow/_api_impl/project_runtime.py
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | |
shutdown
¶
shutdown()
Source code in src/ms_flow/_api_impl/project_runtime.py
374 375 376 377 378 379 380 381 382 383 | |
executors
¶
executors() -> list[dict]
One row for each executor kept: name, kind, hosts, default, and state:
on, starting, off, or blocked when it is off and not to be started now, with
why. Safe to poll.
Source code in src/ms_flow/_api_impl/job_api.py
143 144 145 146 147 148 149 150 151 152 153 154 | |
activate
¶
activate(name: str, on_line=None) -> None
Start the executor kept as name, which takes jobs under that name from then on.
Refused with ValueError while another active one has one of its machines or is a Ray
too (see blocked). A Ray that takes this machine puts the pool of it aside until it
is deactivated, so the pool must have no job that has not ended. A host that is not
reached or not prepared raises, and leaves everything as it was. on_line hears what
is done on a host that is being prepared. It waits for its hosts: not from a thread
that cannot.
Source code in src/ms_flow/_api_impl/job_api.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
deactivate
¶
deactivate(name: str) -> None
Stop the executor active as name. Refused with ValueError while a job submitted
to it has not ended. The pool of this machine comes back if it was aside for this one.
Source code in src/ms_flow/_api_impl/job_api.py
199 200 201 202 203 204 205 206 207 208 | |
save_host
¶
save_host(name: str, **fields) -> None
SettingsManager.save_host, refused while an active executor works on that machine.
Source code in src/ms_flow/_api_impl/job_api.py
227 228 229 | |
remove_host
¶
remove_host(name: str) -> None
Source code in src/ms_flow/_api_impl/job_api.py
231 232 | |
save_executor
¶
save_executor(name: str, **fields) -> None
SettingsManager.save_executor, refused while that executor is active.
Source code in src/ms_flow/_api_impl/job_api.py
234 235 236 | |
remove_executor
¶
remove_executor(name: str) -> None
Source code in src/ms_flow/_api_impl/job_api.py
238 239 | |
submit
¶
submit(job, params=None, *, executor: str | None = None, **options) -> int
Run a ms_flow.engine.job in the active project and return its id, for engine.status
and engine.wait.
Its results go to the project's store, and the job is recorded in the project's
executor.db: closing the project interrupts it, and submitting it again with the same
key= continues it. executor is thread or the name of an active one (see
activate), by default the one the settings give (general.default_executor);
options are those of ms_flow.engine.Flow.submit.
Source code in src/ms_flow/_api_impl/job_api.py
250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
origin
¶
origin(key: str) -> dict
What the job recorded under key in the active project was submitted with: job (its
module:qualname), params and options. Raises for one resume cannot submit again.
Source code in src/ms_flow/_api_impl/job_api.py
272 273 274 275 | |
resume
¶
resume(key: str, **options) -> int
Submit again the job recorded under key (JobSnapshot.key) in the active project,
with the params and options it was submitted with: one that was interrupted (the project
or the application was closed) or canceled continues where it was. options say
otherwise for this run, and bring what the record cannot keep (on_terminal).
Source code in src/ms_flow/_api_impl/job_api.py
277 278 279 280 281 282 283 284 285 | |
move
¶
move(key: str, executor: str, **options) -> int
Continue on executor the job recorded under key: resume, for one that may be running.
A job this session runs is canceled first and waited for, so not from a thread that cannot
wait: what its tasks in flight had done is done again there.
Source code in src/ms_flow/_api_impl/job_api.py
287 288 289 290 291 292 293 294 295 296 297 | |
remote_jobs
¶
remote_jobs(executor: str) -> list[dict]
What the executor of that name has on another machine, whichever session or project
sent it and with or without a project open: see RemoteExecutor.jobs_there. Nothing
when it runs its jobs here. It asks that machine, so not from a thread that cannot wait.
Source code in src/ms_flow/_api_impl/job_api.py
299 300 301 302 303 304 | |
remove_remote_job
¶
remove_remote_job(executor: str, dir: str) -> None
Take one of remote_jobs out of the queue, with what it has there. One that a job of
this session waits for is refused: cancel the job.
Source code in src/ms_flow/_api_impl/job_api.py
306 307 308 309 | |
jobs
¶
jobs(limit: int | None = None) -> list
The jobs of the active project, oldest first; with limit, the last ones.
What a monitor shows: every job on record in the project's journal, the ones of earlier sessions too, each as the engine has it now when it ran it in this session. Reads the journal without owning it and never builds the engine, so it is safe to poll.
Source code in src/ms_flow/_api_impl/job_api.py
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | |
job_tasks
¶
job_tasks(job_id: int | None) -> list
The tasks in flight of a job this session runs, by its JobSnapshot.id: see
Engine.tasks. Nothing for a job of another session. Never builds the engine.
Source code in src/ms_flow/_api_impl/job_api.py
336 337 338 339 | |
job_failures
¶
job_failures(key: str, limit: int = 200) -> list[dict]
attempt, error (the traceback) and input (a short repr of the element) of the
chunks that failed in the job recorded under key (JobSnapshot.key) in the active
project, at most limit.
Source code in src/ms_flow/_api_impl/job_api.py
341 342 343 344 345 346 347 | |
get_project_store
¶
get_project_store()
Source code in src/ms_flow/_api_impl/job_api.py
349 350 | |
Tasks and jobs¶
engine
¶
In-memory job engine.
Classes:
| Name | Description |
|---|---|
JobSnapshot |
|
TaskSnapshot |
A task that is with its executor: one chunk, or the several that share it. |
Functions:
| Name | Description |
|---|---|
task |
Mark a function as a task: what runs on one element, and what it needs to run. |
job |
Mark a function |
report |
From a task, on the thread it was called on: how far the element it is working on is, |
launched |
From a task, on the thread it was called on: a program it started (a |
content_key |
16 bytes that identify a chunk by what it carries, the same in every run. |
writer |
A |
JobSnapshot
dataclass
¶
JobSnapshot(id: int | None, name: str, status: str, chunks_total: int | None, chunks_done: int, chunks_failed: int, chunks_inflight: int, error: str | None, peak_mem_mb: int, started_at: float, finished_at: float | None, key: str | None, chunks_retried: int, progress: float | None)
TaskSnapshot
dataclass
¶
TaskSnapshot(input: str, chunks: int, attempt: int, seconds: float, progress: float | None)
A task that is with its executor: one chunk, or the several that share it.
task
¶
task(*, cpu: float = 1, gpu: float = 0, mem_mb: float = 0, retries: int = 0, batch: int | None = None, batched: bool = False, label=None, thread: bool = False)
Mark a function as a task: what runs on one element, and what it needs to run.
batch is how many elements the author wants to travel together; batched says the
function takes the list of them and returns one result for each.
label is what an element goes by in the monitor and in a failure: a template its keys
fill ("dock-R{receptor}-L{ligand}") or a function (element) that returns the text.
thread is for a function that only waits for a program it starts (a docking binary): on
this machine it runs on a thread of the engine's process and no worker is kept for it. It
tells of the program with launched, which is how it is stopped.
Source code in src/ms_flow/engine/flow.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
job
¶
job(*, task, name: str = '', params_model=None, output=None, reads=(), finalize=None)
Mark a function (params, project) as a job: the elements it gives go to task, the results to output.
It is a generator, or returns whatever can be read one element at a time: ms_flow.query.Rows
for the rows of a table, which also gives the job its total and the table it reads.
output is a table_sink (the task returns rows) or a graph_sink (it returns
{node: rows}; a $ref names its row within what the element returned), a file_sink
(it returns text or bytes, written as they come, or anything else, written as JSON) or a
function (params, project) that returns the job's own writer (see the module's note).
reads is the tables the source reads, besides the ones it says itself: the job waits for
the jobs writing them.
finalize(params, project, snapshot) is called once the job has ended, however it ended:
what there is to tidy up when it failed or was canceled.
Source code in src/ms_flow/engine/flow.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
report
¶
report(fraction: float) -> None
From a task, on the thread it was called on: how far the element it is working on is, from 0 to 1.
Does nothing unless the task is watched: any on the process pool; elsewhere one of a job of few
elements, or of a job submitted with progress=True.
Source code in src/ms_flow/engine/executors.py
386 387 388 389 390 391 392 393 394 395 | |
launched
¶
launched(program) -> None
From a task, on the thread it was called on: a program it started (a subprocess.Popen)
and waits for.
A task on a thread (thread=True) cannot be stopped, only what it runs can: when the engine
stops what is running (a cancel, a time limit) the programs it told of are killed, and one it
tells of after that is killed at once. A task that tells of none runs to its end. Does nothing
in a worker process, which is killed with everything it started.
Source code in src/ms_flow/engine/executors.py
398 399 400 401 402 403 404 405 406 407 408 409 410 411 | |
content_key
¶
content_key(payload) -> bytes
16 bytes that identify a chunk by what it carries, the same in every run.
Equal keys mean equal payloads. The reverse is not promised: a payload built another way (dict order, a set) gets another key, so on resume it is repeated, never skipped.
Source code in src/ms_flow/engine/engine.py
70 71 72 73 74 75 76 77 78 79 80 | |
writer
¶
writer(output, project)
A table_sink, a graph_sink or a file_sink as the writer the engine makes of it: for
a job's own writer that changes what its tasks returned and leaves the writing to a sink.
Source code in src/ms_flow/engine/flow.py
218 219 220 221 | |
Sinks¶
sinks
¶
Functions:
| Name | Description |
|---|---|
table_sink |
Simple helper to persist results into a SQLite table. |
graph_sink |
Helper to persist a multi-table relational graph into SQLite. |
file_sink |
Simple helper to persist results to a file. A relative |
table_sink
¶
table_sink(table: str = '', *, model: type[SQLModel] | None = None, columns: Iterable[str] = (), write_mode: str = 'bulk', conflict_keys: Iterable[str] = (), validate_model: bool = False, db_role: str = 'project', db_path: str | Path = '', schema: str = '', meta: Optional[dict[str, Any]] = None) -> DbOutputSpec
Simple helper to persist results into a SQLite table.
write_mode="upsert" updates existing rows via INSERT ... ON CONFLICT
(conflict_keys) DO UPDATE. Each emitted row only updates the columns it carries
(SET is built per payload), so it needs model and conflict_keys to be the PK
or a UNIQUE constraint of the table.
Source code in src/ms_flow/sinks.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
graph_sink
¶
graph_sink(*, nodes: Iterable[Mapping[str, Any]], relations: Iterable[Mapping[str, Any]] = (), db_role: str = 'project', db_path: str | Path = '', schema: str = '', meta: Optional[dict[str, Any]] = None) -> DbOutputSpec
Helper to persist a multi-table relational graph into SQLite.
Source code in src/ms_flow/sinks.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |
file_sink
¶
file_sink(path: str | Path, *, root: str = 'project', fmt: str = 'json', encoding: str = 'utf-8', ensure_parent: bool = True, append: bool = False, per_batch: bool = False, schema: str = '', meta: Optional[dict[str, Any]] = None) -> FileOutputSpec
Simple helper to persist results to a file. A relative path is under the project.
What a task returns is written as it comes when it is text or bytes, and as a JSON line
otherwise; with fmt="json" the file is one JSON list of what the tasks returned.
append=True (text/binary fmt only) is what a long job needs: each group of results is
added to the file as it arrives. per_batch=True is the other bounded option: one numbered
file (name_00001.ext) for each group. With neither, everything is kept in memory and the
file is written once, when the job completes.
Source code in src/ms_flow/sinks.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |
Queries¶
query
¶
Classes:
| Name | Description |
|---|---|
QuerySpec |
Declarative query specification for project paths and DB transport. |
Rows |
The rows of a table as the source of a job, read in pages by |
Functions:
| Name | Description |
|---|---|
db_pages |
The same rows as |
QuerySpec
dataclass
¶
QuerySpec(table: str = '', fields: tuple[str, ...] = (), filters: dict[str, Any] = dict(), order: tuple[str, ...] = (), limit: Optional[int] = None, offset: int = 0, query: str = '', params: tuple[Any, ...] = (), db_role: str = 'project', db_path: str = '', schema: str = '', meta: dict[str, Any] = dict())
Declarative query specification for project paths and DB transport.
Rows
¶
Rows(source: Any, spec: QuerySpec, *, key: str = 'id', page_size: int = 500)
The rows of a table as the source of a job, read in pages by key.
It stops at the highest key the table had when the job first read it, and the job keeps that: rows that arrive later stay out, also when the job continues. It knows how many rows it has left, which gives the job its total.
Source code in src/ms_flow/query.py
263 264 265 266 267 268 269 270 271 272 | |
db_pages
¶
db_pages(source: Any, spec: QuerySpec, *, key: str = 'id', page_size: int = 500) -> Iterator[dict[str, Any]]
The same rows as db_stream, but in keyset pages.
Each page is its own short read (WHERE key > last ORDER BY key LIMIT n) instead of a single
cursor held open for the whole feed. Feeding a million-ligand screen takes hours, and a read
snapshot held open that long prevents SQLite from checkpointing the WAL: project.db-wal
grows without bound while the job itself writes its results.
Without a usable key (raw SQL, offset, or an order other than key) no keyset is possible:
it falls back to db_stream, which returns exactly the same rows.
Source code in src/ms_flow/query.py
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
The engine¶
engine
¶
In-memory job engine.
Classes:
| Name | Description |
|---|---|
Engine |
|
ProcessExecutor |
loky process pool. A dead worker costs the pool, never the executor: it is rebuilt. |
ThreadExecutor |
|
RayExecutor |
Tasks on a Ray cluster, which shares out its own CPUs, GPUs and memory. |
Flow |
Submits jobs for one project: |
Engine
¶
Engine(cpus: int | None = None, gpus: int = 0, mem_mb: int | None = None, state_dir=None)
mem_mb is the memory budget for chunks; by default physical RAM minus a reserve.
With state_dir, this is the one engine that shares out the local machine: it owns
engine.lock there, and a second process gets Busy instead of the same CPUs.
Methods:
| Name | Description |
|---|---|
register |
Add an executor (see |
unregister |
Shut down the executor registered as |
executor |
The executor registered as |
attach |
Open the journal at |
detach |
Interrupt the jobs recorded in this journal, wait until it is written and close it. |
submit |
Run |
recorded |
The jobs on record in an attached journal, oldest first, as rows of its |
discard |
Drop the job recorded as |
status |
Where the job is. An ended job is forgotten once |
tasks |
The tasks of the job that are with its executor now, the oldest first: what a monitor |
wait |
Block until the job is terminal and its callbacks have run, or |
close |
Interrupt what is running, let pending callbacks finish and stop the executors. |
Source code in src/ms_flow/engine/engine.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
register
¶
register(name: str, executor) -> None
Add an executor (see executors for the contract), in the place of the one that had
the name: see unregister.
Source code in src/ms_flow/engine/engine.py
195 196 197 198 199 200 201 202 | |
unregister
¶
unregister(name: str) -> None
Shut down the executor registered as name, if there is one.
Raises ValueError while a job submitted to it has not ended: an executor changes
between jobs, never under one.
Source code in src/ms_flow/engine/engine.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | |
executor
¶
executor(name: str)
The executor registered as name.
Source code in src/ms_flow/engine/engine.py
220 221 222 | |
attach
¶
attach(name: str, path) -> None
Open the journal at path; jobs submitted with journal=name are recorded in it.
Raises Busy when another process owns that file.
Source code in src/ms_flow/engine/engine.py
224 225 226 227 228 229 230 231 | |
detach
¶
detach(name: str) -> None
Interrupt the jobs recorded in this journal, wait until it is written and close it.
Source code in src/ms_flow/engine/engine.py
233 234 235 236 237 238 239 240 241 242 243 244 245 | |
submit
¶
submit(executor: str, fn, chunks, *, cpu: float = 1, gpu: float = 0, mem_mb: float = 0, window: int | None = None, max_cpu: float | None = None, priority: int = 0, total: int | None = None, name: str = '', journal: str | None = None, key: str | None = None, after=(), retries: int = 0, retry_wait: float = 1.0, timeout: float | None = None, batch: int | None = None, batched: bool = False, progress: bool | None = None, origin=None, label=None, thread: bool = False, on_result=None, on_results=None, on_error=None, on_finish=None, on_terminal=None) -> int
Run fn(payload) for every payload in chunks on executor. Returns the job id.
cpu, gpu and mem_mb are what one chunk needs; a dict payload may say otherwise
for itself with _cpu_required, _gpu_required and _mem_mb, which fn never sees.
max_cpu caps the job as a whole; a chunk above the cap runs alone in its job.
chunks is read on its own thread. window is how many chunks may be between dispatch
and delivered result, and how many more may be read ahead.
Chunks that take well under a task's worth of time travel several to a task where the
executor allows it; each still succeeds, fails and is recorded on its own. Left to
itself the window grows with them, since such chunks are small by construction.
batch settles it instead: that many chunks to a task, fewer only in the last one and
where chunks with different needs meet. With batched, fn takes the list of payloads
of a task and returns one result for each, in order; if it raises, all of them failed.
A chunk's key is content_key(payload), or the _key of a dict payload.
on_result(key, result), on_error(key, error) and on_terminal(snapshot) run on
the delivery thread, in order; a chunk counts as done once on_result returns.
on_results(pairs) takes its place for whoever writes results somewhere: it gets every
(key, result) of the job that is waiting, up to _DELIVER_MAX, and all of them count as
done once it returns. When it raises, each pair is offered again alone and only the ones
that raise again fail, so it must leave nothing behind when it raises: one transaction.
on_finish() is the job's last word: called once every chunk has ended, on the delivery
thread, before the job counts as completed and the jobs after it start. If it raises, the
job fails with that error. A job that failed, was canceled or interrupted never gets there.
chunks may know more than how to be read. With remaining(), how many chunks it has yet
to give, the job has a total while it runs: counted at its first chunk and every
_RECOUNT seconds after, exact once the source is read. With open(state), it is called
before the first read and what it returns (JSON) is kept with the job in its journal and
passed back as state when the job continues; state is None the first time.
progress says whether the tasks of the job are watched, for JobSnapshot.progress to
count how far the running chunks are and not only the ones that ended (see
executors.report). By default every task is on an executor that watches for nothing (the
process pool); on another, a task of several chunks, and a task of one when the job has at
most two for each worker.
thread says that fn spends its time waiting for a program it starts, not in Python:
an executor that keeps a worker for each task and can spare it (the process pool) runs it
on a thread of this process, so no worker holds memory for it. Such a chunk is stopped
through the programs it tells of (see executors.launched).
label is what a chunk goes by in tasks and in a failure, in place of a short repr of
its payload: a template the payload fills ("dock-R{receptor}-L{ligand}", the keys of a
dict payload) or a function (payload) that returns the text. It runs here, never in a
worker, and only for a chunk that is shown.
journal names an attached journal; without it nothing about the job reaches the disk.
key is what the job is called there. Submitting an interrupted or canceled job again
under its key continues it: the chunks it had done are recognised by their own keys and
not run again. discard is how such a job is given up instead.
origin is whatever whoever submits wants kept with the job there (JSON): recorded
gives it back in spec.
after is the ids of the jobs that must end first; chunks is not read until they do,
and the job fails if one of them fails, is canceled or is interrupted.
A chunk that fails runs again up to retries times, retry_wait seconds after the first
failure and twice as long after each one that follows. Failing is raising, running past
timeout seconds or killing its worker; falling with somebody else's worker is not, and
costs the chunk nothing. on_error hears of a chunk only once it has failed for good.
timeout needs an executor that can stop a running chunk; the process pool stops all
of its chunks to stop one, and the others start again.
Source code in src/ms_flow/engine/engine.py
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
recorded
¶
recorded(journal: str) -> list[dict]
The jobs on record in an attached journal, oldest first, as rows of its jobs table.
Source code in src/ms_flow/engine/engine.py
362 363 364 | |
discard
¶
discard(journal: str, key: str) -> None
Drop the job recorded as key, which must not be running: what it had done is forgotten
and the key is free again. An interrupted or canceled job keeps a row per done chunk until
it is continued or discarded.
Source code in src/ms_flow/engine/engine.py
366 367 368 369 370 371 | |
status
¶
status(job_id: int) -> JobSnapshot
Where the job is. An ended job is forgotten once _REMEMBERED others ended after it;
what a journal recorded of it stays in recorded().
Source code in src/ms_flow/engine/engine.py
373 374 375 376 377 | |
tasks
¶
tasks(job_id: int) -> list[TaskSnapshot]
The tasks of the job that are with its executor now, the oldest first: what a monitor unfolds under a running job. At most a window of them, and none once the job ended.
Source code in src/ms_flow/engine/engine.py
379 380 381 382 383 384 385 386 387 388 389 390 391 | |
wait
¶
wait(job_id: int, timeout: float | None = None) -> JobSnapshot
Block until the job is terminal and its callbacks have run, or timeout passes.
Source code in src/ms_flow/engine/engine.py
397 398 399 400 401 402 403 | |
close
¶
close() -> None
Interrupt what is running, let pending callbacks finish and stop the executors.
Source code in src/ms_flow/engine/engine.py
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | |
ProcessExecutor
¶
ProcessExecutor(max_workers: int, idle: float | None = 60.0)
loky process pool. A dead worker costs the pool, never the executor: it is rebuilt.
The tasks of a job submitted with thread=True run on threads of this process instead, and
no worker is started or kept for them: they count against the same capacity.
A worker with nothing to do for idle seconds leaves, with the memory of what it imported,
and one is started again when there is work for it; None keeps them all.
Source code in src/ms_flow/engine/executors.py
119 120 121 122 123 124 125 126 127 128 129 | |
ThreadExecutor
¶
ThreadExecutor(max_workers: int, uses_tokens: bool = False)
Source code in src/ms_flow/engine/executors.py
85 86 87 88 | |
RayExecutor
¶
RayExecutor(address: str | None = None, capacity: int | None = None, shared: bool | None = None, **init)
Tasks on a Ray cluster, which shares out its own CPUs, GPUs and memory.
address=None starts a cluster on this machine and stops it at shutdown; "auto" or
host:port joins one that is running, on this machine or on others. What ray.init takes
goes through. The functions and what they import have to be installed on every node.
shared says that every node sees the disk of this machine, as a cluster started here does.
Without it each File of an element goes once to Ray's object store and a node keeps its
copy in its temporary directory, and what a result names under a Dir comes back with it.
Source code in src/ms_flow/engine/executors.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | |
Flow
¶
Flow(engine, project)
Submits jobs for one project: project is what a source receives and where the sinks write.
Methods:
| Name | Description |
|---|---|
submit |
Start |
origin |
What the job recorded as |
resume |
Submit again the job recorded as |
Source code in src/ms_flow/engine/flow.py
277 278 279 280 | |
submit
¶
submit(job, params=None, *, executor: str = 'process', batch: int | None = None, cpu: float | None = None, thread: bool | None = None, after=(), on_terminal=None, **engine) -> int
Start job and return its id. batch, cpu and thread override the task's; after
adds jobs to wait for.
What else Engine.submit takes (key, journal, priority, total...) passes through.
The sink is written on the engine's delivery thread, one transaction for each group of
results, and an element is done once its rows are in.
A job that goes to a journal always has a key, its own or one made here, and is recorded
with what it was submitted with: see resume.
Source code in src/ms_flow/engine/flow.py
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
origin
¶
origin(key: str, journal: str) -> dict
What the job recorded as key in journal was submitted with: job (as named reads
it), params and options.
Source code in src/ms_flow/engine/flow.py
337 338 339 340 341 342 343 344 345 | |
resume
¶
resume(key: str, *, journal: str, **options) -> int
Submit again the job recorded as key in journal, with what it was submitted with:
one that was interrupted or canceled continues. options say otherwise for this run,
and bring what a record cannot keep (on_terminal, after).
Source code in src/ms_flow/engine/flow.py
347 348 349 350 351 352 353 | |
Hosts and executors¶
HostConfig
¶
Bases: BaseModel
A machine that runs jobs, kept by name. The one named local is this machine.
ExecutorConfig
¶
Bases: BaseModel
Who runs jobs, kept by name. hosts are names in Settings.hosts.
loky is the pool of processes of this machine. ray is a Ray cluster started from this
machine over its hosts: with local among them this machine works in it too. queue is
the queue of an HPC, and its one host the login node (local when this runs on it).