mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	Refactor Python
This commit is contained in:
		@@ -24,13 +24,13 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	friend class EffectModule;
 | 
						friend class EffectModule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Effect(Hyperion *hyperion
 | 
						Effect(Hyperion* hyperion
 | 
				
			||||||
				, int priority
 | 
							, int priority
 | 
				
			||||||
				, int timeout
 | 
							, int timeout
 | 
				
			||||||
				, const QString &script
 | 
							, const QString& script
 | 
				
			||||||
				, const QString &name
 | 
							, const QString& name
 | 
				
			||||||
				, const QJsonObject &args = QJsonObject()
 | 
							, const QJsonObject& args = QJsonObject()
 | 
				
			||||||
				, const QString &imageData = ""
 | 
							, const QString& imageData = ""
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	~Effect() override;
 | 
						~Effect() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,20 +64,20 @@ public:
 | 
				
			|||||||
	QString getScript() const { return _script; }
 | 
						QString getScript() const { return _script; }
 | 
				
			||||||
	QString getName() const { return _name; }
 | 
						QString getName() const { return _name; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int getTimeout() const {return _timeout; }
 | 
						int getTimeout() const { return _timeout; }
 | 
				
			||||||
	bool isEndless() const { return _isEndless; }
 | 
						bool isEndless() const { return _isEndless; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QJsonObject getArgs() const { return _args; }
 | 
						QJsonObject getArgs() const { return _args; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
signals:
 | 
					signals:
 | 
				
			||||||
	void setInput(int priority, const std::vector<ColorRgb> &ledColors, int timeout_ms, bool clearEffect);
 | 
						void setInput(int priority, const std::vector<ColorRgb>& ledColors, int timeout_ms, bool clearEffect);
 | 
				
			||||||
	void setInputImage(int priority, const Image<ColorRgb> &image, int timeout_ms, bool clearEffect);
 | 
						void setInputImage(int priority, const Image<ColorRgb>& image, int timeout_ms, bool clearEffect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	void setModuleParameters();
 | 
						bool setModuleParameters();
 | 
				
			||||||
	void addImage();
 | 
						void addImage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Hyperion *_hyperion;
 | 
						Hyperion* _hyperion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const int _priority;
 | 
						const int _priority;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,12 +95,12 @@ private:
 | 
				
			|||||||
	/// Buffer for colorData
 | 
						/// Buffer for colorData
 | 
				
			||||||
	QVector<ColorRgb> _colors;
 | 
						QVector<ColorRgb> _colors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Logger *_log;
 | 
						Logger* _log;
 | 
				
			||||||
	// Reflects whenever this effects should interrupt (timeout or external request)
 | 
						// Reflects whenever this effects should interrupt (timeout or external request)
 | 
				
			||||||
	std::atomic<bool> _interupt {};
 | 
						std::atomic<bool> _interupt{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QSize           _imageSize;
 | 
						QSize           _imageSize;
 | 
				
			||||||
	QImage          _image;
 | 
						QImage          _image;
 | 
				
			||||||
	QPainter       *_painter;
 | 
						QPainter*       _painter;
 | 
				
			||||||
	QVector<QImage> _imageStack;
 | 
						QVector<QImage> _imageStack;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,47 +8,41 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Effect;
 | 
					class Effect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EffectModule: public QObject
 | 
					class EffectModule : public QObject
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Q_OBJECT
 | 
						Q_OBJECT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	// Python 3 module def
 | 
					 | 
				
			||||||
	static struct PyModuleDef moduleDef;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Init module
 | 
					 | 
				
			||||||
	static PyObject* PyInit_hyperion();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Register module once
 | 
						// Register module once
 | 
				
			||||||
	static void registerHyperionExtensionModule();
 | 
						static void registerHyperionExtensionModule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// json 2 python
 | 
						// json 2 python
 | 
				
			||||||
	static PyObject * json2python(const QJsonValue & jsonData);
 | 
						static PyObject* json2python(const QJsonValue& jsonData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Wrapper methods for Python interpreter extra buildin methods
 | 
						// Wrapper methods for Python interpreter extra buildin methods
 | 
				
			||||||
	static PyMethodDef effectMethods[];
 | 
						static PyMethodDef effectMethods[];
 | 
				
			||||||
	static PyObject* wrapSetColor              (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapSetColor(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapSetImage              (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapSetImage(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapGetImage              (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapGetImage(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapAbort                 (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapAbort(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageShow             (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageShow(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageLinearGradient   (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageLinearGradient(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageConicalGradient  (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageConicalGradient(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageRadialGradient   (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageRadialGradient(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageSolidFill        (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageSolidFill(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageDrawLine         (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageDrawLine(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageDrawPoint        (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageDrawPoint(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageDrawRect         (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageDrawRect(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageDrawPolygon      (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageDrawPolygon(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageDrawPie          (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageDrawPie(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageSetPixel         (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageSetPixel(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageGetPixel         (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageGetPixel(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageSave             (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageSave(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageMinSize          (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageMinSize(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageWidth            (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageWidth(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageHeight           (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageHeight(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageCRotate          (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageCRotate(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageCOffset          (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageCOffset(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageCShear           (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageCShear(PyObject* self, PyObject* args);
 | 
				
			||||||
	static PyObject* wrapImageResetT           (PyObject *self, PyObject *args);
 | 
						static PyObject* wrapImageResetT(PyObject* self, PyObject* args);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#undef slots
 | 
				
			||||||
 | 
					#include <Python.h>
 | 
				
			||||||
 | 
					#define slots Q_SLOTS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @brief Handle the PythonInit, module registers and DeInit
 | 
					/// @brief Handle the PythonInit, module registers and DeInit
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@@ -10,4 +14,6 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	PythonInit();
 | 
						PythonInit();
 | 
				
			||||||
	~PythonInit();
 | 
						~PythonInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void handlePythonError(PyStatus status, PyConfig& config);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@
 | 
				
			|||||||
#include "Python.h"
 | 
					#include "Python.h"
 | 
				
			||||||
#define slots
 | 
					#define slots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <python/PythonUtils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Logger;
 | 
					class Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PythonProgram
 | 
					class PythonProgram
 | 
				
			||||||
@@ -17,9 +19,15 @@ public:
 | 
				
			|||||||
	PythonProgram(const QString & name, Logger * log);
 | 
						PythonProgram(const QString & name, Logger * log);
 | 
				
			||||||
	~PythonProgram();
 | 
						~PythonProgram();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						operator PyThreadState* ()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return _tstate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void execute(const QByteArray &python_code);
 | 
						void execute(const QByteArray &python_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QString _name;
 | 
						QString _name;
 | 
				
			||||||
	Logger* _log;
 | 
						Logger* _log;
 | 
				
			||||||
	PyThreadState* _tstate;
 | 
						PyThreadState* _tstate;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
// python utils
 | 
					// python utils
 | 
				
			||||||
#include <python/PythonProgram.h>
 | 
					#include <python/PythonProgram.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData)
 | 
					Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString& script, const QString& name, const QJsonObject& args, const QString& imageData)
 | 
				
			||||||
	: QThread()
 | 
						: QThread()
 | 
				
			||||||
	, _hyperion(hyperion)
 | 
						, _hyperion(hyperion)
 | 
				
			||||||
	, _priority(priority)
 | 
						, _priority(priority)
 | 
				
			||||||
@@ -26,7 +26,7 @@ Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &scr
 | 
				
			|||||||
	, _endTime(-1)
 | 
						, _endTime(-1)
 | 
				
			||||||
	, _interupt(false)
 | 
						, _interupt(false)
 | 
				
			||||||
	, _imageSize(hyperion->getLedGridSize())
 | 
						, _imageSize(hyperion->getLedGridSize())
 | 
				
			||||||
	, _image(_imageSize,QImage::Format_ARGB32_Premultiplied)
 | 
						, _image(_imageSize, QImage::Format_ARGB32_Premultiplied)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	_colors.resize(_hyperion->getLedCount());
 | 
						_colors.resize(_hyperion->getLedCount());
 | 
				
			||||||
	_colors.fill(ColorRgb::BLACK);
 | 
						_colors.fill(ColorRgb::BLACK);
 | 
				
			||||||
@@ -61,41 +61,81 @@ int Effect::getRemaining() const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (timeout >= 0)
 | 
						if (timeout >= 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		timeout = static_cast<int>( _endTime - QDateTime::currentMSecsSinceEpoch());
 | 
							timeout = static_cast<int>(_endTime - QDateTime::currentMSecsSinceEpoch());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return timeout;
 | 
						return timeout;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Effect::setModuleParameters()
 | 
					bool Effect::setModuleParameters()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// import the buildtin Hyperion module
 | 
						// import the buildtin Hyperion module
 | 
				
			||||||
	PyObject * module = PyImport_ImportModule("hyperion");
 | 
						PyObject* module = PyImport_ImportModule("hyperion");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
 | 
						if (module == nullptr) {
 | 
				
			||||||
	PyModule_AddObject(module, "__effectObj", PyCapsule_New((void*)this, "hyperion.__effectObj", nullptr));
 | 
							PyErr_Print();  // Print error if module import fails
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// add ledCount variable to the interpreter
 | 
						// Add a capsule containing 'this' to the module for callback access
 | 
				
			||||||
 | 
						PyObject* capsule = PyCapsule_New((void*)this, "hyperion.__effectObj", nullptr);
 | 
				
			||||||
 | 
						if (capsule == nullptr || PyModule_AddObject(module, "__effectObj", capsule) < 0) {
 | 
				
			||||||
 | 
							PyErr_Print();  // Print error if adding capsule fails
 | 
				
			||||||
 | 
							Py_XDECREF(module);  // Clean up if capsule addition fails
 | 
				
			||||||
 | 
							Py_XDECREF(capsule);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add ledCount variable to the interpreter
 | 
				
			||||||
	int ledCount = 0;
 | 
						int ledCount = 0;
 | 
				
			||||||
	QMetaObject::invokeMethod(_hyperion, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ledCount));
 | 
						QMetaObject::invokeMethod(_hyperion, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ledCount));
 | 
				
			||||||
	PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", ledCount));
 | 
						PyObject* ledCountObj = Py_BuildValue("i", ledCount);
 | 
				
			||||||
 | 
						if (PyObject_SetAttrString(module, "ledCount", ledCountObj) < 0) {
 | 
				
			||||||
 | 
							PyErr_Print();  // Print error if setting attribute fails
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Py_XDECREF(ledCountObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// add minimumWriteTime variable to the interpreter
 | 
						// Add minimumWriteTime variable to the interpreter
 | 
				
			||||||
	int latchTime = 0;
 | 
						int latchTime = 0;
 | 
				
			||||||
	QMetaObject::invokeMethod(_hyperion, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, latchTime));
 | 
						QMetaObject::invokeMethod(_hyperion, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, latchTime));
 | 
				
			||||||
	PyObject_SetAttrString(module, "latchTime", Py_BuildValue("i", latchTime));
 | 
						PyObject* latchTimeObj = Py_BuildValue("i", latchTime);
 | 
				
			||||||
 | 
						if (PyObject_SetAttrString(module, "latchTime", latchTimeObj) < 0) {
 | 
				
			||||||
 | 
							PyErr_Print();  // Print error if setting attribute fails
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Py_XDECREF(latchTimeObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// add a args variable to the interpreter
 | 
						// Add args variable to the interpreter
 | 
				
			||||||
	PyObject_SetAttrString(module, "args", EffectModule::json2python(_args));
 | 
						PyObject* argsObj = EffectModule::json2python(_args);
 | 
				
			||||||
 | 
						if (PyObject_SetAttrString(module, "args", argsObj) < 0) {
 | 
				
			||||||
 | 
							PyErr_Print();  // Print error if setting attribute fails
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Py_XDECREF(argsObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// decref the module
 | 
						// Decrement module reference
 | 
				
			||||||
	Py_XDECREF(module);
 | 
						Py_XDECREF(module);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Effect::run()
 | 
					void Effect::run()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	PythonProgram program(_name, _log);
 | 
						PythonProgram program(_name, _log);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setModuleParameters();
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
						PyThreadState* prev_thread_state = PyThreadState_Swap(program);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!setModuleParameters())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Error(_log, "Failed to set Module parameters. Effect will not be executed.");
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
							PyThreadState_Swap(prev_thread_state);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
						PyThreadState_Swap(prev_thread_state);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set the end time if applicable
 | 
						// Set the end time if applicable
 | 
				
			||||||
	if (_timeout > 0)
 | 
						if (_timeout > 0)
 | 
				
			||||||
@@ -104,7 +144,7 @@ void Effect::run()
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Run the effect script
 | 
						// Run the effect script
 | 
				
			||||||
	QFile file (_script);
 | 
						QFile file(_script);
 | 
				
			||||||
	if (file.open(QIODevice::ReadOnly))
 | 
						if (file.open(QIODevice::ReadOnly))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		program.execute(file.readAll());
 | 
							program.execute(file.readAll());
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -18,7 +18,7 @@
 | 
				
			|||||||
#include <HyperionConfig.h>
 | 
					#include <HyperionConfig.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef _WIN32
 | 
					#ifdef _WIN32
 | 
				
			||||||
	#include <stdexcept>
 | 
					#include <stdexcept>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define STRINGIFY2(x) #x
 | 
					#define STRINGIFY2(x) #x
 | 
				
			||||||
@@ -44,14 +44,14 @@ PythonInit::PythonInit()
 | 
				
			|||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					#if (PY_VERSION_HEX >= 0x03080000)
 | 
				
			||||||
	status = PyConfig_SetString(&config, &config.program_name, programName);
 | 
						status = PyConfig_SetString(&config, &config.program_name, programName);
 | 
				
			||||||
	if (PyStatus_Exception(status)) {
 | 
						if (PyStatus_Exception(status)) {
 | 
				
			||||||
		goto exception;
 | 
							handlePythonError(status, config);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	Py_SetProgramName(programName);
 | 
						Py_SetProgramName(programName);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		// set Python module path when exists
 | 
							// set Python module path when it exists
 | 
				
			||||||
		QString py_path = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
							QString py_path = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
				
			||||||
		QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + STRINGIFY(PYTHON_VERSION_MINOR) + ".zip");
 | 
							QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + STRINGIFY(PYTHON_VERSION_MINOR) + ".zip");
 | 
				
			||||||
		QString py_framework = QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current/lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
							QString py_framework = QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current/lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
				
			||||||
@@ -59,21 +59,23 @@ PythonInit::PythonInit()
 | 
				
			|||||||
		if (QFile(py_file).exists() || QDir(py_path).exists() || QDir(py_framework).exists())
 | 
							if (QFile(py_file).exists() || QDir(py_path).exists() || QDir(py_framework).exists())
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
#if (PY_VERSION_HEX >= 0x030C0000)
 | 
					#if (PY_VERSION_HEX >= 0x030C0000)
 | 
				
			||||||
            config.site_import = 0;
 | 
								config.site_import = 0;
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
            Py_NoSiteFlag++;
 | 
								Py_NoSiteFlag++;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
			if (QFile(py_file).exists()) // Windows
 | 
								if (QFile(py_file).exists()) // Windows
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					#if (PY_VERSION_HEX >= 0x03080000)
 | 
				
			||||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(py_file));
 | 
									status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(py_file));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				config.module_search_paths_set = 1;
 | 
									config.module_search_paths_set = 1;
 | 
				
			||||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(py_file.toStdWString().c_str()));
 | 
									status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(py_file.toStdWString().c_str()));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
				Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
 | 
									Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
 | 
				
			||||||
@@ -85,18 +87,21 @@ PythonInit::PythonInit()
 | 
				
			|||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					#if (PY_VERSION_HEX >= 0x03080000)
 | 
				
			||||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../")));
 | 
									status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../")));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				config.module_search_paths_set = 1;
 | 
									config.module_search_paths_set = 1;
 | 
				
			||||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path).absolutePath().toStdWString().c_str()));
 | 
									status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path).absolutePath().toStdWString().c_str()));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
									status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
				QStringList python_paths;
 | 
									QStringList python_paths;
 | 
				
			||||||
@@ -114,18 +119,21 @@ PythonInit::PythonInit()
 | 
				
			|||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					#if (PY_VERSION_HEX >= 0x03080000)
 | 
				
			||||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current")));
 | 
									status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current")));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				config.module_search_paths_set = 1;
 | 
									config.module_search_paths_set = 1;
 | 
				
			||||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework).absolutePath().toStdWString().c_str()));
 | 
									status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework).absolutePath().toStdWString().c_str()));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
									status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
				
			||||||
				if (PyStatus_Exception(status)) {
 | 
									if (PyStatus_Exception(status)) {
 | 
				
			||||||
					goto exception;
 | 
										handlePythonError(status, config);
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
				QStringList python_paths;
 | 
									QStringList python_paths;
 | 
				
			||||||
@@ -146,7 +154,8 @@ PythonInit::PythonInit()
 | 
				
			|||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					#if (PY_VERSION_HEX >= 0x03080000)
 | 
				
			||||||
	status = Py_InitializeFromConfig(&config);
 | 
						status = Py_InitializeFromConfig(&config);
 | 
				
			||||||
	if (PyStatus_Exception(status)) {
 | 
						if (PyStatus_Exception(status)) {
 | 
				
			||||||
		goto exception;
 | 
							handlePythonError(status, config);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	PyConfig_Clear(&config);
 | 
						PyConfig_Clear(&config);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -154,7 +163,8 @@ PythonInit::PythonInit()
 | 
				
			|||||||
	// init Python
 | 
						// init Python
 | 
				
			||||||
	Debug(Logger::getInstance("DAEMON"), "Initializing Python interpreter");
 | 
						Debug(Logger::getInstance("DAEMON"), "Initializing Python interpreter");
 | 
				
			||||||
	Py_InitializeEx(0);
 | 
						Py_InitializeEx(0);
 | 
				
			||||||
	if ( !Py_IsInitialized() )
 | 
					
 | 
				
			||||||
 | 
						if (!Py_IsInitialized())
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		throw std::runtime_error("Initializing Python failed!");
 | 
							throw std::runtime_error("Initializing Python failed!");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -165,20 +175,25 @@ PythonInit::PythonInit()
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mainThreadState = PyEval_SaveThread();
 | 
						mainThreadState = PyEval_SaveThread();
 | 
				
			||||||
	return;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
					// Error handling function to replace goto exception
 | 
				
			||||||
exception:
 | 
					void PythonInit::handlePythonError(PyStatus status, PyConfig& config)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
	Error(Logger::getInstance("DAEMON"), "Initializing Python config failed with error [%s]", status.err_msg);
 | 
						Error(Logger::getInstance("DAEMON"), "Initializing Python config failed with error [%s]", status.err_msg);
 | 
				
			||||||
	PyConfig_Clear(&config);
 | 
						PyConfig_Clear(&config);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	throw std::runtime_error("Initializing Python failed!");
 | 
						throw std::runtime_error("Initializing Python failed!");
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PythonInit::~PythonInit()
 | 
					PythonInit::~PythonInit()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter");
 | 
						Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
	PyEval_RestoreThread(mainThreadState);
 | 
						PyEval_RestoreThread(mainThreadState);
 | 
				
			||||||
	Py_Finalize();
 | 
					#else
 | 
				
			||||||
 | 
						PyThreadState_Swap(mainThreadState);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int rc = Py_FinalizeEx();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,173 +1,184 @@
 | 
				
			|||||||
#include <python/PythonProgram.h>
 | 
					#include <python/PythonProgram.h>
 | 
				
			||||||
#include <python/PythonUtils.h>
 | 
					#include <python/PythonUtils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <utils/Logger.h>
 | 
					#include <utils/Logger.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <QThread>
 | 
					#include <QThread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyThreadState* mainThreadState;
 | 
					PyThreadState* mainThreadState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PythonProgram::PythonProgram(const QString & name, Logger * log) :
 | 
					PythonProgram::PythonProgram(const QString& name, Logger* log) :
 | 
				
			||||||
	_name(name), _log(log), _tstate(nullptr)
 | 
						_name(name)
 | 
				
			||||||
 | 
						, _log(log)
 | 
				
			||||||
 | 
						, _tstate(nullptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// we probably need to wait until mainThreadState is available
 | 
						// we probably need to wait until mainThreadState is available
 | 
				
			||||||
	while(mainThreadState == nullptr){};
 | 
						QThread::msleep(10);
 | 
				
			||||||
 | 
						while (mainThreadState == nullptr)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							QThread::msleep(10);  // Wait with delay to avoid busy waiting
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a new subinterpreter for this thread
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
	// get global lock
 | 
						// get global lock
 | 
				
			||||||
	PyEval_RestoreThread(mainThreadState);
 | 
						PyEval_RestoreThread(mainThreadState);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Initialize a new thread state
 | 
					 | 
				
			||||||
	_tstate = Py_NewInterpreter();
 | 
						_tstate = Py_NewInterpreter();
 | 
				
			||||||
	if(_tstate == nullptr)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
#if (PY_VERSION_HEX >= 0x03020000)
 | 
					 | 
				
			||||||
		PyThreadState_Swap(mainThreadState);
 | 
					 | 
				
			||||||
		PyEval_SaveThread();
 | 
					 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
		PyEval_ReleaseLock();
 | 
						PyThreadState* prev = PyThreadState_Swap(NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a new interpreter configuration object
 | 
				
			||||||
 | 
						PyInterpreterConfig config{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set configuration options
 | 
				
			||||||
 | 
						config.use_main_obmalloc = 0;
 | 
				
			||||||
 | 
						config.allow_fork = 0;
 | 
				
			||||||
 | 
						config.allow_exec = 0;
 | 
				
			||||||
 | 
						config.allow_threads = 1;
 | 
				
			||||||
 | 
						config.allow_daemon_threads = 0;
 | 
				
			||||||
 | 
						config.check_multi_interp_extensions = 1;
 | 
				
			||||||
 | 
						config.gil = PyInterpreterConfig_OWN_GIL;
 | 
				
			||||||
 | 
						Py_NewInterpreterFromConfig(&_tstate, &config);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
		Error(_log, "Failed to get thread state for %s",QSTRING_CSTR(_name));
 | 
					
 | 
				
			||||||
 | 
						if (_tstate == nullptr)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							PyThreadState_Swap(mainThreadState);
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
							PyEval_SaveThread();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							Error(_log, "Failed to get thread state for %s", QSTRING_CSTR(_name));
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
	PyThreadState_Swap(_tstate);
 | 
						PyThreadState_Swap(_tstate);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PythonProgram::~PythonProgram()
 | 
					PythonProgram::~PythonProgram()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!_tstate)
 | 
						if (!_tstate)
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// stop sub threads if needed
 | 
					 | 
				
			||||||
	for (PyThreadState* s = PyInterpreterState_ThreadHead(_tstate->interp), *old = nullptr; s;)
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (s == _tstate)
 | 
							return;
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			s = s->next;
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (old != s)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Debug(_log,"ID %s: Waiting on thread %u", QSTRING_CSTR(_name), s->thread_id);
 | 
					 | 
				
			||||||
			old = s;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Py_BEGIN_ALLOW_THREADS;
 | 
					 | 
				
			||||||
		QThread::msleep(100);
 | 
					 | 
				
			||||||
		Py_END_ALLOW_THREADS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		s = PyInterpreterState_ThreadHead(_tstate->interp);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
						PyThreadState* prev_thread_state = PyThreadState_Swap(_tstate);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Clean up the thread state
 | 
						// Clean up the thread state
 | 
				
			||||||
	Py_EndInterpreter(_tstate);
 | 
						Py_EndInterpreter(_tstate);
 | 
				
			||||||
#if (PY_VERSION_HEX >= 0x03020000)
 | 
					
 | 
				
			||||||
	PyThreadState_Swap(mainThreadState);
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
						PyThreadState_Swap(prev_thread_state);
 | 
				
			||||||
	PyEval_SaveThread();
 | 
						PyEval_SaveThread();
 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
	PyEval_ReleaseLock();
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PythonProgram::execute(const QByteArray & python_code)
 | 
					void PythonProgram::execute(const QByteArray& python_code)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!_tstate)
 | 
						if (!_tstate)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PyObject *main_module = PyImport_ImportModule("__main__"); // New Reference
 | 
						PyThreadState* prev_thread_state = PyThreadState_Swap(_tstate);
 | 
				
			||||||
	PyObject *main_dict = PyModule_GetDict(main_module); // Borrowed reference
 | 
					 | 
				
			||||||
	Py_INCREF(main_dict); // Incref "main_dict" to use it in PyRun_String(), because PyModule_GetDict() has decref "main_dict"
 | 
					 | 
				
			||||||
	Py_DECREF(main_module); // // release "main_module" when done
 | 
					 | 
				
			||||||
	PyObject *result = PyRun_String(python_code.constData(), Py_file_input, main_dict, main_dict); // New Reference
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PyObject* main_module = PyImport_ImportModule("__main__");
 | 
				
			||||||
 | 
						if (!main_module)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Restore the previous thread state
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
							PyThreadState_Swap(mainThreadState);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							PyThreadState_Swap(prev_thread_state);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PyObject* main_dict = PyModule_GetDict(main_module);  // Borrowed reference to globals
 | 
				
			||||||
 | 
						PyObject* result = PyRun_String(python_code.constData(), Py_file_input, main_dict, main_dict);
 | 
				
			||||||
	if (!result)
 | 
						if (!result)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (PyErr_Occurred()) // Nothing needs to be done for a borrowed reference
 | 
							if (PyErr_Occurred())
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Error(_log,"###### PYTHON EXCEPTION ######");
 | 
								Error(_log, "###### PYTHON EXCEPTION ######");
 | 
				
			||||||
			Error(_log,"## In effect '%s'", QSTRING_CSTR(_name));
 | 
								Error(_log, "## In effect '%s'", QSTRING_CSTR(_name));
 | 
				
			||||||
			/* Objects all initialized to NULL for Py_XDECREF */
 | 
					 | 
				
			||||||
			PyObject *errorType = NULL, *errorValue = NULL, *errorTraceback = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			PyErr_Fetch(&errorType, &errorValue, &errorTraceback); // New Reference or NULL
 | 
								PyObject* errorType = NULL, * errorValue = NULL, * errorTraceback = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								PyErr_Fetch(&errorType, &errorValue, &errorTraceback);
 | 
				
			||||||
			PyErr_NormalizeException(&errorType, &errorValue, &errorTraceback);
 | 
								PyErr_NormalizeException(&errorType, &errorValue, &errorTraceback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Extract exception message from "errorValue"
 | 
								if (errorValue)
 | 
				
			||||||
			if(errorValue)
 | 
					 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				QString message;
 | 
									QString message;
 | 
				
			||||||
				if(PyObject_HasAttrString(errorValue, "__class__"))
 | 
									if (PyObject_HasAttrString(errorValue, "__class__"))
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					PyObject *classPtr = PyObject_GetAttrString(errorValue, "__class__"); // New Reference
 | 
										PyObject* classPtr = PyObject_GetAttrString(errorValue, "__class__");
 | 
				
			||||||
					PyObject *class_name = NULL; /* Object "class_name" initialized to NULL for Py_XDECREF */
 | 
										PyObject* class_name = classPtr ? PyObject_GetAttrString(classPtr, "__name__") : NULL;
 | 
				
			||||||
					class_name = PyObject_GetAttrString(classPtr, "__name__"); // New Reference or NULL
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if(class_name && PyUnicode_Check(class_name))
 | 
										if (class_name && PyUnicode_Check(class_name))
 | 
				
			||||||
						message.append(PyUnicode_AsUTF8(class_name));
 | 
											message.append(PyUnicode_AsUTF8(class_name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					Py_DECREF(classPtr); // release "classPtr" when done
 | 
										Py_XDECREF(class_name);
 | 
				
			||||||
					Py_XDECREF(class_name); // Use Py_XDECREF() to ignore NULL references
 | 
										Py_DECREF(classPtr);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Object "class_name" initialized to NULL for Py_XDECREF
 | 
									PyObject* valueString = PyObject_Str(errorValue);
 | 
				
			||||||
				PyObject *valueString = NULL;
 | 
					 | 
				
			||||||
				valueString = PyObject_Str(errorValue); // New Reference or NULL
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if(valueString && PyUnicode_Check(valueString))
 | 
									if (valueString && PyUnicode_Check(valueString))
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					if(!message.isEmpty())
 | 
										if (!message.isEmpty())
 | 
				
			||||||
						message.append(": ");
 | 
											message.append(": ");
 | 
				
			||||||
 | 
					 | 
				
			||||||
					message.append(PyUnicode_AsUTF8(valueString));
 | 
										message.append(PyUnicode_AsUTF8(valueString));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				Py_XDECREF(valueString); // Use Py_XDECREF() to ignore NULL references
 | 
									Py_XDECREF(valueString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				Error(_log, "## %s", QSTRING_CSTR(message));
 | 
									Error(_log, "## %s", QSTRING_CSTR(message));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Extract exception message from "errorTraceback"
 | 
								if (errorTraceback)
 | 
				
			||||||
			if(errorTraceback)
 | 
					 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				// Object "tracebackList" initialized to NULL for Py_XDECREF
 | 
									PyObject* tracebackModule = PyImport_ImportModule("traceback");
 | 
				
			||||||
				PyObject *tracebackModule = NULL, *methodName = NULL, *tracebackList = NULL;
 | 
									PyObject* methodName = PyUnicode_FromString("format_exception");
 | 
				
			||||||
				QString tracebackMsg;
 | 
									PyObject* tracebackList = tracebackModule && methodName
 | 
				
			||||||
 | 
										? PyObject_CallMethodObjArgs(tracebackModule, methodName, errorType, errorValue, errorTraceback, NULL)
 | 
				
			||||||
 | 
										: NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				tracebackModule = PyImport_ImportModule("traceback"); // New Reference or NULL
 | 
									if (tracebackList)
 | 
				
			||||||
				methodName = PyUnicode_FromString("format_exception"); // New Reference or NULL
 | 
					 | 
				
			||||||
				tracebackList = PyObject_CallMethodObjArgs(tracebackModule, methodName, errorType, errorValue, errorTraceback, NULL); // New Reference or NULL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if(tracebackList)
 | 
					 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					PyObject* iterator = PyObject_GetIter(tracebackList); // New Reference
 | 
										PyObject* iterator = PyObject_GetIter(tracebackList);
 | 
				
			||||||
 | 
					 | 
				
			||||||
					PyObject* item;
 | 
										PyObject* item;
 | 
				
			||||||
					while( (item = PyIter_Next(iterator)) ) // New Reference
 | 
										while ((item = PyIter_Next(iterator)))
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
						Error(_log, "## %s",QSTRING_CSTR(QString(PyUnicode_AsUTF8(item)).trimmed()));
 | 
											Error(_log, "## %s", QSTRING_CSTR(QString(PyUnicode_AsUTF8(item)).trimmed()));
 | 
				
			||||||
						Py_DECREF(item); // release "item" when done
 | 
											Py_DECREF(item);
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					Py_DECREF(iterator);  // release "iterator" when done
 | 
										Py_DECREF(iterator);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Use Py_XDECREF() to ignore NULL references
 | 
					 | 
				
			||||||
				Py_XDECREF(tracebackModule);
 | 
									Py_XDECREF(tracebackModule);
 | 
				
			||||||
				Py_XDECREF(methodName);
 | 
									Py_XDECREF(methodName);
 | 
				
			||||||
				Py_XDECREF(tracebackList);
 | 
									Py_XDECREF(tracebackList);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Give the exception back to python and print it to stderr in case anyone else wants it.
 | 
					 | 
				
			||||||
				Py_XINCREF(errorType);
 | 
					 | 
				
			||||||
				Py_XINCREF(errorValue);
 | 
					 | 
				
			||||||
				Py_XINCREF(errorTraceback);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				PyErr_Restore(errorType, errorValue, errorTraceback);
 | 
									PyErr_Restore(errorType, errorValue, errorTraceback);
 | 
				
			||||||
				//PyErr_PrintEx(0); // Remove this line to switch off stderr output
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			Error(_log,"###### EXCEPTION END ######");
 | 
								Error(_log, "###### EXCEPTION END ######");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Py_DECREF(result);  // release "result" when done
 | 
							Py_DECREF(result);  // Release result when done
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Py_DECREF(main_dict);  // release "main_dict" when done
 | 
						Py_DECREF(main_module);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Restore the previous thread state
 | 
				
			||||||
 | 
					#if (PY_VERSION_HEX < 0x030C0000)
 | 
				
			||||||
 | 
						PyThreadState_Swap(mainThreadState);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						PyThreadState_Swap(prev_thread_state);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user