gpt4 book ai didi

python-3.x - C 扩展 - 如何将 printf 重定向到 python 记录器?

转载 作者:行者123 更新时间:2023-12-04 00:55:06 24 4
gpt4 key购买 nike

我有一个简单的 C 扩展(参见下面的示例),有时使用 printf 函数打印。
我正在寻找一种方法来包装从该 C 扩展中对函数的调用,以便所有这些 printfs 都将被重定向到我的 python 记录器。
你好ç:

#include <Python.h>

static PyObject* hello(PyObject* self)
{
printf("example print from a C code\n");
return Py_BuildValue("");
}

static char helloworld_docs[] =
"helloworld(): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
{"hello", (PyCFunction)hello,
METH_NOARGS, helloworld_docs},
{NULL}
};

static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"helloworld",
"Extension module example!",
-1,
helloworld_funcs
};

PyMODINIT_FUNC PyInit_helloworld(void)
{
return PyModule_Create(&cModPyDem);
};
设置.py:
from distutils.core import setup, Extension
setup(name = 'helloworld', version = '1.0', \
ext_modules = [Extension('helloworld', ['hello.c'])])
使用第一次运行

python3 setup.py install


进而:
import helloworld
helloworld.hello()
我希望能够做这样的事情:
with redirect_to_logger(my_logger)
helloworld.hello()
编辑:我看到许多帖子展示了如何使 C 中的打印静音,但我无法从中找出如何在 python 中捕获打印。
此类帖子的示例: Redirect stdout from python for C calls
我认为这个问题没有引起太大的关注,因为我可能问的太多了,所以我不再关心日志记录......如何在 python 中捕获 C 打印?到列表或其他任何东西。
编辑
所以我能够在某种程度上实现我想要的工作代码 - 将 c printf 重定向到 python 记录器:
import select
import threading
import time
import logging
import re

from contextlib import contextmanager

from wurlitzer import pipes
from helloworld import hello


logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)



class CPrintsHandler(threading.Thread):
def __init__(self, std, poll_std, err, poll_err, logger):
super(CPrintsHandler, self).__init__()
self.std = std
self.poll_std = poll_std
self.err = err
self.poll_err = poll_err
self.logger = logger
self.stop_event = threading.Event()

def stop(self):
self.stop_event.set()

def run(self):
while not self.stop_event.is_set():
# How can I poll both std and err at the same time?
if self.poll_std.poll(1):
line = self.std.readline()
if line:
self.logger.debug(line.strip())

if self.poll_err.poll(1):
line = self.err.readline()
if line:
self.logger.debug(line.strip())


@contextmanager
def redirect_to_logger(some_logger):
handler = None
try:
with pipes() as (std, err):
poll_std = select.poll()
poll_std.register(std, select.POLLIN)
poll_err = select.poll()
poll_err.register(err, select.POLLIN)
handler = CPrintsHandler(std, poll_std, err, poll_err, some_logger)
handler.start()
yield
finally:
if handler:
time.sleep(0.1) # why do I have to sleep here for the foo prints to finish?
handler.stop()
handler.join()


def foo():
logger.debug('logger print from foo()')
hello()


def main():
with redirect_to_logger(logger):
# I don't want the logs from here to be redirected as well, only printf.
logger.debug('logger print from main()')
foo()


