Переглянути джерело

Using lists is faster than cStringIO.

Clean up stdin management.
Remove stale comments.

R=dpranke@chromium.org
BUG=
TEST=


Review URL: http://codereview.chromium.org/8808002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@113065 0039d316-1c4b-4281-b951-d872f2087c98
maruel@chromium.org 13 роки тому
батько
коміт
740a6c0bd5
2 змінених файлів з 40 додано та 32 видалено
  1. 30 32
      subprocess2.py
  2. 10 0
      tests/subprocess2_test.py

+ 30 - 32
subprocess2.py

@@ -173,25 +173,22 @@ class Popen(subprocess.Popen):
 
 
     self.stdout_cb = None
     self.stdout_cb = None
     self.stderr_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):
       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')
         kwargs[stream] = open(os.devnull, 'w')
-        setattr(self, stream + '_void', True)
+        setattr(self, stream + '_is_void', True)
       if callable(kwargs.get(stream)):
       if callable(kwargs.get(stream)):
-        # Callable stdout/stderr should be used only with call() wrappers.
         setattr(self, stream + '_cb', kwargs[stream])
         setattr(self, stream + '_cb', kwargs[stream])
         kwargs[stream] = PIPE
         kwargs[stream] = PIPE
 
 
-    fix('stdout')
-    fix('stderr')
-
     self.start = time.time()
     self.start = time.time()
     self.timeout = None
     self.timeout = None
     self.shell = kwargs.get('shell', None)
     self.shell = kwargs.get('shell', None)
@@ -288,6 +285,9 @@ class Popen(subprocess.Popen):
         target=_queue_pipe_read, args=(self.stderr, 'stderr'))
         target=_queue_pipe_read, args=(self.stderr, 'stderr'))
     if input:
     if input:
       threads['stdin'] = threading.Thread(target=write_stdin)
       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():
     for t in threads.itervalues():
       t.start()
       t.start()
 
 
@@ -346,20 +346,19 @@ class Popen(subprocess.Popen):
     stderr = None
     stderr = None
     # Convert to a lambda to workaround python's deadlock.
     # Convert to a lambda to workaround python's deadlock.
     # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
     # 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)
     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)
     return (stdout, stderr)
 
 
 
 
@@ -374,17 +373,16 @@ def communicate(args, timeout=None, **kwargs):
   """
   """
   stdin = kwargs.pop('stdin', None)
   stdin = kwargs.pop('stdin', None)
   if stdin is not 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
       # When stdin is passed as an argument, use it as the actual input data and
       # set the Popen() parameter accordingly.
       # set the Popen() parameter accordingly.
       kwargs['stdin'] = PIPE
       kwargs['stdin'] = PIPE
+    else:
+      kwargs['stdin'] = stdin
+      stdin = None
 
 
   proc = Popen(args, **kwargs)
   proc = Popen(args, **kwargs)
-  if stdin not in (None, VOID):
+  if stdin:
     return proc.communicate(stdin, timeout), proc.returncode
     return proc.communicate(stdin, timeout), proc.returncode
   else:
   else:
     return proc.communicate(None, timeout), proc.returncode
     return proc.communicate(None, timeout), proc.returncode

+ 10 - 0
tests/subprocess2_test.py

@@ -400,6 +400,16 @@ class S2Test(BaseTestCase):
       self._check_res(res, None, None, 10)
       self._check_res(res, None, None, 10)
     self._run_test(fn)
     self._run_test(fn)
 
 
+  def test_stdin_empty(self):
+    def fn(c, e, un):
+      stdin = ''
+      res = subprocess2.communicate(
+          e + ['--read'],
+          stdin=stdin,
+          universal_newlines=un)
+      self._check_res(res, None, None, 0)
+    self._run_test(fn)
+
   def test_stdin_void(self):
   def test_stdin_void(self):
     res = subprocess2.communicate(self.exe + ['--read'], stdin=VOID)
     res = subprocess2.communicate(self.exe + ['--read'], stdin=VOID)
     self._check_res(res, None, None, 0)
     self._check_res(res, None, None, 0)