8000 bpo-39983: Add test.support.print_warning() (GH-19683) · python/cpython@d663d34 · GitHub
[go: up one dir, main page]

Skip to content

Commit d663d34

Browse files
authored
bpo-39983: Add test.support.print_warning() (GH-19683)
Log "Warning -- ..." test warnings into sys.__stderr__ rather than sys.stderr, to ensure to display them even if sys.stderr is captured. test.libregrtest.utils.print_warning() now calls test.support.print_warning().
1 parent 02e4484 commit d663d34

File tree

6 files changed

+59
-31
lines changed

6 files changed

+59
-31
lines changed

Doc/library/test.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,15 @@ The :mod:`test.support` module defines the following functions:
825825
target of the "as" clause, if there is one.
826826

827827

828+
.. function:: print_warning(msg)
829+
830+
Print a warning into :data:`sys.__stderr__`. Format the message as:
831+
``f"Warning -- {msg}"``. If *msg* is made of multiple lines, add
832+
``"Warning -- "`` prefix to each line.
833+
834+
.. versionadded:: 3.9
835+
836+
828837
.. function:: wait_process(pid, *, exitcode, timeout=None)
829838

830839
Wait until process *pid* completes and check that the process exit code is

Lib/test/_test_multiprocessing.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5341,10 +5341,9 @@ def wait_proc_exit(self):
53415341
dt = time.monotonic() - start_time
53425342
if dt >= 5.0:
53435343
test.support.environment_altered = True
5344-
print("Warning -- multiprocessing.Manager still has %s active "
5345-
"children after %s seconds"
5346-
% (multiprocessing.active_children(), dt),
5347-
file=sys.stderr)
5344+
support.print_warning(f"multiprocessing.Manager still has "
5345+
f"{multiprocessing.active_children()} "
5346+
f"active children after {dt} seconds")
53485347
break
53495348

53505349
def run_worker(self, worker, obj):
@@ -5544,15 +5543,13 @@ def tearDownClass(cls):
55445543
processes = set(multiprocessing.process._dangling) - set(cls.dangling[0])
55455544
if processes:
55465545
test.support.environment_altered = True
5547-
print('Warning -- Dangling processes: %s' % processes,
5548-
file=sys.stderr)
5546+
support.print_warning(f'Dangling processes: {processes}')
55495547
processes = None
55505548

55515549
threads = set(threading._dangling) - set(cls.dangling[1])
55525550
if threads:
55535551
test.support.environment_altered = True
5554-
print('Warning -- Dangling threads: %s' % threads,
5555-
file=sys.stderr)
5552+
support.print_warning(f'Dangling threads: {threads}')
55565553
threads = None
55575554

55585555

@@ -5620,10 +5617,9 @@ def tearDownClass(cls):
56205617
dt = time.monotonic() - start_time
56215618
if dt >= 5.0:
56225619
test.support.environment_altered = True
5623-
print("Warning -- multiprocessing.Manager still has %s active "
5624-
"children after %s seconds"
5625-
% (multiprocessing.active_children(), dt),
5626-
file=sys.stderr)
5620+
support.print_warning(f"multiprocessing.Manager still has "
5621+
f"{multiprocessing.active_children()} "
5622+
f"active children after {dt} seconds")
56275623
break
56285624

56295625
gc.collect() # do garbage collection
@@ -5632,9 +5628,9 @@ def tearDownClass(cls):
56325628
# ensure that all processes which hold a reference to a
56335629
# managed object have been joined.
56345630
test.support.environment_altered = True
5635-
print('Warning -- Shared objects which still exist at manager '
5636-
'shutdown:')
5637-
print(cls.manager._debug_info())
5631+
support.print_warning('Shared objects which still exist '
5632+
'at manager shutdown:')
5633+
support.print_warning(cls.manager._debug_info())
56385634
cls.manager.shutdown()
56395635
cls.manager.join()
56405636
cls.manager = None
@@ -5731,16 +5727,14 @@ def tearDownModule():
57315727
if processes:
57325728
need_sleep = True
57335729
test.support.environment_altered = True
5734-
print('Warning -- Dangling processes: %s' % processes,
5735-
file=sys.stderr)
5730+
support.print_warning(f'Dangling processes: {processes}')
57365731
processes = None
57375732

