将sys.stdout重定向到特定的Jupyter Notebook单元格

时间:2016-11-13 23:07:44

标签: python ipython jupyter-notebook io-redirection sys

Jupyter == 4.1.0,Python == 2.7.10,IPython == 4.2.0

我正在为我的Jupyter笔记本编写一个SQL UI,并希望合并多线程,以便我可以在一个单元格中运行查询,并在查询运行时继续在其他单元格中工作。

我遇到的问题是,如果我在一个单元格中执行查询,输出将显示在最后执行的单元格的输出提示中,而不是在执行的单元格的输出提示中查询。

我搜索了内部网并发现this clever trick,但我认为它已经过时和/或不再适用于我的Jupyter版本。当我运行它时,我只获得上次执行的任何单元格的输出。因此,如果我同时运行两者,我只获得最后执行的输出,而不是输出打印以同时分离单元格。

所以我有我的上下文管理器设置parent_header

import sys
import threading
from contextlib import contextmanager

# we need a lock, so that other threads don't snatch control
# while we have set a temporary parent
stdout_lock = threading.Lock()

@contextmanager
def set_stdout_parent(parent):
    """a context manager for setting a particular parent for sys.stdout 
    the parent determines the destination cell of output
    """
    save_parent = sys.stdout.parent_header
    with stdout_lock:
        sys.stdout.parent_header = parent
        try:
            yield
        finally:
            # the flush is important, because that's when the parent_header actually has its effect
            sys.stdout.flush()
            sys.stdout.parent_header = save_parent

我基本上希望能够获得单元格[1]中的parent_header并将单元格[2]的输出重定向到输入[1]。

示例:

获取[1]中的parent_header

In[1]: t = sys.stdout.parent_header

然后将运行以下代码,但输出应打印到Out [1](当前,我运行此代码时没有输出):

In [2]: with set_stdout_parent(t):
            print 'FOO'

哪个应该产生:

In[1]: t = sys.stdout.parent_header
Out[1]:'FOO'

2 个答案:

答案 0 :(得分:0)

您可以结合使用 ipywidgets.Output (docs) 和锁定:

enter image description here

jupyter 单元格中的代码:

# In[1]:


from threading import Thread, Lock
import time
from ipywidgets import Output


# In[2]:


output1 = Output()
output1


# In[3]:


output2 = Output()
output2


# In[4]:


print_lock = Lock()
def t1_main():    
    for i in range(10):
        with print_lock, output1:
            print('thread1', i)
        time.sleep(0.5)

def t2_main():
    for i in range(10):
        with print_lock, output2:
            print('thread2', i)
        time.sleep(0.5)

output1.clear_output()
output2.clear_output()
        
t1 = Thread(target=t1_main)
t2 = Thread(target=t2_main)
t1.start()
t2.start()
t1.join()
t2.join()

答案 1 :(得分:0)

ipywidgets.Output 的文档中有一节是关于 interacting with output widgets from background threads。使用 Output.append_stdout 方法不需要锁定。 this answer 中的最后一个单元格可以替换为:

def t1_main():
    for i in range(10):
        output1.append_stdout(f'thread1 {i}\n')
        time.sleep(0.5)


def t2_main():
    for i in range(10):
        output2.append_stdout(f'thread2 {i}\n')
        time.sleep(0.5)

output1.clear_output()
output2.clear_output()
        
t1 = Thread(target=t1_main)
t2 = Thread(target=t2_main)
t1.start()
t2.start()
t1.join()
t2.join()