Skip to content

Commit 1fc5e5f

Browse files
committed
docs: update part 1
1 parent 7165c4a commit 1fc5e5f

20 files changed

Lines changed: 281 additions & 198 deletions

File tree

docs/apidoc_t/package.rst_t

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@
1515

1616
{%- if is_namespace %}
1717
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
18-
{% elif 'tomato.drivers.dummy' == pkgname %}
19-
{{- "**dummy**: A dummy driver module" | heading }}
20-
{% elif 'tomato.drivers.biologic' == pkgname %}
21-
{{- "**biologic**: Driver for BioLogic potentiostats" | heading }}
18+
{% elif 'tomato.daemon' == pkgname %}
19+
{{- "**tomato.daemon**: Functions and modules comprising the tomato daemon" | heading }}
20+
{% elif 'tomato.driverinterface_2_0' == pkgname %}
21+
{{- "**tomato.DriverInterface**: version 2.0" | heading }}
22+
{% elif 'tomato.driverinterface_2_1' == pkgname %}
23+
{{- "**tomato.DriverInterface**: version 2.1" | heading }}
24+
{% elif 'tomato.ketchup' == pkgname %}
25+
{{- "**tomato.ketchup**: CLI and API for the tomato job queue" | heading }}
26+
{% elif 'tomato.tomato' == pkgname %}
27+
{{- "**tomato.tomato**: CLI and API for the tomato daemon" | heading }}
28+
{% elif 'tomato.passata' == pkgname %}
29+
{{- "**tomato.passata**: CLI and API for tomato drivers and components" | heading }}
2230
{% else %}
2331
{{- [pkgname, "package"] | join(" ") | e | heading }}
2432
{% endif %}

docs/source/quickstart.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ The following concepts are used in **tomato**:
141141

142142
pip3[pipeline 3] -.-> c3
143143

144+
.. _settings-file:
144145

