summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md2
-rw-r--r--lib/pure/osproc.nim51
-rw-r--r--tests/osproc/treadlines.nim20
3 files changed, 73 insertions, 0 deletions
diff --git a/changelog.md b/changelog.md
index 3bae911e9..3cdf138ad 100644
--- a/changelog.md
+++ b/changelog.md
@@ -199,6 +199,8 @@
 
 - Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
 
+- Add `readLines(p: Process)` to `osproc` module for `startProcess` convenience.
+
 ## Language changes
 
 - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 5113695d8..2d48597dc 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -444,6 +444,57 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
+iterator lines*(p: Process): string {.since: (1, 3), tags: [ReadIOEffect].} =
+  ## Convenience iterator for working with `startProcess` to read data from a
+  ## background process.
+  ##
+  ## See also:
+  ## * `readLines proc <#readLines,Process>`_
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: Nim
+  ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
+  ##   var ps: seq[Process]
+  ##   for prog in ["a", "b"]: # run 2 progs in parallel
+  ##     ps.add startProcess("nim", "", ["r", prog], nil, opts)
+  ##   for p in ps:
+  ##     var i = 0
+  ##     for line in p.lines:
+  ##       echo line
+  ##       i.inc
+  ##       if i > 100: break
+  ##     p.close
+  var outp = p.outputStream
+  var line = newStringOfCap(120)
+  while true:
+    if outp.readLine(line):
+      yield line
+    else:
+      if p.peekExitCode != -1: break
+
+proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} =
+  ## Convenience function for working with `startProcess` to read data from a
+  ## background process.
+  ##
+  ## See also:
+  ## * `lines iterator <#lines.i,Process>`_
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: Nim
+  ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
+  ##   var ps: seq[Process]
+  ##   for prog in ["a", "b"]: # run 2 progs in parallel
+  ##     ps.add startProcess("nim", "", ["r", prog], nil, opts)
+  ##   for p in ps:
+  ##     let (lines, exCode) = p.readLines
+  ##     if exCode != 0:
+  ##       for line in lines: echo line
+  ##     p.close
+  for line in p.lines: result[0].add(line)
+  result[1] = p.peekExitCode
+
 when not defined(useNimRtl):
   proc execProcess(command: string, workingDir: string = "",
       args: openArray[string] = [], env: StringTableRef = nil,
diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim
new file mode 100644
index 000000000..3a8303321
--- /dev/null
+++ b/tests/osproc/treadlines.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''Error: cannot open 'a.nim'
+Error: cannot open 'b.nim'
+'''
+  targets: "c"
+"""
+
+import osproc
+
+var ps: seq[Process] # compile & run 2 progs in parallel
+for prog in ["a", "b"]:
+  ps.add startProcess("nim", "",
+                      ["r", "--hint[Conf]=off", "--hint[Processing]=off", prog],
+                      options = {poUsePath, poDaemon, poStdErrToStdOut})
+
+for p in ps:
+  let (lines, exCode) = p.readLines
+  if exCode != 0:
+    for line in lines: echo line
+  p.close