Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
2 changes: 1 addition & 1 deletion 2 Lib/asyncio/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ def tearDown(self):

self.doCleanups()
support.threading_cleanup(*self._thread_cleanup)
support.reap_children()
# support.reap_children()


@contextlib.contextmanager
Expand Down
9 changes: 7 additions & 2 deletions 9 Lib/socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ class ForkingMixIn:
active_children = None
max_children = 40

def collect_children(self):
def collect_children(self, *, blocking=False):
"""Internal routine to wait for children that have exited."""
if self.active_children is None:
return
Expand All @@ -571,7 +571,8 @@ def collect_children(self):
# Now reap all defunct children.
for pid in self.active_children.copy():
try:
pid, _ = os.waitpid(pid, os.WNOHANG)
flags = 0 if blocking else os.WNOHANG
pid, _ = os.waitpid(pid, flags)
# if the child hasn't exited yet, pid will be 0 and ignored by
# discard() below
self.active_children.discard(pid)
Expand Down Expand Up @@ -620,6 +621,10 @@ def process_request(self, request, client_address):
finally:
os._exit(status)

def server_close(self):
super().server_close()
self.collect_children(blocking=True)


class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""
Expand Down
44 changes: 35 additions & 9 deletions 44 Lib/test/libregrtest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@ def format_duration(seconds):
return '%.0f min %.0f sec' % (minutes, seconds)


def run_bisect_in_subprocess(testname):
from subprocess import Popen, PIPE
import json

cmd = [sys.executable, *support.args_from_interpreter_flags(),
'-u', # Unbuffered stdout and stderr
'-m', 'test.bisect',
'--fail-env-changed',
testname]

# Running the child from the same working directory as regrtest's original
# invocation ensures that TEMPDIR for the child is the same when
# sysconfig.is_python_build() is true. See issue 15300.
popen = Popen(cmd,
close_fds=(os.name != 'nt'),
cwd=support.SAVEDCWD)
with popen:
retcode = popen.wait()
return retcode


class Regrtest:
"""Execute a test suite.

Expand Down Expand Up @@ -276,26 +297,27 @@ def list_cases(self):
print(count(len(self.skipped), "test"), "skipped:", file=sys.stderr)
printlist(self.skipped, file=sys.stderr)

def rerun_failed_tests(self):
def rerun_failed_tests(self, tests):
self.ns.verbose = True
self.ns.failfast = False
self.ns.verbose3 = False
self.ns.match_tests = None

print("Re-running failed tests in verbose mode")
for test in self.bad[:]:
print("Re-running test %r in verbose mode" % test, flush=True)
print("Run bisection on failed tests")
for test in tests:
print("Bisect test %r" % test, flush=True)
try:
self.ns.verbose = True
ok = runtest(self.ns, test)
retcode = run_bisect_in_subprocess(test)
except KeyboardInterrupt:
self.interrupted = True
# print a newline separate from the ^C
print()
break
else:
if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}:
self.bad.remove(test)
pass
#if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}:
# self.bad.remove(test)
else:
if self.bad:
print(count(len(self.bad), 'test'), "failed again:")
Expand Down Expand Up @@ -538,8 +560,12 @@ def _main(self, tests, kwargs):
self.run_tests()
self.display_result()

if self.ns.verbose2 and self.bad:
self.rerun_failed_tests()
tests = self.bad
for test in self.environment_changed:
if test not in tests:
tests.append(test)
if self.ns.verbose2 and tests:
self.rerun_failed_tests(tests)

self.finalize()
if self.bad:
Expand Down
55 changes: 35 additions & 20 deletions 55 Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ def get_attribute(obj, name):
real_max_memuse = 0
failfast = False
match_tests = None
_match_tests_pattern = None

# _original_stdout is meant to hold stdout at the time regrtest began.
# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever.
Expand Down Expand Up @@ -1906,20 +1907,21 @@ def _run_suite(suite):


def _match_test(test):
global match_tests
global match_tests, _match_tests_pattern

if match_tests is None:
return True
test_id = test.id()

for match_test in match_tests:
if fnmatch.fnmatchcase(test_id, match_test):
return True
if _match_tests_pattern is None or _match_tests_pattern[0] != match_tests:
patterns = [fnmatch.translate(pattern) for pattern in match_tests]
regex = '(?:%s)' % '|'.join(patterns)
match = re.compile(regex).match
_match_tests_pattern = (match_tests, match)
else:
match = _match_tests_pattern[1]

for name in test_id.split("."):
if fnmatch.fnmatchcase(name, match_test):
return True
return False
return (match(test_id) is not None)


def run_unittest(*classes):
Expand Down Expand Up @@ -2073,26 +2075,39 @@ def decorator(*args):
threading_cleanup(*key)
return decorator


def reap_children():
"""Use this function at the end of test_main() whenever sub-processes
are started. This will help ensure that no extra children (zombies)
stick around to hog resources and create problems when looking
for refleaks.
"""
global environment_altered

# Need os.waitpid(-1, os.WNOHANG): Windows is not supported
if not (hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG')):
return

# XXX: give 1 second to child processes to complete
# XXX: temporary change to detect bugs
time.sleep(1.0)

# Reap all our dead child processes so we don't leave zombies around.
# These hog resources and might be causing some of the buildbots to die.
if hasattr(os, 'waitpid'):
any_process = -1
while True:
try:
# This will raise an exception on Windows. That's ok.
pid, status = os.waitpid(any_process, os.WNOHANG)
if pid == 0:
break
print("Warning -- reap_children() reaped child process %s"
% pid, file=sys.stderr)
except:
break
while True:
try:
# Read the exit status of any child process which already completed
pid, status = os.waitpid(-1, os.WNOHANG)
except OSError:
break

if pid == 0:
break

print("Warning -- reap_children() reaped child process %s"
% pid, file=sys.stderr)
environment_altered = True


@contextlib.contextmanager
def start_threads(threads, unlock=None):
Expand Down
2 changes: 1 addition & 1 deletion 2 Lib/test/test_concurrent_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def tearDown(self):
self.assertLess(dt, 60, "synchronization issue: test lasted too long")

test.support.threading_cleanup(*self._thread_cleanup)
test.support.reap_children()
#test.support.reap_children()

def _prime_executor(self):
# Make sure that the executor is ready to do work before running the
Expand Down
3 changes: 2 additions & 1 deletion 3 Lib/test/test_crashers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def test_crashers_crash(self):


def tearDownModule():
test.support.reap_children()
#test.support.reap_children()
pass

if __name__ == "__main__":
unittest.main()
6 changes: 3 additions & 3 deletions 6 Lib/test/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ def test_list_tests(self):
self.assertEqual(output.rstrip().splitlines(),
tests)

def test_list_cases(self):
def Xtest_list_cases(self):
# test --list-cases
code = textwrap.dedent("""
import unittest
Expand Down Expand Up @@ -890,7 +890,7 @@ def parse_methods(self, output):
regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE)
return [match.group(1) for match in regex.finditer(output)]