145146
Settings file
146147
`````````````
@@ -212,6 +213,8 @@ Additional, *driver*-specific settings may be provided in this section. Each *dr
212213

213214
Further *driver*-specific settings, such as ``dllpath`` or ``calibration``, can be specified here. All of these *driver*-specific settings are passed to each *driver* when its process is launched and the :class:`DriverInterface` is initialised, and can therefore contain paths to various libraries or other files necessary for the *driver* to function.
214215

216+
.. _devices-file:
217+
215218
Devices file
216219
````````````
217220
This ``yaml``-formatted file contains information about each *device*, corresponding to an individual piece of hardware managed by **tomato**, as well as information about the organisation of the individually-addressable *components* of those *devices* into *pipelines*.
@@ -344,4 +347,4 @@ As of ``tomato-2.0``, the :obj:`task_params` specified in the *payload* are vali
344347

345348
.. |devfile| replace:: *devices file*
346349

347-
.. _devfile: quickstart.html#devices-file
350+
.. _devfile: quickstart.html#devices-file

src/tomato/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def parse_args(parser, verbose, is_tomato=False):
4545
if "func" in args:
4646
ret = args.func(**vars(args), verbosity=verbosity, context=context, **kwargs)
4747
if args.yaml:
48-
print(yaml.dump(ret.dict()))
48+
ret.data.model_dump()
49+
print(yaml.dump(ret.model_dump()))
4950
else:
5051
print(f"{'Success' if ret.success else 'Failure'}: {ret.msg}")
5152

src/tomato/daemon/__init__.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"""
2-
**tomato.daemon**: module of functions comprising the tomato daemon
3-
-------------------------------------------------------------------
42
.. codeauthor::
53
Peter Kraus
64
@@ -24,8 +22,7 @@
2422

2523
def setup_logging(daemon: Daemon):
2624
"""
27-
Helper function to set up logging (folder, filename, verbosity, format) based on
28-
the passed daemon state.
25+
Helper function to set up logging (folder, filename, verbosity, format) based on the passed daemon state.
2926
"""
3027
logdir = Path(daemon.settings["logdir"])
3128
logdir.mkdir(parents=True, exist_ok=True)
@@ -39,12 +36,9 @@ def setup_logging(daemon: Daemon):
3936

4037
def tomato_daemon():
4138
"""
42-
The function called when `tomato-daemon` is executed.
39+
The function called when :obj:`tomato-daemon` is executed.
4340
44-
Manages the state of the tomato daemon, including recovery of state via
45-
:mod:`~tomato.daemon.io`, processing state updates via :mod:`~tomato.daemon.cmd`,
46-
and the manager threads for both jobs (:mod:`~tomato.daemon.job`) and drivers
47-
(:mod:`~tomato.daemon.driver`).
41+
Manages the state of the tomato daemon, spawning manager threads for jobs (:mod:`~tomato.daemon.job`), drivers (:mod:`~tomato.daemon.driver`), and pipelines (:mod:`~tomato.daemon.pip`). Parses the configuration in the :ref:`settings file <settings-file>` and :ref:`devices file <devices-file>`.
4842
"""
4943
parser = argparse.ArgumentParser(add_help=False)
5044
parser.add_argument("--port", "-p", type=int, default=1234)
@@ -57,8 +51,7 @@ def tomato_daemon():
5751
setup_logging(daemon)
5852
logger.info("logging set up with verbosity %s", daemon.verbosity)
5953

60-
# TODO: setup should not be a thing really.
61-
cmd.setup(msg={}, daemon=daemon)
54+
cmd.reload(msg={}, daemon=daemon)
6255
context = zmq.Context()
6356
rep = context.socket(zmq.REP)
6457
logger.debug("binding zmq.REP socket on port %d", daemon.port)
@@ -80,15 +73,15 @@ def tomato_daemon():
8073
socks = dict(poller.poll(1000))
8174
if rep in socks:
8275
msg = rep.recv_pyobj()
83-
logger.debug(f"received {msg=}")
76+
logger.debug("received msg: %s", msg)
8477
if "cmd" not in msg:
85-
logger.error(f"received msg without cmd: {msg=}")
78+
logger.error("received msg without cmd: %s", msg)
8679
ret = Reply(success=False, msg="received msg without cmd", data=msg)
8780
elif hasattr(cmd, msg["cmd"]):
8881
ret = getattr(cmd, msg["cmd"])(msg, daemon)
8982
else:
90-
logger.error(f"received msg with an invalid cmd: {msg=}")
91-
logger.debug(f"reply with {ret=}")
83+
logger.error("received msg with an invalid cmd: %s", msg["cmd"])
84+
logger.debug("reply: %s", ret)
9285
rep.send_pyobj(ret)
9386
if daemon.status == "stop":
9487
end = True

src/tomato/daemon/cmd.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
.. codeauthor::
55
Peter Kraus
66
7-
All functions in this module expect a :class:`dict` containing the command specification
8-
and a :class:`~tomato.models.Daemon` object as arguments. The :class:`Daemon` object is
9-
altered by the command.
7+
All functions in this module expect a :class:`dict` containing the command specification and a :class:`~tomato.models.Daemon` object as arguments. The :class:`~tomato.models.Daemon` object may be altered by the command.
108
119
All functions in this module return a :class:`~tomato.models.Reply`.
1210
@@ -27,10 +25,12 @@
2725

2826

2927
def status(msg: dict, daemon: Daemon) -> Reply:
28+
"""Return daemon status, containing the current tomato configuration."""
3029
return Reply(success=True, msg=daemon.status, data=daemon)
3130

3231

3332
def stop(msg: dict, daemon: Daemon) -> Reply:
33+
"""Stop the tomato daemon."""
3434
logger = logging.getLogger(f"{__name__}.stop")
3535
logger.debug("%s", msg)
3636
dbpath = daemon.settings["jobs"]["dbpath"]
@@ -44,9 +44,16 @@ def stop(msg: dict, daemon: Daemon) -> Reply:
4444
return Reply(success=True, msg="daemon set to stop")
4545

4646

47-
def setup(msg: dict, daemon: Daemon) -> Reply:
48-
logger = logging.getLogger(f"{__name__}.setup")
47+
def reload(msg: dict, daemon: Daemon) -> Reply:
48+
"""
49+
Set-up or reload the tomato daemon using its configuration files.
50+
51+
.. note::
52+
53+
When reloading settings, tomato checks whether any running jobs use resources (i.e. drivers, pipelines, components) that would be removed if the new configuration were to be applied. If this is the case, the configuration **will not be updated**.
4954
55+
"""
56+
logger = logging.getLogger(f"{__name__}.setup")
5057
if daemon.status == "bootstrap":
5158
dbpath = daemon.settings["jobs"]["dbpath"]
5259
drvs = []
@@ -82,7 +89,6 @@ def setup(msg: dict, daemon: Daemon) -> Reply:
8289
success=False,
8390
msg="could not parse updated settings",
8491
)
85-
logger.debug(f"{nd=}")
8692
ndf = nd.devicefile
8793
# First, check that we're not touching anything associated with a running job
8894
check_components = set()
@@ -148,9 +154,4 @@ def setup(msg: dict, daemon: Daemon) -> Reply:
148154
daemon.devicefile = ndf
149155
logger.info("reload successful with pipelines: '%s'", ndf.pipelines.keys())
150156

151-
return Reply(success=True, msg="setup successful", data=daemon)
152-
153-
154-
def reload(msg: dict, daemon: Daemon, **kwargs: dict) -> Reply:
155-
# daemon.settings = toml.load(Path(daemon.appdir) / "settings.toml")
156-
return Reply(success=True, msg="daemon settings reloaded", data=daemon.settings)
157+
return Reply(success=True, msg="reload successful", data=daemon)

src/tomato/daemon/crates.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
**tomato.daemon.crates**: functions for creating RO-crates
3+
----------------------------------------------------------
4+
.. codeauthor::
5+
Peter Kraus
6+
"""
7+
18
import logging
29
from typing import Union
310

src/tomato/daemon/db.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
**tomato.daemon.db**: shared functions using :mod:`sqlite3`
3+
-----------------------------------------------------------
4+
.. codeauthor::
5+
Peter Kraus
6+
"""
7+
18
import logging
29
import os
310
import sqlite3

src/tomato/daemon/driver.py

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,19 @@
3434

3535

3636
def tomato_driver_bootstrap(
37-
req: zmq.Socket, logger: logging.Logger, interface: ModelInterface, driver: str
37+
req: zmq.Socket,
38+
logger: logging.Logger,
39+
interface: ModelInterface,
40+
driver: str,
3841
):
42+
"""
43+
Function that attempts to register all configured components for this driver.
44+
45+
This helper function is executed when the ``register`` command is set to the driver process. The daemon is first polled for up-to-date configuration, and then each of the returned components is registered, if necessary, using :func:`cmp_register` of the driver interface.
46+
47+
In case the registration fails, a limited number of retries (as specified by the ``MAX_REGISTER_RETRIES`` constant) can be attempted on subsequent runs of this function.
48+
49+
"""
3950
logger.debug("getting daemon status")
4051
req.send_pyobj(dict(cmd="status"))
4152
daemon: Daemon = req.recv_pyobj().data
@@ -73,6 +84,16 @@ def tomato_driver_bootstrap(
7384
def perform_idle_measurements(
7485
interface: ModelInterface, t_last: Union[float, None]
7586
) -> Union[float, None]:
87+
"""
88+
Function running idle measurements on the driver.
89+
90+
This function periodically runs the :func:`cmp_measure` on each component on the driver. The interval is determined from driver configuration using the ``"idle_measurement_interval"`` setting, driver defaults using the :obj:`interface.idle_measurement_interval` object, or tomato default (``IDLE_MEASUREMENT_INTERVAL``).
91+
92+
.. note::
93+
94+
How idle measurements are handled is up to the individual driver. By default, the :func:`cmp_measure` function will not submit new measurements when a task or a measurement is already running.
95+
96+
"""
7697
if not hasattr(interface, "cmp_measure"):
7798
return t_last
7899

@@ -93,17 +114,29 @@ def perform_idle_measurements(
93114
return t_now
94115

95116

117+
def stop_tomato_driver(port: int, context) -> Reply:
118+
"""
119+
The default mechanism for stopping tomato drivers.
120+
121+
This function is used by the tomato driver manager to gracefully stop the driver, if an existing driver port is known.
122+
"""
123+
req = context.socket(zmq.REQ)
124+
req.connect(f"tcp://127.0.0.1:{port}")
125+
req.send_pyobj(dict(cmd="stop", sender=f"{__name__}.stop_tomato_driver"))
126+
return req.recv_pyobj()
127+
128+
96129
def kill_tomato_driver(pid: int):
97130
"""
98-
Wrapper around :func:`psutil.terminate`.
131+
The backup mechanism for stoping tomato drivers.
132+
133+
This function is useful if the driver port is unknown or not responsive.
134+
135+
Wrapper around :func:`psutil.terminate`. Here we kill the (grand)children of the process with the name of `tomato-job`, i.e. the individual task functions. This allows the `tomato-job` process to exit gracefully once the task functions join.
99136
100-
Here we kill the (grand)children of the process with the name of `tomato-job`,
101-
i.e. the individual task functions. This allows the `tomato-job` process to exit
102-
gracefully once the task functions join.
137+
.. note::
103138
104-
Note that on Windows, the `tomato-job.exe` process has two children: a `python.exe`
105-
which is the actual process running the job, and `conhost.exe`, which we want to
106-
avoid killing.
139+
On Windows, the `tomato-job.exe` process has two children: a `python.exe` process which is the actual process running the job, and `conhost.exe` process, which we want to avoid killing.
107140
108141
"""
109142
proc = psutil.Process(pid)
@@ -121,17 +154,11 @@ def tomato_driver() -> None:
121154
"""
122155
The function called when `tomato-driver` is executed.
123156
124-
This function is responsible for managing all activities involving devices of a
125-
single driver type.
157+
This function is responsible for managing all activities involving devices of a single driver type.
126158
127-
First, the list of devices (and their channel/address) for the specified driver is
128-
fetched from the `tomato-daemon`. Then, a new instance of the specified driver is
129-
spawned, populating its device map using the above list. If successful, the current
130-
process information is fed back to the `tomato-daemon`.
159+
First, the list of devices (and their channel/address) for the specified driver is fetched from the `tomato-daemon`. Then, a new instance of the specified driver is spawned, populating its device map using the above list. The state of the driver is stored .
131160
132-
Afterwards, the main loop handles all requests related to each of the devices
133-
managed by this driver process, including job commands. Finally, if the driver is
134-
instructed to stop, it attempts to perform a teardown before exiting.
161+
Afterwards, the main loop handles all requests related to each of the devices managed by this driver process, including job commands. Finally, if the driver is instructed to stop, it attempts to perform a teardown before exiting.
135162
"""
136163
# ARGUMENT PARSING
137164
parser = argparse.ArgumentParser()
@@ -299,19 +326,11 @@ def tomato_driver() -> None:
299326
logger.info("driver '%s' is quitting", args.driver)
300327

301328

302-
def stop_tomato_driver(port: int, context) -> Reply:
303-
req = context.socket(zmq.REQ)
304-
req.connect(f"tcp://127.0.0.1:{port}")
305-
req.send_pyobj(dict(cmd="stop", sender=f"{__name__}.stop_tomato_driver"))
306-
return req.recv_pyobj()
307-
308-
309329
def manager(port: int, timeout: int = 1000):
310330
"""
311331
The driver manager thread of `tomato-daemon`.
312332
313-
This manager ensures individual driver processes are (re-)spawned and instructed to
314-
quit as necessary.
333+
This manager ensures individual driver processes are (re-)spawned and instructed to quit as necessary. The drivers are periodically checked using the ``HEARTBEAT`` constant as the interval. All changes are stored in the drivers table.
315334
"""
316335
sender = f"{__name__}.manager"
317336
context = zmq.Context()

src/tomato/daemon/drvdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
**tomato.daemon.drvdb**: the sqlite database for drivers in tomato
3-
--------------------------------------------------------------------
3+
------------------------------------------------------------------
44
.. codeauthor::
55
Peter Kraus
66

src/tomato/daemon/io.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
**tomato.daemon.io**: functions for storing and loading data
3-
------------------------------------------------------------
2+
**tomato.daemon.io**: functions for storing job data
3+
----------------------------------------------------
44
.. codeauthor::
55
Peter Kraus
66
@@ -20,9 +20,7 @@
2020

2121
def merge_netcdfs(job: Job, snapshot=False) -> str:
2222
"""
23-
Merges the individual pickled :class:`xr.Datasets` of each Component found in :obj:`job.jobpath`
24-
into a single :class:`xr.DataTree`, which is then stored in the NetCDF file,
25-
using the Component `role` as the group label.
23+
Merges all of the individual pickled :class:`~xarray.Dataset` files from each component found in :obj:`job.jobpath` into a single :class:`~xarray.DataTree`, which is then stored in the NetCDF file. The role of each component is used as the group label.
2624
"""
2725
logger = logging.getLogger(f"{__name__}.merge_netcdf")
2826
assert job.jobpath is not None
@@ -52,8 +50,7 @@ def merge_netcdfs(job: Job, snapshot=False) -> str:
5250

5351
def data_to_pickle(ds: xr.Dataset, path: Path, role: str):
5452
"""
55-
Dumps the data provided as :class:`xr.Dataset` into a ``pickle``. Concatenates with
56-
any existing data stored in the ``pickle``.
53+
Dumps the data provided as :class:`~xarray.Dataset` using :mod:`pickle`. Concatenates the new data with any existing data stored in the existing ``.pkl`` file.
5754
"""
5855
logger = logging.getLogger(f"{__name__}.data_to_pickle")
5956
ds.attrs["role"] = role

0 commit comments

Comments
 (0)