LINUX.ORG.RU

Получить вывод встроенного Python 3

 , ,


1

1

Есть программа, в которой требуется на лету выполнять скрипты. Для этого я задействовал libpython, что (поправьте если ошибаюсь) позволяет с собой таскать полноценный интерпретатор в виде дополнительный библиотеки к C++ программе, т.е. не просто API доступа к установленному в системе Python, а именно библиотека с полноценной реализацией интерпретатора. Чтобы иметь возможность парсить вывод скрипта, я перенаправляю stdout в файл, который попадает не в файловую систему а остается в памяти. Мне требуется именно вывод скрипта в stdout. Для начала Python 2.7:

#include <python2.7/Python.h>
...
Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *out = PyFile_FromString("python_out", "w+");
PyObject_SetAttrString(sys, "stdout", out);
FILE *output = PyFile_AsFile(out);
qDebug() << "python=" << PyRun_SimpleString("import math\n"
	"print('%d' % math.sqrt(144))\n");
rewind(output);
QTextStream ts(output);
qDebug() << "ts=" << ts.readAll();
Py_Finalize();
На выходе видим правильный результат (достаточно лишь получить окончательный вывод, выхлоп в процессе не требуется):
python= 0
ts= "12\n"

При #include <python3.4m/Python.h> нет никаких PyFile_*. Как решить эту задачу для Python 3? Гугление постоянно выдает трюк с PyFile_.

★★★★★

Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от I-Love-Microsoft

по твоему примеру, открываешь файл в си, в пистон отправляешь дескриптор. если не нужен файл в файловой системе, используй fmemopen.

hizel ★★★★★
()
Ответ на: комментарий от hizel

Я нашел другое решение в инете, и быть может оно отрабатывает (когда исполняется строка redirect то вывод в stdout пропадает от скрипта). Последняя проблема в том, что я не могу получить значение строки от питона:

	QString _redirect =
"import sys\n\
class _redirect:\n\
	def __init__(self):\n\
		self.value = ''\n\
	def write(self, text):\n\
		self.value += text\n\
redirect = _redirect()\n\
sys.stdout = redirect\n\
sys.stderr = redirect\n";

	Py_Initialize();
	PyObject *module = PyImport_AddModule("__main__");
//	PyRun_SimpleString(_redirect.toLatin1().data());
	qDebug() << "python=" << PyRun_SimpleString("import math\ntest = 'haha'\nprint('%d' % math.sqrt(144))\n");
//	PyObject *redirect = PyObject_GetAttrString(module, "redirect");
//	PyErr_Print();
//	PyObject *output = PyObject_GetAttrString(redirect, "value");
//	qDebug() << "output=" << QString(PyBytes_AsString(output));
	PyObject *test = PyObject_GetAttrString(module, "test");
	qDebug() << "test=" << QString(PyBytes_AsString(PyObject_Str(test)));
	Py_Finalize();
