Examples¶
Simple iteration over output¶
Iterate over the output lines of a “one-and-done” command.
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
for line in Pr(command).output:
print(line)
Using with() syntax (context manager)¶
Simplify some uses of ProcessRunner by using with():
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
output_lines = []
with Pr(command) as proc:
lines = proc.readlines()
for line in lines:
print(line)
Writing output to a file¶
To quickly direct output to a file, the write() method has you covered.
from processrunner import ProcessRunner as Pr
# Python shell
Pr(['seq', '-f', 'Line %g', '10']).write('output.txt')
# Python file
if __name__ == "__main__":
Pr(['seq', '-f', 'Line %g', '10']).write('output.txt').wait()
Note
Non-blocking!
Note the use of wait() after write() above. The
write() method is non-blocking, meaning that it will return
immediately. In this case, that return happens before the output from
the seq command makes it into output.txt, and as a result
the program would exit before writing any of seq’s output.
Start the command after instantiating ProcessRunner¶
When you want to control the start time of the command, use
autostart=False along with start().
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
output_lines = []
with Pr(command, autostart=False) as proc:
# Do something else here
print("Doing other things before the command starts")
# Ready to start now
proc.start()
lines = proc.readlines()
for line in lines:
print(line)
Process output in real time, in the foreground¶
Read output as it occurs from the command.
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
proc = Pr(command)
# Can also use proc.stdout or proc.stderr for those specific pipes
for line in proc.output:
print("My line: {}".format(line))
Process output in real time, in the background¶
Process output as it occurs from the command in the background, while the main application continues to do other things.
from processrunner import ProcessRunner as Pr
from time import sleep
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
proc = Pr(command, autostart=False)
# A function to run against each line
def f1(line):
print("My line: {}".format(line))
# Map each line against the function f1
# Returns a multiprocessing.Event to signal when the map is complete
f1_stop_event = proc.map(func=f1, procPipeName="stdout")
# Start the command
proc.start()
while not f1_stop_event.is_set():
print("waiting, doing other stuff")
sleep(0.01)
The output will look something like this. (The exact output depends on random timing factors)
waiting
My line: Line 1
My line: Line 2
My line: Line 3
My line: Line 4
My line: Line 5
waiting
My line: Line 6
My line: Line 7
My line: Line 8
My line: Line 9
My line: Line 10
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
waiting
Note
New lines
Notice the extra lines after each of the lines returned from the
command. Unlike output (and stdout/stderr)
map() does not strip whitespace. (They’re seen as “extra” lines
here because print() also prints a newline after each invocation.
Using a direct pipe writer like sys.stdout.write() the user needs
to supply newlines where necessary.)
Process output in real time and write to a file¶
Read output as it occurs from the command and write the output to a file.
Note
Stripping newlines
This example applies strip() to each line to remove the trailing
newline.
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
proc = Pr(command, autostart=False)
def f1(line):
print("My line: {}".format(line.strip()))
# Map each line against the function f1
proc.map(func=f1, procPipeName="stdout")
# Also write the output to a file (truncating it first)
proc.write("output.txt")
# Start the command and wait for it (and the map/write) to finish
proc.start().wait()
with open("output.txt", "r") as f:
for line in f:
print("Line from file: {}".format(line.strip()))
The output will look like this:
My line: Line 1
My line: Line 2
My line: Line 3
My line: Line 4
My line: Line 5
My line: Line 6
My line: Line 7
My line: Line 8
My line: Line 9
My line: Line 10
Line from file: Line 1
Line from file: Line 2
Line from file: Line 3
Line from file: Line 4
Line from file: Line 5
Line from file: Line 6
Line from file: Line 7
Line from file: Line 8
Line from file: Line 9
Line from file: Line 10
Adding real-time values to map() output¶
Especially for timing, it’s nice to have annotation generated at runtime for
map() functions.
import sys
from datetime import datetime
from processrunner import ProcessRunner as Pr
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
proc = Pr(command, autostart=False)
# A function to run against each line
def f1(line):
sys.stdout.write("{} {}".format(datetime.now().isoformat(), line))
# Map each line against the function f1
proc.map(func=f1, procPipeName="stdout")
# Start the command
proc.start().wait()
This produces:
2021-09-12T14:31:29.926617 Line 1
2021-09-12T14:31:29.927616 Line 2
2021-09-12T14:31:29.928213 Line 3
2021-09-12T14:31:29.929022 Line 4
2021-09-12T14:31:29.929822 Line 5
2021-09-12T14:31:29.930501 Line 6
2021-09-12T14:31:29.931075 Line 7
2021-09-12T14:31:29.931562 Line 8
2021-09-12T14:31:29.931988 Line 9
2021-09-12T14:31:29.932412 Line 10
For more complex examples where you’re not totally sure about encoding and
need to make sure the pipe is being flushed, a helper function exists called
WriteOut(). This example is a bit contrived, but produces the same
results:
import sys
from datetime import datetime
from processrunner import ProcessRunner as Pr
from processrunner import WriteOut
if __name__ == "__main__":
command = ['seq', '-f', 'Line %g', '10']
proc = Pr(command, autostart=False)
# A function to run against each line
# Date/time notation for output lines in files
class DateNote:
def init(self):
pass
def __repr__(self):
return datetime.now().isoformat() + " "
# pipe is the terminal pipe where the combined value to go
# outputPrefix is a class instance/string to include before a
# command line
f1 = WriteOut(pipe=sys.stdout, outputPrefix=DateNote())
# Map each line against the function f1
proc.map(func=f1, procPipeName="stdout")
# Start the command and wait for everything to finish
proc.start().wait()
Using WriteOut to concurrently map output to the console and files¶
Note
This example uses a script from the ProcessRunner source tree to generate output on both stdout and stderr.
While an external command runs, write the external process’ stdout and
stderr to the corresponding local pipes, as well as corresponding files.
Further, prefix the local pipe output with dedicated notes, and prefix the
file output with timestamps.
# Imports
import os
import sys
from datetime import datetime
from processrunner import ProcessRunner, WriteOut
if __name__ == "__main__":
# Logging files
working_dir = os.path.dirname(os.path.realpath(__file__))
stdoutFile = open(working_dir+'/stdout.txt', 'a')
stderrFile = open(working_dir+'/stderr.txt', 'a')
# Date/time notation for output lines in files
class DateNote:
def init(self):
pass
def __repr__(self):
return datetime.now().isoformat() + " "
# Prep the process
command = ["tests/test-output-script.py",
"--lines", "5",
"--out-pipe", "both"]
proc = ProcessRunner(command, autostart=False)
# Attach output mechanisms to the process's output pipes. These are
# handled asynchronously, so you can see the output while it is happening
# Write to the console's stdout and stderr, with custom prefixes for each
proc.mapLines(WriteOut(pipe=sys.stdout,
outputPrefix="validation-stdout> "),
procPipeName="stdout")
proc.mapLines(WriteOut(pipe=sys.stderr,
outputPrefix="validation-stderr> "),
procPipeName="stderr")
# Write to the log files, prepending each line with a date/time stamp
proc.mapLines(WriteOut(pipe=stdoutFile, outputPrefix=DateNote()),
procPipeName="stdout")
proc.mapLines(WriteOut(pipe=stderrFile, outputPrefix=DateNote()),
procPipeName="stderr")
# Start the process, then block regular execution until the
# process finishes
proc.start().wait()
stdoutFile.close()
stderrFile.close()
Viewing log output¶
# Quick way to get output
import logging
from processrunner import ProcessRunner as Pr
logging.basicConfig(level=logging.INFO)
if __name__ == "__main__":
print("\n".join(
Pr(['seq', '-f', 'Line %g', '10']).output
))