diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index 558f2099c66f5e..6dca88ece89def 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -226,6 +226,9 @@ def _runtest_inner2(ns, test_name): if test_runner is None: test_runner = functools.partial(_test_module, the_module) + pid = os.getpid() + nchild_before = support.get_child_processes(pid) + try: if ns.huntrleaks: # Return True if the test leaked references @@ -249,6 +252,16 @@ def _runtest_inner2(ns, test_name): gc.garbage.clear() support.reap_children() + if nchild_before is not None: + nchild_after = support.get_child_processes(pid) + diff = len(nchild_after) - len(nchild_before) + if diff: + print(f"Warning -- {test_name} leaked " + f"{diff} child processes: " + f"created={nchild_after - nchild_before}, " + f"completed={nchild_before - nchild_after}", + file=sys.stderr) + support.environment_altered = True return refleak diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 215bab8131a044..cf20241be7daca 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -3403,3 +3403,38 @@ def __exit__(self, *exc_info): del self.exc_value del self.exc_traceback del self.thread + + +def get_child_processes(ppid): + """ + Get directly child processes of parent process identifier 'pid'. + """ + proc_dir = '/proc' + try: + names = os.listdir(proc_dir) + except FileNotFoundError: + return None + + children = [] + for name in names: + if not name.isdigit(): + continue + pid = int(name) + + filename = os.path.join(proc_dir, str(pid), 'status') + try: + fp = open(filename, encoding="utf-8") + except FileNotFoundError: + # process completed: ignore it + continue + + proc_ppid = None + with fp: + for line in fp: + if line.startswith('PPid:'): + proc_ppid = int(line[5:].strip()) + break + if proc_ppid == ppid: + children.append(pid) + + return set(children)