简体   繁体   English

如何在python扩展上隐藏stdout/stderr

[英]How to hide stdout/stderr on python extension

I want to hide stdout/stderr in a python script BUT the print is not done into the python script itself but inside the underlying .c extension when a python module needs one.我想在 python 脚本中隐藏 stdout/stderr但是当 python 模块需要一个时,打印不是在 python 脚本本身中完成,而是在底层 .c 扩展中完成。

it is the case for netsnmp python module, it requires compilation, the problem is that there are some 'printf' directly into the C (client_intf.c) code that I would like to capture and hide : netsnmp python模块就是这种情况,它需要编译,问题是有一些'printf'直接进入我想捕获和隐藏的C(client_intf.c)代码中:

from netsnmp import *
import sys,os
devnull = open(os.devnull, 'w')
sys.stdout, sys.stderr = devnull, devnull
session = Session(DestHost='myhostthatdoesnotexisttogenerateerror',Version=2,Community='demopublic')

When I run it I still have some traces :当我运行它时,我仍然有一些痕迹:

python test.py
getaddrinfo: myhostthatdoesnotexisttogenerateerror nodename nor servname provided, or not known
error:snmp_new_session: Couldn't open SNMP session

How can I get rid of these messages on stdout/stderr ?如何摆脱 stdout/stderr 上的这些消息? this is important because I am developping Nagios plugins, and I can afford such output (nevertheless, I want to capture them to raise a custom exception)这很重要,因为我正在开发 Nagios 插件,而且我可以负担得起这样的输出(尽管如此,我想捕获它们以引发自定义异常)

Eric's answer is excellent but it has an important drawback: if you already used sys.stderr to initialize some stuff it will become useless. Eric 的回答非常好,但它有一个重要的缺点:如果您已经使用sys.stderr来初始化一些东西,它将变得毫无用处。 As an example: if you created a custom error logger using sys.stderr it will stop working.例如:如果您使用sys.stderr创建了一个自定义错误记录器,它将停止工作。

Here is a modified version:这是一个修改后的版本:

import os
from contextlib import contextmanager

@contextmanager
def hide_stderr():
    """ Low level stderr supression. """
    newstderr = os.dup(2)
    devnull = os.open('/dev/null', os.O_WRONLY)
    os.dup2(devnull, 2)
    os.close(devnull)
    yield
    os.dup2(newstderr, 2)


@contextmanager
def hide_stdout():
    """ Low level stdout supression. """
    newstderr = os.dup(1)
    devnull = os.open('/dev/null', os.O_WRONLY)
    os.dup2(devnull, 1)
    os.close(devnull)
    yield
    os.dup2(newstderr, 1)


import sys

my_stderr = sys.stderr
print("Test 1: no stderr, no stdout")
with hide_stderr():
    with hide_stdout():
        print("1 stdout")
        print("2 stderr", file=my_stderr)
print("Test 2: no stderr, but stdout")
with hide_stderr():
    print("1 stdout")
    print("2 stderr", file=my_stderr)
print("Test 3: stderr, no stdout")
with hide_stdout():
    print("1 stdout")
    print("2 stderr", file=my_stderr)
print("Test 4: stderr and stdout")
print("1 stdout")
print("2 stderr", file=my_stderr)

Note that the main difference is the use of os.dup2 at the end.请注意,主要区别在于最后使用了os.dup2 We recover the file descriptor from the copy we did and we don't mess with sys.stderr .我们从我们所做的副本中恢复文件描述符,我们不会弄乱sys.stderr

Depending on your OS (seems to work on both Windows and Linux at least) the following would shut up fx stdout completely, by replacing the (low level) file descriptor:根据您的操作系统(似乎至少适用于 Windows 和 Linux),以下内容将通过替换(低级)文件描述符完全关闭 fx stdout

nulf = os.open(os.devnull, os.O_WRONLY)
os.dup2(nulf, stdout.fileno())
os.close(nulf)

the same to stderr doesn't seem to work that well in interactive mode (I guess that the python interpreter tries to write it's stuff to stderr).stderr相同,在交互模式下似乎效果不佳(我猜 python 解释器试图将它的内容写入 stderr)。

This solution will have the same effect that redirect to /dev/null or equivalent when starting the interpreter (except it takes effect later), including any printouts that are made in native code (and of course any printout your script is trying to do).此解决方案将具有相同的效果,即在启动解释器时重定向到/dev/null或等效项(除非它稍后生效),包括以本机代码制作的任何打印输出(当然还有您的脚本尝试执行的任何打印输出) . If you want to do any logging you would have to open a file for that (you could also open a file other than os.devnull in the first place if you'd like to redirect stdout to a file instead).如果您想进行任何日志记录,您必须为此打开一个文件(如果您想将stdout重定向到文件,您也可以首先打开os.devnull以外的文件)。

Note that normally a program doesn't check the result of printouts and in that case one could simply close the descriptor (ie os.close(stdout.fileno()) ), but that would fail if the extension actually checks the results and acts differently based on that.请注意,通常程序不会检查打印输出的结果,在这种情况下,可以简单地关闭描述符(即os.close(stdout.fileno()) ),但如果扩展程序实际检查结果并采取行动,则会失败基于此不同。

Here is the code to temporary block stdout and stderr :这是临时阻止 stdout 和 stderr 的代码:

from netsnmp import *
import sys,os

newstdout = os.dup(1)
newstderr = os.dup(2)
devnull = os.open('/dev/null', os.O_WRONLY)
os.dup2(devnull, 1)
os.dup2(devnull, 2)
os.close(devnull)
# stdout/stderr are blocked here
print "I will be back but nobody will know it"
session = Session(DestHost='myhostthatdoesnotexisttogenerateerror',Version=2,Community='demopublic')
sys.stdout = os.fdopen(newstdout, 'w')
sys.stderr = os.fdopen(newstderr, 'w')
# stdout/stderr are unblocked here
print "I'm back"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM