Serve requests, synchronously (no thread or fork).
(self)
| 210 | return {"platform": self.options.platform, "python_version": py_version} |
| 211 | |
| 212 | def serve(self) -> None: |
| 213 | """Serve requests, synchronously (no thread or fork).""" |
| 214 | |
| 215 | command = None |
| 216 | server = IPCServer(CONNECTION_NAME, self.timeout) |
| 217 | orig_stdout = sys.stdout |
| 218 | orig_stderr = sys.stderr |
| 219 | |
| 220 | try: |
| 221 | with open(self.status_file, "w") as f: |
| 222 | json.dump({"pid": os.getpid(), "connection_name": server.connection_name}, f) |
| 223 | f.write("\n") # I like my JSON with a trailing newline |
| 224 | while True: |
| 225 | with server: |
| 226 | data = receive(server) |
| 227 | sys.stdout = WriteToConn(server, "stdout", sys.stdout.isatty()) |
| 228 | sys.stderr = WriteToConn(server, "stderr", sys.stderr.isatty()) |
| 229 | resp: dict[str, Any] = {} |
| 230 | if "command" not in data: |
| 231 | resp = {"error": "No command found in request"} |
| 232 | else: |
| 233 | command = data["command"] |
| 234 | if not isinstance(command, str): |
| 235 | resp = {"error": "Command is not a string"} |
| 236 | else: |
| 237 | command = data.pop("command") |
| 238 | try: |
| 239 | resp = self.run_command(command, data) |
| 240 | except Exception: |
| 241 | # If we are crashing, report the crash to the client |
| 242 | tb = traceback.format_exception(*sys.exc_info()) |
| 243 | resp = {"error": "Daemon crashed!\n" + "".join(tb)} |
| 244 | resp.update(self._response_metadata()) |
| 245 | resp["final"] = True |
| 246 | send(server, resp) |
| 247 | raise |
| 248 | resp["final"] = True |
| 249 | try: |
| 250 | resp.update(self._response_metadata()) |
| 251 | send(server, resp) |
| 252 | except OSError: |
| 253 | pass # Maybe the client hung up |
| 254 | if command == "stop": |
| 255 | reset_global_state() |
| 256 | sys.exit(0) |
| 257 | finally: |
| 258 | # Revert stdout/stderr so we can see any errors. |
| 259 | sys.stdout = orig_stdout |
| 260 | sys.stderr = orig_stderr |
| 261 | |
| 262 | # If the final command is something other than a clean |
| 263 | # stop, remove the status file. (We can't just |
| 264 | # simplify the logic and always remove the file, since |
| 265 | # that could cause us to remove a future server's |
| 266 | # status file.) |
| 267 | if command != "stop": |
| 268 | os.unlink(self.status_file) |
| 269 | try: |
no test coverage detected