Skip to content Skip to sidebar Skip to footer

Python Subprocess - Filter Out Logging

Python 3.6 I want to take all input from a subprocess which I run with the subprocess module. I can easily pipe this output to a log file, and it works great. But, I want to filter

Solution 1:

This is an interesting situation in which the file object abstraction partially breaks down. The reason your solution does not work, is because subprocess is not actually using your QuietLogger but is getting the raw file number out of it (then repackaging it as a io.TextIOWrapper object).

I don't know if this is an intrinsic limitation in how the subprocess is handled, relying on OS support, or if this is just a mistake in the Python design, but in order to achieve what you want, you need to use the standard subprocess.PIPE and then roll your own file writer.

If you can wait for the subprocess to finish, then it can be trivially done, using the subprocess.run and then picking the stream out of the CompletedProcess (p) object:

p = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True)

data = filter(p.stdout)

withopen(logfile, 'w') as f:
    f.write(data)

If you must work with the ouput while it is being generated (thus, you cannot wait for the subprocess to end) the simplest way is to resort to subprocess.Popen and threads:

import subprocess
import threading

logfile ='tmp.txt'
filter_passed = lambda line: line[:3] != 'Bad'
command = ['my_cmd', 'arg']

defwriter(p, logfile):
    withopen(logfile, 'w') as f:
        for line in p.stdout:
            if filter_passed(line):
                f.write(line)

p = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)

t = threading.Thread(target=writer, args=(p,logfile))
t.start()

t.join()

Solution 2:

[Edit: My brain got derailed along the way, and I ended up answering another question than was actually asked. The following solution is useful for concurrently writing to a file, not for using the logging module in any way. However, since at least it's useful for that, I'll leave the answer in place for now.]

If you were just using threads, not separate processes, you'd just have to have a standard lock. So you could try something similar.

There's always the option of locking the output file. I don't know if your operating system supports anything like that, but the usual Unix way of doing it is to create a lock file. Basically, if the file exists, then wait; otherwise create the file before writing to your log file, and after you're done, remove the lock file again. You could use a context manager like this:

import os
import os.path
from time import sleep


classLockedFile():
    def__init__(self, filename, mode):
        self.filename = filename
        self.lockfile = filename + '.lock'
        self.mode = mode

    def__enter__(self):
        whileTrue:
            if os.path.isfile(self.lockfile):
                sleep(0.1)
            else:
                breakwithopen(self.lockfile, 'a'):
            os.utime(self.lockfile)
        self.f = open(self.filename, self.mode)
        return self.f

    def__exit__(self, *args):
        self.f.close()
        os.remove(self.lockfile)

# And here's how to use it:with LockedFile('blorg', 'a') as f:
    f.write('foo\n')

Post a Comment for "Python Subprocess - Filter Out Logging"