Python要调用子流程,用户要么请求子流程stdout转到文件(或返回os.devnull),要么子流程输出通过"实时"传递。
我目前对如何做到这一点的最佳猜测似乎是可行的:
- 设
file_path
为open()
的有效输入 - 设
logging
为布尔指示符,true表示使用file_path
进行日志记录,false表示传递到stdout
with open(file_path, 'wb') if logging else None as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
在修补/测试中,这似乎是正确的值,我认为它与subprocess.call配合良好。然而,不出所料,我得到了以下异常:
AttributeError: __exit__
所以None
不是上下文,它没有__exit__
;
目标
- 如果用户不希望进行日志记录,则根本不打开文件
- 使用上下文(由stdlib提供),(Preference;我无法想象手动执行文件打开/关闭操作会更干净。)
- 不需要try/catch(首选避免进一步嵌套)
- 仅对
subprocesses.call
(非重复线路)进行一次调用
那么,这种行为是如何实现的呢?或者你建议你做些什么?
您可以创建一个"无操作"上下文管理器:
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)
当日志记录为True
时,条件表达式返回一个文件对象。当logging
为False时,它返回一个noop()
上下文管理器(因此它可以在with-statement
中使用),该管理器将shell_out
设置为None
,但在退出时不执行任何特殊操作。
根据文档,当stdout=None
、
不会发生重定向;子级的文件句柄将从父级继承。
通常,父对象的stdout等于sys.stdout
。然而,可以将sys.stdout
重定向到其他地方,无论是明确地(例如sys.stdout = open('/tmp/stdout', 'wb')
)还是间接地,例如通过使用重定向sys.stdout
的模块。例如,来自标准库的fileinput
模块重定向sys.stdout
。在这种情况下,noop()
可能有助于将stdout引导到父级的stdout,这可能与sys.stdout
不同。
如果这个角落的情况不影响你,那么Padraic Cunningham的解决方案更简单:
with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
根据日志记录为True或False,将shell_stdout
设置为stdout
或file object
,无需过于复杂,您只有一个条件,否则另一个if logging
将为True或False,打开不使用with
的文件没有错,有时使用with
不合适。
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()
要想在你的问题中做你想做的事情,你可以做以下事情,你不需要任何东西,除了sys.stdout
:
with open(file_path, 'wb') if logging else sys.stdout as shell_stdout:
subprocess.call(['ls'], stdout=shell_stdout)
只有当日志记录为True时,才会创建该文件。
从Python 3.3开始,tou可以使用contextlib.ExitStack
:定义无操作上下文管理器
from contextlib import ExitStack
with open(...) if ... else ExitStack():
...