1################################################################ 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20################################################################ 21 22"""Starting a process in its own session or process group, and killing it together with everything it started.""" 23import os 24import signal 25import subprocess 26 27POSIX = os.name == "posix" 28 29 30def spawn(cmd, **kw): 31 """Popen in a new session (POSIX) or process group (Windows), so kill_tree() can reach the whole tree.""" 32 if POSIX: 33 kw.setdefault("start_new_session", True) 34 else: 35 kw.setdefault("creationflags", subprocess.CREATE_NEW_PROCESS_GROUP) 36 return subprocess.Popen(cmd, **kw) 37 38 39def kill_tree(proc, wait=10): 40 """Kills `proc` (which must come from spawn()) and its descendants, then reaps it. 41 42 POSIX: SIGKILL to the process group, only while the leader is still alive (its pid cannot have been reused) and 43 never to our own group. Windows: taskkill /T /F. A leader that has already exited leaves its children alone.""" 44 if proc.poll() is None: 45 if POSIX: 46 try: 47 if os.getpgid(proc.pid) != os.getpgrp(): 48 os.killpg(proc.pid, signal.SIGKILL) 49 else: 50 proc.kill() 51 except ProcessLookupError: 52 pass 53 else: 54 subprocess.run(["taskkill", "/T", "/F", "/PID", str(proc.pid)], capture_output=True) 55 if proc.poll() is None: 56 proc.kill() 57 try: 58 proc.wait(timeout=wait) 59 except subprocess.TimeoutExpired: 60 pass 61