def __init__(self,
                 reactor, executable, args, environment, path, proto,
                 uid=None, gid=None, childFDs=None):
        """
        Spawn an operating-system process.

        This is where the hard work of disconnecting all currently open
        files / forking / executing the new process happens.  (This is
        executed automatically when a Process is instantiated.)

        This will also run the subprocess as a given user ID and group ID, if
        specified.  (Implementation Note: this doesn't support all the arcane
        nuances of setXXuid on UNIX: it will assume that either your effective
        or real UID is 0.)
        """
        if not proto:
            assert 'r' not in childFDs.values()
            assert 'w' not in childFDs.values()
        _BaseProcess.__init__(self, proto)

        self.pipes = {}
        # keys are childFDs, we can sense them closing
        # values are ProcessReader/ProcessWriters

        helpers = {}
        # keys are childFDs
        # values are parentFDs

        if childFDs is None:
            childFDs = {0: "w", # we write to the child's stdin
                        1: "r", # we read from their stdout
                        2: "r", # and we read from their stderr
                        }

        debug = self.debug
        if debug: print("childFDs", childFDs)

        _openedPipes = []
        def pipe():
            r, w = os.pipe()
            _openedPipes.extend([r, w])
            return r, w

        # fdmap.keys() are filenos of pipes that are used by the child.
        fdmap = {} # maps childFD to parentFD
        try:
            for childFD, target in items(childFDs):
                if debug: print("[%d]" % childFD, target)
                if target == "r":
                    # we need a pipe that the parent can read from
                    readFD, writeFD = pipe()
                    if debug: print("readFD=%d, writeFD=%d" % (readFD, writeFD))
                    fdmap[childFD] = writeFD     # child writes to this
                    helpers[childFD] = readFD    # parent reads from this
                elif target == "w":
                    # we need a pipe that the parent can write to
                    readFD, writeFD = pipe()
                    if debug: print("readFD=%d, writeFD=%d" % (readFD, writeFD))
                    fdmap[childFD] = readFD      # child reads from this
                    helpers[childFD] = writeFD   # parent writes to this
                else:
                    assert type(target) == int, '%r should be an int' % (target,)
                    fdmap[childFD] = target      # parent ignores this
            if debug: print("fdmap", fdmap)
            if debug: print("helpers", helpers)
            # the child only cares about fdmap.values()

            self._fork(path, uid, gid, executable, args, environment, fdmap=fdmap)
        except:
            for pipe in _openedPipes:
                os.close(pipe)
            raise

        # we are the parent process:
        self.proto = proto

        # arrange for the parent-side pipes to be read and written
        for childFD, parentFD in items(helpers):
            os.close(fdmap[childFD])
            if childFDs[childFD] == "r":
                reader = self.processReaderFactory(reactor, self, childFD,
                                        parentFD)
                self.pipes[childFD] = reader

            if childFDs[childFD] == "w":
                writer = self.processWriterFactory(reactor, self, childFD,
                                        parentFD, forceReadHack=True)
                self.pipes[childFD] = writer

        try:
            # the 'transport' is used for some compatibility methods
            if self.proto is not None:
                self.proto.makeConnection(self)
        except:
            log.err()

        # The reactor might not be running yet.  This might call back into
        # processEnded synchronously, triggering an application-visible
        # callback.  That's probably not ideal.  The replacement API for
        # spawnProcess should improve upon this situation.
        registerReapProcessHandler(self.pid, self)

        Spawn an operating-system process.

        This is where the hard work of disconnecting all currently open
        files / forking / executing the new process happens.  (This is
        executed automatically when a Process is instantiated.)

        This will also run the subprocess as a given user ID and group ID, if
        specified.  (Implementation Note: this doesn't support all the arcane
        nuances of setXXuid on UNIX: it will assume that either your effective
        or real UID is 0.)
        
stackoverflow/venv/lib/python3.6/site-packages/twisted/internet/process.py