From f0830901564c8b677d6d020e5cec4f10f85a4350 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 23 May 2018 19:19:31 -0400 Subject: [PATCH] Allow file objects to be passed to loop.subprocess* functions. Fixes #136. --- tests/test_process.py | 57 +++++++++++++++++++++++++++++++++++++- uvloop/handles/process.pyx | 9 ++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/tests/test_process.py b/tests/test_process.py index fced856..4bf714b 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -342,6 +342,10 @@ class _AsyncioTests: 'data = sys.stdin.buffer.read()', 'sys.stdout.buffer.write(data)'))] + PROGRAM_ERROR = [ + sys.executable, '-c', '1/0' + ] + def test_stdin_not_inheritable(self): # asyncio issue #209: stdin must not be inheritable, otherwise # the Process.communicate() hangs @@ -363,7 +367,7 @@ class _AsyncioTests: self.assertEqual(output.rstrip(), b'3') self.assertEqual(exitcode, 0) - def test_stdin_stdout(self): + def test_stdin_stdout_pipe(self): args = self.PROGRAM_CAT @asyncio.coroutine @@ -390,6 +394,57 @@ class _AsyncioTests: self.assertEqual(exitcode, 0) self.assertEqual(stdout, b'some data') + def test_stdin_stdout_file(self): + args = self.PROGRAM_CAT + + @asyncio.coroutine + def run(data, stderr): + proc = yield from asyncio.create_subprocess_exec( + *args, + stdin=subprocess.PIPE, + stderr=stdout, + loop=self.loop) + + # feed data + proc.stdin.write(data) + yield from proc.stdin.drain() + proc.stdin.close() + + exitcode = yield from proc.wait() + return exitcode + + with tempfile.TemporaryFile('w+b') as new_stdout: + task = run(b'some data', new_stdout) + task = asyncio.wait_for(task, 60.0, loop=self.loop) + exitcode = self.loop.run_until_complete(task) + self.assertEqual(exitcode, 0) + + new_stdout.seek(0) + self.assertEqual(new_stdout.read(), b'some data') + + def test_stdin_stderr_file(self): + args = self.PROGRAM_ERROR + + @asyncio.coroutine + def run(stderr): + proc = yield from asyncio.create_subprocess_exec( + *args, + stdin=subprocess.PIPE, + stderr=stderr, + loop=self.loop) + + exitcode = yield from proc.wait() + return exitcode + + with tempfile.TemporaryFile('w+b') as new_stderr: + task = run(new_stderr) + task = asyncio.wait_for(task, 60.0, loop=self.loop) + exitcode = self.loop.run_until_complete(task) + self.assertEqual(exitcode, 1) + + new_stderr.seek(0) + self.assertIn(b'ZeroDivisionError', new_stderr.read()) + def test_communicate(self): args = self.PROGRAM_CAT diff --git a/uvloop/handles/process.pyx b/uvloop/handles/process.pyx index 22821b0..9b76177 100644 --- a/uvloop/handles/process.pyx +++ b/uvloop/handles/process.pyx @@ -424,8 +424,7 @@ cdef class UVProcessTransport(UVProcess): raise ValueError( 'subprocess.STDOUT is supported only by stderr parameter') else: - raise ValueError( - 'invalid stdin argument value {!r}'.format(_stdin)) + io[0] = self._file_redirect_stdio(_stdin) else: io[0] = self._file_redirect_stdio(sys.stdin.fileno()) @@ -453,8 +452,7 @@ cdef class UVProcessTransport(UVProcess): raise ValueError( 'subprocess.STDOUT is supported only by stderr parameter') else: - raise ValueError( - 'invalid stdout argument value {!r}'.format(_stdout)) + io[1] = self._file_redirect_stdio(_stdout) else: io[1] = self._file_redirect_stdio(sys.stdout.fileno()) @@ -482,8 +480,7 @@ cdef class UVProcessTransport(UVProcess): elif _stderr == subprocess_DEVNULL: io[2] = self._file_devnull() else: - raise ValueError( - 'invalid stderr argument value {!r}'.format(_stderr)) + io[2] = self._file_redirect_stdio(_stderr) else: io[2] = self._file_redirect_stdio(sys.stderr.fileno())