Source code for ophyd_async.tango.testing._launch

"""Launch, and build TRLs for, this repo's Tango device server subprocesses.

Kept separate from `_tango_device_servers.py` (this package's and
`ophyd_async.tango.demo`'s) deliberately: those two files are genuinely
standalone (zero `ophyd_async` imports - can be copied into or run from a
bare PyTango venv), and importing `ophyd_async.testing` here would break
that if this lived in the same file.

`start_tango_device_servers` mirrors `ophyd_async.epics.testing.start_ioc`'s
shape exactly - generic, doesn't know or care which catalog it's launching.
"""

import sys
from pathlib import Path

from ophyd_async.testing import ManagedSubprocess, start_subprocess

_READY_MARKER = "TANGO_DEVICE_SERVERS_READY"


[docs] def start_tango_device_servers(script: Path, *args: str) -> ManagedSubprocess: """Start a Tango device servers subprocess by running `script` with `args`. Generic: doesn't know or care which catalog `script` is - e.g. `ophyd_async.tango.testing.DEVICE_SERVERS` (this package's fixed `basic`/`everything` catalog) or `ophyd_async.tango.demo.DEVICE_SERVERS` (motor/channel/detector). Invoked with the current interpreter, not `-m ophyd_async...`, so the exact same argv works verbatim from a venv that doesn't have `ophyd_async` installed at all. If you need a different Tango install/version, run `script` with that venv's python directly instead of going through this function - same as any other standalone script. :param script: The script implementing the device server catalog to host. :param args: Positional args for that script's `__main__`, e.g. `(prefix, str(port))` or `(prefix, str(port), str(num_channels))` - whatever that particular catalog's script expects. You choose `port` yourself, e.g. via `ophyd_async.testing.find_free_port`, and need to hang onto it to build TRLs afterwards (see `trl` below). """ return start_subprocess( [sys.executable, str(script), *args], _READY_MARKER, # MultiDeviceTestContext's own startup timeout is 30s; give the outer # readiness wait some headroom above that rather than racing it. startup_timeout=45.0, stop_input=None, # each catalog's __main__ block exits on stdin EOF )
[docs] def trl(prefix: str, port: int, device_name: str) -> str: """Build the TRL a device server subprocess serves `device_name` at.""" return f"tango://127.0.0.1:{port}/{prefix}/{device_name}#dbase=no"