Skip to content

SubprocessTao

pytao.SubprocessTao

SubprocessTao(*args, env=None, **kwargs)

Bases: Tao

Subprocess helper for Tao.

This special version of the Tao class executes a Python subprocess which interacts with Tao through ctypes.

This can be used exactly as the normal Tao object with the primary added benefit that Fortran crashes will not affect the main Python process.

For full parameter information, see the Tao class documentation.

Usage

When creating many SubprocessTao objects, ensure to close the subprocess when done with it. This can be done manually:

>>> tao.close_subprocess()

Or automatically by way of a context manager:

>>> with SubprocessTao(init_file="$ACC_ROOT_DIR/bmad-doc/tao_examples/cbeta_cell/tao.init", plot=True) as tao:
...     tao.plot("floor")

To add a new environment variable in addition to the parent process environment:

>>> import os
>>> with SubprocessTao(init_file="...", env={**os.environ, "NEW_VAR": "NEW_VALUE"}) as tao:
...     print(tao.version())

Parameters:

Name Type Description Default
env dict[str, str] or None

Environment variables to use for the subprocess. If None, defaults to os.environ.

None

Attributes:

Name Type Description
subprocess_env dict[str, str]

Environment variables to use for the subprocess. It is recommended to use a new SubprocessTao instance in order to update these environment variables. However, while this dictionary may be updated in place, it will only be applied after the next subprocess starts and initializes. That is, tao.close_subprocess() and tao.init().

Source code in pytao/subproc.py
434
435
436
437
438
439
440
441
442
443
444
445
def __init__(self, *args, env: dict[str, str] | None = None, **kwargs):
    self._subproc_pipe_ = None
    self.subprocess_env = dict(env if env is not None else os.environ)

    try:
        super().__init__(*args, **kwargs)
    except Exception:
        try:
            self.close_subprocess()
        except Exception:
            pass
        raise

Attributes

pytao.SubprocessTao.subprocess_alive property
subprocess_alive

Subprocess is still running.

Functions

pytao.SubprocessTao.close_subprocess
close_subprocess(*, force=False)

Close the Tao subprocess.

Source code in pytao/subproc.py
454
455
456
457
458
459
460
461
def close_subprocess(self, *, force: bool = False) -> None:
    """Close the Tao subprocess."""
    if self._subproc_pipe_ is not None:
        if force:
            self._subproc_pipe_.close_forcefully()
        else:
            self._subproc_pipe_.close()
    self._subproc_pipe_ = None
pytao.SubprocessTao.cmd
cmd(cmd, raises=True)

Runs a command, and returns the output.

Source code in pytao/subproc.py
568
569
570
571
@override
def cmd(self, cmd: str, raises: bool = True) -> list[str]:
    """Runs a command, and returns the output."""
    return self._send_command_through_pipe("cmd", cmd, raises=raises)
pytao.SubprocessTao.cmd_integer
cmd_integer(cmd, raises=True)

Runs a command, and returns an integer array.

Source code in pytao/subproc.py
581
582
583
584
585
586
587
@override
def cmd_integer(self, cmd: str, raises: bool = True) -> np.ndarray | None:
    """Runs a command, and returns an integer array."""
    return cast(
        Optional[np.ndarray],
        self._send_command_through_pipe("cmd_integer", cmd, raises=raises),
    )
pytao.SubprocessTao.cmd_real
cmd_real(cmd, raises=True)

Runs a command, and returns a floating point array.

Source code in pytao/subproc.py
573
574
575
576
577
578
579
@override
def cmd_real(self, cmd: str, raises: bool = True) -> np.ndarray | None:
    """Runs a command, and returns a floating point array."""
    return cast(
        Optional[np.ndarray],
        self._send_command_through_pipe("cmd_real", cmd, raises=raises),
    )
pytao.SubprocessTao.get_active_beam_track_element
get_active_beam_track_element()

Get the active element index being tracked.

Source code in pytao/subproc.py
589
590
591
592
593
594
def get_active_beam_track_element(self) -> int:
    """Get the active element index being tracked."""
    return cast(
        int,
        self._send_command_through_pipe("get_active_beam_track_element", "", raises=True),
    )
pytao.SubprocessTao.subprocess_call
subprocess_call(func, **kwargs)

Run a custom function in the subprocess.

The function must be readily importable by Python and not a dynamically created function or lambda. The first argument passed will be the tao object, and the remainder of the arguments are user-specified by keyword only.

Source code in pytao/subproc.py
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
def subprocess_call(self, func: Callable, **kwargs):
    """
    Run a custom function in the subprocess.

    The function must be readily importable by Python and not a dynamically
    created function or `lambda`.  The first argument passed will be the
    `tao` object, and the remainder of the arguments are user-specified by
    keyword only.
    """
    if not self.subprocess_alive:
        raise TaoDisconnectedError(
            "Tao subprocess is no longer running. Make a new `SubprocessTao` "
            "object or reinitialize with `.init()`."
        )
    assert self._subproc_pipe_ is not None
    return self._subproc_pipe_.send_receive_custom(func, kwargs)
pytao.SubprocessTao.timeout
timeout(when)

Context manager to set a timeout for a block of SubprocessTao calls.

Note that there is no possibility for a graceful timeout. In the event of a timeout, the subprocess will be terminated.

Parameters:

Name Type Description Default
when float

The timeout duration in seconds.

required

Yields:

Type Description
None

Yields control back to the calling context.

Raises:

Type Description
TimeoutError

If the block of code does not execute within when seconds. The Tao subprocess is forcefully terminated at this point.

Source code in pytao/subproc.py
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@contextlib.contextmanager
def timeout(self, when: float):
    """
    Context manager to set a timeout for a block of SubprocessTao calls.

    Note that there is no possibility for a graceful timeout. In the event
    of a timeout, the subprocess will be terminated.

    Parameters
    ----------
    when : float
        The timeout duration in seconds.

    Yields
    ------
    None
        Yields control back to the calling context.

    Raises
    ------
    TimeoutError
        If the block of code does not execute within `when` seconds.
        The Tao subprocess is forcefully terminated at this point.
    """
    with subprocess_timeout_context([self], timeout=when):
        yield