def test_matchfile(self):
def Xtest_matchfile(self):
code = textwrap.dedent("""
import unittest

Expand Down Expand Up @@ -931,7 +931,7 @@ def test_method4(self):
subset = ['test_method1', 'test_method3']
self.assertEqual(methods, subset)

def test_env_changed(self):
def Xtest_env_changed(self):
code = textwrap.dedent("""
import unittest

Expand Down
3 changes: 2 additions & 1 deletion 3 Lib/test/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ def fileno(self):
self.assertEqual(select.select([], a, []), ([], a[:5], []))

def tearDownModule():
support.reap_children()
#support.reap_children()
pass

if __name__ == "__main__":
unittest.main()
3 changes: 2 additions & 1 deletion 3 Lib/test/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,8 @@ def handler(signum, frame):


def tearDownModule():
support.reap_children()
#support.reap_children()
pass

if __name__ == "__main__":
unittest.main()
11 changes: 6 additions & 5 deletions 11 Lib/test/test_socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def setUp(self):

def tearDown(self):
signal_alarm(0) # Didn't deadlock.
reap_children()
#reap_children()

for fn in self.test_files:
try:
Expand Down Expand Up @@ -144,6 +144,10 @@ def run_server(self, svrcls, hdlrbase, testfunc):
t.join()
server.server_close()
self.assertEqual(-1, server.socket.fileno())
if isinstance(server, socketserver.ForkingMixIn):
# bpo-31151: Check that ForkingMixIn.server_close() waits until
# all children completed
self.assertFalse(server.active_children)
if verbose: print("done")

def stream_examine(self, proto, addr):
Expand Down Expand Up @@ -371,10 +375,7 @@ def wait_done(self):

if HAVE_FORKING:
class ForkingErrorTestServer(socketserver.ForkingMixIn, BaseErrorTestServer):
def wait_done(self):
[child] = self.active_children
os.waitpid(child, 0)
self.active_children.clear()
pass


class SocketWriterTest(unittest.TestCase):
Expand Down
5 changes: 3 additions & 2 deletions 5 Lib/test/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@ class BaseTestCase(unittest.TestCase):
def setUp(self):
# Try to minimize the number of children we have so this test
# doesn't crash on some buildbots (Alphas in particular).
support.reap_children()
#support.reap_children()
pass

def tearDown(self):
for inst in subprocess._active:
inst.wait()
subprocess._cleanup()
self.assertFalse(subprocess._active, "subprocess._active not empty")
self.doCleanups()
support.reap_children()
#support.reap_children()

def assertStderrEqual(self, stderr, expected, msg=None):
# In a debug build, stuff like "[6580 refs]" is printed to stderr at
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.