You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Suppressor类仅在Notebook生效,命令行环境下失效问题求助

问题描述

我实现了一个Suppressor类用于抑制并记录RDKit方法的输出,代码如下:

import sys
import traceback
import warnings
from rdkit import RDLogger
from rdkit import Chem

class Suppressor:
    def __init__(self, log_file):
        self.log_file = log_file
        self.stdout = sys.stdout
        self.stderr = sys.stderr

    def __enter__(self):
        sys.stdout = self
        sys.stderr = self

    def __exit__(self, exception_type, value, tb):
        sys.stdout = self.stdout
        sys.stderr = self.stderr
        if exception_type is not None:
            raise Exception(f"Got exception: {exception_type} {value} {traceback}")

    def write(self, x):
        with open(self.log_file, "a") as log:
            log.write(x)

使用方式:

with Suppressor("error.log"):
    Chem.MolFromSmiles('CO(C)C')

该类在Notebook环境中可正常抑制并记录Chem.MolFromSmiles('CO(C)C')的输出,但在命令行运行脚本时,无法抑制和记录该方法的输出,不过print语句的输出能被正常抑制。测试其他抛出错误的函数时,类功能正常,需排查此问题。

问题原因

RDKit的MolFromSmiles等方法的输出并非通过Python的sys.stdout/sys.stderr输出,而是直接调用底层C代码的标准输出/错误流。在Notebook环境中,Python会将底层系统流重定向到Notebook的输出系统,因此Suppressor类的Python级流替换能生效;但在命令行环境中,底层C流与Python的sys流相互独立,仅替换Python级别的流无法影响底层C++的输出。

解决方案

方法1:使用RDKit自带日志控制工具

RDKit提供了专门的RDLogger模块,可直接关闭或重定向其日志输出,无需手动替换流:

from rdkit import RDLogger

# 关闭所有RDKit日志输出
RDLogger.DisableLog('rdApp.*')

# 或指定日志输出文件及级别
logger = RDLogger.logger()
logger.SetLogFile('error.log')
logger.SetLevel(RDLogger.ERROR)  # 仅记录错误级别日志

配置完成后,调用Chem.MolFromSmiles的相关输出会被写入指定文件,不会在命令行显示。

方法2:重定向系统级文件描述符

若需同时处理Python代码和底层C++代码的输出,可通过重定向系统级文件描述符实现。修改Suppressor类如下:

import sys
import traceback
import os
from contextlib import contextmanager

@contextmanager
def suppress_fd(fd, log_file):
    # 保存原始文件描述符
    original_fd = os.dup(fd)
    # 打开日志文件
    log = open(log_file, 'a')
    # 将目标文件描述符重定向到日志文件
    os.dup2(log.fileno(), fd)
    try:
        yield
    finally:
        # 恢复原始文件描述符
        os.dup2(original_fd, fd)
        log.close()
        os.close(original_fd)

class Suppressor:
    def __init__(self, log_file):
        self.log_file = log_file
        self.stdout = sys.stdout
        self.stderr = sys.stderr
        self.fd_suppressors = [suppress_fd(1, log_file), suppress_fd(2, log_file)]

    def __enter__(self):
        sys.stdout = self
        sys.stderr = self
        # 启动文件描述符重定向
        for suppressor in self.fd_suppressors:
            suppressor.__enter__()

    def __exit__(self, exception_type, value, tb):
        sys.stdout = self.stdout
        sys.stderr = self.stderr
        # 恢复文件描述符
        for suppressor in reversed(self.fd_suppressors):
            suppressor.__exit__(exception_type, value, tb)
        if exception_type is not None:
            raise Exception(f"Got exception: {exception_type} {value} {traceback.format_exc()}")

    def write(self, x):
        with open(self.log_file, "a") as log:
            log.write(x)

该版本同时替换了Python级别的流和系统级文件描述符,可覆盖Python代码及底层C++代码的输出抑制与记录需求。

内容的提问来源于stack exchange,提问作者user17230397

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 19:55:30