diff options
| author | David Schulz <[email protected]> | 2025-06-19 14:58:36 +0200 |
|---|---|---|
| committer | David Schulz <[email protected]> | 2025-06-30 10:26:46 +0000 |
| commit | 975eaa91eae8f8fc5789a3db82fad36749dd8a4e (patch) | |
| tree | 85646ec8601424edc3389d5eb31fbf8c6d78dc26 /src/plugins/python/pythonrunconfiguration.cpp | |
| parent | c1f917ee490b6e504c31e23da4b95efcf734cae6 (diff) | |
Python: improve missing pyside handling
Fixes: QTCREATORBUG-33057
Change-Id: I41fca7728c10fbf512b383cf04c4b3dacdb536ac
Reviewed-by: Christian Stenger <[email protected]>
Diffstat (limited to 'src/plugins/python/pythonrunconfiguration.cpp')
| -rw-r--r-- | src/plugins/python/pythonrunconfiguration.cpp | 139 |
1 files changed, 91 insertions, 48 deletions
diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index d1dbac54de2..3b7f8742f6e 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -3,6 +3,8 @@ #include "pythonrunconfiguration.h" +#include "pyside.h" +#include "pythonbuildconfiguration.h" #include "pythonconstants.h" #include "pythonproject.h" #include "pythontr.h" @@ -23,6 +25,8 @@ #include <utils/fileutils.h> #include <utils/outputformatter.h> +#include <QUrl> + using namespace ProjectExplorer; using namespace Utils; @@ -34,10 +38,18 @@ static const QRegularExpression &tracebackFilePattern() return s_filePattern; } +static const QRegularExpression &moduleNotFoundPattern() +{ + static const QRegularExpression s_functionPattern( + "^ModuleNotFoundError: No module named '([_a-zA-Z][_a-zA-Z0-9]*)'$"); + return s_functionPattern; +} + class PythonOutputLineParser : public OutputLineParser { public: - PythonOutputLineParser() + PythonOutputLineParser(const FilePath &python) + : m_python(python) { TaskHub::clearTasks(PythonErrorTaskCategory); } @@ -45,62 +57,87 @@ public: private: Result handleLine(const QString &text, OutputFormat format) final { - if (!m_inTraceBack) { - m_inTraceBack = format == StdErrFormat - && text.startsWith("Traceback (most recent call last):"); - if (m_inTraceBack) - return Status::InProgress; - return Status::NotHandled; - } - const Id category(PythonErrorTaskCategory); - const QRegularExpressionMatch match = tracebackFilePattern().match(text); - if (match.hasMatch()) { - const LinkSpec link(match.capturedStart(2), match.capturedLength(2), match.captured(2)); - const auto fileName = FilePath::fromString(match.captured(3)); - const int lineNumber = match.captured(4).toInt(); - m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category}); - return {Status::InProgress, {link}}; - } - Status status = Status::InProgress; - if (text.startsWith(' ')) { - // Neither traceback start, nor file, nor error message line. - // Not sure if that can actually happen. - if (m_tasks.isEmpty()) { - m_tasks.append({Task::Warning, text.trimmed(), {}, -1, category}); + if (m_inTraceBack) { + const QRegularExpressionMatch match = tracebackFilePattern().match(text); + if (match.hasMatch()) { + const LinkSpec link(match.capturedStart(2), match.capturedLength(2), match.captured(2)); + const auto fileName = FilePath::fromUserInput(match.captured(3)); + const int lineNumber = match.captured(4).toInt(); + m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category}); + return {Status::InProgress, {link}}; + } + + if (text.startsWith(' ')) { + // Neither traceback start, nor file, nor error message line. + // Not sure if that can actually happen. + if (m_tasks.isEmpty()) { + m_tasks.append({Task::Warning, text.trimmed(), {}, -1, category}); + } else { + Task &task = m_tasks.back(); + if (!task.summary().isEmpty()) + task.addToSummary(QChar(' ')); + task.addToSummary(text.trimmed()); + } } else { - Task &task = m_tasks.back(); - QString t = text.trimmed(); - if (!task.summary().isEmpty()) - t.prepend(' '); - task.addToSummary(t); + // The actual exception. This ends the traceback. + Task exception{Task::Error, text, {}, -1, category}; + const QString detail = Tr::tr("Install %1 (requires pip)"); + const QString pySide6Text = Tr::tr("PySide6"); + const QString link = QString("pysideinstall:") + + QUrl::toPercentEncoding(m_python.toFSPathString()); + exception.addToDetails(detail.arg(pySide6Text)); + QTextCharFormat format; + format.setAnchor(true); + format.setAnchorHref(link); + const int offset = exception.summary().length() + detail.indexOf("%1") + 1; + exception.setFormats( + {QTextLayout::FormatRange{offset, int(pySide6Text.length()), format}}); + TaskHub::addTask(exception); + for (auto rit = m_tasks.crbegin(), rend = m_tasks.crend(); rit != rend; ++rit) + TaskHub::addTask(*rit); + m_inTraceBack = false; + const QRegularExpressionMatch match = moduleNotFoundPattern().match(text); + if (match.hasMatch()) { + const QString moduleName = match.captured(1); + if (moduleName == "PySide6") { + const LinkSpec + link(match.capturedStart(1), match.capturedLength(1), moduleName); + return {Status::Done, {link}}; + } + } + return Status::Done; } - } else { - // The actual exception. This ends the traceback. - TaskHub::addTask({Task::Error, text, {}, -1, category}); - for (auto rit = m_tasks.crbegin(), rend = m_tasks.crend(); rit != rend; ++rit) - TaskHub::addTask(*rit); - m_tasks.clear(); - m_inTraceBack = false; - status = Status::Done; + return Status::InProgress; + } + + if (format == StdErrFormat) { + m_inTraceBack = text.startsWith("Traceback (most recent call last):"); + if (m_inTraceBack) + return Status::InProgress; } - return status; + + return Status::NotHandled; } bool handleLink(const QString &href) final { - const QRegularExpressionMatch match = tracebackFilePattern().match(href); - if (!match.hasMatch()) - return false; - const QString fileName = match.captured(3); - const int lineNumber = match.captured(4).toInt(); - Core::EditorManager::openEditorAt({FilePath::fromString(fileName), lineNumber}); - return true; + if (const QRegularExpressionMatch match = tracebackFilePattern().match(href); + match.hasMatch()) { + const QString fileName = match.captured(3); + const int lineNumber = match.captured(4).toInt(); + Core::EditorManager::openEditorAt({FilePath::fromString(fileName), lineNumber}); + return true; + } + if (href == "PySide6") + PySideInstaller::instance().installPySide(m_python, href); + return false; } QList<Task> m_tasks; bool m_inTraceBack = false; + const FilePath m_python; }; // RunConfiguration @@ -195,12 +232,18 @@ void setupPythonDebugWorker() void setupPythonOutputParser() { - addOutputParserFactory([](Target *t) -> OutputLineParser * { - if (!t) + addOutputParserFactory([](BuildConfiguration *bc) -> OutputLineParser * { + auto *pythonBuildConfig = qobject_cast<PythonBuildConfiguration *>(bc); + if (!pythonBuildConfig) return nullptr; + + Target *t = pythonBuildConfig->target(); + QTC_ASSERT(t, return nullptr); + if (t->project()->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE - || t->project()->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE_TOML) - return new PythonOutputLineParser; + || t->project()->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE_TOML) { + return new PythonOutputLineParser(pythonBuildConfig->python()); + } return nullptr; }); } |
