• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import signal
3import sys
4import unittest
5import warnings
6from unittest import mock
7
8import asyncio
9from asyncio import base_subprocess
10from asyncio import subprocess
11from test.test_asyncio import utils as test_utils
12from test import support
13
14if sys.platform != 'win32':
15    from asyncio import unix_events
16
17# Program blocking
18PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)']
19
20# Program copying input to output
21PROGRAM_CAT = [
22    sys.executable, '-c',
23    ';'.join(('import sys',
24              'data = sys.stdin.buffer.read()',
25              'sys.stdout.buffer.write(data)'))]
26
27
28def tearDownModule():
29    asyncio.set_event_loop_policy(None)
30
31
32class TestSubprocessTransport(base_subprocess.BaseSubprocessTransport):
33    def _start(self, *args, **kwargs):
34        self._proc = mock.Mock()
35        self._proc.stdin = None
36        self._proc.stdout = None
37        self._proc.stderr = None
38        self._proc.pid = -1
39
40
41class SubprocessTransportTests(test_utils.TestCase):
42    def setUp(self):
43        super().setUp()
44        self.loop = self.new_test_loop()
45        self.set_event_loop(self.loop)
46
47    def create_transport(self, waiter=None):
48        protocol = mock.Mock()
49        protocol.connection_made._is_coroutine = False
50        protocol.process_exited._is_coroutine = False
51        transport = TestSubprocessTransport(
52                        self.loop, protocol, ['test'], False,
53                        None, None, None, 0, waiter=waiter)
54        return (transport, protocol)
55
56    def test_proc_exited(self):
57        waiter = self.loop.create_future()
58        transport, protocol = self.create_transport(waiter)
59        transport._process_exited(6)
60        self.loop.run_until_complete(waiter)
61
62        self.assertEqual(transport.get_returncode(), 6)
63
64        self.assertTrue(protocol.connection_made.called)
65        self.assertTrue(protocol.process_exited.called)
66        self.assertTrue(protocol.connection_lost.called)
67        self.assertEqual(protocol.connection_lost.call_args[0], (None,))
68
69        self.assertFalse(transport.is_closing())
70        self.assertIsNone(transport._loop)
71        self.assertIsNone(transport._proc)
72        self.assertIsNone(transport._protocol)
73
74        # methods must raise ProcessLookupError if the process exited
75        self.assertRaises(ProcessLookupError,
76                          transport.send_signal, signal.SIGTERM)
77        self.assertRaises(ProcessLookupError, transport.terminate)
78        self.assertRaises(ProcessLookupError, transport.kill)
79
80        transport.close()
81
82    def test_subprocess_repr(self):
83        waiter = self.loop.create_future()
84        transport, protocol = self.create_transport(waiter)
85        transport._process_exited(6)
86        self.loop.run_until_complete(waiter)
87
88        self.assertEqual(
89            repr(transport),
90            "<TestSubprocessTransport pid=-1 returncode=6>"
91        )
92        transport._returncode = None
93        self.assertEqual(
94            repr(transport),
95            "<TestSubprocessTransport pid=-1 running>"
96        )
97        transport._pid = None
98        transport._returncode = None
99        self.assertEqual(
100            repr(transport),
101            "<TestSubprocessTransport not started>"
102        )
103        transport.close()
104
105
106class SubprocessMixin:
107
108    def test_stdin_stdout(self):
109        args = PROGRAM_CAT
110
111        async def run(data):
112            proc = await asyncio.create_subprocess_exec(
113                *args,
114                stdin=subprocess.PIPE,
115                stdout=subprocess.PIPE,
116            )
117
118            # feed data
119            proc.stdin.write(data)
120            await proc.stdin.drain()
121            proc.stdin.close()
122
123            # get output and exitcode
124            data = await proc.stdout.read()
125            exitcode = await proc.wait()
126            return (exitcode, data)
127
128        task = run(b'some data')
129        task = asyncio.wait_for(task, 60.0)
130        exitcode, stdout = self.loop.run_until_complete(task)
131        self.assertEqual(exitcode, 0)
132        self.assertEqual(stdout, b'some data')
133
134    def test_communicate(self):
135        args = PROGRAM_CAT
136
137        async def run(data):
138            proc = await asyncio.create_subprocess_exec(
139                *args,
140                stdin=subprocess.PIPE,
141                stdout=subprocess.PIPE,
142            )
143            stdout, stderr = await proc.communicate(data)
144            return proc.returncode, stdout
145
146        task = run(b'some data')
147        task = asyncio.wait_for(task, support.LONG_TIMEOUT)
148        exitcode, stdout = self.loop.run_until_complete(task)
149        self.assertEqual(exitcode, 0)
150        self.assertEqual(stdout, b'some data')
151
152    def test_shell(self):
153        proc = self.loop.run_until_complete(
154            asyncio.create_subprocess_shell('exit 7')
155        )
156        exitcode = self.loop.run_until_complete(proc.wait())
157        self.assertEqual(exitcode, 7)
158
159    def test_start_new_session(self):
160        # start the new process in a new session
161        proc = self.loop.run_until_complete(
162            asyncio.create_subprocess_shell(
163                'exit 8',
164                start_new_session=True,
165            )
166        )
167        exitcode = self.loop.run_until_complete(proc.wait())
168        self.assertEqual(exitcode, 8)
169
170    def test_kill(self):
171        args = PROGRAM_BLOCKED
172        proc = self.loop.run_until_complete(
173            asyncio.create_subprocess_exec(*args)
174        )
175        proc.kill()
176        returncode = self.loop.run_until_complete(proc.wait())
177        if sys.platform == 'win32':
178            self.assertIsInstance(returncode, int)
179            # expect 1 but sometimes get 0
180        else:
181            self.assertEqual(-signal.SIGKILL, returncode)
182
183    def test_terminate(self):
184        args = PROGRAM_BLOCKED
185        proc = self.loop.run_until_complete(
186            asyncio.create_subprocess_exec(*args)
187        )
188        proc.terminate()
189        returncode = self.loop.run_until_complete(proc.wait())
190        if sys.platform == 'win32':
191            self.assertIsInstance(returncode, int)
192            # expect 1 but sometimes get 0
193        else:
194            self.assertEqual(-signal.SIGTERM, returncode)
195
196    @unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
197    def test_send_signal(self):
198        # bpo-31034: Make sure that we get the default signal handler (killing
199        # the process). The parent process may have decided to ignore SIGHUP,
200        # and signal handlers are inherited.
201        old_handler = signal.signal(signal.SIGHUP, signal.SIG_DFL)
202        try:
203            code = 'import time; print("sleeping", flush=True); time.sleep(3600)'
204            args = [sys.executable, '-c', code]
205            proc = self.loop.run_until_complete(
206                asyncio.create_subprocess_exec(
207                    *args,
208                    stdout=subprocess.PIPE,
209                )
210            )
211
212            async def send_signal(proc):
213                # basic synchronization to wait until the program is sleeping
214                line = await proc.stdout.readline()
215                self.assertEqual(line, b'sleeping\n')
216
217                proc.send_signal(signal.SIGHUP)
218                returncode = await proc.wait()
219                return returncode
220
221            returncode = self.loop.run_until_complete(send_signal(proc))
222            self.assertEqual(-signal.SIGHUP, returncode)
223        finally:
224            signal.signal(signal.SIGHUP, old_handler)
225
226    def prepare_broken_pipe_test(self):
227        # buffer large enough to feed the whole pipe buffer
228        large_data = b'x' * support.PIPE_MAX_SIZE
229
230        # the program ends before the stdin can be feeded
231        proc = self.loop.run_until_complete(
232            asyncio.create_subprocess_exec(
233                sys.executable, '-c', 'pass',
234                stdin=subprocess.PIPE,
235            )
236        )
237
238        return (proc, large_data)
239
240    def test_stdin_broken_pipe(self):
241        proc, large_data = self.prepare_broken_pipe_test()
242
243        async def write_stdin(proc, data):
244            await asyncio.sleep(0.5)
245            proc.stdin.write(data)
246            await proc.stdin.drain()
247
248        coro = write_stdin(proc, large_data)
249        # drain() must raise BrokenPipeError or ConnectionResetError
250        with test_utils.disable_logger():
251            self.assertRaises((BrokenPipeError, ConnectionResetError),
252                              self.loop.run_until_complete, coro)
253        self.loop.run_until_complete(proc.wait())
254
255    def test_communicate_ignore_broken_pipe(self):
256        proc, large_data = self.prepare_broken_pipe_test()
257
258        # communicate() must ignore BrokenPipeError when feeding stdin
259        self.loop.set_exception_handler(lambda loop, msg: None)
260        self.loop.run_until_complete(proc.communicate(large_data))
261        self.loop.run_until_complete(proc.wait())
262
263    def test_pause_reading(self):
264        limit = 10
265        size = (limit * 2 + 1)
266
267        async def test_pause_reading():
268            code = '\n'.join((
269                'import sys',
270                'sys.stdout.write("x" * %s)' % size,
271                'sys.stdout.flush()',
272            ))
273
274            connect_read_pipe = self.loop.connect_read_pipe
275
276            async def connect_read_pipe_mock(*args, **kw):
277                transport, protocol = await connect_read_pipe(*args, **kw)
278                transport.pause_reading = mock.Mock()
279                transport.resume_reading = mock.Mock()
280                return (transport, protocol)
281
282            self.loop.connect_read_pipe = connect_read_pipe_mock
283
284            proc = await asyncio.create_subprocess_exec(
285                sys.executable, '-c', code,
286                stdin=asyncio.subprocess.PIPE,
287                stdout=asyncio.subprocess.PIPE,
288                limit=limit,
289            )
290            stdout_transport = proc._transport.get_pipe_transport(1)
291
292            stdout, stderr = await proc.communicate()
293
294            # The child process produced more than limit bytes of output,
295            # the stream reader transport should pause the protocol to not
296            # allocate too much memory.
297            return (stdout, stdout_transport)
298
299        # Issue #22685: Ensure that the stream reader pauses the protocol
300        # when the child process produces too much data
301        stdout, transport = self.loop.run_until_complete(test_pause_reading())
302
303        self.assertEqual(stdout, b'x' * size)
304        self.assertTrue(transport.pause_reading.called)
305        self.assertTrue(transport.resume_reading.called)
306
307    def test_stdin_not_inheritable(self):
308        # asyncio issue #209: stdin must not be inheritable, otherwise
309        # the Process.communicate() hangs
310        async def len_message(message):
311            code = 'import sys; data = sys.stdin.read(); print(len(data))'
312            proc = await asyncio.create_subprocess_exec(
313                sys.executable, '-c', code,
314                stdin=asyncio.subprocess.PIPE,
315                stdout=asyncio.subprocess.PIPE,
316                stderr=asyncio.subprocess.PIPE,
317                close_fds=False,
318            )
319            stdout, stderr = await proc.communicate(message)
320            exitcode = await proc.wait()
321            return (stdout, exitcode)
322
323        output, exitcode = self.loop.run_until_complete(len_message(b'abc'))
324        self.assertEqual(output.rstrip(), b'3')
325        self.assertEqual(exitcode, 0)
326
327    def test_empty_input(self):
328
329        async def empty_input():
330            code = 'import sys; data = sys.stdin.read(); print(len(data))'
331            proc = await asyncio.create_subprocess_exec(
332                sys.executable, '-c', code,
333                stdin=asyncio.subprocess.PIPE,
334                stdout=asyncio.subprocess.PIPE,
335                stderr=asyncio.subprocess.PIPE,
336                close_fds=False,
337            )
338            stdout, stderr = await proc.communicate(b'')
339            exitcode = await proc.wait()
340            return (stdout, exitcode)
341
342        output, exitcode = self.loop.run_until_complete(empty_input())
343        self.assertEqual(output.rstrip(), b'0')
344        self.assertEqual(exitcode, 0)
345
346    def test_devnull_input(self):
347
348        async def empty_input():
349            code = 'import sys; data = sys.stdin.read(); print(len(data))'
350            proc = await asyncio.create_subprocess_exec(
351                sys.executable, '-c', code,
352                stdin=asyncio.subprocess.DEVNULL,
353                stdout=asyncio.subprocess.PIPE,
354                stderr=asyncio.subprocess.PIPE,
355                close_fds=False,
356            )
357            stdout, stderr = await proc.communicate()
358            exitcode = await proc.wait()
359            return (stdout, exitcode)
360
361        output, exitcode = self.loop.run_until_complete(empty_input())
362        self.assertEqual(output.rstrip(), b'0')
363        self.assertEqual(exitcode, 0)
364
365    def test_devnull_output(self):
366
367        async def empty_output():
368            code = 'import sys; data = sys.stdin.read(); print(len(data))'
369            proc = await asyncio.create_subprocess_exec(
370                sys.executable, '-c', code,
371                stdin=asyncio.subprocess.PIPE,
372                stdout=asyncio.subprocess.DEVNULL,
373                stderr=asyncio.subprocess.PIPE,
374                close_fds=False,
375            )
376            stdout, stderr = await proc.communicate(b"abc")
377            exitcode = await proc.wait()
378            return (stdout, exitcode)
379
380        output, exitcode = self.loop.run_until_complete(empty_output())
381        self.assertEqual(output, None)
382        self.assertEqual(exitcode, 0)
383
384    def test_devnull_error(self):
385
386        async def empty_error():
387            code = 'import sys; data = sys.stdin.read(); print(len(data))'
388            proc = await asyncio.create_subprocess_exec(
389                sys.executable, '-c', code,
390                stdin=asyncio.subprocess.PIPE,
391                stdout=asyncio.subprocess.PIPE,
392                stderr=asyncio.subprocess.DEVNULL,
393                close_fds=False,
394            )
395            stdout, stderr = await proc.communicate(b"abc")
396            exitcode = await proc.wait()
397            return (stderr, exitcode)
398
399        output, exitcode = self.loop.run_until_complete(empty_error())
400        self.assertEqual(output, None)
401        self.assertEqual(exitcode, 0)
402
403    def test_cancel_process_wait(self):
404        # Issue #23140: cancel Process.wait()
405
406        async def cancel_wait():
407            proc = await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED)
408
409            # Create an internal future waiting on the process exit
410            task = self.loop.create_task(proc.wait())
411            self.loop.call_soon(task.cancel)
412            try:
413                await task
414            except asyncio.CancelledError:
415                pass
416
417            # Cancel the future
418            task.cancel()
419
420            # Kill the process and wait until it is done
421            proc.kill()
422            await proc.wait()
423
424        self.loop.run_until_complete(cancel_wait())
425
426    def test_cancel_make_subprocess_transport_exec(self):
427
428        async def cancel_make_transport():
429            coro = asyncio.create_subprocess_exec(*PROGRAM_BLOCKED)
430            task = self.loop.create_task(coro)
431
432            self.loop.call_soon(task.cancel)
433            try:
434                await task
435            except asyncio.CancelledError:
436                pass
437
438        # ignore the log:
439        # "Exception during subprocess creation, kill the subprocess"
440        with test_utils.disable_logger():
441            self.loop.run_until_complete(cancel_make_transport())
442
443    def test_cancel_post_init(self):
444
445        async def cancel_make_transport():
446            coro = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
447                                             *PROGRAM_BLOCKED)
448            task = self.loop.create_task(coro)
449
450            self.loop.call_soon(task.cancel)
451            try:
452                await task
453            except asyncio.CancelledError:
454                pass
455
456        # ignore the log:
457        # "Exception during subprocess creation, kill the subprocess"
458        with test_utils.disable_logger():
459            self.loop.run_until_complete(cancel_make_transport())
460            test_utils.run_briefly(self.loop)
461
462    def test_close_kill_running(self):
463
464        async def kill_running():
465            create = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
466                                               *PROGRAM_BLOCKED)
467            transport, protocol = await create
468
469            kill_called = False
470            def kill():
471                nonlocal kill_called
472                kill_called = True
473                orig_kill()
474
475            proc = transport.get_extra_info('subprocess')
476            orig_kill = proc.kill
477            proc.kill = kill
478            returncode = transport.get_returncode()
479            transport.close()
480            await asyncio.wait_for(transport._wait(), 5)
481            return (returncode, kill_called)
482
483        # Ignore "Close running child process: kill ..." log
484        with test_utils.disable_logger():
485            try:
486                returncode, killed = self.loop.run_until_complete(
487                    kill_running()
488                )
489            except asyncio.TimeoutError:
490                self.skipTest(
491                    "Timeout failure on waiting for subprocess stopping"
492                )
493        self.assertIsNone(returncode)
494
495        # transport.close() must kill the process if it is still running
496        self.assertTrue(killed)
497        test_utils.run_briefly(self.loop)
498
499    def test_close_dont_kill_finished(self):
500
501        async def kill_running():
502            create = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
503                                               *PROGRAM_BLOCKED)
504            transport, protocol = await create
505            proc = transport.get_extra_info('subprocess')
506
507            # kill the process (but asyncio is not notified immediately)
508            proc.kill()
509            proc.wait()
510
511            proc.kill = mock.Mock()
512            proc_returncode = proc.poll()
513            transport_returncode = transport.get_returncode()
514            transport.close()
515            return (proc_returncode, transport_returncode, proc.kill.called)
516
517        # Ignore "Unknown child process pid ..." log of SafeChildWatcher,
518        # emitted because the test already consumes the exit status:
519        # proc.wait()
520        with test_utils.disable_logger():
521            result = self.loop.run_until_complete(kill_running())
522            test_utils.run_briefly(self.loop)
523
524        proc_returncode, transport_return_code, killed = result
525
526        self.assertIsNotNone(proc_returncode)
527        self.assertIsNone(transport_return_code)
528
529        # transport.close() must not kill the process if it finished, even if
530        # the transport was not notified yet
531        self.assertFalse(killed)
532
533        # Unlike SafeChildWatcher, FastChildWatcher does not pop the
534        # callbacks if waitpid() is called elsewhere. Let's clear them
535        # manually to avoid a warning when the watcher is detached.
536        if (sys.platform != 'win32' and
537                isinstance(self, SubprocessFastWatcherTests)):
538            asyncio.get_child_watcher()._callbacks.clear()
539
540    async def _test_popen_error(self, stdin):
541        if sys.platform == 'win32':
542            target = 'asyncio.windows_utils.Popen'
543        else:
544            target = 'subprocess.Popen'
545        with mock.patch(target) as popen:
546            exc = ZeroDivisionError
547            popen.side_effect = exc
548
549            with warnings.catch_warnings(record=True) as warns:
550                with self.assertRaises(exc):
551                    await asyncio.create_subprocess_exec(
552                        sys.executable,
553                        '-c',
554                        'pass',
555                        stdin=stdin
556                    )
557                self.assertEqual(warns, [])
558
559    def test_popen_error(self):
560        # Issue #24763: check that the subprocess transport is closed
561        # when BaseSubprocessTransport fails
562        self.loop.run_until_complete(self._test_popen_error(stdin=None))
563
564    def test_popen_error_with_stdin_pipe(self):
565        # Issue #35721: check that newly created socket pair is closed when
566        # Popen fails
567        self.loop.run_until_complete(
568            self._test_popen_error(stdin=subprocess.PIPE))
569
570    def test_read_stdout_after_process_exit(self):
571
572        async def execute():
573            code = '\n'.join(['import sys',
574                              'for _ in range(64):',
575                              '    sys.stdout.write("x" * 4096)',
576                              'sys.stdout.flush()',
577                              'sys.exit(1)'])
578
579            process = await asyncio.create_subprocess_exec(
580                sys.executable, '-c', code,
581                stdout=asyncio.subprocess.PIPE,
582            )
583
584            while True:
585                data = await process.stdout.read(65536)
586                if data:
587                    await asyncio.sleep(0.3)
588                else:
589                    break
590
591        self.loop.run_until_complete(execute())
592
593    def test_create_subprocess_exec_text_mode_fails(self):
594        async def execute():
595            with self.assertRaises(ValueError):
596                await subprocess.create_subprocess_exec(sys.executable,
597                                                        text=True)
598
599            with self.assertRaises(ValueError):
600                await subprocess.create_subprocess_exec(sys.executable,
601                                                        encoding="utf-8")
602
603            with self.assertRaises(ValueError):
604                await subprocess.create_subprocess_exec(sys.executable,
605                                                        errors="strict")
606
607        self.loop.run_until_complete(execute())
608
609    def test_create_subprocess_shell_text_mode_fails(self):
610
611        async def execute():
612            with self.assertRaises(ValueError):
613                await subprocess.create_subprocess_shell(sys.executable,
614                                                         text=True)
615
616            with self.assertRaises(ValueError):
617                await subprocess.create_subprocess_shell(sys.executable,
618                                                         encoding="utf-8")
619
620            with self.assertRaises(ValueError):
621                await subprocess.create_subprocess_shell(sys.executable,
622                                                         errors="strict")
623
624        self.loop.run_until_complete(execute())
625
626    def test_create_subprocess_exec_with_path(self):
627        async def execute():
628            p = await subprocess.create_subprocess_exec(
629                support.FakePath(sys.executable), '-c', 'pass')
630            await p.wait()
631            p = await subprocess.create_subprocess_exec(
632                sys.executable, '-c', 'pass', support.FakePath('.'))
633            await p.wait()
634
635        self.assertIsNone(self.loop.run_until_complete(execute()))
636
637    def test_exec_loop_deprecated(self):
638        async def go():
639            with self.assertWarns(DeprecationWarning):
640                proc = await asyncio.create_subprocess_exec(
641                    sys.executable, '-c', 'pass',
642                    loop=self.loop,
643                )
644            await proc.wait()
645        self.loop.run_until_complete(go())
646
647    def test_shell_loop_deprecated(self):
648        async def go():
649            with self.assertWarns(DeprecationWarning):
650                proc = await asyncio.create_subprocess_shell(
651                    "exit 0",
652                    loop=self.loop,
653                )
654            await proc.wait()
655        self.loop.run_until_complete(go())
656
657
658if sys.platform != 'win32':
659    # Unix
660    class SubprocessWatcherMixin(SubprocessMixin):
661
662        Watcher = None
663
664        def setUp(self):
665            super().setUp()
666            policy = asyncio.get_event_loop_policy()
667            self.loop = policy.new_event_loop()
668            self.set_event_loop(self.loop)
669
670            watcher = self.Watcher()
671            watcher.attach_loop(self.loop)
672            policy.set_child_watcher(watcher)
673
674        def tearDown(self):
675            super().tearDown()
676            policy = asyncio.get_event_loop_policy()
677            watcher = policy.get_child_watcher()
678            policy.set_child_watcher(None)
679            watcher.attach_loop(None)
680            watcher.close()
681
682    class SubprocessThreadedWatcherTests(SubprocessWatcherMixin,
683                                         test_utils.TestCase):
684
685        Watcher = unix_events.ThreadedChildWatcher
686
687    class SubprocessMultiLoopWatcherTests(SubprocessWatcherMixin,
688                                          test_utils.TestCase):
689
690        Watcher = unix_events.MultiLoopChildWatcher
691
692    class SubprocessSafeWatcherTests(SubprocessWatcherMixin,
693                                     test_utils.TestCase):
694
695        Watcher = unix_events.SafeChildWatcher
696
697    class SubprocessFastWatcherTests(SubprocessWatcherMixin,
698                                     test_utils.TestCase):
699
700        Watcher = unix_events.FastChildWatcher
701
702    def has_pidfd_support():
703        if not hasattr(os, 'pidfd_open'):
704            return False
705        try:
706            os.close(os.pidfd_open(os.getpid()))
707        except OSError:
708            return False
709        return True
710
711    @unittest.skipUnless(
712        has_pidfd_support(),
713        "operating system does not support pidfds",
714    )
715    class SubprocessPidfdWatcherTests(SubprocessWatcherMixin,
716                                      test_utils.TestCase):
717        Watcher = unix_events.PidfdChildWatcher
718
719else:
720    # Windows
721    class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase):
722
723        def setUp(self):
724            super().setUp()
725            self.loop = asyncio.ProactorEventLoop()
726            self.set_event_loop(self.loop)
727
728
729class GenericWatcherTests:
730
731    def test_create_subprocess_fails_with_inactive_watcher(self):
732
733        async def execute():
734            watcher = mock.create_authspec(asyncio.AbstractChildWatcher)
735            watcher.is_active.return_value = False
736            asyncio.set_child_watcher(watcher)
737
738            with self.assertRaises(RuntimeError):
739                await subprocess.create_subprocess_exec(
740                    support.FakePath(sys.executable), '-c', 'pass')
741
742            watcher.add_child_handler.assert_not_called()
743
744        self.assertIsNone(self.loop.run_until_complete(execute()))
745
746
747
748
749if __name__ == '__main__':
750    unittest.main()
751