23 #include <QCoreApplication>
26 #include <QRegularExpression>
29 #include <QElapsedTimer>
39 #define TYMPAN_REL_CYTHON_PATH "cython_d"
41 #define TYMPAN_REL_CYTHON_PATH "cython"
44 #define COMPUTATION_TIMEOUT 10000
47 static std::atomic_bool g_python_cancel_requested{
false};
51 g_python_cancel_requested.store(
true, std::memory_order_relaxed);
56 return g_python_cancel_requested.load(std::memory_order_relaxed);
61 static QString _tympan_app_dir()
63 const QProcessEnvironment pe = QProcessEnvironment::systemEnvironment();
64 if (pe.contains(
"TYMPAN_INSTALL_PATH"))
66 return QDir::cleanPath(pe.value(
"TYMPAN_INSTALL_PATH"));
68 if (QCoreApplication::instance())
70 return QCoreApplication::applicationDirPath();
73 return QDir::currentPath();
78 QStringList env(QProcess::systemEnvironment());
80 QString cythonlibs_path(_tympan_app_dir());
82 cythonlibs_path.append(
"/");
84 cythonlibs_path = QDir::toNativeSeparators(cythonlibs_path);
86 QRegularExpression pythonpath_regexp(
"^PYTHONPATH=(.*)", QRegularExpression::CaseInsensitiveOption);
87 int pythonpath_index = env.indexOf(pythonpath_regexp);
89 if (pythonpath_index > 0)
91 pythonpath = env[pythonpath_index];
93 if (pythonpath !=
"PYTHONPATH=")
95 #if TY_PLATFORM == TY_PLATFORM_WIN32 || TY_PLATFORM == TY_PLATFORM_WIN64
96 pythonpath.append(
";");
98 pythonpath.append(
":");
101 pythonpath.append(cythonlibs_path);
102 env.removeAt(pythonpath_index);
106 pythonpath =
"PYTHONPATH=";
107 pythonpath.append(cythonlibs_path);
109 env.append(pythonpath);
110 #if TY_PLATFORM == TY_PLATFORM_WIN32 || TY_PLATFORM == TY_PLATFORM_WIN64
112 QRegularExpression path_regexp(
"^Path=(.*)", QRegularExpression::CaseInsensitiveOption);
113 int path_index = env.indexOf(path_regexp);
114 QString path = env[path_index];
115 QRegularExpression equal_regexp(
117 QRegularExpression::CaseInsensitiveOption);
118 int equal_index = path.indexOf(equal_regexp) + 1;
119 QString application_path(_tympan_app_dir());
120 application_path = QDir::toNativeSeparators(application_path);
121 path.insert(equal_index, application_path +
";");
122 env.removeAt(path_index);
130 QStringList env(QProcess::systemEnvironment());
133 int python_interp_idx = env.indexOf(QRegularExpression(
"^TYMPAN_PYTHON_INTERP=(.*)"));
134 if (python_interp_idx < 0)
137 "Can't access python interpreter. TYMPAN_PYTHON_INTERP environment variable is not set.");
139 QString python_interp_path = env.at(python_interp_idx).split(
'=')[1].remove(
"\"");
140 QFile python_interp(python_interp_path);
141 if (!python_interp.exists())
144 "variable is not correctly set.");
146 return python_interp_path;
151 std::string variables =
"\nVariables d'environnement:\n";
152 int pythonpath_index =
153 env.indexOf(QRegularExpression(
"^PYTHONPATH=(.*)", QRegularExpression::CaseInsensitiveOption));
154 if (pythonpath_index >= 0)
156 variables += env[pythonpath_index].toStdString() +
"\n";
160 variables +=
"PYTHONPATH absente\n";
162 #if TY_PLATFORM == TY_PLATFORM_WIN32 || TY_PLATFORM == TY_PLATFORM_WIN64
163 int path_index = env.indexOf(QRegularExpression(
"^Path=(.*)", QRegularExpression::CaseInsensitiveOption));
166 variables += env[path_index].toStdString() +
"\n";
170 variables +=
"Path absente\n";
172 int python_interp_index = env.indexOf(QRegularExpression(
"^TYMPAN_PYTHON_INTERP=(.*)"));
173 if (python_interp_index >= 0)
175 variables += env[python_interp_index].toStdString() +
"\n";
179 variables +=
"TYMPAN_PYTHON_INTERP absente\n";
187 QStringList appli_env(QProcess::systemEnvironment());
188 int tympan_debug_idx = appli_env.indexOf(QRegularExpression(
"^TYMPAN_DEBUG=(.*)"));
189 if (tympan_debug_idx >= 0)
191 QString debug_option = appli_env[tympan_debug_idx].split(
'=')[1];
192 if (debug_option.contains(
"keep_tmp_files", Qt::CaseInsensitive))
202 if (!tmp_file.open())
209 tmp_file.setAutoRemove(
false);
213 bool python(QStringList args, std::string& error_msg)
215 g_python_cancel_requested.store(
false, std::memory_order_relaxed);
220 logger.
debug(
"Lancement du script python: %s", args.join(
" ").toStdString().c_str());
223 float comp_duration = 0.0f;
227 python.setEnvironment(env);
228 python.setProcessChannelMode(QProcess::SeparateChannels);
231 QString python_interp;
238 error_msg =
"L'interpreteur python n'a pas pu etre trouve.\nVeuillez verifier que la variable "
239 "d'environnement TYMPAN_PYTHON_INTERP est correctement positionnee\n";
245 python.start(python_interp, args);
248 const int poll_ms = 100;
249 bool terminate_sent =
false;
250 qint64 kill_deadline_ms = -1;
252 QElapsedTimer elapsed;
254 qint64 last_log_ms = 0;
258 if (
python.waitForFinished(poll_ms))
264 const qint64 now = elapsed.elapsed();
267 comp_duration =
static_cast<float>(now) / 1000.0f;
268 logger.
info(
"Le script python s'execute encore apres %.0f secondes", comp_duration);
273 if (g_python_cancel_requested.load(std::memory_order_relaxed))
277 logger.
warning(
"Demande d'annulation reçue : envoi de terminate() au sous-processus Python.");
279 terminate_sent =
true;
280 kill_deadline_ms = now + 1500;
282 else if (kill_deadline_ms >= 0 && now >= kill_deadline_ms &&
283 python.state() != QProcess::NotRunning)
285 logger.
warning(
"Le sous-processus Python ne s'est pas termineÌ : kill().");
293 const QString std_error =
python.readAllStandardError();
294 const int exit_code =
python.exitCode();
296 if (
python.exitStatus() != QProcess::NormalExit || exit_code != 0)
298 error_msg =
"Le sous-process python s'est terminé avec le code d'erreur ";
299 error_msg += std::to_string(
static_cast<long long>(exit_code));
301 error_msg += std_error.toStdString();
303 error_msg +=
"Veuillez lire tympan.log pour plus d'information.\n";
304 logger.
error(
"%s", error_msg.c_str());
309 logger.
info(
"Le sous-processus Python s'est terminé correctement");
310 if (!std_error.isEmpty())
311 logger.
warning(std_error.toStdString().c_str());
317 unsigned long second = duration.
getTime() / 1000;
318 unsigned long millisecond = duration.
getTime() - second * 1000;
319 logger.
info(
"Temps de calcul : %02ld,%03ld sec. (%ld msec.)", second, millisecond, duration.
getTime());
unsigned long getTime() const
virtual void debug(const char *message,...)
virtual void warning(const char *message,...)
virtual void error(const char *message,...)
static OMessageManager * get()
virtual void info(const char *message,...)
Utilities to handle exceptions and to pretty-print value.
The base exception class for errors due to invalid data.
bool python(QStringList args, std::string &error_msg)
Launch a Python subprocess and wait for it using a non-blocking UI loop.
bool must_keep_tmp_files()
Tell whether temporary files should be preserved (debug mode).
bool init_tmp_file(QTemporaryFile &tmp_file, bool keep_file)
Create and initialize a QTemporaryFile according to the current policy.
QString _get_python_interp()
std::string _read_environment_variables(QStringList env)
QStringList _python_qprocess_environment()
void python_request_cancel()
Request cancellation of the currently running Python subprocess (UI hook, stage A).
#define TYMPAN_REL_CYTHON_PATH
bool python_cancel_was_requested()
Query whether a cancellation was requested since the last call to python().
#define COMPUTATION_TIMEOUT
Utilities to interact with Python subprocesses from the Tympan application.