main()
但我有几个问题:
  • python 日志也被 CPrintsHandler 重定向和捕获。有没有办法避免这种情况?
  • 打印的顺序不完全正确:

    python3 redirect_c_example_for_stackoverflow.py


    2020-08-18 19:50:47,732 - 根 - 调试 - 从 C 代码打印的示例
    2020-08-18 19:50:47,733 - 根 - 调试 - 2020-08-18 19:50:47,731 - 根 - 调试 - 来自 main() 的记录器打印
    2020-08-18 19:50:47,733 - 根 - 调试 - 2020-08-18 19:50:47,731 - 根 - 调试 - 来自 foo() 的记录器打印

  • 此外,记录器打印全部出错,也许我轮询它们的方式导致了这个顺序。
  • 我对 python 中的 select 不太熟悉,并且不确定是否有办法同时轮询 std 和 err 并打印先有内容的那个。
  • 最佳答案

    在 Linux 上,您可以使用 wurlitzer 这将捕获 fprint 的输出,例如:

    from wurlitzer import pipes
    with pipes() as (out, err):
    helloworld.hello()
    out.read()
    #'example print from a C code\n'
    wurlitzer基于 this article of Eli Bendersky ,如果您不喜欢依赖第三方库,可以使用的代码。
    可悲的是, wurlitzer并且文章中的代码仅适用于 Linux(和可能的 MacOS)。
    这是一个原型(prototype)(原型(prototype)的改进版本可以从 my github 安装),使用 Eli 的方法作为 Cython 扩展(如果需要,可能可以转换为 ctypes):
    %%cython

    import io
    import os

    cdef extern from *:
    """
    #include <windows.h>
    #include <io.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>

    int open_temp_file() {
    TCHAR lpTempPathBuffer[MAX_PATH+1];//path+NULL

    // Gets the temp path env string (no guarantee it's a valid path).
    DWORD dwRetVal = GetTempPath(MAX_PATH, // length of the buffer
    lpTempPathBuffer); // buffer for path
    if(dwRetVal > MAX_PATH || (dwRetVal == 0))
    {
    return -1;
    }

    // Generates a temporary file name.
    TCHAR szTempFileName[MAX_PATH + 1];//path+NULL
    DWORD uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files
    TEXT("tmp"), // temp file name prefix
    0, // create unique name
    szTempFileName); // buffer for name
    if (uRetVal == 0)
    {
    return -1;
    }

    HANDLE tFile = CreateFile((LPTSTR)szTempFileName, // file name
    GENERIC_READ | GENERIC_WRITE, // first we write than we read
    0, // do not share
    NULL, // default security
    CREATE_ALWAYS, // overwrite existing
    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, // "temporary" temporary file, see https://docs.microsoft.com/en-us/archive/blogs/larryosterman/its-only-temporary
    NULL); // no template

    if (tFile == INVALID_HANDLE_VALUE) {
    return -1;
    }

    return _open_osfhandle((intptr_t)tFile, _O_APPEND | _O_TEXT);
    }

    int replace_stdout(int temp_fileno)
    {
    fflush(stdout);
    int old;
    int cstdout = _fileno(stdout);

    old = _dup(cstdout); // "old" now refers to "stdout"
    if (old == -1)
    {
    return -1;
    }
    if (-1 == _dup2(temp_fileno, cstdout))
    {
    return -1;
    }
    return old;
    }

    int restore_stdout(int old_stdout){
    fflush(stdout);

    // Restore original stdout
    int cstdout = _fileno(stdout);
    return _dup2(old_stdout, cstdout);
    }


    void rewind_fd(int fd) {
    _lseek(fd, 0L, SEEK_SET);
    }
    """
    int open_temp_file()
    int replace_stdout(int temp_fileno)
    int restore_stdout(int old_stdout)
    void rewind_fd(int fd)
    void close_fd "_close" (int fd)

    cdef class CStdOutCapture():
    cdef int tmpfile_fd
    cdef int old_stdout_fd
    def start(self): #start capturing
    self.tmpfile_fd = open_temp_file()
    self.old_stdout_fd = replace_stdout(self.tmpfile_fd)

    def stop(self): # stops capturing, frees resources and returns the content
    restore_stdout(self.old_stdout_fd)
    rewind_fd(self.tmpfile_fd) # need to read from the beginning
    buffer = io.TextIOWrapper(os.fdopen(self.tmpfile_fd, 'rb'))
    result = buffer.read()
    close_fd(self.tmpfile_fd)
    return result

    现在:
    b = CStdOutCapture()
    b.start()
    helloworld.hello()
    out = b.stop()
    print("HERE WE GO:", out)
    # HERE WE GO: example print from a C code

    关于python-3.x - C 扩展 - 如何将 printf 重定向到 python 记录器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63112945/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com