Skip to content

Commit

Permalink
Add pre/post setup for recursive async calls
Browse files Browse the repository at this point in the history
  • Loading branch information
martyngigg committed Sep 3, 2017
1 parent 26335d6 commit 4036cd3
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 152 deletions.
25 changes: 16 additions & 9 deletions MantidPlot/src/ApplicationWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,8 @@ void ApplicationWindow::init(bool factorySettings, const QStringList &args) {

// Scripting
m_script_envs = QHash<QString, ScriptingEnv *>();
m_iface_script = nullptr;
setScriptingLanguage(defaultScriptingLang);
m_iface_script = NULL;

m_interpreterDock = new QDockWidget(this);
m_interpreterDock->setObjectName(
Expand Down Expand Up @@ -15043,10 +15043,9 @@ bool ApplicationWindow::runPythonScript(const QString &code, bool async,
bool quiet, bool redirect) {
if (code.isEmpty())
return false;

if (m_iface_script == NULL) {
if (!m_iface_script) {
if (setScriptingLanguage("Python")) {
m_iface_script = scriptingEnv()->newScript("<Interface>", NULL,
m_iface_script = scriptingEnv()->newScript("<Interface>", nullptr,
Script::NonInteractive);
} else {
return false;
Expand All @@ -15065,12 +15064,20 @@ bool ApplicationWindow::runPythonScript(const QString &code, bool async,
}
bool success(false);
if (async) {
QFuture<bool> job = m_iface_script->executeAsync(ScriptCode(code));
while (job.isRunning()) {
QCoreApplication::instance()->processEvents();
m_iface_script->recursiveAsyncSetup();
auto job = m_iface_script->executeAsync(ScriptCode(code));
// Start a local event loop to keep processing events
// while we are running the script. Inspired by the IPython
// Qt inputhook in IPython.terminal.pt_inputhooks.qt
QEventLoop eventLoop(QApplication::instance());
QTimer timer;
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
while (!job.isFinished()) {
timer.start(50);
eventLoop.exec();
timer.stop();
}
// Ensure the remaining events are processed
QCoreApplication::instance()->processEvents();
m_iface_script->recursiveAsyncTeardown();
success = job.result();
} else {
success = m_iface_script->execute(ScriptCode(code));
Expand Down
128 changes: 20 additions & 108 deletions MantidPlot/src/PythonScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,26 @@ void PythonScript::endStdoutRedirect() {
Py_XDECREF(stderrSave);
}

/**
* To be called from the main thread before an async call that is
* recursive. See ApplicationWindow::runPythonScript
*/
void PythonScript::recursiveAsyncSetup() {
if (gil().locked()) {
gil().release();
}
}

/**
* To be called from the main thread immediately after an async call
* that is recursive. See ApplicationWindow::runPythonScript
*/
void PythonScript::recursiveAsyncTeardown() {
if (!gil().locked()) {
gil().acquire();
}
}

/**
* Compile the code returning true if successful, false otherwise
* @param code
Expand Down Expand Up @@ -793,111 +813,3 @@ PyObject *PythonScript::compileToByteCode(bool for_eval) {
}
return compiledCode;
}

//--------------------------------------------------------------------------------------------

/**
* Listen to add notifications from the ADS and add a Python variable of the
* workspace name
* to the current scope
* @param wsName The name of the workspace
* @param ws The ws ptr (unused)
*/
void PythonScript::addHandle(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) {
addPythonReference(wsName, ws);
}

/**
* Listen to add/replace notifications from the ADS and add a Python variable
* of
* the workspace name
* to the current scope
* @param wsName The name of the workspace
* @param ws The ws ptr (unused)
*/
void PythonScript::afterReplaceHandle(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) {
addPythonReference(wsName, ws);
}

/**
* Removes a Python variable of the workspace name from the current scope
* @param wsName The name of the workspace
* @param ws The ws ptr (unused)
*/
void PythonScript::postDeleteHandle(const std::string &wsName) {
deletePythonReference(wsName);
}

/**
* Clear all workspace handle references
*/
void PythonScript::clearADSHandle() {
for (auto itr = m_workspaceHandles.cbegin();
itr != m_workspaceHandles.cend();) {
// This also erases the element from current set. The standard says that
// erase only invalidates
// iterators of erased elements so we need to increment the iterator and
// get
// back the previous value
// i.e. the postfix operator
this->deletePythonReference(*(itr++));
}

assert(m_workspaceHandles.empty());
}

/**
* Add a Python variable of the workspace name
* to the current scope
* @param wsName The name of the workspace
* @param ws The ws ptr (unused)
*/
void PythonScript::addPythonReference(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) {
UNUSED_ARG(ws);

// Compile a code object
const size_t length = wsName.length() * 2 + 10;
char *code = new char[length + 1];
const char *name = wsName.c_str();
sprintf(code, "%s = mtd['%s']", name, name);
PyObject *codeObj =
Py_CompileString(code, "PythonScript::addPythonReference", Py_file_input);
if (codeObj) {
PyObject *ret = PyEval_EvalCode(CODE_OBJECT(codeObj), localDict, localDict);
Py_XDECREF(ret);
}
if (PyErr_Occurred()) {
PyErr_Clear();
} else {
// Keep track of it
m_workspaceHandles.insert(m_workspaceHandles.end(), wsName);
}
Py_XDECREF(codeObj);
delete[] code;
}

/**
* Delete a Python reference to the given workspace name
* @param wsName The name of the workspace
*/
void PythonScript::deletePythonReference(const std::string &wsName) {
const size_t length = wsName.length() + 4;
char *code = new char[length + 1];
sprintf(code, "del %s", wsName.c_str());
PyObject *codeObj =
Py_CompileString(code, "PythonScript::deleteHandle", Py_file_input);
if (codeObj) {
PyObject *ret = PyEval_EvalCode(CODE_OBJECT(codeObj), localDict, localDict);
Py_XDECREF(ret);
}
if (PyErr_Occurred()) {
PyErr_Clear();
} else {
m_workspaceHandles.erase(wsName);
}
Py_XDECREF(codeObj);
delete[] code;
}
18 changes: 0 additions & 18 deletions MantidPlot/src/PythonScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,24 +195,6 @@ class PythonScript : public Script, MantidQt::API::WorkspaceObserver {
/// Compile to bytecode
PyObject *compileToByteCode(bool for_eval = true);

// ---------------------------- Variable reference
// ---------------------------------------------
/// Listen to add notifications from the ADS
void addHandle(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) override;
/// Listen to add/replace notifications from the ADS
void afterReplaceHandle(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) override;
/// Listen to delete notifications
void postDeleteHandle(const std::string &wsName) override;
/// Listen to ADS clear notifications
void clearADSHandle() override;
/// Add/update a Python reference to the given workspace
void addPythonReference(const std::string &wsName,
const Mantid::API::Workspace_sptr ws);
/// Delete a Python reference to the given workspace name
void deletePythonReference(const std::string &wsName);

/// Send out an error and clear it from python.
void emit_error();

Expand Down
11 changes: 0 additions & 11 deletions MantidPlot/src/Script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,3 @@ void Script::setIsRunning() { m_execMode = Running; }
* Sets the offset & code string
*/
void Script::setupCode(const ScriptCode &code) { m_code = code; }

/**
* Ensure that any line endings are converted to single '\n' so that the Python
* C API is happy
* @param text :: The text to check and convert
*/
QString Script::normaliseLineEndings(QString text) const {
text = text.replace(QRegExp("\\r\\n"), QString("\n"));
text = text.replace(QRegExp("\\r"), QString("\n"));
return text;
}
11 changes: 5 additions & 6 deletions MantidPlot/src/Script.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class Script : public QObject {
bool redirectStdOut() const { return m_redirectOutput; }
void redirectStdOut(bool on) { m_redirectOutput = on; }

virtual void recursiveAsyncSetup() {}
virtual void recursiveAsyncTeardown() {}

/// Create a list of keywords for the code completion API
virtual void generateAutoCompleteList() {}
// Does the code compile to a complete statement, i.e no more input is
Expand Down Expand Up @@ -152,6 +155,8 @@ public slots:
virtual bool executeImpl() = 0;
/// Implementation of the abort request
virtual void abortImpl() = 0;
/// Setup the code from a script code object
void setupCode(const ScriptCode &code);

private:
/**
Expand All @@ -175,12 +180,6 @@ public slots:
ScriptThreadPool();
};

/// Setup the code from a script code object
void setupCode(const ScriptCode &code);
/// Normalise line endings for the given code. The Python C/API does not seem
/// to like CRLF endings so normalise to just LF
QString normaliseLineEndings(QString text) const;

ScriptingEnv *m_env;
std::string m_name; // Easier to convert to C string
ScriptCode m_code;
Expand Down

0 comments on commit 4036cd3

Please sign in to comment.