docs: def spawn(self, args, executable=None, stdin=None, stdout=None, stderr=None,
                shell=False, cwd=None, env=None, flags=0, extra_handles=None):
          """Spawn a new child process.
  
          The executable to spawn and its arguments are determined by *args*,
          *executable* and *shell*.
  
          When *shell* is set to ``False`` (the default), *args* is normally a
          sequence and it contains both the program to execute (at index 0), and
          its arguments.
  
          When *shell* is set to ``True``, then *args* is normally a string and
          it indicates the command to execute through the shell.
  
          The *executable* argument can be used to override the executable to
          execute. If *shell* is ``False``, it overrides ``args[0]``. This is
          sometimes used on Unix to implement "fat" executables that behave
          differently based on argv[0]. If *shell* is ``True``, it overrides the
          shell to use. The default shell is ``'/bin/sh'`` on Unix, and the value
          of $COMSPEC (or ``'cmd.exe'`` if it is unset) on Windows.
  
          The *stdin*, *stdout* and *stderr* arguments specify how to handle
          standard input, output, and error, respectively. If set to None, then
          the child will inherit our respective stdio handle. If set to the
          special constant ``PIPE`` then a pipe is created. The pipe will be
          connected to a :class:`gruvi.StreamProtocol` which you can use to read
          or write from it. The stream protocol instance is available under
          either :attr:`stdin`, :attr:`stdout` or :attr:`stderr`. All 3 stdio
          arguments can also be a file descriptor, a file-like object, or a pyuv
          ``Stream`` instance.
  
          The *extra_handles* specifies any extra handles to pass to the client.
          It must be a sequence where each element is either a file descriptor, a
          file-like objects, or a ``pyuv.Stream`` instance. The position in the
          sequence determines the file descriptor in the client. The first
          position corresponds to FD 3, the second to 4, etc. This places these
          file descriptors directly after the stdio handles.
  
          The *cwd* argument specifies the directory to change to before
          executing the child. If not provided, the current directory is used.
  
          The *env* argument specifies the environment to use when executing the
          child. If provided, it must be a dictionary. By default, the current
          environment is used.
  
          The *flags* argument can be used to specify optional libuv
          ``uv_process_flags``. The only relevant flags are
          ``pyuv.UV_PROCESS_DETACHED`` and ``pyuv.UV_PROCESS_WINDOWS_HIDE``. Both
          are Windows specific and are silently ignored on Unix.
          """
          if self._process:
              raise RuntimeError('child process already spawned')
          self._child_exited.clear()
          self._closed.clear()
          self._exit_status = None
          self._term_signal = None
          hub = get_hub()
          if isinstance(args, str):
              args = [args]
              flags |= pyuv.UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS
          else:
              args = list(args)
          if shell:
              if hasattr(os, 'fork'):
                  # Unix
                  if executable is None:
                      executable = '/bin/sh'
                  args = [executable, '-c'] + args
              else:
                  # Windows
                  if executable is None:
                      executable = os.environ.get('COMSPEC', 'cmd.exe')
                  args = [executable, '/c'] + args
          if executable is None:
              executable = args[0]
          kwargs = {}
          if env is not None:
              kwargs['env'] = env
          if cwd is not None:
              kwargs['cwd'] = cwd
          kwargs['flags'] = flags
          handles = self._get_child_handles(hub.loop, stdin, stdout, stderr, extra_handles)
          kwargs['stdio'] = handles
          process = pyuv.Process.spawn(hub.loop, args, executable,
                                       exit_callback=self._on_child_exit, **kwargs)
          # Create stdin/stdout/stderr transports/protocols.
          if handles[0].stream:
              self._stdin = self._connect_child_handle(handles[0])
          if handles[1].stream:
              self._stdout = self._connect_child_handle(handles[1])
          if handles[2].stream:
              self._stderr = self._connect_child_handle(handles[2])
          self._process = process