That just-wait-for-completion 'subprocess.run' is not my option. A way of inter-processes communication. Interrupt the relays. A utility class.
Topics
About: Python
The table of contents of this article
- Starting Context
- Target Context
- Orientation
- Main Body
- 1: Why 'subprocess.run' Does Not Do, in General
- 2: The Basics of Invoking a Process and Handling Its Standard Output, Standard Error Output, and Standard Input
- 3: Why the Relays Should Be Done in the Background
- 4: Doing the Relays and Interrupting Them
- 5: Creating a Utility Class
- 6: The Usage of the Utility Class
Starting Context
- The reader has a basic knowledge on Python.
Target Context
- The reader will know how to invoke a process and relay the standard output, the standard error output, and the standard input of the process in the background, in Python.
Orientation
There is an article on how to interrupt any standard input wait in Python.
There is an article that introduces an objects pipe in Python.
There is an article that introduces a string pipe in Python.
There are some article that introduce some objects pipes in some other programming languages (Java, C++, and C#).
Main Body
Stage DirectionHypothesizer 7 soliloquies.
1: Why 'subprocess.run' Does Not Do, in General
Hypothesizer 7
'subprocess.run' inevitably waits for the invoked process to complete? . . . No kidding.
The process may take a long time and meanwhile may emit a large amount of standard output, and my program has to be idly waiting until the process ends, before it can begin to deal with the backlog? . . . Rejected.
The standard input data are able to be passed into the method, but they have to be determined a priori? . . . Unusable.
Oh, a correction: of course, the method may be usable in some limited cases, but it cannot be regarded as a general purpose method, which is what I mean.
Generally speaking, I think of interacting with the invoked process, with my program sending and receiving messages to-and-fro, an inter-processes communication.
The presumption of the method seems to be "You should a priori know what the invoked process will require in the standard input, and you should be OK if the standard outputs from the invoked process are read only after the process ends.", which I do not happen to share.
2: The Basics of Invoking a Process and Handling Its Standard Output, Standard Error Output, and Standard Input
Hypothesizer 7
In Python, a process can be invoked by instantiating the 'subprocess.Popen' class, and that instance contains a pointer to a 'TextIO' stream of each of the standard output, the standard error output, and the standard input of the process.
Of course, the constructor returns immediately without waiting for the invoked process to terminate, which is an indispensable feature for the class to be usable for general purposes.
This is an example of invoking a process and reading the standard output.
@Python Source Code
import subprocessfrom subprocess import Popenimport sysl_process: Popen = Popen (args = ["ls", "-l"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = None, env = None, encoding = "UTF-8")try:l_standardOutputData: strwhile True:l_standardOutputData = l_process.stdout.read (1)if l_standardOutputData == "":breaksys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_process.communicate ()sys.stdout.write ("### the process return: {0:d}\n".format (l_process.returncode))sys.stdout.flush ()
The pointers to the streams of the standard error output and the standard input are 'l_process.stderr' and 'l_process.stdin'.
The command and the arguments are passed as a list into the 'args' argument of the constructor.
The working directory is passed as a string into the 'cwd' argument.
The environment variables can be passed as a map into the 'env' argument, but if 'None' is passed, the environment variables of the invoking process will be inherited to the invoked process.
Well, what is the relation between the lifetime of the invoked process and the duration of the accessibility to the standard output or something? . . . If the process finishes, will the standard output become inaccessible without the contents having been read yet? Or does yet-not-having-read the standard output block the process from terminating? . . . Let me test.
@Python Source Code
import subprocessfrom subprocess import Popenimport sysimport timel_process: Popen = Popen (args = ["ls", "-l"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = None, env = None, encoding = "UTF-8")sys.stdout.write ("### waiting 30 seconds before starting to read the standard output . . .\n")sys.stdout.flush ()time.sleep (30)try:l_standardOutputData: strwhile True:l_standardOutputData = l_process.stdout.read (1)if l_standardOutputData == "":breaksys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()sys.stdout.write ("### waiting 30 seconds before calling 'communicate (input, timeout)' . . .\n")sys.stdout.flush ()time.sleep (30)l_processReturn: int = l_process.communicate ()sys.stdout.write ("### the process return: {0:d}\n".format (l_process.returncode))sys.stdout.flush ()
While the program was asleep, I looked up the invoked process with a 'ps' command in Linux.
Well, the invoked process became "defunct" even before the program began to read the standard output and survived until the 'communicate (input, timeout)' method was called; so I do not have to worry about failing to read the standard output or the standard error output because of not reading fast enough. On the other hand, I do not have to necessarily read the standard output or the standard error output: not having read them does not prevent the invoked process from terminating.
A lesson is that I have to call the 'communicate (input, timeout)' method somehow, even if my program does not want to wait for the invoked process to finish. . . . If the main thread does not want to wait, I can let a background thread wait.
3: Why the Relays Should Be Done in the Background
Hypothesizer 7
Obviously, if my program just reads the standard output in the foreground, the other 2 streams will not be able to be took cared of until the standard output is closed.
So, at least 2 of the 3 streams have to be handled in the background.
Preferably, all the 3 streams are handled in the background, because otherwise, the main thread may be unpredictably blocked by an invoked process that happens to keep silence.
A critical problem of Python 'TextIO' is that it cannot be interrupted or timed out, being stuck possibly for an indefinite time.
As 'Thtread.join (timeout)' and 'Popen.communicate (input, timeout)' can be timed out, this will prevent the main thread from being stuck.
@Python Source Code
import subprocessfrom subprocess import Popenfrom subprocess import TimeoutExpiredimport sysfrom threading import Threadl_process: Popen = Popen (args = ["ls", "-l"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = None, env = None, encoding = "UTF-8")def l_printStandardOutputThreadFunction () -> None:try:l_standardOutputData: strwhile True:l_standardOutputData = l_process.stdout.read (1)if l_standardOutputData == "":breaksys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_printStandardOutputThread: Thread = Thread (target = l_printStandardOutputThreadFunction)l_printStandardOutputThread.start ()while True:l_printStandardOutputThread.join (0.1)if not l_printStandardOutputThread.is_alive ():breakwhile True:try:l_process.communicate (None, 1.0)sys.stdout.write ("### the process return: {0:d}\n".format (l_process.returncode))sys.stdout.flush ()breakexcept (TimeoutExpired) as l_exception:sys.stdout.write ("### The process has not terminated yet.\n")sys.stdout.flush ()
In fact, as the standard output has been already naturally closed, 'Popen.communicate (input, timeout)' will not particularly have to be timed out in that case.
4: Doing the Relays and Interrupting Them
Hypothesizer 7
Here, let me relay the standard output and the standard error output of the invoked process to those of the invoking program and relay the standard input of the invoking program to that of the invoked process.
In the 1st attempt, it has become like this.
@Python Source Code
import subprocessfrom subprocess import Popenfrom subprocess import TimeoutExpiredimport sysfrom threading import Threadl_process: Popen = Popen (args = ["ls", "-l"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = None, env = None, encoding = "UTF-8")def l_printStandardOutputThreadFunction () -> None:try:l_standardOutputData: strwhile True:l_standardOutputData = l_process.stdout.read (1)if l_standardOutputData == "":breaksys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_printStandardOutputThread: Thread = Thread (target = l_printStandardOutputThreadFunction)l_printStandardOutputThread.start ()def l_printStandardErrorOutputThreadFunction () -> None:try:l_standardErrorOutputData: strwhile True:l_standardErrorOutputData = l_process.stderr.read (1)if l_standardErrorOutputData == "":breaksys.stderr.write (l_standardErrorOutputData)sys.stderr.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard error output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_printStandardErrorOutputThread: Thread = Thread (target = l_printStandardErrorOutputThreadFunction)l_printStandardErrorOutputThread.start ()def l_relayStandardInputThreadFunction () -> None:try:l_standardInputDatum: str = Nonewhile True:l_standardInputDatum = sys.stdin.read (1)l_process.stdin.write (l_standardInputDatum)l_process.stdin.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard input error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_relayStandardInputThread: Thread = Thread (target = l_relayStandardInputThreadFunction)l_relayStandardInputThread.start ()while True:l_printStandardOutputThread.join (0.1)if not l_printStandardOutputThread.is_alive ():breakwhile True:l_printStandardErrorOutputThread.join (0.1)if not l_printStandardErrorOutputThread.is_alive ():breakwhile True:try:l_process.communicate (None, 1.0)sys.stdout.write ("### the process return: {0:d}\n".format (l_process.returncode))sys.stdout.flush ()breakexcept (TimeoutExpired) as l_exception:sys.stdout.write ("### The process has not terminated yet.\n")sys.stdout.flush ()while True:l_relayStandardInputThread.join (1.0)if not l_relayStandardInputThread.is_alive ():breakelse:sys.stdout.write ("### l_relayStandardInputThread has not terminated yet.\n")
That is a problem because the standard-input-relay thread will not end, because the thread will keep waiting for the next standard input datum of the invoking process. The standard input wait or the thread cannot be interrupted in any way, as far as I know (end runs like making the thread a daemon thread are not acceptable in general) . . .
In fact, the solution is here.
So, my improved code is this.
@Python Source Code
import subprocessfrom subprocess import Popenfrom subprocess import TimeoutExpiredimport sysfrom threading import Threadfrom theBiasPlanet.coreUtilities.inputs.HaltableStandardInputReader import HaltableStandardInputReaderfrom theBiasPlanet.coreUtilities.inputsHandling.NoMoreDataException import NoMoreDataExceptionfrom theBiasPlanet.coreUtilities.timersHandling.TimeOutException import TimeOutExceptionl_process: Popen = Popen (args = ["ls", "-l"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = None, env = None, encoding = "UTF-8")def l_printStandardOutputThreadFunction () -> None:try:l_standardOutputData: strwhile True:l_standardOutputData = l_process.stdout.read (1)if l_standardOutputData == "":breaksys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_printStandardOutputThread: Thread = Thread (target = l_printStandardOutputThreadFunction)l_printStandardOutputThread.start ()def l_printStandardErrorOutputThreadFunction () -> None:try:l_standardErrorOutputData: strwhile True:l_standardErrorOutputData = l_process.stderr.read (1)if l_standardErrorOutputData == "":breaksys.stderr.write (l_standardErrorOutputData)sys.stderr.flush ()except (Exception) as l_exception:sys.stderr.write ("### A standard error output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_printStandardErrorOutputThread: Thread = Thread (target = l_printStandardErrorOutputThreadFunction)l_printStandardErrorOutputThread.start ()l_haltableStandardInputReader: "HaltableStandardInputReader" = HaltableStandardInputReader.getInstance ()def l_relayStandardInputThreadFunction () -> None:l_threadIdentification: str = "{0:d}".format (id (threading.current_thread ()))try:l_standardInputDatum: str = Nonewhile True:try:l_standardInputDatum = l_haltableStandardInputReader.read (l_threadIdentification, 1)if l_standardInputDatum == "":breakl_process.stdin.write (l_standardInputDatum)l_process.stdin.flush ()except (NoMoreDataException) as l_exception:breakexcept (TimeOutException) as l_exception:# impossibleNoneexcept (Exception) as l_exception:sys.stderr.write ("### A standard input error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()l_relayStandardInputThread: Thread = Thread (target = l_relayStandardInputThreadFunction)l_haltableStandardInputReader.addSubscriber ("{0:d}".format (id (l_relayStandardInputThread)))sys.stdout.flush ()l_relayStandardInputThread.start ()sys.stdout.flush ()while True:l_printStandardOutputThread.join (0.1)if not l_printStandardOutputThread.is_alive ():breakwhile True:l_printStandardErrorOutputThread.join (0.1)if not l_printStandardErrorOutputThread.is_alive ():breakwhile True:try:l_process.communicate (None, 1.0)sys.stdout.write ("### the process return: {0:d}\n".format (l_process.returncode))sys.stdout.flush ()breakexcept (TimeoutExpired) as l_exception:sys.stdout.write ("### The process has not terminated yet.\n")sys.stdout.flush ()l_haltableStandardInputReader.removeSubscriber ("{0:d}".format (id (l_relayStandardInputThread)))while True:l_relayStandardInputThread.join (1.0)if not l_relayStandardInputThread.is_alive ():breakelse:sys.stdout.write ("### l_relayStandardInputThread has not terminated yet.\n")
Well, how can the reads on the standard output and the standard error output be interrupted?
I think that they usually do not have to be interrupted, because why should the background threads not be left moving or waiting until the standard output is voluntarily closed by the invoked process? But if they happen to have to, I can close the outputs from my program.
5: Creating a Utility Class
Hypothesizer 7
As such code is a boilerplate, it is not wise to write that in multiple places.
So, let me create a utility class.
theBiasPlanet/coreUtilities/processesHandling/ProcessHandler.py
@Python Source Code
from typing import Dictfrom typing import Listfrom typing import TextIOfrom typing import castfrom io import StringIOimport osimport signalimport subprocessfrom subprocess import Popenimport sysimport threadingfrom threading import Conditionfrom threading import Threadfrom theBiasPlanet.coreUtilities.inputs.HaltableStandardInputReader import HaltableStandardInputReaderfrom theBiasPlanet.coreUtilities.inputsHandling.NoMoreDataException import NoMoreDataExceptionfrom theBiasPlanet.coreUtilities.messagingHandling.Publisher import Publisherfrom theBiasPlanet.coreUtilities.timersHandling.TimeOutException import TimeOutExceptionclass ProcessHandler ():s_haltableStandardInputReader: "HaltableStandardInputReader" = HaltableStandardInputReader.getInstance ()# the static initializer Starts_haltableStandardInputReader.startDispatchDataThread ()# the static initializer End@staticmethoddef interruptRelayStandardInputThread (a_thread: Thread) -> None:ProcessHandler.s_haltableStandardInputReader.removeSubscriber ("{0:d}".format (id (a_thread)))class StandardInputAndOutputs:def __init__ (a_this: "ProcessHandler.StandardInputAndOutputs", a_process: Popen) -> None:a_this.i_process: Popena_this.i_standardInputOutputStream: TextIOa_this.i_standardOutputInputStream: TextIOa_this.i_standardErrorOutputInputStream: TextIOa_this.i_relayStandardInputThread: Threada_this.i_printStandardOutputThread: Threada_this.i_printStandardErrorOutputThread: Threada_this.i_thereWereStandardInputContents: boola_this.i_thereWereStandardOutputContents: boola_this.i_thereWereStandardErrorOutputContents: boola_this.i_process = a_processa_this.i_standardInputOutputStream = cast (TextIO, a_this.i_process.stdin)a_this.i_standardOutputInputStream = cast (TextIO, a_this.i_process.stdout)a_this.i_standardErrorOutputInputStream = cast (TextIO, a_this.i_process.stderr)a_this.i_relayStandardInputThread = Nonea_this.i_printStandardOutputThread = Nonea_this.i_printStandardErrorOutputThread = Nonea_this.i_thereWereStandardInputContents = Falsea_this.i_thereWereStandardOutputContents = Falsea_this.i_thereWereStandardErrorOutputContents = Falsedef getStandardInputOutputStream (a_this: "ProcessHandler.StandardInputAndOutputs", ) -> TextIO:return a_this.i_standardInputOutputStreamdef getStandardOutputInputStream (a_this: "ProcessHandler.StandardInputAndOutputs", ) -> TextIO:return a_this.i_standardOutputInputStreamdef getStandardErrorOutputInputStream (a_this: "ProcessHandler.StandardInputAndOutputs", ) -> TextIO:return a_this.i_standardErrorOutputInputStreamdef relayStandardInputAsynchronously (a_this: "ProcessHandler.StandardInputAndOutputs") -> None:def l_relayStandardInputThreadFunction () -> None:l_threadIdentification: str = "{0:d}".format (id (threading.current_thread ()))try:l_processStandardInputWriter: TextIO = a_this.i_standardInputOutputStreaml_standardInputDatum: str = Nonewhile True:try:l_standardInputDatum = ProcessHandler.s_haltableStandardInputReader.read (l_threadIdentification, 1)a_this.i_thereWereStandardInputContents = Truel_processStandardInputWriter.write (l_standardInputDatum)l_processStandardInputWriter.flush ()except (NoMoreDataException) as l_exception:breakexcept (TimeOutException) as l_exception:# impossibleNoneexcept (Exception) as l_exception:Publisher.logErrorInformation ("### A standard input error: {0:s}.".format (str (l_exception)))a_this.i_relayStandardInputThread = Thread (target = l_relayStandardInputThreadFunction)ProcessHandler.s_haltableStandardInputReader.addSubscriber ("{0:d}".format (id (a_this.i_relayStandardInputThread)))a_this.i_relayStandardInputThread.start ()def printStandardOutputAsynchronously (a_this: "ProcessHandler.StandardInputAndOutputs") -> None:def l_printStandardOutputThreadFunction () -> None:try:l_standardOutputReader: TextIO = a_this.i_standardOutputInputStreaml_standardOutputData: strwhile True:l_standardOutputData = l_standardOutputReader.read (1)if l_standardOutputData == "":breaka_this.i_thereWereStandardOutputContents = Truesys.stdout.write (l_standardOutputData)sys.stdout.flush ()except (Exception) as l_exception:Publisher.logErrorInformation (l_exception)sys.stderr.write ("### A standard output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()a_this.i_printStandardOutputThread = Thread (target = l_printStandardOutputThreadFunction)a_this.i_printStandardOutputThread.start ()def printStandardErrorOutputAsynchronously (a_this: "ProcessHandler.StandardInputAndOutputs") -> None:def l_printStandardErrorOutputThreadFunction () -> None:try:l_standardErrorOutputReader: TextIO = a_this.i_standardErrorOutputInputStreaml_standardErrorOutputData: strwhile True:l_standardErrorOutputData = l_standardErrorOutputReader.read (1)if l_standardErrorOutputData == "":breaka_this.i_thereWereStandardErrorOutputContents = Truesys.stderr.write (l_standardErrorOutputData)sys.stderr.flush ()except (Exception) as l_exception:Publisher.logErrorInformation (l_exception)sys.stderr.write ("### A standard error output error: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()a_this.i_printStandardErrorOutputThread = Thread (target = l_printStandardErrorOutputThreadFunction)a_this.i_printStandardErrorOutputThread .start ()def setStandardInputNextLine (a_this: "ProcessHandler.StandardInputAndOutputs", a_line: str) -> None:try:a_this.i_standardInputOutputStream.write ("{0:s}\n".format (a_line))a_this.i_standardInputOutputStream.flush ()a_this.i_thereWereStandardInputContents = Trueexcept (Exception) as l_exception:sys.stderr.write ("The standard input couldn't be written into: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()a_this.i_standardInputOutputStream.close ()def getStandardOutputNextLine (a_this: "ProcessHandler.StandardInputAndOutputs") -> str:try:l_line: str = Nonel_line = a_this.i_standardOutputInputStream.readline ()if l_line != "":a_this.i_thereWereStandardOutputContents = Truereturn l_lineelse:a_this.i_standardOutputInputStream.close ()return Noneexcept (Exception) as l_exception:sys.stderr.write ("The standard output couldn't be scanned: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()a_this.i_standardOutputInputStream.close ()return Nonedef getStandardErrorOutputNextLine (a_this: "ProcessHandler.StandardInputAndOutputs") -> str:try:l_line: str = Nonel_line = a_this.i_standardErrorOutputInputStream.readline ()if l_line != "":a_this.i_thereWereStandardErrorOutputContents = Truereturn l_lineelse:a_this.i_standardErrorOutputInputStream.close ()return Noneexcept (Exception) as l_exception:sys.stderr.write ("The standard error output couldn't be scanned: {0:s}.\n".format (str (l_exception)))sys.stderr.flush ()a_this.i_standardErrorOutputInputStream.close ()return Nonedef thereWereStandardInputContents (a_this: "ProcessHandler.StandardInputAndOutputs") -> bool:return a_this.i_thereWereStandardInputContentsdef thereWereStandardOutputContents (a_this: "ProcessHandler.StandardInputAndOutputs") -> bool:return a_this.i_thereWereStandardOutputContentsdef thereWereStandardErrorOutputContents (a_this: "ProcessHandler.StandardInputAndOutputs") -> bool:return a_this.i_thereWereStandardErrorOutputContentsdef waitUntillFinish (a_this: "ProcessHandler.StandardInputAndOutputs") -> int:if a_this.i_printStandardErrorOutputThread is not None:a_this.i_printStandardErrorOutputThread.join ()a_this.i_printStandardErrorOutputThread = Noneif a_this.i_printStandardOutputThread is not None:a_this.i_printStandardOutputThread.join ()a_this.i_printStandardOutputThread = Nonel_processReturn: int = a_this.i_process.wait ()if a_this.i_relayStandardInputThread is not None:ProcessHandler.interruptRelayStandardInputThread (a_this.i_relayStandardInputThread)a_this.i_relayStandardInputThread.join ()a_this.i_relayStandardInputThread = Nonereturn l_processReturn# The environment variables of the current process are automatically passed into the sub process.@staticmethoddef executeAndReturnStandardInputAndOutputs (a_workingDirectoryPath: str, a_environmentVariableNameToValueMap: "Dict [str, str]", a_commandAndArguments: List [str]) -> "ProcessHandler.StandardInputAndOutputs":l_process: Popen = Popen (args = a_commandAndArguments, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, cwd = a_workingDirectoryPath, env = a_environmentVariableNameToValueMap, encoding = "UTF-8")return ProcessHandler.StandardInputAndOutputs (l_process)@staticmethoddef execute (a_workingDirectory: str, a_environmentVariableNameToValueMap: "Dict [str, str]", a_commandAndArguments: List [str], a_waitsUntilFinish: bool) -> int:l_standardInputAndOutputs: "ProcessHandler.StandardInputAndOutputs" = ProcessHandler.executeAndReturnStandardInputAndOutputs (a_workingDirectory, a_environmentVariableNameToValueMap, a_commandAndArguments)l_childProcessReturn: int = 0if a_waitsUntilFinish:l_standardInputAndOutputs.printStandardOutputAsynchronously ()l_standardInputAndOutputs.printStandardErrorOutputAsynchronously ()l_standardInputAndOutputs.relayStandardInputAsynchronously ()l_childProcessReturn = l_standardInputAndOutputs.waitUntillFinish ()else:def l_waitForInvokedProcessToEndThreadFunction () -> None:try:l_standardInputAndOutputs.printStandardOutputAsynchronously ()l_standardInputAndOutputs.printStandardErrorOutputAsynchronously ()l_standardInputAndOutputs.relayStandardInputAsynchronously ()l_standardInputAndOutputs.waitUntillFinish ()except (Exception) as l_exception:Nonel_waitForInvokedProcessToEndThread = Thread (target = l_waitForInvokedProcessToEndThreadFunction)l_waitForInvokedProcessToEndThread.start ()return l_childProcessReturn
6: The Usage of the Utility Class
Hypothesizer 7
If I want to just do as I did in a section and wait until the invoked process finishes, I can just call the 'execute (a_workingDirectory: str, a_environmentVariableNameToValueMap: "Dict [str, str]", a_commandAndArguments: List [str], a_waitsUntilFinish: bool)' method like this.
@Python Source Code
from theBiasPlanet.coreUtilities.processesHandling.ProcessHandler import ProcessHandlerl_executionResult: int = ProcessHandler.execute (None, None, ["ls", "-l"], False)
If not, I can call the 'executeAndReturnStandardInputAndOutputs (a_workingDirectoryPath: str, a_environmentVariableNameToValueMap: "Dict [str, str]", a_commandAndArguments: List [str])' method and afterward handle the returned 'ProcessHandler.StandardInputAndOutputs' object freely, for example, like this.
@Python Source Code
import sysfrom theBiasPlanet.coreUtilities.processesHandling.ProcessHandler import ProcessHandlerl_StandardInputAndOutputs: "ProcessHandler.StandardInputAndOutputs" = ProcessHandler.executeAndReturnStandardInputAndOutputs (None, None, ["ls", "-l"])l_StandardInputAndOutputs.printStandardErrorOutputAsynchronously ()l_StandardInputAndOutputs.relayStandardInputAsynchronously ()sys.stdout.write ("### The 1st line: {0:s}\n".format (l_StandardInputAndOutputs.getStandardOutputNextLine ()))l_StandardInputAndOutputs.waitUntillFinish ()