我在寻找一种方法来做到这一点时偶然发现了这篇文章。我想制作一个交互式 Python 控制台,它使用 ajax 将请求代理到服务器,并仅返回执行线程的所有输出。我最终弄清楚了,并想分享我的解决方案。
werkzeug
python库附带一个类,local.LocalProxy
它可以使模块级函数表现得像一个属性。例如,这将使sys.stdout
行为完全正常,但它将通过LocalProxy
类代理。
import sys
import werkzeug
sys.stdout = werkzeug.local.LocalProxy(lambda: sys.stdout)
对此进行扩展,然后我编写了一个函数来代替上面的函数,如果它是不同的线程,则lambda
返回一个对象:StringIO
import threading
import sys
import cStringIO
import werkzeug
thread_proxies = {}
def redirect():
ident = threading.currentThread().ident
thread_proxies[ident] = cStringIO.StringIO()
return thread_proxies[ident]
def proxy():
ident = threading.currentThread().ident
return thread_proxies.get(ident, sys.stdout)
sys.stdout = werkzeug.local.LocalProxy(proxy)
然后在我想要重定向的任何线程中,我可以调用:
string_io = redirect()
并且所有将去的输出sys.stdout
现在都写入StringIO
对象。
可是等等!我需要捕获sys.stdout
、sys.__stdout__
、sys.stderr
和sys.__stderr__
,所以我编写了这个库,我stdout_helpers
在我的代码库中调用了它:
import threading
import sys
import cStringIO
from werkzeug import local
# Save all of the objects for use later.
orig___stdout__ = sys.__stdout__
orig___stderr__ = sys.__stderr__
orig_stdout = sys.stdout
orig_stderr = sys.stderr
thread_proxies = {}
def redirect():
"""
Enables the redirect for the current thread's output to a single cStringIO
object and returns the object.
:return: The StringIO object.
:rtype: ``cStringIO.StringIO``
"""
# Get the current thread's identity.
ident = threading.currentThread().ident
# Enable the redirect and return the cStringIO object.
thread_proxies[ident] = cStringIO.StringIO()
return thread_proxies[ident]
def stop_redirect():
"""
Enables the redirect for the current thread's output to a single cStringIO
object and returns the object.
:return: The final string value.
:rtype: ``str``
"""
# Get the current thread's identity.
ident = threading.currentThread().ident
# Only act on proxied threads.
if ident not in thread_proxies:
return
# Read the value, close/remove the buffer, and return the value.
retval = thread_proxies[ident].getvalue()
thread_proxies[ident].close()
del thread_proxies[ident]
return retval
def _get_stream(original):
"""
Returns the inner function for use in the LocalProxy object.
:param original: The stream to be returned if thread is not proxied.
:type original: ``file``
:return: The inner function for use in the LocalProxy object.
:rtype: ``function``
"""
def proxy():
"""
Returns the original stream if the current thread is not proxied,
otherwise we return the proxied item.
:return: The stream object for the current thread.
:rtype: ``file``
"""
# Get the current thread's identity.
ident = threading.currentThread().ident
# Return the proxy, otherwise return the original.
return thread_proxies.get(ident, original)
# Return the inner function.
return proxy
def enable_proxy():
"""
Overwrites __stdout__, __stderr__, stdout, and stderr with the proxied
objects.
"""
sys.__stdout__ = local.LocalProxy(_get_stream(sys.__stdout__))
sys.__stderr__ = local.LocalProxy(_get_stream(sys.__stderr__))
sys.stdout = local.LocalProxy(_get_stream(sys.stdout))
sys.stderr = local.LocalProxy(_get_stream(sys.stderr))
def disable_proxy():
"""
Overwrites __stdout__, __stderr__, stdout, and stderr with the original
objects.
"""
sys.__stdout__ = orig___stdout__
sys.__stderr__ = orig___stderr__
sys.stdout = orig_stdout
sys.stderr = orig_stderr
现在在我的应用程序开始时,我调用:
stdout_helpers.enable_proxy()
在我现在调用的任何线程中:
string_io = stdout_helpers.redirect()