Python is going to call a subprocess, the user either requested that subprocesses stdout is to go to a file (or back-holed to os.devnull), or the subprocesses output is to be passed though "in real time".
My current best guess as how to do this would seemingly work:
file_path
be valid input for open()
logging
be a Boolean indicator, true indicating use file_path
for logging or false to passthough to stdout.with open(file_path, 'wb') if logging else None as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
In tinkering/testing this appears to be the right value, which I have assumed work well with subprocess.call. However, and unsurprisingly I get the following exception:
AttributeError: __exit__
So None
is not a context, it has no __exit__
;
subprocesses.call
(Non duplicated line)So, how could this behavior be achieved? Or what would you suggest doing instead/alternatively?
Either set shell_stdout
to stdout
or a file object
based on logging being True or False, there is no need to overcomplicate it, you only have one condition or the other if logging
will either be True or False, there is nothing wrong with opening a file not using with
, there are times when using with
does not fit.
import sys
if logging:
shell_stdout = open(file_path, 'wb') # to file or devnull if logging
else:
shell_stdout = sys.stdout
subprocess.call(['ls'], stdout=shell_stdout) # real time if not logging
shell_stdout.close()
To do what you wanted in your question you can do the following, you don't need anything bar sys.stdout
:
with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
The file will only be created if logging is True.
You could create a "no-op" context manager:
import subprocess
import contextlib
@contextlib.contextmanager
def noop():
yield None
logging = False
file_path = '/tmp/out'
with open(file_path, 'wb') if logging else noop() as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
When logging is True
, the conditional expression returns a file object. When logging
is False, it returns a noop()
context manager (so it can be used in the with-statement
), which sets shell_out
to None
but doesn't do anything special upon exit.
Per the docs, when stdout=None
,
... no redirection will occur; the child’s file handles will be inherited from the parent.
Usually the parent's stdout would equal sys.stdout
. However, it is possible to redirect sys.stdout
somewhere else, either explicitly (e.g. sys.stdout = open('/tmp/stdout', 'wb')
) or indirectly, such as by using module that redirects sys.stdout
. The fileinput
module from the standard library redirects sys.stdout
, for example. In such cases noop()
might be useful for directing stdout to the parent's stdout, which may be different than sys.stdout
.
If this corner case does not affect you, then Padraic Cunningham's solution is simpler:
with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With