procutil: allow to specify arbitrary stdin bytes to runbgcommand
authorPierre-Yves David <pierre-yves.david@octobus.net>
Wed, 14 Oct 2020 17:52:18 +0200
changeset 45786 37c65704869d
parent 45785 80f32ec8653a
child 45787 225e513c444e
procutil: allow to specify arbitrary stdin bytes to runbgcommand For automatic clonebundles generation I need to pass arbitrary large amount of data to the process (eg: common nodes, target nodes). I am updating the `runbgcommand` to allow for this. Previously not stdin input was possible, now, one can provide raw bytes and they will be feed to the command through an unnamed temporary files. Differential Revision: https://phab.mercurial-scm.org/D9212
mercurial/utils/procutil.py
--- a/mercurial/utils/procutil.py	Wed Oct 14 17:46:28 2020 +0200
+++ b/mercurial/utils/procutil.py	Wed Oct 14 17:52:18 2020 +0200
@@ -635,21 +635,35 @@
         stderr=None,
         ensurestart=True,
         record_wait=None,
+        stdin_bytes=None,
     ):
         '''Spawn a command without waiting for it to finish.'''
         # we can't use close_fds *and* redirect stdin. I'm not sure that we
         # need to because the detached process has no console connection.
-        p = subprocess.Popen(
-            tonativestr(script),
-            shell=shell,
-            env=tonativeenv(env),
-            close_fds=True,
-            creationflags=_creationflags,
-            stdout=stdout,
-            stderr=stderr,
-        )
-        if record_wait is not None:
-            record_wait(p.wait)
+
+        try:
+            stdin = None
+            if stdin_bytes is not None:
+                stdin = pycompat.unnamedtempfile()
+                stdin.write(stdin_bytes)
+                stdin.flush()
+                stdin.seek(0)
+
+            p = subprocess.Popen(
+                tonativestr(script),
+                shell=shell,
+                env=tonativeenv(env),
+                close_fds=True,
+                creationflags=_creationflags,
+                stdin=stdin,
+                stdout=stdout,
+                stderr=stderr,
+            )
+            if record_wait is not None:
+                record_wait(p.wait)
+        finally:
+            if stdin is not None:
+                stdin.close()
 
 
 else:
@@ -662,6 +676,7 @@
         stderr=None,
         ensurestart=True,
         record_wait=None,
+        stdin_bytes=None,
     ):
         '''Spawn a command without waiting for it to finish.
 
@@ -722,15 +737,21 @@
             if record_wait is None:
                 # Start a new session
                 os.setsid()
+            # connect stdin to devnull to make sure the subprocess can't
+            # muck up that stream for mercurial.
+            if stdin_bytes is None:
+                stdin = open(os.devnull, b'r')
+            else:
+                stdin = pycompat.unnamedtempfile()
+                stdin.write(stdin_bytes)
+                stdin.flush()
+                stdin.seek(0)
 
-            stdin = open(os.devnull, b'r')
             if stdout is None:
                 stdout = open(os.devnull, b'w')
             if stderr is None:
                 stderr = open(os.devnull, b'w')
 
-            # connect stdin to devnull to make sure the subprocess can't
-            # muck up that stream for mercurial.
             p = subprocess.Popen(
                 cmd,
                 shell=shell,
@@ -754,5 +775,6 @@
         finally:
             # mission accomplished, this child needs to exit and not
             # continue the hg process here.
+            stdin.close()
             if record_wait is None:
                 os._exit(returncode)