Commit 691f8407 authored by Nikita Sirgienko's avatar Nikita Sirgienko
Browse files

[GSoC 2020] Realise requested feature from https://bugs.kde.org/show_bug.cgi?id=409137

Now, for Python/Julia/Octave Cantor instead of "too long variable" will show bytes size of the variable
parent 460eb8e0
Pipeline #31014 passed with stage
in 28 minutes and 35 seconds
......@@ -91,6 +91,7 @@ void RCompletionObject::receiveCompletions(Cantor::Expression::Status status)
setCommand(token);
setCompletions(options);
}
break;
}
case Expression::Status::Error:
qWarning() << "R code for completion command finishs with error message: " << m_expression->errorMessage();
......
......@@ -162,6 +162,7 @@ void JuliaServer::parseModules(bool variableManagement)
void JuliaServer::parseJlModule(jl_module_t* module, bool parseValue)
{
jl_function_t* jl_string_function = jl_get_function(jl_base_module, "string");
jl_function_t* jl_sizeof_function = jl_get_function(jl_base_module, "sizeof");
if (module != JL_MAIN_MODULE)
{
......@@ -202,6 +203,7 @@ void JuliaServer::parseJlModule(jl_module_t* module, bool parseValue)
{
if (module == JL_MAIN_MODULE && !INTERNAL_VARIABLES.contains(name))
{
const QString& size = fromJuliaString(jl_call1(jl_string_function, jl_call1(jl_sizeof_function, value)));
if (parseValue)
{
const QString& valueString = fromJuliaString(jl_call1(jl_string_function, value));
......@@ -209,17 +211,27 @@ void JuliaServer::parseJlModule(jl_module_t* module, bool parseValue)
{
int i = m_variables.indexOf(name);
m_variableValues[i] = valueString;
m_variableSize[i] = size;
}
else
{
m_variables.append(name);
m_variableValues.append(valueString);
m_variableSize.append(size);
}
}
else
{
if (!m_variables.contains(name))
if (m_variables.contains(name))
{
int i = m_variables.indexOf(name);
m_variableSize[i] = size;
}
else
{
m_variables.append(name);
m_variableSize.append(size);
}
}
}
}
......@@ -242,6 +254,11 @@ QStringList JuliaServer::variableValuesList()
return m_variableValues;
}
QStringList JuliaServer::variableSizesList()
{
return m_variableSize;
}
QStringList JuliaServer::functionsList()
{
return m_functions;
......
......@@ -90,6 +90,11 @@ public Q_SLOTS:
*/
Q_SCRIPTABLE QStringList variableValuesList();
/**
* @return corresponding list of values for variables from variablesList.
*/
Q_SCRIPTABLE QStringList variableSizesList();
/**
* @return list of function in internal Julia's module
*/
......@@ -106,6 +111,7 @@ private:
QStringList parsedModules;
QStringList m_variables;
QStringList m_variableValues;
QStringList m_variableSize;
QStringList m_functions;
static QStringList INTERNAL_VARIABLES;
};
......@@ -59,6 +59,8 @@ void JuliaVariableModel::update()
{
const QStringList& values =
static_cast<QDBusReply<QStringList>>(m_interface->call(QLatin1String("variableValuesList"))).value();
const QStringList& variablesSizes =
static_cast<QDBusReply<QStringList>>(m_interface->call(QLatin1String("variableSizesList"))).value();
for (int i = 0; i < variables.size(); i++)
{
......@@ -70,20 +72,21 @@ void JuliaVariableModel::update()
const QString& name = variables[i];
QString value = values[i];
size_t size = variablesSizes[i].toULongLong();
if (value != JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER)
{
// Register variable
// We use replace here, because julia return data type for some variables, and we need
// remove it to make variable view more consistent with the other backends
// More info: https://bugs.kde.org/show_bug.cgi?id=377771
vars << Variable{name, value.replace(typeVariableInfo,QLatin1String("["))};
vars << Variable(name, value.replace(typeVariableInfo, QLatin1String("[")), size);
}
}
}
else
{
for (int i = 0; i < variables.size(); i++)
vars << Variable{variables[i], QString()};
vars << Variable(variables[i], QString());
}
setVariables(vars);
......
......@@ -48,11 +48,15 @@ void OctaveVariableModel::update()
" try"
" eval(['__cantor_string__ = disp(' __cantor_varname__ ');']);"
" printf(__cantor_string__);"
" printf([num2str(eval(['sizeof(' __cantor_varname__ ');'])) '\\n']);"
" catch"
" printf(['<unprintable value>' '\\n']);"
" printf(['0' '\\n']);"
" end_try_catch;"
" else"
" printf('');"
" endif;"
" printf('__cantor_delimiter_line__\\n')"
" printf('__cantor_delimiter_line__\\n');"
"endfor;"
"split_long_rows(__cantor_split_var__);"
"clear __cantor_list__;"
......@@ -96,8 +100,9 @@ void OctaveVariableModel::parseNewVariables(Expression::Status status)
const QString& name = line.section(QLatin1String("\n"), 0, 0);
QString value;
if (OctaveSettings::self()->variableManagement())
value = line.section(QLatin1String("\n"), 1);
vars << Variable{name, value};
value = line.section(QLatin1String("\n"), 1, 1);
size_t size = line.section(QLatin1String("\n"), 2, 2).toULongLong();
vars << Variable(name, value, size);
}
setVariables(vars);
......
......@@ -20,22 +20,20 @@
#include "pythonserver.h"
#include <vector>
#include <cassert>
#include <iostream>
#include <Python.h>
static_assert(PY_MAJOR_VERSION == 3, "This python server works only with Python 3");
using namespace std;
namespace
{
string pyObjectToQString(PyObject* obj)
{
#if PY_MAJOR_VERSION == 3
return string(PyUnicode_AsUTF8(obj));
#elif PY_MAJOR_VERSION == 2
return string(PyString_AsString(obj));
#else
#warning Unknown Python version
#endif
}
}
......@@ -44,6 +42,7 @@ void PythonServer::login()
Py_InspectFlag = 1;
Py_Initialize();
m_pModule = PyImport_AddModule("__main__");
PyRun_SimpleString("import sys");
filePath = "python_cantor_worksheet";
}
......@@ -73,7 +72,6 @@ void PythonServer::runPythonCommand(const string& command)
"sys.stderr = errorPythonBackend\n";
PyRun_SimpleString(prepareCommand);
#if PY_MAJOR_VERSION == 3
PyObject* compile = Py_CompileString(command.c_str(), filePath.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
......@@ -92,42 +90,7 @@ void PythonServer::runPythonCommand(const string& command)
}
}
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.c_str(), filePath.c_str(), Py_file_input);
if (PyErr_Occurred())
{
m_error = true;
PyErr_PrintEx(0);
return;
}
PyObject* codeSingle = Py_CompileString(command.c_str(), filePath.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())
{
m_error = true;
......@@ -166,21 +129,18 @@ void PythonServer::setFilePath(const string& path, const string& dir)
}
}
string PythonServer::variables(bool parseValue) const
string PythonServer::variables(bool parseValue)
{
PyRun_SimpleStringFlags(
"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", nullptr
);
PyObject* py_dict = PyModule_GetDict(m_pModule);
PyRun_SimpleString("__tmp_globals__ = globals()");
PyObject* globals = PyObject_GetAttrString(m_pModule,"__tmp_globals__");
PyObject *key, *value;
......@@ -207,10 +167,15 @@ string PythonServer::variables(bool parseValue) const
continue;
string valueString;
string sizeString;
if (parseValue)
{
valueString = pyObjectToQString(PyObject_Repr(value));
std::string command = "sys.getsizeof("+keyString+")";
sizeString = pyObjectToQString(PyObject_Repr(PyRun_String(command.c_str(), Py_eval_input, py_dict, py_dict)));
}
vars.push_back(keyString + char(17) + valueString);
vars.push_back(keyString + char(17) + valueString + char(17) + sizeString);
}
PyRun_SimpleStringFlags(
......@@ -218,11 +183,7 @@ string PythonServer::variables(bool parseValue) const
" 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", nullptr
);
......
......@@ -38,7 +38,7 @@ class PythonServer
std::string getOutput() const;
std::string getError() const;
bool isError() const;
std::string variables(bool parseValue) const;
std::string variables(bool parseValue);
private:
PyObject* m_pModule{nullptr};
......
......@@ -72,8 +72,9 @@ void PythonVariableModel::extractVariables(Cantor::Expression::Status status)
// DC1(17) is delimiter between variable name and its value.
const QString& name = record.section(QChar(17), 0, 0);
const QString& value = record.section(QChar(17), 1, 1);
const QString& size = record.section(QChar(17), 2, 2);
variables << Variable{name, value};
variables << Variable(name, value, size.toULongLong());
}
setVariables(variables);
......@@ -84,6 +85,8 @@ void PythonVariableModel::extractVariables(Cantor::Expression::Status status)
case Cantor::Expression::Error:
{
qDebug() << "python variable model update finished with status" << (status == Cantor::Expression::Error? "Error" : "Interrupted");
if (status == Cantor::Expression::Error)
qDebug() << "error message: " << m_expression->errorMessage();
break;
}
default:
......
......@@ -108,11 +108,16 @@ QVariant DefaultVariableModel::data(const QModelIndex& index, int role) const
return QVariant(d->variables[index.row()].name);
case ValueColumn:
{
const QString& value = d->variables[index.row()].value;
if (value.size() < 1000 || role == DefaultVariableModel::DataRole)
return QVariant(value);
const Variable& var = d->variables[index.row()];
if (var.value.size() < 1000 || role == DefaultVariableModel::DataRole)
return QVariant(var.value);
else
return QVariant(QString::fromLatin1("<too big variable>"));
{
if (var.size != 0)
return QVariant(i18n("<%1 bytes>", QString::number(var.size)));
else
return QVariant(i18n("<too big variable>"));
}
}
default:
return QVariant();
......@@ -150,9 +155,7 @@ bool DefaultVariableModel::setData(const QModelIndex& index, const QVariant& val
void DefaultVariableModel::addVariable(const QString& name, const QString& value)
{
Variable v;
v.name = name;
v.value = value;
Variable v(name, value);
addVariable(v);
}
......@@ -177,8 +180,7 @@ void DefaultVariableModel::addVariable(const Cantor::DefaultVariableModel::Varia
void DefaultVariableModel::removeVariable(const QString& name)
{
Variable v;
v.name = name;
Variable v(name, QString());
removeVariable(v);
}
......@@ -262,6 +264,7 @@ void DefaultVariableModel::setVariables(const QList<DefaultVariableModel::Variab
{
QModelIndex index = createIndex(i, ValueColumn);
d->variables[i].value = newvar.value;
d->variables[i].size = newvar.size;
emit dataChanged(index, index);
}
break;
......
......@@ -58,6 +58,8 @@ public:
*/
struct Variable
{
Variable(): size(0) {}
Variable(QString name, QString value, size_t size = 0): name(name), value(value), size(size) {}
/**
* The variable's name
*/
......@@ -66,6 +68,11 @@ public:
* The variable's value, represented as a string
*/
QString value;
/**
* Optional parameter. Size of variable in bytes
*/
size_t size;
};
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment