python 底层库_python:当底层库记录到stdou时使用ncurses

一个最小的演示示例将有望展示这一切是如何工作的。我不打算仅仅为此设置SWIG,而是选择一个快速而肮脏的演示,通过ctypes调用.so文件来模拟外部C库的使用。只需将以下内容放入工作目录即可。在

测试库c#include

int vomit(void);

int vomit()

{

printf("vomiting output onto stdout\n");

fflush(stdout);

return 1;

}

使用gcc -shared -Wl,-soname,testlib -o _testlib.so -fPIC testlib.c生成

在测试库.py

^{pr2}$

在演示.py(用于最低限度的演示)import os

import sys

import testlib

from tempfile import mktemp

pipename = mktemp()

os.mkfifo(pipename)

pipe_fno = os.open(pipename, os.O_RDWR | os.O_NONBLOCK)

stdout_fno = os.dup(sys.stdout.fileno())

os.dup2(pipe_fno, 1)

result = testlib.testlib.vomit()

os.dup2(stdout_fno, 1)

buf = bytearray()

while True:

try:

buf += os.read(pipe_fno, 1)

except Exception:

break

print("the captured output is: %s" % open('scratch').read())

print('the result of the program is: %d' % result)

os.unlink(pipename)

需要注意的是,.so生成的输出可能会在ctypes系统中以某种方式缓冲(我不知道这部分是如何工作的),并且我无法找到一种方法来刷新输出以确保它们都被输出,除非fflush代码在.so中;所以最终的行为可能会有一些复杂的问题。在

使用线程,这也可以实现(代码变得相当糟糕,但它显示了想法):import os

import sys

import testlib

from threading import Thread

from time import sleep

from tempfile import mktemp

def external():

# the thread that will call the .so that produces output

for i in range(7):

testlib.testlib.vomit()

sleep(1)

# setup

stdout_fno = os.dup(sys.stdout.fileno())

pipename = mktemp()

os.mkfifo(pipename)

pipe_fno = os.open(pipename, os.O_RDWR | os.O_NONBLOCK)

os.dup2(pipe_fno, 1)

def main():

thread = Thread(target=external)

thread.start()

buf = bytearray()

counter = 0

while thread.isAlive():

sleep(0.2)

try:

while True:

buf += os.read(pipe_fno, 1)

except BlockingIOError:

if buf:

# do some processing to show that the string is fully

# captured

output = 'external lib: [%s]\n' % buf.strip().decode('utf8')

# low level write to original stdout

os.write(stdout_fno, output.encode('utf8'))

buf.clear()

os.write(stdout_fno, b'tick: %d\n' % counter)

counter += 1

main()

# cleanup

os.dup2(stdout_fno, 1)

os.close(pipe_fno)

os.unlink(pipename)

示例执行:$ python demo2.py

external lib: [vomiting output onto stdout]

tick: 0

tick: 1

tick: 2

tick: 3

external lib: [vomiting output onto stdout]

tick: 4

请注意,所有内容都已捕获。在

现在,由于您确实使用了ncurses并且也在线程中运行该函数,这有点棘手。龙来了。在

我们将需要ncursesapi,它实际上允许我们创建一个新的屏幕来重定向输出,而且ctypes可以很方便地实现这一点。不幸的是,我对系统上的DLL使用绝对路径;根据需要进行调整。在

在库.pyimport ctypes

libc = ctypes.CDLL('/lib64/libc.so.6')

ncurses = ctypes.CDLL('/lib64/libncursesw.so.6')

class FILE(ctypes.Structure):

pass

class SCREEN(ctypes.Structure):

pass

FILE_p = ctypes.POINTER(FILE)

libc.fdopen.restype = FILE_p

SCREEN_p = ctypes.POINTER(SCREEN)

ncurses.newterm.restype = SCREEN_p

ncurses.set_term.restype = SCREEN_p

fdopen = libc.fdopen

newterm = ncurses.newterm

set_term = ncurses.set_term

delscreen = ncurses.delscreen

endwin = ncurses.endwin

现在我们有了newterm和set_term,我们终于可以完成这个脚本了。从主函数中删除所有内容,并添加以下内容:# setup the curse window

import curses

from lib import newterm, fdopen, set_term, endwin, delscreen

stdin_fno = sys.stdin.fileno()

stdscr = curses.initscr()

# use the ctypes library to create a new screen and redirect output

# back to the original stdout

screen = newterm(None, fdopen(stdout_fno, 'w'), fdopen(stdin_fno, 'r'))

old_screen = set_term(screen)

stdscr.clear()

curses.noecho()

border = curses.newwin(8, 68, 4, 4)

border.border()

window = curses.newwin(6, 66, 5, 5)

window.scrollok(True)

window.clear()

border.refresh()

window.refresh()

def main():

thread = Thread(target=external)

thread.start()

buf = bytearray()

counter = 0

while thread.isAlive():

sleep(0.2)

try:

while True:

buf += os.read(pipe_fno, 1)

except BlockingIOError:

if buf:

output = 'external lib: [%s]\n' % buf.strip().decode('utf8')

buf.clear()

window.addstr(output)

window.refresh()

window.addstr('tick: %d\n' % counter)

counter += 1

window.refresh()

main()

# cleanup

os.dup2(stdout_fno, 1)

endwin()

delscreen(screen)

os.close(pipe_fno)

os.unlink(pipename)

这应该表明使用ncurses达到了预期的效果,但是对于我的案例,它挂在了最后,我不确定还会发生什么。我认为这可能是由于在使用64位共享对象时意外地使用了32位Python造成的,但是在退出时,某些情况下并不能很好地发挥作用(我认为滥用ctypes很容易,但事实证明确实如此!)。总之,它至少显示了ncurse窗口中的输出,正如您所期望的那样。在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值