Reimplements Executor and SimpleExecutor start to allow setting stdin/stdout/stderr/cwd It may break input/output/communicate, but will ensure child output redirects won't break parent process by filling the PIPE. Also, catches ProcessExitedWithError and raise FileN
(self)
| 102 | return False |
| 103 | |
| 104 | def start(self): |
| 105 | """ |
| 106 | Reimplements Executor and SimpleExecutor start to allow setting stdin/stdout/stderr/cwd |
| 107 | |
| 108 | It may break input/output/communicate, but will ensure child output redirects won't |
| 109 | break parent process by filling the PIPE. |
| 110 | Also, catches ProcessExitedWithError and raise FileNotFoundError if exitcode was 127 |
| 111 | """ |
| 112 | if self.pre_start_check(): |
| 113 | # Some other executor (or process) is running with same config: |
| 114 | raise AlreadyRunning(self) |
| 115 | |
| 116 | if self.process is None: |
| 117 | command = self.command |
| 118 | if not self._shell: |
| 119 | command = self.command_parts # type: ignore |
| 120 | |
| 121 | if isinstance(self.stdio, (list, tuple)): |
| 122 | stdin, stdout, stderr = self.stdio |
| 123 | else: |
| 124 | stdin = stdout = stderr = self.stdio # type: ignore |
| 125 | env = os.environ.copy() |
| 126 | env[ENV_UUID] = self._uuid |
| 127 | popen_kwargs = { |
| 128 | "shell": self._shell, |
| 129 | "stdin": stdin, |
| 130 | "stdout": stdout, |
| 131 | "stderr": stderr, |
| 132 | "universal_newlines": True, |
| 133 | "env": env, |
| 134 | "cwd": self.cwd, |
| 135 | } |
| 136 | if platform.system() != "Windows": |
| 137 | popen_kwargs["preexec_fn"] = os.setsid # type: ignore |
| 138 | self.process = subprocess.Popen(command, **popen_kwargs) |
| 139 | |
| 140 | self._set_timeout() |
| 141 | |
| 142 | try: |
| 143 | self.wait_for(self.check_subprocess) |
| 144 | except ProcessExitedWithError as e: |
| 145 | if e.exit_code == 127: |
| 146 | raise FileNotFoundError( |
| 147 | f"Can not execute {command!r}, check that the executable exists." |
| 148 | ) from e |
| 149 | else: |
| 150 | output_file_names = { |
| 151 | io.name for io in (stdout, stderr) if hasattr(io, "name") # type: ignore |
| 152 | } |
| 153 | if output_file_names: |
| 154 | log.warning("Process output file(s)", output_files=output_file_names) |
| 155 | raise |
| 156 | return self |
| 157 | |
| 158 | def kill(self): |
| 159 | STDOUT = subprocess.STDOUT # pylint: disable=no-member |