| 156 | return Pidfile.load(self.name, self.cache_dir) |
| 157 | |
| 158 | def launch( |
| 159 | self, |
| 160 | document_root, # type: str |
| 161 | port=0, # type: int |
| 162 | timeout=5.0, # type: float |
| 163 | verbose_error=False, # type: bool |
| 164 | ): |
| 165 | # type: (...) -> LaunchResult |
| 166 | |
| 167 | pidfile = self.pidfile() |
| 168 | if pidfile and pidfile.alive(): |
| 169 | return LaunchResult(server_info=pidfile.server_info, already_running=True) |
| 170 | |
| 171 | # Not proper daemonization, but good enough. |
| 172 | log = os.path.join(self.cache_dir, "log.txt") |
| 173 | http_server_module = "http.server" if sys.version_info[0] == 3 else "SimpleHTTPServer" |
| 174 | env = os.environ.copy() |
| 175 | # N.B.: We set up line buffering for the process pipes as well as the underlying Python |
| 176 | # running the http server to ensure we can observe the `Serving HTTP on ...` line we need to |
| 177 | # grab the ephemeral port chosen. |
| 178 | env.update(PYTHONUNBUFFERED="1") |
| 179 | with safe_open(log, "w") as fp: |
| 180 | process = launch_python_daemon( |
| 181 | args=[sys.executable, "-m", http_server_module, str(port)], |
| 182 | env=env, |
| 183 | cwd=document_root, |
| 184 | # N.B.: Line buffering under Python 3 requires both bufsize=1 and |
| 185 | # universal_newlines=True. |
| 186 | bufsize=1, |
| 187 | universal_newlines=True, |
| 188 | stdout=fp.fileno(), |
| 189 | stderr=subprocess.STDOUT, |
| 190 | ) |
| 191 | |
| 192 | pidfile = Pidfile.record( |
| 193 | cache_dir=self.cache_dir, server_log=log, pid=process.pid, timeout=timeout |
| 194 | ) |
| 195 | if not pidfile: |
| 196 | try: |
| 197 | kill(process.pid) |
| 198 | except OSError as e: |
| 199 | raise LaunchError( |
| 200 | log, |
| 201 | additional_msg=( |
| 202 | "Also failed to kill the partially launched server at pid {pid}: " |
| 203 | "{err}".format(pid=process.pid, err=e) |
| 204 | ), |
| 205 | verbose=verbose_error, |
| 206 | ) |
| 207 | raise LaunchError(log, verbose=verbose_error) |
| 208 | return LaunchResult(server_info=pidfile.server_info, already_running=False) |
| 209 | |
| 210 | def shutdown(self): |
| 211 | # type: () -> Optional[ServerInfo] |