Skip to content

Commit

Permalink
BUG: Fix setting of paths that contain special characters in Python
Browse files Browse the repository at this point in the history
ctkAbstractPythonManager::toPythonStringLiteral helper function is copied from 3D Slicer.

This function allows creating a valid Python string literal, even if the input string
contains special characters, such as ' or \.
  • Loading branch information
lassoan committed Jan 6, 2021
1 parent 2902545 commit 3b19577
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 6 deletions.
21 changes: 15 additions & 6 deletions Libs/Scripting/Python/Core/ctkAbstractPythonManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ void ctkAbstractPythonManager::initPythonQt(int flags)
initCode << "import sys";
foreach (const QString& path, this->pythonPaths())
{
initCode << QString("sys.path.append('%1')").arg(QDir::fromNativeSeparators(path));
initCode << QString("sys.path.append(%1)").arg(QDir::fromNativeSeparators(path));
}

PythonQtObjectPtr _mainContext = PythonQt::self()->getMainModule();
Expand Down Expand Up @@ -332,20 +332,20 @@ void ctkAbstractPythonManager::executeFile(const QString& filename)
// Re-throwing is only needed in Python 2.7
QStringList code = QStringList()
<< "import sys"
<< QString("sys.path.insert(0, '%1')").arg(path)
<< QString("sys.path.insert(0, %1)").arg(ctkAbstractPythonManager::toPythonStringLiteral(path))
<< "_updated_globals = globals()"
<< QString("_updated_globals['__file__'] = '%1'").arg(filename)
<< QString("_updated_globals['__file__'] = %1").arg(ctkAbstractPythonManager::toPythonStringLiteral(filename))
#if PY_MAJOR_VERSION >= 3
<< QString("exec(open('%1').read(), _updated_globals)").arg(filename);
<< QString("exec(open(%1).read(), _updated_globals)").arg(ctkAbstractPythonManager::toPythonStringLiteral(filename));
#else
<< "_ctk_executeFile_exc_info = None"
<< "try:"
<< QString(" execfile('%1', _updated_globals)").arg(filename)
<< QString(" execfile(%1, _updated_globals)").arg(ctkAbstractPythonManager::toPythonStringLiteral(filename))
<< "except Exception as e:"
<< " _ctk_executeFile_exc_info = sys.exc_info()"
<< "finally:"
<< " del _updated_globals"
<< QString(" if sys.path[0] == '%1': sys.path.pop(0)").arg(path)
<< QString(" if sys.path[0] == %1: sys.path.pop(0)").arg(ctkAbstractPythonManager::toPythonStringLiteral(path))
<< " if _ctk_executeFile_exc_info:"
<< " raise _ctk_executeFile_exc_info[1], None, _ctk_executeFile_exc_info[2]";
#endif
Expand Down Expand Up @@ -728,3 +728,12 @@ void ctkAbstractPythonManager::printStderr(const QString& text)
{
std::cerr << qPrintable(text);
}

//-----------------------------------------------------------------------------
QString ctkAbstractPythonManager::toPythonStringLiteral(QString path)
{
path = path.replace("\\", "\\\\");
path = path.replace("'", "\\'");
// since we enclose string in single quotes, double-quotes do not require escaping
return "'" + path + "'";
}
11 changes: 11 additions & 0 deletions Libs/Scripting/Python/Core/ctkAbstractPythonManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,17 @@ class CTK_SCRIPTING_PYTHON_CORE_EXPORT ctkAbstractPythonManager : public QObject
/// \sa PythonQt::clearError()
void resetErrorFlag();

/// Convert a string to a safe python string literal.
/// Backslash, single-quote characters are escaped
/// and the string is enclosed between single quotes.
///
/// Examples:
/// some simple string => 'some simple string'
/// some " string => 'some " string'
/// some other ' string => 'some other \' string'
/// some backslash \ str => 'some backslash \\ str'
Q_INVOKABLE static QString toPythonStringLiteral(QString path);

Q_SIGNALS:

/// This signal is emitted after python is pre-initialized. Observers can listen
Expand Down

0 comments on commit 3b19577

Please sign in to comment.