os.process
==========

.. py:module:: os.process

.. autoapi-nested-parse::

   Suprocesses management.

   This module provides some functions and classes to ease spawn of
   processes in blocking or non blocking mode, redirection of its stdout,
   stderr and stdin. It also provides some helpers to check the process
   status



Attributes
----------

.. autoapisummary::

   os.process.CmdLine
   os.process.logger
   os.process.CMD_LOGGER_NAME
   os.process.cmdlogger
   os.process.has_psutil


Exceptions
----------

.. autoapisummary::

   os.process.WaitError


Classes
-------

.. autoapisummary::

   os.process.Run
   os.process.File


Functions
---------

.. autoapisummary::

   os.process.subprocess_setup
   os.process.get_rlimit
   os.process.quote_arg
   os.process.to_cmd_lines
   os.process.command_line_image
   os.process.enable_commands_handler
   os.process.disable_commands_handler
   os.process.wait_for_processes
   os.process.is_running
   os.process.kill_process_tree


Module Contents
---------------

.. py:data:: CmdLine

.. py:data:: logger

.. py:data:: CMD_LOGGER_NAME
   :value: 'os.process.cmdline'


.. py:data:: cmdlogger

.. py:data:: has_psutil
   :value: True


.. py:function:: subprocess_setup() -> None

   Reset SIGPIPE handler.

   Python installs a SIGPIPE handler by default. This is usually not
   what non-Python subprocesses expect.


.. py:function:: get_rlimit(platform: str | None = None) -> str

.. py:function:: quote_arg(arg: str) -> str

   Return the quoted version of the given argument.

   Returns a human-friendly representation of the given argument, but with all
   extra quoting done if necessary.  The intent is to produce an argument
   image that can be copy/pasted on a POSIX shell command (at a shell prompt).
   :param arg: argument to quote


.. py:function:: to_cmd_lines(cmds: AnyCmdLine) -> list[CmdLine]

.. py:function:: command_line_image(cmds: AnyCmdLine) -> str

   Return a string image of the given command(s).

   :param cmds: Same as the cmds parameter in the Run.__init__ method.

   This method also handles quoting as defined for POSIX shells.
   This means that arguments containing special characters
   (such as a simple space, or a backslash, for instance),
   are properly quoted.  This makes it possible to execute
   the same command by copy/pasting the image in a shell
   prompt.

   The result is expected to be a string that can be sent verbatim
   to a shell for execution.


.. py:function:: enable_commands_handler(filename: str | pathlib.Path, mode: str = 'a') -> logging.Handler

   Add a handler that log all commands launched with Run in a file.

   :param filename: path to log the commands
   :param mode: mode used to open the file (default is 'a')
   :return: the added handler


.. py:function:: disable_commands_handler(handler: logging.Handler) -> None

   Disable special handler for commands.

   :param handler: Handler returned by enable_commands_handler


.. py:class:: Run(cmds: AnyCmdLine, cwd: str | pathlib.Path | None = None, output: DEVNULL_VALUE | PIPE_VALUE | str | pathlib.Path | IO | None = PIPE, error: STDOUT_VALUE | DEVNULL_VALUE | PIPE_VALUE | str | pathlib.Path | IO | None = STDOUT, input: DEVNULL_VALUE | PIPE_VALUE | str | bytes | pathlib.Path | IO | None = None, bg: bool = False, timeout: int | None = None, env: dict | None = None, set_sigpipe: bool = True, parse_shebang: bool = False, ignore_environ: bool = True)

   Class to handle processes.

   :ivar cmds: The ``cmds`` argument passed to the __init__ method
       (a command line passed in a list, or a list of command lines passed as
       a list of list).
   :ivar status: The exit status. As the exit status is only meaningful after
       the process has exited, its initial value is None (see __init__'s "bg"
       parameter for more information about this).

       When a problem running the command is detected and a process does not
       get created, its value gets set to the special value 127.
   :ivar raw_out: process standard output as bytes (if instanciated with
       output = PIPE). Use self.out to get a decoded string.
   :ivar raw_err: same as raw_out but for standard error.
   :ivar pid: PID. Set to -1 if the command failed to run.


   .. py:attribute:: input_file


   .. py:attribute:: output_file


   .. py:attribute:: error_file


   .. py:attribute:: status
      :type:  int | None
      :value: None



   .. py:attribute:: raw_out
      :type:  bytes
      :value: b''



   .. py:attribute:: raw_err
      :type:  bytes
      :value: b''



   .. py:attribute:: cmds
      :value: []



   .. py:attribute:: pid


   .. py:property:: out
      :type: str


      Process output as string.

      Attempt is done to decode as utf-8 the output. If the output is not in
      utf-8 a string representation will be returned
      (see e3.text.bytes_as_str).



   .. py:property:: err
      :type: str


      Process error as string.

      Attempt is done to decode as utf-8 the output. If the output is not in
      utf-8 a string representation will be returned
      (see e3.text.bytes_as_str).



   .. py:method:: command_line_image() -> str

      Get shell command line image of the spawned command(s).

      This just a convenient wrapper around the function of the same
      name.



   .. py:method:: close_files() -> None

      Close all file descriptors.



   .. py:method:: __error(error: Exception, cmds: list[CmdLine]) -> None

      Set pid to -1 and status to 127 before closing files.



   .. py:method:: wait() -> int

      Wait until process ends and return its status.

      :return: exit code of the process



   .. py:method:: poll() -> int | None

      Check the process status and set self.status if available.

      This method checks whether the underlying process has exited
      or not. If it hasn't, then it just returns None immediately.
      Otherwise, it stores the process' exit code in self.status
      and then returns it.

      :return: None if the process is still alive; otherwise, returns
        the process exit status.



   .. py:method:: kill(recursive: bool = True, timeout: int = 3) -> None

      Kill the process.

      :param recursive: if True, try to kill the complete process tree
      :param timeout: wait timeout (in seconds) after sending the kill
          signal (when recursive=True)



   .. py:method:: interrupt() -> None

      Send SIGINT to the process, kill on Windows.



   .. py:method:: is_running() -> bool

      Check whether the process is running.



   .. py:method:: children() -> list[Any]

      Return list of child processes (using psutil).



.. py:class:: File(name: Any, mode: str = 'r')

   Can be a PIPE, a file object.


   .. py:attribute:: fd
      :type:  int | IO[str]


   .. py:attribute:: name


   .. py:attribute:: to_close
      :value: False



   .. py:method:: get_command() -> str | bytes | None

      Return the command to run to create the pipe.



   .. py:method:: close() -> None

      Close the file if needed.



.. py:exception:: WaitError

   Bases: :py:obj:`Exception`


   Common base class for all non-exit exceptions.


.. py:function:: wait_for_processes(process_list: list[Run], timeout: float) -> int | None

   Wait for several processes spawned with Run.

   :param process_list: a list of Run objects
   :param timeout: a timeout in seconds. If 0 block until a process ends.

   :return: None in case of timeout or the index in process Run corresponding
       to the first process that end


.. py:function:: is_running(pid: int) -> bool

   Check whether a process with the given pid is running.

   :param pid: an integer (e.g the value of Run().pid)


.. py:function:: kill_process_tree(pid: int | Any, timeout: int = 3) -> bool

   Kill a hierarchy of processes.

   :param pid: pid of the toplevel process
   :param timeout: wait timeout after sending the kill signal
   :return: True if all processes either don't exist or have been killed,
       False if there are some processes still alive.