Тут есть строка test = 'haha', по идее там должно быть значение, но при выводе в qDebug оказывается пустая. То ли цепочка QString(PyBytes_AsString(PyObject_Str не верна, то ли объект module не верен...

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от hizel

Это решение не работает, хотя fmemopen и открывает файл в памяти (out не равно NULL), но при этом fileno(out) все время -1, а чтобы получить из этого FILE* нужный int для первого аргумента PyFile_FromFd я знаю лишь fileno, которые дает постоянно -1. К тому же, какое имя у файла будет для Python-а после PyFile_FromFd? Документация говорит что name там для совместимости торчит, но по факту не работает: name is ignored and kept for backward compatibility. Не ясно что sys.stdout = ???

	char buffer[1024];
	FILE *out = fmemopen(buffer, 1024, "w");

	if(out == NULL)
	{
		qDebug() << "out error";
		return;
	}

	Py_Initialize();
	fprintf(out, "test");
	qDebug() << "out=" << out << "fileno(out)=" << fileno(out);
	PyFile_FromFd(fileno(out), "w", "w", -1, NULL, NULL, NULL, 0);
	PyRun_SimpleString("import sys");
	qDebug() << "python=" << PyRun_SimpleString("import math\ntest = 'haha'\nprint('%d' % math.sqrt(144))\n");
	Py_Finalize();
В результате:
out= 0x12707a0 fileno(out)= -1
python= 0
ValueError: Negative filedescriptor
Virtuos86

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Открой уже man 2 pipe.

std::string out;
int fd[2];
pipe2(fd, O_CLOEXEC);
std::thread writer([&fd,&out]{
  char buf[1024];
  int len;
  while ((len = read(fd[0], buf, sizeof(buf))) > 0)
    out.append(buf, len);
});
/* используй fd[1] в качестве файлового дескриптора и работай с питоном */
close(fd[1]); /* это только после отработки питона */
writer.join();
close(fd[0]);
/* в out выхлоп питоньего скрипта */

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Какое будет имя внутри интерпретатора после вызова PyFile_FromFd(fd[1], ...)? Или можно PyObject *redirect = PyFile_FromFd(fd[1], ... затем этому redirect как-то присвоить питонское имя?

За pipe2 спасибо, попробую и этот вариант.

Попутно вопрос

PyRun_SimpleString("import math\ntest = 'haha'\nprint('%d' % math.sqrt(144))\n");
PyObject *test = PyObject_GetAttrString(module, "test");
qDebug() << "test=" << QString(PyBytes_AsString(PyObject_Str(test)));
Правильно ли я пытаюсь преобразовать в строку питонский объект? А то пустая строка получается...

I-Love-Microsoft ★★★★★
() автор топика
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от hizel

Кажется разобрался как применить PyFile_FromFd, не зная его питонского имени:

FILE *out = fopen("/home/user/test_py/test_out.txt", "w");

	if(out == NULL)
	{
		qDebug() << "out error";
		return;
	}

	Py_Initialize();
	qDebug() << "out=" << out << "fileno(out)=" << fileno(out);
	PyObject *py_out = PyFile_FromFd(fileno(out), "w", "w", -1, NULL, NULL, NULL, 0);
	PyObject *sys = PyImport_ImportModule("sys");
	PyObject_SetAttrString(sys, "stdout", py_out);
	qDebug() << "python=" << PyRun_SimpleString("import math\ntest = 'haha'\nprint('%d -> %s' % (math.sqrt(144), test))\n");
	Py_Finalize();
	fclose(out);
С выводом в файл на диске всё работает, а вот так:
	size_t size = 0;
	char *buffer = NULL;
	FILE *out = open_memstream(&buffer, &size);
// или
	char buffer[1024];
	FILE *out = fmemopen(buffer, 1024, "w");
Не работает, ибо fileno дает -1. Спасибо, проблема решена, а вот попытки создать файл в памяти с ненулевым fileno это видимо другая задача.

Спасибо за подсказку!

I-Love-Microsoft ★★★★★
() автор топика

Более правильный вариант, всё в памяти:

	QString _redirect =
"import sys\n\
class _redirect:\n\
	def __init__(self):\n\
		self.value = ''\n\
	def write(self, text):\n\
		self.value += text\n\
redirect = _redirect()\n\
sys.stdout = redirect\n\
sys.stderr = redirect\n";

	Py_Initialize();
	PyObject *module = PyImport_AddModule("__main__");
	PyRun_SimpleString(_redirect.toLatin1().data());
	qDebug() << "python=" << PyRun_SimpleString("import math\ntest = 'haйфыha'\nprint('%d -> %s' % (math.sqrt(144), test))\n");
	PyRun_SimpleString("sys_stdout = redirect.value");
	PyObject *_out = PyObject_GetAttrString(module, "sys_stdout");
	char *c_str = PyUnicode_AsUTF8(_out);
	QString out = QString(c_str);
	qDebug() << "out=" << out;
	Py_Finalize();

I-Love-Microsoft ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.