C ++コードでPython stdoutをキャッチする方法
-
29-09-2019 - |
質問
いくつかのタスクを事前に形成するために、実行中にPythonを呼び出す必要があるプログラムがあります。 Pythonを呼び出す関数が必要です Pythons stdoutをキャッチします そして、それをいくつかのファイルに入れます。これは関数の宣言です
pythonCallBackFunc(const char* pythonInput)
私の問題はキャッチすることです 特定のコマンドのすべてのPython出力 (pythoninput)。 Python APIの経験はありませんが、これを行うのに適したテクニックが何であるかわかりません。私が最初に試したことは、Py_run_simplestringを使用してPythonのsdtoutとstderrをリダイレクトすることです。これは、私が書いたコードのいくつかの例です。
#include "boost\python.hpp"
#include <iostream>
void pythonCallBackFunc(const char* inputStr){
PyRun_SimpleString(inputStr);
}
int main () {
...
//S0me outside functions does this
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("old_stdout = sys.stdout");
PyRun_SimpleString("fsock = open('python_out.log','a')");
PyRun_SimpleString("sys.stdout = fsock");
...
//my func
pythonCallBackFunc("print 'HAHAHAHAHA'");
pythonCallBackFunc("result = 5");
pythonCallBackFunc("print result");
pythonCallBackFunc("result = 'Hello '+'World!'");
pythonCallBackFunc("print result");
pythonCallBackFunc("'KUKU '+'KAKA'");
pythonCallBackFunc("5**3");
pythonCallBackFunc("prinhghult");
pythonCallBackFunc("execfile('stdout_close.py')");
...
//Again anothers function code
PyRun_SimpleString("sys.stdout = old_stdout");
PyRun_SimpleString("fsock.close()");
Py_Finalize();
return 0;
}
これを行うためのより良い方法はありますか?さらに、何らかの理由で、Pyrun_simplestringは数学的な式を得ると何もしません。たとえば、pyrun_simplestring( "5 ** 3")は何も印刷しません(python conlsulが結果を印刷します:125)
多分それは重要です、私はVisual Studio2008を使用しています。ありがとう、アレックス
マークの提案に従って私が行った変更:
#include <python.h>
#include <string>
using namespace std;
void PythonPrinting(string inputStr){
string stdOutErr =
"import sys\n\
class CatchOut:\n\
def __init__(self):\n\
self.value = ''\n\
def write(self, txt):\n\
self.value += txt\n\
catchOut = CatchOut()\n\
sys.stdout = catchOut\n\
sys.stderr = catchOut\n\
"; //this is python code to redirect stdouts/stderr
PyObject *pModule = PyImport_AddModule("__main__"); //create main module
PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect
PyRun_SimpleString(inputStr.c_str());
PyObject *catcher = PyObject_GetAttrString(pModule,"catchOut");
PyObject *output = PyObject_GetAttrString(catcher,"value");
printf("Here's the output: %s\n", PyString_AsString(output));
}
int main(int argc, char** argv){
Py_Initialize();
PythonPrinting("print 123");
PythonPrinting("1+5");
PythonPrinting("result = 2");
PythonPrinting("print result");
Py_Finalize();
return 0;
}
メインを実行した後に得られる出力:
Here's the output: 123
Here's the output:
Here's the output:
Here's the output: 2
それは私にとっては良いことですが、1つの問題だけ、それは
Here's the output: 123
Here's the output: 6
Here's the output:
Here's the output: 2
理由はわかりませんが、このコマンドを実行した後:pythonprinting( "1+5")、pystring_asstring(output)コマンドは6の代わりに空の文字列(char*)を返します:(これを緩めることはできませんか出力?
タックス、アレックス
解決
私があなたの質問を正しく読んでいるなら、あなたはstdout/stderrをあなたのc ++内の変数にキャプチャしたいですか?これを行うには、stdout/stderrをPython変数にリダイレクトし、この変数をC ++に照会することで行うことができます。以下で適切なrefカウントを行っていないわけではありません。
#include <Python.h>
#include <string>
int main(int argc, char** argv)
{
std::string stdOutErr =
"import sys\n\
class CatchOutErr:\n\
def __init__(self):\n\
self.value = ''\n\
def write(self, txt):\n\
self.value += txt\n\
catchOutErr = CatchOutErr()\n\
sys.stdout = catchOutErr\n\
sys.stderr = catchOutErr\n\
"; //this is python code to redirect stdouts/stderr
Py_Initialize();
PyObject *pModule = PyImport_AddModule("__main__"); //create main module
PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect
PyRun_SimpleString("print(1+1)"); //this is ok stdout
PyRun_SimpleString("1+a"); //this creates an error
PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above
PyErr_Print(); //make python print any errors
PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object
printf("Here's the output:\n %s", PyString_AsString(output)); //it's not in our C++ portion
Py_Finalize();
return 0;
}
他のヒント
これが私が最近開発したC ++に優しいソリューションです。
私は私のブログでそれのいくつかの詳細を説明します: C ++のPython sys.stdoutリダイレクト また、最新のバージョンを見つけることができる私のgithubでリポジトリを指しています。この回答を投稿する時点での現在のコードに基づいた完全な例を次に示します。
#include <functional>
#include <iostream>
#include <string>
#include <Python.h>
namespace emb
{
typedef std::function<void(std::string)> stdout_write_type;
struct Stdout
{
PyObject_HEAD
stdout_write_type write;
};
PyObject* Stdout_write(PyObject* self, PyObject* args)
{
std::size_t written(0);
Stdout* selfimpl = reinterpret_cast<Stdout*>(self);
if (selfimpl->write)
{
char* data;
if (!PyArg_ParseTuple(args, "s", &data))
return 0;
std::string str(data);
selfimpl->write(str);
written = str.size();
}
return PyLong_FromSize_t(written);
}
PyObject* Stdout_flush(PyObject* self, PyObject* args)
{
// no-op
return Py_BuildValue("");
}
PyMethodDef Stdout_methods[] =
{
{"write", Stdout_write, METH_VARARGS, "sys.stdout.write"},
{"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"},
{0, 0, 0, 0} // sentinel
};
PyTypeObject StdoutType =
{
PyVarObject_HEAD_INIT(0, 0)
"emb.StdoutType", /* tp_name */
sizeof(Stdout), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"emb.Stdout objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Stdout_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyModuleDef embmodule =
{
PyModuleDef_HEAD_INIT,
"emb", 0, -1, 0,
};
// Internal state
PyObject* g_stdout;
PyObject* g_stdout_saved;
PyMODINIT_FUNC PyInit_emb(void)
{
g_stdout = 0;
g_stdout_saved = 0;
StdoutType.tp_new = PyType_GenericNew;
if (PyType_Ready(&StdoutType) < 0)
return 0;
PyObject* m = PyModule_Create(&embmodule);
if (m)
{
Py_INCREF(&StdoutType);
PyModule_AddObject(m, "Stdout", reinterpret_cast<PyObject*>(&StdoutType));
}
return m;
}
void set_stdout(stdout_write_type write)
{
if (!g_stdout)
{
g_stdout_saved = PySys_GetObject("stdout"); // borrowed
g_stdout = StdoutType.tp_new(&StdoutType, 0, 0);
}
Stdout* impl = reinterpret_cast<Stdout*>(g_stdout);
impl->write = write;
PySys_SetObject("stdout", g_stdout);
}
void reset_stdout()
{
if (g_stdout_saved)
PySys_SetObject("stdout", g_stdout_saved);
Py_XDECREF(g_stdout);
g_stdout = 0;
}
} // namespace emb
int main()
{
PyImport_AppendInittab("emb", emb::PyInit_emb);
Py_Initialize();
PyImport_ImportModule("emb");
PyRun_SimpleString("print(\'hello to console\')");
// here comes the ***magic***
std::string buffer;
{
// switch sys.stdout to custom handler
emb::stdout_write_type write = [&buffer] (std::string s) { buffer += s; };
emb::set_stdout(write);
PyRun_SimpleString("print(\'hello to buffer\')");
PyRun_SimpleString("print(3.14)");
PyRun_SimpleString("print(\'still talking to buffer\')");
emb::reset_stdout();
}
PyRun_SimpleString("print(\'hello to console again\')");
Py_Finalize();
// output what was written to buffer object
std::clog << buffer << std::endl;
}
これにより、傍受できます sys.stdout.write
あらゆる種類の呼び出し可能なC ++エンティティを使用した出力:フリー関数、クラスメンバー関数、名前のfunctionオブジェクト、または私が使用している上記の例のように匿名関数さえ C ++ 11ラムダ.
注意してください、これは重要な概念を提示するための最小限の例です。生産対応のコードでは、参照カウントの周りにもっと注意が必要です PyObject
, 、グローバルな状態を取り除くなど。
私はこの質問が古いことを知っていますが、質問の一部はまだ答えられていません:
「1+1のように、Pythonのstdoutに直接書き込まないコマンドの出力をキャッチする方法?」
ここにステップがあります(Python 3.4の場合):
Markのソリューションを使用して、stdout/stderrをPython変数にリダイレクトします。 https://stackoverflow.com/a/4307737/1046299
コピー機能
PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
Pythonソースコードから。ファイルにありますpythonrun.c
変更します
PyRun_InteractiveOneObject
新しい関数がconst char*
(あなたのコマンド)の代わりに最初のパラメーターとしてFILE*
. 。その後、使用する必要がありますPyParser_ASTFromStringObject
それ以外のPyParser_ASTFromFileObject
関数実装で。関数をコピーする必要があることに注意してくださいrun_mod
そのまま Pythonから関数内と呼ばれるため。たとえば、コマンドで新しい関数を呼び出してください
1+1
. 。 STDOUTは出力を受信する必要があります2
.