57385733
threads = set(threading._dangling) - set(dangling[1])
57395734
if threads:
57405735
need_sleep = True
57415736
test.support.environment_altered = True
5742-
print('Warning -- Dangling threads: %s' % threads,
5743-
file=sys.stderr)
5737+
support.print_warning(f'Dangling threads: {threads}')
57445738
threads = None
57455739

57465740
# Sleep 500 ms to give time to child processes to complete.

Lib/test/libregrtest/runtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ def cleanup_test_droppings(test_name, verbose):
327327
f"directory nor file")
328328

329329
if verbose:
330-
print_warning("%r left behind %s %r" % (test_name, kind, name))
330+
print_warning(f"{test_name} left behind {kind} {name!r}")
331331
support.environment_altered = True
332332

333333
try:

Lib/test/libregrtest/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def printlist(x, width=70, indent=4, file=None):
6262

6363

6464
def print_warning(msg):
65-
print(f"Warning -- {msg}", file=sys.stderr, flush=True)
65+
support.print_warning(msg)
6666

6767

6868
orig_unraisablehook = None

Lib/test/support/__init__.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2250,6 +2250,12 @@ def run_doctest(module, verbosity=None, optionflags=0):
22502250
#=======================================================================
22512251
# Support for saving and restoring the imported modules.
22522252

2253+
def print_warning(msg):
2254+
# bpo-39983: Print into sys.__stderr__ to display the warning even
2255+
# when sys.stderr is captured temporarily by a test
2256+
for line in msg.splitlines():
2257+
print(f"Warning -- {line}", file=sys.__stderr__, flush=True)
2258+
22532259
def modules_setup():
22542260
return sys.modules.copy(),
22552261

@@ -2305,14 +2311,12 @@ def threading_cleanup(*original_values):
23052311
# Display a warning at the first iteration
23062312
environment_altered = True
23072313
dangling_threads = values[1]
2308-
print("Warning -- threading_cleanup() failed to cleanup "
2309-
"%s threads (count: %s, dangling: %s)"
2310-
% (values[0] - original_values[0],
2311-
values[0], len(dangling_threads)),
2312-
file=sys.stderr)
2314+
print_warning(f"threading_cleanup() failed to cleanup "
2315+
f"{values[0] - original_values[0]} threads "
2316+
f"(count: {values[0]}, "
2317+
f"dangling: {len(dangling_threads)})")
23132318
for thread in dangling_threads:
2314-
print(f"Dangling thread: {thread!r}", file=sys.stderr)
2315-
sys.stderr.flush()
2319+
print_warning(f"Dangling thread: {thread!r}")
23162320

23172321
# Don't hold references to threads
23182322
dangling_threads = None
@@ -2409,8 +2413,7 @@ def reap_children():
24092413
if pid == 0:
24102414
break
24112415

2412-
print("Warning -- reap_children() reaped child process %s"
2413-
% pid, file=sys.stderr)
2416+
print_warning(f"reap_children() reaped child process {pid}")
24142417
environment_altered = True
24152418

24162419

Lib/test/test_support.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,12 @@ def test_reap_children(self):
430430
if time.monotonic() > deadline:
431431
self.fail("timeout")
432432

433-
with contextlib.redirect_stderr(stderr):
433+
old_stderr = sys.__stderr__
434+
try:
435+
sys.__stderr__ = stderr
434436
support.reap_children()
437+
finally:
438+
sys.__stderr__ = old_stderr
435439

436440
# Use environment_altered to check if reap_children() found
437441
# the child process
@@ -629,6 +633,24 @@ def test_fd_count(self):
629633
os.close(fd)
630634
self.assertEqual(more - start, 1)
631635

636+
def check_print_warning(self, msg, expected):
637+
stderr = io.StringIO()
638+
639+
old_stderr = sys.__stderr__
640+
try:
641+
sys.__stderr__ = stderr
642+
support.print_warning(msg)
643+
finally:
644+
sys.__stderr__ = old_stderr
645+
646+
self.assertEqual(stderr.getvalue(), expected)
647+
648+
def test_print_warning(self):
649+
self.check_print_warning("msg",
650+
"Warning -- msg\n")
651+
self.check_print_warning("a\nb",
652+
'Warning -- a\nWarning -- b\n')
653+
632654
# XXX -follows a list of untested API
633655
# make_legacy_pyc
634656
# is_resource_enabled

0 commit comments

Comments
 (0)
0