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
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
def __init__(self, *args, env: Optional[Dict[str, str]] = None, **kwargs):
    self._subproc_pipe_ = None
    self.subprocess_env = dict(env if env is not None else os.environ)

    try:
        # There is a bit of spaghetti here:
        # * super().__init__() calls
        # * self.init() which wraps
        # * self._init() which is defined below in this class, opening the subproc
        super().__init__(*args, **kwargs)
    except Exception as ex:
        # In case we don't make a usable SubprocessTao object, close the
        # subprocess so it doesn't linger.
        try:
            self.close_subprocess()
        except Exception:
            pass
        raise ex

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
557
558
559
560
561
562
563
564
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
643
644
645
646
647
@override
def cmd(self, cmd: str, raises: bool = True) -> List[str]:
    """Runs a command, and returns the output."""
    res = self._send_command_through_pipe("cmd", cmd, raises=raises)
    return cast(List[str], res)
pytao.SubprocessTao.cmd_integer
cmd_integer(cmd, raises=True)

Runs a command, and returns an integer array.

Source code in pytao/subproc.py
655
656
657
658
659
@override
def cmd_integer(self, cmd: str, raises: bool = True) -> Optional[np.ndarray]:
    """Runs a command, and returns an integer array."""
    res = self._send_command_through_pipe("cmd_integer", cmd, raises=raises)
    return cast(Optional[np.ndarray], res)
pytao.SubprocessTao.cmd_real
cmd_real(cmd, raises=True)

Runs a command, and returns a floating point array.

Source code in pytao/subproc.py
649
650
651
652
653
@override
def cmd_real(self, cmd: str, raises: bool = True) -> Optional[np.ndarray]:
    """Runs a command, and returns a floating point array."""
    res = self._send_command_through_pipe("cmd_real", cmd, raises=raises)
    return cast(Optional[np.ndarray], res)
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
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
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
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
@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