Skip to content

Commit

Permalink
PythonDLL: additional checks in the absence of initialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
Roffild committed Apr 24, 2019
1 parent 6fdc1df commit c4279c2
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 50 deletions.
8 changes: 2 additions & 6 deletions Experts/Roffild/Examples/PythonDLL_Example.mq5
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#property copyright "Roffild"
#property link "https://github.com/Roffild/RoffildLibrary"

extern string PythonHome = "";
input string PythonHome = "";

#include <Roffild/PythonDLL.mqh>
#include <Roffild/ToIndicator.mqh>
Expand All @@ -42,11 +42,7 @@ int OnInit()
return INIT_FAILED;
}
if (python.eval(_PyCode_, true) == false) {
const string errmycode = python.getErrorText();
if (errmycode != "") {
Print(errmycode);
return INIT_FAILED;
}
return INIT_FAILED;
}
uchar array[];
StringToCharArray("Version_info: ", array);
Expand Down
16 changes: 13 additions & 3 deletions Include/Roffild/PythonDLL.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ public:
/**
* It is not necessary to use this function,
* because it is always called when a test is completed or MetaTrader is closed.
*
* WARNING: Do not call this function when using the NumPy library.
*
* Bug:
* https://github.com/numpy/numpy/issues/8097
* https://bugs.python.org/issue34309
*/
void finalize()
{
Expand All @@ -76,19 +82,23 @@ public:
*
* @param[in] pycode
* @param[in] override_class when changing the variable __mql__, set to true.
* @return error status.
* @return false if error.
*/
bool eval(const string pycode, const bool override_class = false)
{
return pyEval(pycode, override_class);
if (pyEval(pycode, override_class) == false) {
Print(getErrorText());
return false;
}
return true;
}

/**
* Checks for errors.
* To get an error message, use getErrorText().
*
* @param[in] clear clears the error.
* @return error status.
* @return true if error.
*/
bool isError(const bool clear = true)
{
Expand Down
67 changes: 37 additions & 30 deletions Libraries/Roffild/PythonDLL/PythonDLL.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,15 @@ BOOL APIENTRY DllMain(HMODULE hModule,
break;
case DLL_PROCESS_DETACH:
for (size_t x = 1; x < __interps_count; x++) {
stInterpreter *__interp = &__interps[x];
if (__interp->interp != NULL && _PyThreadState_UncheckedGet() != __interp->interp) {
PyEval_AcquireThread(__interp->interp);
__clearInterp(__interp);
PyErr_Clear();
Py_EndInterpreter(__interp->interp);
__interp->interp = NULL;
__clearInterp(__interp);
PY_THREAD_ANY_STOP;
}
_PY_THREAD_START_OR(&__interps[x], continue);
__clearInterp(__interp);
PyErr_Clear();
Py_EndInterpreter(__interp->interp);
__interp->interp = NULL;
__clearInterp(__interp);
PY_THREAD_ANY_STOP;
}
PY_THREAD_MAIN_START;
PY_THREAD_MAIN_START_OR(break);
Py_Finalize();
break;
}
Expand All @@ -66,7 +63,11 @@ BOOL APIENTRY DllMain(HMODULE hModule,

_DLLSTD(mqlbool) pyIsInitialized()
{
return Py_IsInitialized() && __getInterp()->interp != NULL;
if (Py_IsInitialized()) {
stInterpreter *stinterp = __getInterp();
return stinterp != NULL && stinterp->interp != NULL;
}
return false;
}

_DLLSTD(mqlbool) pyInitialize(const mqlstring paths_to_packages, const mqlstring paths_to_dlls,
Expand All @@ -89,7 +90,7 @@ _DLLSTD(mqlbool) pyInitialize(const mqlstring paths_to_packages, const mqlstring
}
stInterpreter *stinterp = __getInterp();
if (stinterp->interp == NULL) {
PY_THREAD_MAIN_START;
PY_THREAD_MAIN_START_OR(return false);
stinterp = __setInterp(Py_NewInterpreter());
if (stinterp->interp != NULL) {
stinterp->main = PyImport_AddModule("__main__");
Expand Down Expand Up @@ -139,26 +140,22 @@ _DLLSTD(mqlbool) pyInitialize(const mqlstring paths_to_packages, const mqlstring

_DLLSTD(void) pyFinalize()
{
if (Py_IsInitialized()) {
stInterpreter *stinterp = __getInterp();
// DLL_THREAD_DETACH without DLL_THREAD_ATTACH
if (stinterp != NULL && stinterp->interp == NULL) {
PY_THREAD_START;
__clearInterp(__interp);
PyErr_Clear();
Py_EndInterpreter(__interp->interp);
__interp->interp = NULL;
__clearInterp(__interp);
PY_THREAD_ANY_STOP;
}
}
// https://github.com/numpy/numpy/issues/8097
// https://bugs.python.org/issue34309
PY_THREAD_START_OR(return);
__clearInterp(__interp);
PyErr_Clear();
Py_EndInterpreter(__interp->interp);
__interp->interp = NULL;
__clearInterp(__interp);
PY_THREAD_ANY_STOP;
}

_DLLSTD(mqlbool) pyEval(const mqlstring pycode, const mqlbool override_class)
{
PY_THREAD_START;
PyErr_Clear();
mqlbool ret = false;
PY_THREAD_START_OR(return ret);
PyErr_Clear();
PyObject *code = PyUnicode_FromWideChar(pycode, -1);
if (code != NULL) {
PyObject *global = PyModule_GetDict(__interp->main);
Expand All @@ -181,7 +178,7 @@ _DLLSTD(mqlbool) pyEval(const mqlstring pycode, const mqlbool override_class)

_DLLSTD(mqlbool) pyIsError(const mqlbool clear)
{
PY_THREAD_START;
PY_THREAD_START_OR(return true);
const mqlbool ret = PyErr_Occurred() != NULL;
if (ret && clear) {
PyErr_Clear();
Expand All @@ -196,7 +193,17 @@ _DLLSTD(mqlint) pyGetErrorText(mqlstring _DLLOUTSTRING(buffer), const mqlint str
if (stringBufferLen < 1) {
return ret;
}
PY_THREAD_START;
PY_THREAD_START_OR(
wchar_t str[] = L"The pyInitialize() was not called or returned an error.";
Py_ssize_t size = wcslen(str);
ret = (mqlint)size;
if (size > stringBufferLen) {
size = stringBufferLen;
}
wmemcpy(buffer, str, size);
buffer[size] = 0;
return ret;
);
if (PyErr_Occurred() == NULL) {
ret = 0;
}
Expand Down
8 changes: 4 additions & 4 deletions Libraries/Roffild/PythonDLL/mql_class.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ _DLLSTD(mqlint) pyMQL_getLong(const mqllong magic, const mqllong value,
mqlint ret = -1;
int overflow;

PY_THREAD_START;
PY_THREAD_START_OR(return ret);
PyErr_Clear();
arg1 = PyLong_FromLongLong(magic);
arg2 = PyLong_FromLongLong(value);
Expand Down Expand Up @@ -85,7 +85,7 @@ _DLLSTD(mqlint) pyMQL_getULong(const mqllong magic, const mqlulong value,
Py_ssize_t x, size;
mqlint ret = -1;

PY_THREAD_START;
PY_THREAD_START_OR(return ret);
PyErr_Clear();
arg1 = PyLong_FromLongLong(magic);
arg2 = PyLong_FromUnsignedLongLong(value);
Expand Down Expand Up @@ -139,7 +139,7 @@ _DLLSTD(mqlint) pyMQL_getDouble(const mqllong magic, const mqldouble value,
Py_ssize_t x, size;
mqlint ret = -1;

PY_THREAD_START;
PY_THREAD_START_OR(return ret);
PyErr_Clear();
arg1 = PyLong_FromLongLong(magic);
arg2 = PyFloat_FromDouble(value);
Expand Down Expand Up @@ -192,7 +192,7 @@ _DLLSTD(mqlint) pyMQL_getString(const mqllong magic, const mqlstring value,
mqlstring str;
mqlint ret = -1;

PY_THREAD_START;
PY_THREAD_START_OR(return ret);
PyErr_Clear();
arg1 = PyLong_FromLongLong(magic);
arg2 = PyUnicode_FromWideChar(value, -1);
Expand Down
17 changes: 10 additions & 7 deletions Libraries/Roffild/PythonDLL/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ stInterpreter* __setInterp(PyThreadState *newinterp);
void __clearInterp(stInterpreter *interp);
void __overrideInterp(stInterpreter *interp);

#define PY_THREAD_MAIN_START stInterpreter *__interp = &__interps[0]; \
do {if (_PyThreadState_UncheckedGet() != __interp->interp) \
PyEval_AcquireThread(__interp->interp);} while(0)
#define PY_THREAD_START stInterpreter *__interp = __getInterp(); \
do {if (_PyThreadState_UncheckedGet() != __interp->interp) \
PyEval_AcquireThread(__interp->interp);} while(0)
#define _PY_THREAD_START_OR(stinterp, ret) stInterpreter *__interp = stinterp; \
if (__interp != NULL && __interp->interp != NULL) { \
if (_PyThreadState_UncheckedGet() != __interp->interp) { \
PyEval_AcquireThread(__interp->interp); \
} \
} else { ret; } do {} while(0)
#define PY_THREAD_START_OR(ret) _PY_THREAD_START_OR(__getInterp(), ret)
#define PY_THREAD_MAIN_START_OR(ret) _PY_THREAD_START_OR(&__interps[0], ret)
#define PY_THREAD_STOP do {PyEval_ReleaseThread(__interp->interp);} while(0)
#define PY_THREAD_MAIN_STOP do {PyThreadState_Swap(__interp->interp);PyEval_ReleaseThread(__interp->interp);} while(0)
#define PY_THREAD_MAIN_STOP do {PyThreadState_Swap(__interp->interp); \
PyEval_ReleaseThread(__interp->interp);} while(0)
#define PY_THREAD_ANY_STOP do {PyEval_ReleaseLock();} while(0)
Binary file modified Libraries/Roffild/PythonDLL/x64/Release/PythonDLL.dll
Binary file not shown.

0 comments on commit c4279c2

Please sign in to comment.