docs: def wait(self, timeout_s: float = None) -> int:
          """
          Wait for up to ``timeout_s`` for the child process to finish.
  
          Args:
              timeout_s: maximum time to wait or ``None`` to wait forever
  
          Returns:
              process return code; or ``0`` if it wasn't running, or ``1`` if
              it managed to exit without a return code
  
          Raises:
              subprocess.TimeoutExpired: if the process continues to run
  
          """
          if not self.running:
              return 0
          retcode = self.process.wait(timeout=timeout_s)
          # We won't get further unless the process has stopped.
          if retcode is None:
              self.error("Subprocess finished, but return code was None")
              retcode = 1  # we're promising to return an int
          elif retcode == 0:
              self.info("Subprocess finished cleanly (return code 0).")
          else:
              self.error(
                  "Subprocess finished, but FAILED (return code {}). "
                  "Logs were: {} (stdout), {} (stderr)".format(
                      retcode,
                      self.details.logfile_out,
                      self.details.logfile_err))
          self.running = False
          return retcode