Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(91047)

Side by Side Diff: Lib/test/test_subprocess.py

Issue 26741: subprocess.Popen should emit a ResourceWarning in destructor if the process is still running
Patch Set: Created 3 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« Lib/subprocess.py ('K') | « Lib/subprocess.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 import unittest 1 import unittest
2 from test.support import script_helper 2 from test.support import script_helper
3 from test import support 3 from test import support
4 import subprocess 4 import subprocess
5 import sys 5 import sys
6 import signal 6 import signal
7 import io 7 import io
8 import locale 8 import locale
9 import os 9 import os
10 import errno 10 import errno
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 'import sys; sys.exit(sys.stdin.read() == "pear")'], 440 'import sys; sys.exit(sys.stdin.read() == "pear")'],
441 stdin=tf) 441 stdin=tf)
442 p.wait() 442 p.wait()
443 self.assertEqual(p.returncode, 1) 443 self.assertEqual(p.returncode, 1)
444 444
445 def test_stdout_pipe(self): 445 def test_stdout_pipe(self):
446 # stdout redirection 446 # stdout redirection
447 p = subprocess.Popen([sys.executable, "-c", 447 p = subprocess.Popen([sys.executable, "-c",
448 'import sys; sys.stdout.write("orange")'], 448 'import sys; sys.stdout.write("orange")'],
449 stdout=subprocess.PIPE) 449 stdout=subprocess.PIPE)
450 self.addCleanup(p.stdout.close) 450 with p:
451 self.assertEqual(p.stdout.read(), b"orange") 451 self.assertEqual(p.stdout.read(), b"orange")
452 452
453 def test_stdout_filedes(self): 453 def test_stdout_filedes(self):
454 # stdout is set to open file descriptor 454 # stdout is set to open file descriptor
455 tf = tempfile.TemporaryFile() 455 tf = tempfile.TemporaryFile()
456 self.addCleanup(tf.close) 456 self.addCleanup(tf.close)
457 d = tf.fileno() 457 d = tf.fileno()
458 p = subprocess.Popen([sys.executable, "-c", 458 p = subprocess.Popen([sys.executable, "-c",
459 'import sys; sys.stdout.write("orange")'], 459 'import sys; sys.stdout.write("orange")'],
460 stdout=d) 460 stdout=d)
461 p.wait() 461 p.wait()
462 os.lseek(d, 0, 0) 462 os.lseek(d, 0, 0)
463 self.assertEqual(os.read(d, 1024), b"orange") 463 self.assertEqual(os.read(d, 1024), b"orange")
464 464
465 def test_stdout_fileobj(self): 465 def test_stdout_fileobj(self):
466 # stdout is set to open file object 466 # stdout is set to open file object
467 tf = tempfile.TemporaryFile() 467 tf = tempfile.TemporaryFile()
468 self.addCleanup(tf.close) 468 self.addCleanup(tf.close)
469 p = subprocess.Popen([sys.executable, "-c", 469 p = subprocess.Popen([sys.executable, "-c",
470 'import sys; sys.stdout.write("orange")'], 470 'import sys; sys.stdout.write("orange")'],
471 stdout=tf) 471 stdout=tf)
472 p.wait() 472 p.wait()
473 tf.seek(0) 473 tf.seek(0)
474 self.assertEqual(tf.read(), b"orange") 474 self.assertEqual(tf.read(), b"orange")
475 475
476 def test_stderr_pipe(self): 476 def test_stderr_pipe(self):
477 # stderr redirection 477 # stderr redirection
478 p = subprocess.Popen([sys.executable, "-c", 478 p = subprocess.Popen([sys.executable, "-c",
479 'import sys; sys.stderr.write("strawberry")'], 479 'import sys; sys.stderr.write("strawberry")'],
480 stderr=subprocess.PIPE) 480 stderr=subprocess.PIPE)
481 self.addCleanup(p.stderr.close) 481 with p:
482 self.assertStderrEqual(p.stderr.read(), b"strawberry") 482 self.assertStderrEqual(p.stderr.read(), b"strawberry")
483 483
484 def test_stderr_filedes(self): 484 def test_stderr_filedes(self):
485 # stderr is set to open file descriptor 485 # stderr is set to open file descriptor
486 tf = tempfile.TemporaryFile() 486 tf = tempfile.TemporaryFile()
487 self.addCleanup(tf.close) 487 self.addCleanup(tf.close)
488 d = tf.fileno() 488 d = tf.fileno()
489 p = subprocess.Popen([sys.executable, "-c", 489 p = subprocess.Popen([sys.executable, "-c",
490 'import sys; sys.stderr.write("strawberry")'], 490 'import sys; sys.stderr.write("strawberry")'],
491 stderr=d) 491 stderr=d)
492 p.wait() 492 p.wait()
(...skipping 13 matching lines...) Expand all
506 506
507 def test_stdout_stderr_pipe(self): 507 def test_stdout_stderr_pipe(self):
508 # capture stdout and stderr to the same pipe 508 # capture stdout and stderr to the same pipe
509 p = subprocess.Popen([sys.executable, "-c", 509 p = subprocess.Popen([sys.executable, "-c",
510 'import sys;' 510 'import sys;'
511 'sys.stdout.write("apple");' 511 'sys.stdout.write("apple");'
512 'sys.stdout.flush();' 512 'sys.stdout.flush();'
513 'sys.stderr.write("orange")'], 513 'sys.stderr.write("orange")'],
514 stdout=subprocess.PIPE, 514 stdout=subprocess.PIPE,
515 stderr=subprocess.STDOUT) 515 stderr=subprocess.STDOUT)
516 self.addCleanup(p.stdout.close) 516 with p:
517 self.assertStderrEqual(p.stdout.read(), b"appleorange") 517 self.assertStderrEqual(p.stdout.read(), b"appleorange")
518 518
519 def test_stdout_stderr_file(self): 519 def test_stdout_stderr_file(self):
520 # capture stdout and stderr to the same open file 520 # capture stdout and stderr to the same open file
521 tf = tempfile.TemporaryFile() 521 tf = tempfile.TemporaryFile()
522 self.addCleanup(tf.close) 522 self.addCleanup(tf.close)
523 p = subprocess.Popen([sys.executable, "-c", 523 p = subprocess.Popen([sys.executable, "-c",
524 'import sys;' 524 'import sys;'
525 'sys.stdout.write("apple");' 525 'sys.stdout.write("apple");'
526 'sys.stdout.flush();' 526 'sys.stdout.flush();'
527 'sys.stderr.write("orange")'], 527 'sys.stderr.write("orange")'],
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
765 'buf.write(b"line5\\r\\n");' 765 'buf.write(b"line5\\r\\n");'
766 'buf.flush();' 766 'buf.flush();'
767 'buf.write(b"line6\\r");' 767 'buf.write(b"line6\\r");'
768 'buf.flush();' 768 'buf.flush();'
769 'buf.write(b"\\nline7");' 769 'buf.write(b"\\nline7");'
770 'buf.flush();' 770 'buf.flush();'
771 'buf.write(b"\\nline8");'], 771 'buf.write(b"\\nline8");'],
772 stdin=subprocess.PIPE, 772 stdin=subprocess.PIPE,
773 stdout=subprocess.PIPE, 773 stdout=subprocess.PIPE,
774 universal_newlines=1) 774 universal_newlines=1)
775 p.stdin.write("line1\n") 775 with p:
776 p.stdin.flush() 776 p.stdin.write("line1\n")
777 self.assertEqual(p.stdout.readline(), "line1\n") 777 p.stdin.flush()
778 p.stdin.write("line3\n") 778 self.assertEqual(p.stdout.readline(), "line1\n")
779 p.stdin.close() 779 p.stdin.write("line3\n")
780 self.addCleanup(p.stdout.close) 780 p.stdin.close()
781 self.assertEqual(p.stdout.readline(), 781 self.addCleanup(p.stdout.close)
Martin Panter 2016/04/13 04:31:30 Can we drop this cleanup now?
782 "line2\n") 782 self.assertEqual(p.stdout.readline(),
783 self.assertEqual(p.stdout.read(6), 783 "line2\n")
784 "line3\n") 784 self.assertEqual(p.stdout.read(6),
785 self.assertEqual(p.stdout.read(), 785 "line3\n")
786 "line4\nline5\nline6\nline7\nline8") 786 self.assertEqual(p.stdout.read(),
787 "line4\nline5\nline6\nline7\nline8")
787 788
788 def test_universal_newlines_communicate(self): 789 def test_universal_newlines_communicate(self):
789 # universal newlines through communicate() 790 # universal newlines through communicate()
790 p = subprocess.Popen([sys.executable, "-c", 791 p = subprocess.Popen([sys.executable, "-c",
791 'import sys,os;' + SETBINARY + 792 'import sys,os;' + SETBINARY +
792 'buf = sys.stdout.buffer;' 793 'buf = sys.stdout.buffer;'
793 'buf.write(b"line2\\n");' 794 'buf.write(b"line2\\n");'
794 'buf.flush();' 795 'buf.flush();'
795 'buf.write(b"line4\\n");' 796 'buf.write(b"line4\\n");'
796 'buf.flush();' 797 'buf.flush();'
(...skipping 613 matching lines...) Expand 10 before | Expand all | Expand 10 after
1410 self.assertEqual(-p.returncode, signal.SIGABRT) 1411 self.assertEqual(-p.returncode, signal.SIGABRT)
1411 1412
1412 def test_preexec(self): 1413 def test_preexec(self):
1413 # DISCLAIMER: Setting environment variables is *not* a good use 1414 # DISCLAIMER: Setting environment variables is *not* a good use
1414 # of a preexec_fn. This is merely a test. 1415 # of a preexec_fn. This is merely a test.
1415 p = subprocess.Popen([sys.executable, "-c", 1416 p = subprocess.Popen([sys.executable, "-c",
1416 'import sys,os;' 1417 'import sys,os;'
1417 'sys.stdout.write(os.getenv("FRUIT"))'], 1418 'sys.stdout.write(os.getenv("FRUIT"))'],
1418 stdout=subprocess.PIPE, 1419 stdout=subprocess.PIPE,
1419 preexec_fn=lambda: os.putenv("FRUIT", "apple")) 1420 preexec_fn=lambda: os.putenv("FRUIT", "apple"))
1420 self.addCleanup(p.stdout.close) 1421 with p:
1421 self.assertEqual(p.stdout.read(), b"apple") 1422 self.assertEqual(p.stdout.read(), b"apple")
1422 1423
1423 def test_preexec_exception(self): 1424 def test_preexec_exception(self):
1424 def raise_it(): 1425 def raise_it():
1425 raise ValueError("What if two swallows carried a coconut?") 1426 raise ValueError("What if two swallows carried a coconut?")
1426 try: 1427 try:
1427 p = subprocess.Popen([sys.executable, "-c", ""], 1428 p = subprocess.Popen([sys.executable, "-c", ""],
1428 preexec_fn=raise_it) 1429 preexec_fn=raise_it)
1429 except subprocess.SubprocessError as e: 1430 except subprocess.SubprocessError as e:
1430 self.assertTrue( 1431 self.assertTrue(
1431 subprocess._posixsubprocess, 1432 subprocess._posixsubprocess,
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
1559 "import sys; sys.exit(47)"], 1560 "import sys; sys.exit(47)"],
1560 creationflags=47) 1561 creationflags=47)
1561 1562
1562 def test_shell_sequence(self): 1563 def test_shell_sequence(self):
1563 # Run command through the shell (sequence) 1564 # Run command through the shell (sequence)
1564 newenv = os.environ.copy() 1565 newenv = os.environ.copy()
1565 newenv["FRUIT"] = "apple" 1566 newenv["FRUIT"] = "apple"
1566 p = subprocess.Popen(["echo $FRUIT"], shell=1, 1567 p = subprocess.Popen(["echo $FRUIT"], shell=1,
1567 stdout=subprocess.PIPE, 1568 stdout=subprocess.PIPE,
1568 env=newenv) 1569 env=newenv)
1569 self.addCleanup(p.stdout.close) 1570 with p:
1570 self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") 1571 self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple")
1571 1572
1572 def test_shell_string(self): 1573 def test_shell_string(self):
1573 # Run command through the shell (string) 1574 # Run command through the shell (string)
1574 newenv = os.environ.copy() 1575 newenv = os.environ.copy()
1575 newenv["FRUIT"] = "apple" 1576 newenv["FRUIT"] = "apple"
1576 p = subprocess.Popen("echo $FRUIT", shell=1, 1577 p = subprocess.Popen("echo $FRUIT", shell=1,
1577 stdout=subprocess.PIPE, 1578 stdout=subprocess.PIPE,
1578 env=newenv) 1579 env=newenv)
1579 self.addCleanup(p.stdout.close) 1580 with p:
1580 self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") 1581 self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple")
1581 1582
1582 def test_call_string(self): 1583 def test_call_string(self):
1583 # call() function with string argument on UNIX 1584 # call() function with string argument on UNIX
1584 fd, fname = tempfile.mkstemp() 1585 fd, fname = tempfile.mkstemp()
1585 # reopen in text mode 1586 # reopen in text mode
1586 with open(fd, "w", errors="surrogateescape") as fobj: 1587 with open(fd, "w", errors="surrogateescape") as fobj:
1587 fobj.write("#!/bin/sh\n") 1588 fobj.write("#!/bin/sh\n")
1588 fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % 1589 fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" %
1589 sys.executable) 1590 sys.executable)
1590 os.chmod(fname, 0o700) 1591 os.chmod(fname, 0o700)
(...skipping 11 matching lines...) Expand all
1602 shells.append(sh) 1603 shells.append(sh)
1603 if not shells: # Will probably work for any shell but csh. 1604 if not shells: # Will probably work for any shell but csh.
1604 self.skipTest("bash or ksh required for this test") 1605 self.skipTest("bash or ksh required for this test")
1605 sh = '/bin/sh' 1606 sh = '/bin/sh'
1606 if os.path.isfile(sh) and not os.path.islink(sh): 1607 if os.path.isfile(sh) and not os.path.islink(sh):
1607 # Test will fail if /bin/sh is a symlink to csh. 1608 # Test will fail if /bin/sh is a symlink to csh.
1608 shells.append(sh) 1609 shells.append(sh)
1609 for sh in shells: 1610 for sh in shells:
1610 p = subprocess.Popen("echo $0", executable=sh, shell=True, 1611 p = subprocess.Popen("echo $0", executable=sh, shell=True,
1611 stdout=subprocess.PIPE) 1612 stdout=subprocess.PIPE)
1612 self.addCleanup(p.stdout.close) 1613 with p:
1613 self.assertEqual(p.stdout.read().strip(), bytes(sh, 'ascii')) 1614 self.assertEqual(p.stdout.read().strip(), bytes(sh, 'ascii'))
1614 1615
1615 def _kill_process(self, method, *args): 1616 def _kill_process(self, method, *args):
1616 # Do not inherit file handles from the parent. 1617 # Do not inherit file handles from the parent.
1617 # It should fix failures on some platforms. 1618 # It should fix failures on some platforms.
1618 # Also set the SIGINT handler to the default to make sure it's not 1619 # Also set the SIGINT handler to the default to make sure it's not
1619 # being ignored (some tests rely on that.) 1620 # being ignored (some tests rely on that.)
1620 old_handler = signal.signal(signal.SIGINT, signal.default_int_handler) 1621 old_handler = signal.signal(signal.SIGINT, signal.default_int_handler)
1621 try: 1622 try:
1622 p = subprocess.Popen([sys.executable, "-c", """if 1: 1623 p = subprocess.Popen([sys.executable, "-c", """if 1:
1623 import sys, time 1624 import sys, time
(...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after
2261 # spawn a Popen, and delete its reference before it exits 2262 # spawn a Popen, and delete its reference before it exits
2262 p = subprocess.Popen([sys.executable, "-c", 2263 p = subprocess.Popen([sys.executable, "-c",
2263 'import sys, time;' 2264 'import sys, time;'
2264 'time.sleep(0.2)'], 2265 'time.sleep(0.2)'],
2265 stdout=subprocess.PIPE, 2266 stdout=subprocess.PIPE,
2266 stderr=subprocess.PIPE) 2267 stderr=subprocess.PIPE)
2267 self.addCleanup(p.stdout.close) 2268 self.addCleanup(p.stdout.close)
2268 self.addCleanup(p.stderr.close) 2269 self.addCleanup(p.stderr.close)
2269 ident = id(p) 2270 ident = id(p)
2270 pid = p.pid 2271 pid = p.pid
2271 del p 2272 with support.check_warnings(('', ResourceWarning)):
2273 p = None
Martin Panter 2016/04/13 04:31:30 I’m a bit curious why you changed “del p” to “p =
2274
2272 # check that p is in the active processes list 2275 # check that p is in the active processes list
2273 self.assertIn(ident, [id(o) for o in subprocess._active]) 2276 self.assertIn(ident, [id(o) for o in subprocess._active])
2274 2277
2275 def test_leak_fast_process_del_killed(self): 2278 def test_leak_fast_process_del_killed(self):
2276 # Issue #12650: on Unix, if Popen.__del__() was called before the 2279 # Issue #12650: on Unix, if Popen.__del__() was called before the
2277 # process exited, and the process got killed by a signal, it would never 2280 # process exited, and the process got killed by a signal, it would never
2278 # be removed from subprocess._active, which triggered a FD and memory 2281 # be removed from subprocess._active, which triggered a FD and memory
2279 # leak. 2282 # leak.
2280 # spawn a Popen, delete its reference and kill it 2283 # spawn a Popen, delete its reference and kill it
2281 p = subprocess.Popen([sys.executable, "-c", 2284 p = subprocess.Popen([sys.executable, "-c",
2282 'import time;' 2285 'import time;'
2283 'time.sleep(3)'], 2286 'time.sleep(3)'],
2284 stdout=subprocess.PIPE, 2287 stdout=subprocess.PIPE,
2285 stderr=subprocess.PIPE) 2288 stderr=subprocess.PIPE)
2286 self.addCleanup(p.stdout.close) 2289 self.addCleanup(p.stdout.close)
2287 self.addCleanup(p.stderr.close) 2290 self.addCleanup(p.stderr.close)
2288 ident = id(p) 2291 ident = id(p)
2289 pid = p.pid 2292 pid = p.pid
2290 del p 2293 with support.check_warnings(('', ResourceWarning)):
2294 p = None
2295
2291 os.kill(pid, signal.SIGKILL) 2296 os.kill(pid, signal.SIGKILL)
2292 # check that p is in the active processes list 2297 # check that p is in the active processes list
2293 self.assertIn(ident, [id(o) for o in subprocess._active]) 2298 self.assertIn(ident, [id(o) for o in subprocess._active])
2294 2299
2295 # let some time for the process to exit, and create a new Popen: this 2300 # let some time for the process to exit, and create a new Popen: this
2296 # should trigger the wait() of p 2301 # should trigger the wait() of p
2297 time.sleep(0.2) 2302 time.sleep(0.2)
2298 with self.assertRaises(OSError) as c: 2303 with self.assertRaises(OSError) as c:
2299 with subprocess.Popen(['nonexisting_i_hope'], 2304 with subprocess.Popen(['nonexisting_i_hope'],
2300 stdout=subprocess.PIPE, 2305 stdout=subprocess.PIPE,
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after
2674 CommandsWithSpaces, 2679 CommandsWithSpaces,
2675 ContextManagerTests, 2680 ContextManagerTests,
2676 RunFuncTestCase, 2681 RunFuncTestCase,
2677 ) 2682 )
2678 2683
2679 support.run_unittest(*unit_tests) 2684 support.run_unittest(*unit_tests)
2680 support.reap_children() 2685 support.reap_children()
2681 2686
2682 if __name__ == "__main__": 2687 if __name__ == "__main__":
2683 unittest.main() 2688 unittest.main()
OLDNEW
« Lib/subprocess.py ('K') | « Lib/subprocess.py ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+