Index: subprocess2.py |
diff --git a/subprocess2.py b/subprocess2.py |
index 3ad804791fcabb0b423be212f31ef16c5d060064..ef95d6f8701c4c7634782e8729e89fab1c07dd44 100644 |
--- a/subprocess2.py |
+++ b/subprocess2.py |
@@ -173,25 +173,22 @@ class Popen(subprocess.Popen): |
self.stdout_cb = None |
self.stderr_cb = None |
- self.stdout_void = False |
- self.stderr_void = False |
- def fix(stream): |
+ self.stdin_is_void = False |
+ self.stdout_is_void = False |
+ self.stderr_is_void = False |
+ |
+ if kwargs.get('stdin') is VOID: |
+ kwargs['stdin'] = open(os.devnull, 'r') |
+ self.stdin_is_void = True |
+ |
+ for stream in ('stdout', 'stderr'): |
if kwargs.get(stream) in (VOID, os.devnull): |
- # Replaces VOID with handle to /dev/null. |
- # Create a temporary file to workaround python's deadlock. |
- # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait |
- # When the pipe fills up, it will deadlock this process. Using a real |
- # file works around that issue. |
kwargs[stream] = open(os.devnull, 'w') |
- setattr(self, stream + '_void', True) |
+ setattr(self, stream + '_is_void', True) |
if callable(kwargs.get(stream)): |
- # Callable stdout/stderr should be used only with call() wrappers. |
setattr(self, stream + '_cb', kwargs[stream]) |
kwargs[stream] = PIPE |
- fix('stdout') |
- fix('stderr') |
- |
self.start = time.time() |
self.timeout = None |
self.shell = kwargs.get('shell', None) |
@@ -288,6 +285,9 @@ class Popen(subprocess.Popen): |
target=_queue_pipe_read, args=(self.stderr, 'stderr')) |
if input: |
threads['stdin'] = threading.Thread(target=write_stdin) |
+ elif self.stdin: |
+ # Pipe but no input, make sure it's closed. |
+ self.stdin.close() |
for t in threads.itervalues(): |
t.start() |
@@ -346,20 +346,19 @@ class Popen(subprocess.Popen): |
stderr = None |
# Convert to a lambda to workaround python's deadlock. |
# http://docs.python.org/library/subprocess.html#subprocess.Popen.wait |
- # When the pipe fills up, it will deadlock this process. Using a thread |
- # works around that issue. No need for thread safe function since the call |
- # backs are guaranteed to be called from the main thread. |
- if self.stdout and not self.stdout_cb and not self.stdout_void: |
- stdout = cStringIO.StringIO() |
- self.stdout_cb = stdout.write |
- if self.stderr and not self.stderr_cb and not self.stderr_void: |
- stderr = cStringIO.StringIO() |
- self.stderr_cb = stderr.write |
+ # When the pipe fills up, it would deadlock this process. |
+ if self.stdout and not self.stdout_cb and not self.stdout_is_void: |
+ stdout = [] |
+ self.stdout_cb = stdout.append |
+ if self.stderr and not self.stderr_cb and not self.stderr_is_void: |
+ stderr = [] |
+ self.stderr_cb = stderr.append |
self._tee_threads(input) |
- if stdout: |
- stdout = stdout.getvalue() |
- if stderr: |
- stderr = stderr.getvalue() |
+ if stdout is not None: |
+ stdout = ''.join(stdout) |
+ stderr = None |
+ if stderr is not None: |
+ stderr = ''.join(stderr) |
return (stdout, stderr) |
@@ -374,17 +373,16 @@ def communicate(args, timeout=None, **kwargs): |
""" |
stdin = kwargs.pop('stdin', None) |
if stdin is not None: |
- if stdin is VOID: |
- kwargs['stdin'] = open(os.devnull, 'r') |
- stdin = None |
- else: |
- assert isinstance(stdin, basestring) |
+ if isinstance(stdin, basestring): |
# When stdin is passed as an argument, use it as the actual input data and |
# set the Popen() parameter accordingly. |
kwargs['stdin'] = PIPE |
+ else: |
+ kwargs['stdin'] = stdin |
+ stdin = None |
proc = Popen(args, **kwargs) |
- if stdin not in (None, VOID): |
+ if stdin: |
return proc.communicate(stdin, timeout), proc.returncode |
else: |
return proc.communicate(None, timeout), proc.returncode |