diff --git a/src/backends/python/import_default_modules.py b/src/backends/python/import_default_modules.py
deleted file mode 100644
index 3ab3a100..00000000
--- a/src/backends/python/import_default_modules.py
+++ /dev/null
@@ -1,4 +0,0 @@
-try:
- import numpy
-except ModuleNotFoundError:
- pass
diff --git a/src/backends/python/python.qrc b/src/backends/python/python.qrc
index 0c824d14..5c6b133b 100644
--- a/src/backends/python/python.qrc
+++ b/src/backends/python/python.qrc
@@ -1,8 +1,7 @@
- import_default_modules.py
variables_cleaner.py
variables_loader.py
variables_saver.py
diff --git a/src/backends/python/pythonserver.cpp b/src/backends/python/pythonserver.cpp
index 3df158af..029e4e49 100644
--- a/src/backends/python/pythonserver.cpp
+++ b/src/backends/python/pythonserver.cpp
@@ -1,203 +1,211 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2015 Minh Ngo
*/
#include "pythonserver.h"
#include
PythonServer::PythonServer(QObject* parent) : QObject(parent), m_pModule(nullptr)
{
}
namespace
{
QString pyObjectToQString(PyObject* obj)
{
#if PY_MAJOR_VERSION == 3
return QString::fromUtf8(PyUnicode_AsUTF8(obj));
#elif PY_MAJOR_VERSION == 2
return QString::fromLocal8Bit(PyString_AsString(obj));
#else
#warning Unknown Python version
#endif
}
}
void PythonServer::login()
{
Py_InspectFlag = 1;
Py_Initialize();
m_pModule = PyImport_AddModule("__main__");
filePath = QStringLiteral("python_cantor_worksheet");
}
void PythonServer::runPythonCommand(const QString& command) const
{
PyObject* py_dict = PyModule_GetDict(m_pModule);
const char* prepareCommand =
"import sys;\n"\
"class CatchOutPythonBackend:\n"\
" def __init__(self):\n"\
" self.value = ''\n"\
" def write(self, txt):\n"\
" self.value += txt\n"\
"outputPythonBackend = CatchOutPythonBackend()\n"\
"errorPythonBackend = CatchOutPythonBackend()\n"\
"sys.stdout = outputPythonBackend\n"\
"sys.stderr = errorPythonBackend\n";
PyRun_SimpleString(prepareCommand);
#if PY_MAJOR_VERSION == 3
PyObject* compile = Py_CompileString(command.toStdString().c_str(), filePath.toStdString().c_str(), Py_single_input);
// There are two reasons for the error:
// 1) This code is not single expression, so we can't compile this with flag Py_single_input
// 2) There are errors in the code
if (PyErr_Occurred())
{
PyErr_Clear();
// Try to recompile code as sequence of expressions
compile = Py_CompileString(command.toStdString().c_str(), filePath.toStdString().c_str(), Py_file_input);
if (PyErr_Occurred())
{
// We now know, that we have a syntax error, so print the traceback and exit
PyErr_PrintEx(0);
return;
}
}
PyEval_EvalCode(compile, py_dict, py_dict);
#elif PY_MAJOR_VERSION == 2
// Python 2.X don't check, that input string contains only one expression.
// So for checking this, we compile string as file and as single expression and compare bytecode
// FIXME?
PyObject* codeFile = Py_CompileString(command.toStdString().c_str(), filePath.toStdString().c_str(), Py_file_input);
if (PyErr_Occurred())
{
PyErr_PrintEx(0);
return;
}
PyObject* codeSingle = Py_CompileString(command.toStdString().c_str(), filePath.toStdString().c_str(), Py_single_input);
if (PyErr_Occurred())
{
// We have error with Py_single_input, but haven't error with Py_file_input
// So, the code can't be compiled as singel input -> use file input right away
PyErr_Clear();
PyEval_EvalCode((PyCodeObject*)codeFile, py_dict, py_dict);
}
else
{
PyObject* bytecode1 = ((PyCodeObject*)codeSingle)->co_code;
PyObject* bytecode2 = ((PyCodeObject*)codeFile)->co_code;
if (PyObject_Length(bytecode1) >= PyObject_Length(bytecode2))
{
PyEval_EvalCode((PyCodeObject*)codeSingle, py_dict, py_dict);
}
else
{
PyEval_EvalCode((PyCodeObject*)codeFile, py_dict, py_dict);
}
}
#else
#warning Unknown Python version
#endif
if (PyErr_Occurred())
PyErr_PrintEx(0);
}
QString PythonServer::getError() const
{
PyObject *errorPython = PyObject_GetAttrString(m_pModule, "errorPythonBackend");
PyObject *error = PyObject_GetAttrString(errorPython, "value");
return pyObjectToQString(error);
}
QString PythonServer::getOutput() const
{
PyObject *outputPython = PyObject_GetAttrString(m_pModule, "outputPythonBackend");
PyObject *output = PyObject_GetAttrString(outputPython, "value");
return pyObjectToQString(output);
}
void PythonServer::setFilePath(const QString& path)
{
this->filePath = path;
PyRun_SimpleString(("__file__ = '"+path.toStdString()+"'").c_str());
}
QString PythonServer::variables(bool parseValue) const
{
PyRun_SimpleString(
"try: \n"
" import numpy \n"
" __cantor_numpy_internal__ = numpy.get_printoptions()['threshold'] \n"
" numpy.set_printoptions(threshold=100000000) \n"
+#if PY_MAJOR_VERSION == 3
"except ModuleNotFoundError: \n"
+#elif PY_MAJOR_VERSION == 2
+ "except ImportError: \n"
+#endif
" pass \n"
);
PyRun_SimpleString("__tmp_globals__ = globals()");
PyObject* globals = PyObject_GetAttrString(m_pModule,"__tmp_globals__");
PyObject *key, *value;
Py_ssize_t pos = 0;
QStringList vars;
while (PyDict_Next(globals, &pos, &key, &value)) {
const QString& keyString = pyObjectToQString(key);
if (keyString.startsWith(QLatin1String("__")))
continue;
if (keyString == QLatin1String("CatchOutPythonBackend")
|| keyString == QLatin1String("errorPythonBackend")
|| keyString == QLatin1String("outputPythonBackend"))
continue;
if (PyModule_Check(value))
continue;
if (PyFunction_Check(value))
continue;
if (PyType_Check(value))
continue;
QString valueString;
if (parseValue)
valueString = pyObjectToQString(PyObject_Repr(value));
vars.append(keyString + QChar(17) + valueString);
}
PyRun_SimpleString(
"try: \n"
" import numpy \n"
" numpy.set_printoptions(threshold=__cantor_numpy_internal__) \n"
" del __cantor_numpy_internal__ \n"
+#if PY_MAJOR_VERSION == 3
"except ModuleNotFoundError: \n"
+#elif PY_MAJOR_VERSION == 2
+ "except ImportError: \n"
+#endif
" pass \n"
);
return vars.join(QChar(18))+QChar(18);
}
diff --git a/src/backends/python/pythonsession.cpp b/src/backends/python/pythonsession.cpp
index d9e681cf..444d6846 100644
--- a/src/backends/python/pythonsession.cpp
+++ b/src/backends/python/pythonsession.cpp
@@ -1,239 +1,234 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2012 Filipe Saraiva
Copyright (C) 2015 Minh Ngo
*/
#include
#include "pythonsession.h"
#include "pythonexpression.h"
#include "pythonvariablemodel.h"
#include "pythonhighlighter.h"
#include "pythoncompletionobject.h"
#include "pythonkeywords.h"
#include "pythonutils.h"
#include
#include
#include
#include
#include
#ifndef Q_OS_WIN
#include
#endif
const QChar recordSep(30);
const QChar unitSep(31);
const QChar messageEnd = 29;
PythonSession::PythonSession(Cantor::Backend* backend, int pythonVersion, const QString serverName)
: Session(backend)
, m_process(nullptr)
, serverName(serverName)
, m_pythonVersion(pythonVersion)
{
setVariableModel(new PythonVariableModel(this));
}
PythonSession::~PythonSession()
{
if (m_process) {
m_process->kill();
m_process->deleteLater();
}
}
void PythonSession::login()
{
qDebug()<<"login";
emit loginStarted();
if (m_process)
m_process->deleteLater();
m_process = new QProcess(this);
m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
m_process->start(QStandardPaths::findExecutable(serverName));
m_process->waitForStarted();
m_process->waitForReadyRead();
QTextStream stream(m_process->readAllStandardOutput());
const QString& readyStatus = QString::fromLatin1("ready");
while (m_process->state() == QProcess::Running)
{
const QString& rl = stream.readLine();
if (rl == readyStatus)
break;
}
connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonSession::readOutput);
sendCommand(QLatin1String("login"));
sendCommand(QLatin1String("setFilePath"), QStringList(worksheetPath));
const QStringList& scripts = autorunScripts();
if(!scripts.isEmpty()){
QString autorunScripts = scripts.join(QLatin1String("\n"));
evaluateExpression(autorunScripts, Cantor::Expression::DeleteOnFinish, true);
variableModel()->update();
}
- const QString& importerFile = QLatin1String(":py/import_default_modules.py");
-
- evaluateExpression(fromSource(importerFile), Cantor::Expression::DeleteOnFinish, true);
-
-
changeStatus(Session::Done);
emit loginDone();
}
void PythonSession::logout()
{
if (!m_process)
return;
sendCommand(QLatin1String("exit"));
m_process = nullptr;
variableModel()->clearVariables();
qDebug()<<"logout";
changeStatus(Status::Disable);
}
void PythonSession::interrupt()
{
if(!expressionQueue().isEmpty())
{
qDebug()<<"interrupting " << expressionQueue().first()->command();
if(m_process->state() != QProcess::NotRunning)
{
#ifndef Q_OS_WIN
const int pid=m_process->pid();
kill(pid, SIGINT);
#else
; //TODO: interrupt the process on windows
#endif
}
for (Cantor::Expression* expression : expressionQueue())
expression->setStatus(Cantor::Expression::Interrupted);
expressionQueue().clear();
// Cleanup inner state and call octave prompt printing
// If we move this code for interruption to Session, we need add function for
// cleaning before setting Done status
m_output.clear();
m_process->write("\n");
qDebug()<<"done interrupting";
}
changeStatus(Cantor::Session::Done);
}
Cantor::Expression* PythonSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal)
{
qDebug() << "evaluating: " << cmd;
PythonExpression* expr = new PythonExpression(this, internal);
changeStatus(Cantor::Session::Running);
expr->setFinishingBehavior(behave);
expr->setCommand(cmd);
expr->evaluate();
return expr;
}
QSyntaxHighlighter* PythonSession::syntaxHighlighter(QObject* parent)
{
return new PythonHighlighter(parent, this, m_pythonVersion);
}
Cantor::CompletionObject* PythonSession::completionFor(const QString& command, int index)
{
return new PythonCompletionObject(command, index, this);
}
void PythonSession::runFirstExpression()
{
if (expressionQueue().isEmpty())
return;
Cantor::Expression* expr = expressionQueue().first();
const QString command = expr->internalCommand();
qDebug() << "run first expression" << command;
expr->setStatus(Cantor::Expression::Computing);
if (expr->isInternal() && command.startsWith(QLatin1String("%variables ")))
{
const QString arg = command.section(QLatin1String(" "), 1);
sendCommand(QLatin1String("model"), QStringList(arg));
}
else
sendCommand(QLatin1String("code"), QStringList(expr->internalCommand()));
}
void PythonSession::sendCommand(const QString& command, const QStringList arguments) const
{
qDebug() << "send command: " << command << arguments;
const QString& message = command + recordSep + arguments.join(unitSep) + messageEnd;
m_process->write(message.toLocal8Bit());
}
void PythonSession::readOutput()
{
while (m_process->bytesAvailable() > 0)
m_output.append(QString::fromLocal8Bit(m_process->readAll()));
qDebug() << "m_output: " << m_output;
if (!m_output.contains(messageEnd))
return;
const QStringList packages = m_output.split(messageEnd, QString::SkipEmptyParts);
if (m_output.endsWith(messageEnd))
m_output.clear();
else
m_output = m_output.section(messageEnd, -1);
for (const QString& message: packages)
{
if (expressionQueue().isEmpty())
break;
const QString& output = message.section(unitSep, 0, 0);
const QString& error = message.section(unitSep, 1, 1);
if(error.isEmpty()){
static_cast(expressionQueue().first())->parseOutput(output);
} else {
static_cast(expressionQueue().first())->parseError(error);
}
finishFirstExpression(true);
}
}
void PythonSession::setWorksheetPath(const QString& path)
{
worksheetPath = path;
}
diff --git a/src/backends/python2/testpython2.cpp b/src/backends/python2/testpython2.cpp
index 51b4f9df..433f1d77 100644
--- a/src/backends/python2/testpython2.cpp
+++ b/src/backends/python2/testpython2.cpp
@@ -1,279 +1,279 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2013 Tuukka Verho
*/
#include "testpython2.h"
#include "session.h"
#include "backend.h"
#include "expression.h"
#include "result.h"
#include "defaultvariablemodel.h"
#include "imageresult.h"
#include "completionobject.h"
#include "settings.h"
QString TestPython2::backendName()
{
return QLatin1String("python2");
}
void TestPython2::testCommandQueue()
{
Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("0+1"));
Cantor::Expression* e2=session()->evaluateExpression(QLatin1String("1+1"));
Cantor::Expression* e3=evalExp(QLatin1String("1+2"));
QVERIFY(e1!=nullptr);
QVERIFY(e2!=nullptr);
QVERIFY(e3!=nullptr);
QVERIFY(e1->result());
QVERIFY(e2->result());
QVERIFY(e3->result());
QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("1"));
QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("2"));
QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("3"));
}
-void TestPython2::testImportNumpy()
+void TestPython2::testImportStatement()
{
- Cantor::Expression* e = evalExp(QLatin1String("import numpy"));
+ Cantor::Expression* e = evalExp(QLatin1String("import sys"));
QVERIFY(e != nullptr);
QCOMPARE(e->status(), Cantor::Expression::Done);
}
void TestPython2::testCodeWithComments()
{
{
Cantor::Expression* e = evalExp(QLatin1String("#comment\n1+2"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("3"));
}
{
Cantor::Expression* e = evalExp(QLatin1String(" #comment\n1+2"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("3"));
}
}
void TestPython2::testSimpleCode()
{
Cantor::Expression* e=evalExp( QLatin1String("2+2"));
QVERIFY( e!=nullptr );
QVERIFY( e->result()!=nullptr );
QCOMPARE( cleanOutput(e->result()->data().toString()), QLatin1String("4") );
}
void TestPython2::testMultilineCode()
{
Cantor::Expression* e=evalExp(QLatin1String(
"a = 2+2\n"
"b = 3+3\n"
"print a,b"
));
QVERIFY( e!=nullptr );
QVERIFY( e->result()!=nullptr );
QCOMPARE( cleanOutput(e->result()->data().toString()), QLatin1String("4 6") );
evalExp(QLatin1String("del a; del b"));
}
void TestPython2::testVariablesCreatingFromCode()
{
QAbstractItemModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(2, model->rowCount());
QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a"));
QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15"));
QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b"));
QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'"));
evalExp(QLatin1String("del a; del b"));
}
void TestPython2::testVariableCleanupAfterRestart()
{
Cantor::DefaultVariableModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(2, static_cast(model)->rowCount());
session()->logout();
session()->login();
QCOMPARE(0, static_cast(model)->rowCount());
}
void TestPython2::testDictVariable()
{
Cantor::DefaultVariableModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("d = {'value': 33}"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(1, static_cast(model)->rowCount());
QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d"));
QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}"));
evalExp(QLatin1String("del d"));
}
void TestPython2::testCommentExpression()
{
Cantor::Expression* e = evalExp(QLatin1String("#only comment"));
QVERIFY(e != nullptr);
QCOMPARE(e->status(), Cantor::Expression::Status::Done);
QCOMPARE(e->results().size(), 0);
}
void TestPython2::testInvalidSyntax()
{
Cantor::Expression* e=evalExp( QLatin1String("2+2*+.") );
QVERIFY( e!=nullptr );
QCOMPARE( e->status(), Cantor::Expression::Error );
}
void TestPython2::testSimplePlot()
{
if (!PythonSettings::integratePlots())
QSKIP("This test needs enabled plots integration in Python2 settings", SkipSingle);
Cantor::Expression* e = evalExp(QLatin1String(
"import matplotlib\n"
"import matplotlib.pyplot as plt\n"
"import numpy as np"
));
QVERIFY(e != nullptr);
QVERIFY(e->errorMessage().isEmpty() == true);
//the variable model shouldn't have any entries after the module imports
QAbstractItemModel* model = session()->variableModel();
QVERIFY(model != nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QVERIFY(model->rowCount() == 0);
//create data for plotting
e = evalExp(QLatin1String(
"t = np.arange(0.0, 2.0, 0.01)\n"
"s = 1 + np.sin(2 * np.pi * t)"
));
QVERIFY(e != nullptr);
QVERIFY(e->errorMessage().isEmpty() == true);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
//the variable model should have two entries now
QVERIFY(model->rowCount() == 2);
//plot the data and check the results
e = evalExp(QLatin1String(
"plt.plot(t,s)\n"
"plt.show()"
));
QVERIFY(e != nullptr);
if (e->result() == nullptr)
waitForSignal(e, SIGNAL(gotResult()));
QVERIFY(e->errorMessage().isEmpty() == true);
QVERIFY(model->rowCount() == 2); //still only two variables
//there must be one single image result
QVERIFY(e->results().size() == 1);
const Cantor::ImageResult* result = dynamic_cast(e->result());
QVERIFY(result != nullptr);
evalExp(QLatin1String("del t; del s"));
}
void TestPython2::testSimpleExpressionWithComment()
{
Cantor::Expression* e = evalExp(QLatin1String("2+2 # comment"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4"));
}
void TestPython2::testMultilineCommandWithComment()
{
Cantor::Expression* e = evalExp(QLatin1String(
"print 2+2 \n"
"#comment in middle \n"
"print 7*5"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4\n35"));
}
void TestPython2::testCompletion()
{
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
Cantor::CompletionObject* help = session()->completionFor(QLatin1String("ma"), 2);
waitForSignal(help, SIGNAL(fetchingDone()));
// Checks all completions for this request
// This correct for Python 2.7.15
const QStringList& completions = help->completions();
qDebug() << completions;
QCOMPARE(completions.size(), 2);
QVERIFY(completions.contains(QLatin1String("map")));
QVERIFY(completions.contains(QLatin1String("max")));
}
QTEST_MAIN(TestPython2)
diff --git a/src/backends/python2/testpython2.h b/src/backends/python2/testpython2.h
index 692c8d45..2aa1f060 100644
--- a/src/backends/python2/testpython2.h
+++ b/src/backends/python2/testpython2.h
@@ -1,54 +1,54 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2013 Tuukka Verho
*/
#ifndef _TESTPYTHON2_H
#define _TESTPYTHON2_H
#include "backendtest.h"
class TestPython2 : public BackendTest
{
Q_OBJECT
private Q_SLOTS:
void testCodeWithComments();
void testSimpleCode();
void testMultilineCode();
void testCommandQueue();
void testSimplePlot();
- void testImportNumpy();
+ void testImportStatement();
void testInvalidSyntax();
void testSimpleExpressionWithComment();
void testCommentExpression();
void testMultilineCommandWithComment();
void testVariablesCreatingFromCode();
void testVariableCleanupAfterRestart();
void testDictVariable();
void testCompletion();
private:
QString backendName() override;
};
#endif /* _TESTPYTHON2_H */
diff --git a/src/backends/python3/testpython3.cpp b/src/backends/python3/testpython3.cpp
index 8d19e133..0735327c 100644
--- a/src/backends/python3/testpython3.cpp
+++ b/src/backends/python3/testpython3.cpp
@@ -1,290 +1,290 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2015 Minh Ngo
*/
#include "testpython3.h"
#include "session.h"
#include "backend.h"
#include "expression.h"
#include "imageresult.h"
#include "defaultvariablemodel.h"
#include "completionobject.h"
#include "settings.h"
QString TestPython3::backendName()
{
return QLatin1String("python3");
}
void TestPython3::testSimpleCommand()
{
Cantor::Expression* e = evalExp(QLatin1String("2+2"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4"));
}
void TestPython3::testMultilineCommand()
{
Cantor::Expression* e = evalExp(QLatin1String("print(2+2)\nprint(7*5)"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4\n35"));
}
void TestPython3::testCommandQueue()
{
Cantor::Expression* e1=session()->evaluateExpression(QLatin1String("0+1"));
Cantor::Expression* e2=session()->evaluateExpression(QLatin1String("1+1"));
Cantor::Expression* e3=evalExp(QLatin1String("1+2"));
QVERIFY(e1!=nullptr);
QVERIFY(e2!=nullptr);
QVERIFY(e3!=nullptr);
QVERIFY(e1->result());
QVERIFY(e2->result());
QVERIFY(e3->result());
QCOMPARE(cleanOutput(e1->result()->data().toString()), QLatin1String("1"));
QCOMPARE(cleanOutput(e2->result()->data().toString()), QLatin1String("2"));
QCOMPARE(cleanOutput(e3->result()->data().toString()), QLatin1String("3"));
}
void TestPython3::testCommentExpression()
{
Cantor::Expression* e = evalExp(QLatin1String("#only comment"));
QVERIFY(e != nullptr);
QCOMPARE(e->status(), Cantor::Expression::Status::Done);
QCOMPARE(e->results().size(), 0);
}
void TestPython3::testSimpleExpressionWithComment()
{
Cantor::Expression* e = evalExp(QLatin1String("2+2 # comment"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4"));
}
void TestPython3::testMultilineCommandWithComment()
{
Cantor::Expression* e = evalExp(QLatin1String(
"print(2+2) \n"
"#comment in middle \n"
"print(7*5)"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("4\n35"));
}
void TestPython3::testInvalidSyntax()
{
Cantor::Expression* e=evalExp( QLatin1String("2+2*+.") );
QVERIFY( e!=nullptr );
QCOMPARE( e->status(), Cantor::Expression::Error );
}
void TestPython3::testCompletion()
{
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
Cantor::CompletionObject* help = session()->completionFor(QLatin1String("p"), 1);
waitForSignal(help, SIGNAL(fetchingDone()));
// Checks all completions for this request
// This correct for Python 3.6.7
const QStringList& completions = help->completions();
qDebug() << completions;
QCOMPARE(completions.size(), 4);
QVERIFY(completions.contains(QLatin1String("pass")));
QVERIFY(completions.contains(QLatin1String("pow")));
QVERIFY(completions.contains(QLatin1String("print")));
QVERIFY(completions.contains(QLatin1String("property")));
}
-void TestPython3::testImportNumpy()
+void TestPython3::testImportStatement()
{
- Cantor::Expression* e = evalExp(QLatin1String("import numpy"));
+ Cantor::Expression* e = evalExp(QLatin1String("import sys"));
QVERIFY(e != nullptr);
QCOMPARE(e->status(), Cantor::Expression::Done);
}
void TestPython3::testCodeWithComments()
{
{
Cantor::Expression* e = evalExp(QLatin1String("#comment\n1+2"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("3"));
}
{
Cantor::Expression* e = evalExp(QLatin1String(" #comment\n1+2"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("3"));
}
}
void TestPython3::testPython3Code()
{
{
Cantor::Expression* e = evalExp(QLatin1String("print 1 + 2"));
QVERIFY(e != nullptr);
QVERIFY(!e->errorMessage().isEmpty());
}
{
Cantor::Expression* e = evalExp(QLatin1String("print(1 + 2)"));
QVERIFY(e != nullptr);
QVERIFY(e->result());
QVERIFY(e->result()->data().toString() == QLatin1String("3"));
}
}
void TestPython3::testSimplePlot()
{
if (!PythonSettings::integratePlots())
QSKIP("This test needs enabled plots integration in Python3 settings", SkipSingle);
Cantor::Expression* e = evalExp(QLatin1String(
"import matplotlib\n"
"import matplotlib.pyplot as plt\n"
"import numpy as np"
));
QVERIFY(e != nullptr);
QVERIFY(e->errorMessage().isEmpty() == true);
//the variable model shouldn't have any entries after the module imports
QAbstractItemModel* model = session()->variableModel();
QVERIFY(model != nullptr);
QVERIFY(model->rowCount() == 0);
//create data for plotting
e = evalExp(QLatin1String(
"t = np.arange(0.0, 2.0, 0.01)\n"
"s = 1 + np.sin(2 * np.pi * t)"
));
QVERIFY(e != nullptr);
QVERIFY(e->errorMessage().isEmpty() == true);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
//the variable model should have two entries now
QVERIFY(model->rowCount() == 2);
//plot the data and check the results
e = evalExp(QLatin1String(
"plt.plot(t,s)\n"
"plt.show()"
));
QVERIFY(e != nullptr);
if (e->result() == nullptr)
waitForSignal(e, SIGNAL(gotResult()));
QVERIFY(e->errorMessage().isEmpty() == true);
QVERIFY(model->rowCount() == 2); //still only two variables
//there must be one single image result
QVERIFY(e->results().size() == 1);
const Cantor::ImageResult* result = dynamic_cast(e->result());
QVERIFY(result != nullptr);
evalExp(QLatin1String("del t; del s"));
}
void TestPython3::testVariablesCreatingFromCode()
{
QAbstractItemModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(2, model->rowCount());
QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a"));
QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15"));
QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b"));
QCOMPARE(model->index(1,1).data().toString(), QLatin1String("'S'"));
evalExp(QLatin1String("del a; del b"));
}
void TestPython3::testVariableCleanupAfterRestart()
{
Cantor::DefaultVariableModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("a = 15; b = 'S';"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(2, static_cast(model)->rowCount());
session()->logout();
session()->login();
QCOMPARE(0, static_cast(model)->rowCount());
}
void TestPython3::testDictVariable()
{
Cantor::DefaultVariableModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("d = {'value': 33}"));
QVERIFY(e!=nullptr);
if(session()->status()==Cantor::Session::Running)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(1, static_cast(model)->rowCount());
QCOMPARE(model->index(0,0).data().toString(), QLatin1String("d"));
QCOMPARE(model->index(0,1).data().toString(), QLatin1String("{'value': 33}"));
evalExp(QLatin1String("del d"));
}
QTEST_MAIN(TestPython3)
diff --git a/src/backends/python3/testpython3.h b/src/backends/python3/testpython3.h
index 61663dda..8c28cab2 100644
--- a/src/backends/python3/testpython3.h
+++ b/src/backends/python3/testpython3.h
@@ -1,54 +1,54 @@
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
---
Copyright (C) 2015 Minh Ngo
*/
#ifndef _TESTPYTHON3_H
#define _TESTPYTHON3_H
#include
class TestPython3 : public BackendTest
{
Q_OBJECT
private Q_SLOTS:
void testSimpleCommand();
void testMultilineCommand();
void testCodeWithComments();
void testCommandQueue();
void testSimplePlot();
- void testImportNumpy();
+ void testImportStatement();
void testPython3Code();
void testInvalidSyntax();
void testSimpleExpressionWithComment();
void testCommentExpression();
void testMultilineCommandWithComment();
void testVariablesCreatingFromCode();
void testVariableCleanupAfterRestart();
void testDictVariable();
void testCompletion();
private:
QString backendName() override;
};
#endif /* _TESTPYTHON3_H */