これを正確に行う方法を探しているときに、この投稿に出くわしました。ajaxを使用してリクエストをサーバーにプロキシし、実行中のスレッドに対してのみすべての出力を返すインタラクティブなPythonコンソールを作成したかったのです。私はそれを理解することになり、私の解決策を共有したいと思いました。
モジュールレベルの関数をプロパティのように動作させることができる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()