2019-07-22 14:39:01 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2019-10-01 13:16:17 +02:00
|
|
|
#include "pythonrunconfiguration.h"
|
|
|
|
|
|
2022-03-16 09:35:02 +01:00
|
|
|
#include "pyside.h"
|
2022-03-30 14:42:33 +02:00
|
|
|
#include "pysidebuildconfiguration.h"
|
2019-07-22 14:39:01 +02:00
|
|
|
#include "pythonconstants.h"
|
2021-12-13 14:19:30 +01:00
|
|
|
#include "pythonlanguageclient.h"
|
2019-07-22 14:39:01 +02:00
|
|
|
#include "pythonproject.h"
|
|
|
|
|
#include "pythonsettings.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
|
2019-10-01 13:16:17 +02:00
|
|
|
#include <languageclient/languageclientmanager.h>
|
|
|
|
|
|
2022-03-30 14:42:33 +02:00
|
|
|
#include <projectexplorer/buildsteplist.h>
|
2019-11-15 15:44:45 +01:00
|
|
|
#include <projectexplorer/buildsystem.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <projectexplorer/localenvironmentaspect.h>
|
|
|
|
|
#include <projectexplorer/runconfigurationaspects.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
#include <projectexplorer/taskhub.h>
|
|
|
|
|
|
2019-10-01 13:16:17 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
|
2020-09-18 12:11:40 +02:00
|
|
|
#include <utils/aspects.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <utils/fileutils.h>
|
2020-09-18 12:11:40 +02:00
|
|
|
#include <utils/layoutbuilder.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <utils/outputformatter.h>
|
|
|
|
|
#include <utils/theme/theme.h>
|
|
|
|
|
|
|
|
|
|
#include <QBoxLayout>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QFormLayout>
|
|
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Python {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2020-04-14 15:28:44 +02:00
|
|
|
class PythonOutputLineParser : public OutputLineParser
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2020-04-14 15:28:44 +02:00
|
|
|
PythonOutputLineParser()
|
2019-07-22 14:39:01 +02:00
|
|
|
// Note that moc dislikes raw string literals.
|
|
|
|
|
: filePattern("^(\\s*)(File \"([^\"]+)\", line (\\d+), .*$)")
|
|
|
|
|
{
|
|
|
|
|
TaskHub::clearTasks(PythonErrorTaskCategory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2020-04-14 15:28:44 +02:00
|
|
|
Result handleLine(const QString &text, OutputFormat format) final
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
2020-03-18 12:56:35 +01:00
|
|
|
if (!m_inTraceBack) {
|
|
|
|
|
m_inTraceBack = format == StdErrFormat
|
|
|
|
|
&& text.startsWith("Traceback (most recent call last):");
|
2020-04-09 17:47:01 +02:00
|
|
|
if (m_inTraceBack)
|
2020-03-19 16:00:37 +01:00
|
|
|
return Status::InProgress;
|
|
|
|
|
return Status::NotHandled;
|
2020-03-18 12:56:35 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
const Utils::Id category(PythonErrorTaskCategory);
|
2020-03-18 12:56:35 +01:00
|
|
|
const QRegularExpressionMatch match = filePattern.match(text);
|
|
|
|
|
if (match.hasMatch()) {
|
2020-04-09 17:47:01 +02:00
|
|
|
const LinkSpec link(match.capturedStart(2), match.capturedLength(2), match.captured(2));
|
2020-03-18 12:56:35 +01:00
|
|
|
const auto fileName = FilePath::fromString(match.captured(3));
|
2020-06-23 13:00:43 +02:00
|
|
|
const int lineNumber = match.captured(4).toInt();
|
2020-03-18 12:56:35 +01:00
|
|
|
m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category});
|
2020-04-09 17:47:01 +02:00
|
|
|
return {Status::InProgress, {link}};
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-19 16:00:37 +01:00
|
|
|
Status status = Status::InProgress;
|
2020-03-18 12:56:35 +01:00
|
|
|
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});
|
2019-07-22 14:39:01 +02:00
|
|
|
} else {
|
2020-03-18 12:56:35 +01:00
|
|
|
Task &task = m_tasks.back();
|
2020-05-12 16:26:34 +02:00
|
|
|
if (!task.summary.isEmpty())
|
|
|
|
|
task.summary += ' ';
|
|
|
|
|
task.summary += text.trimmed();
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
2020-03-18 12:56:35 +01:00
|
|
|
} 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)
|
2019-07-22 14:39:01 +02:00
|
|
|
TaskHub::addTask(*rit);
|
2020-03-18 12:56:35 +01:00
|
|
|
m_tasks.clear();
|
|
|
|
|
m_inTraceBack = false;
|
2020-03-19 16:00:37 +01:00
|
|
|
status = Status::Done;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
2020-03-19 16:00:37 +01:00
|
|
|
return status;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-19 16:00:37 +01:00
|
|
|
bool handleLink(const QString &href) final
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
const QRegularExpressionMatch match = filePattern.match(href);
|
|
|
|
|
if (!match.hasMatch())
|
2020-03-19 16:00:37 +01:00
|
|
|
return false;
|
2019-07-22 14:39:01 +02:00
|
|
|
const QString fileName = match.captured(3);
|
2020-06-23 13:00:43 +02:00
|
|
|
const int lineNumber = match.captured(4).toInt();
|
2021-11-01 17:02:02 +01:00
|
|
|
Core::EditorManager::openEditorAt({FilePath::fromString(fileName), lineNumber});
|
2020-03-19 16:00:37 +01:00
|
|
|
return true;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QRegularExpression filePattern;
|
2020-03-18 12:56:35 +01:00
|
|
|
QList<Task> m_tasks;
|
|
|
|
|
bool m_inTraceBack;
|
2019-07-22 14:39:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-04-13 12:26:54 +02:00
|
|
|
class PythonRunConfiguration : public RunConfiguration
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2022-04-13 12:26:54 +02:00
|
|
|
PythonRunConfiguration(Target *target, Id id)
|
|
|
|
|
: RunConfiguration(target, id)
|
|
|
|
|
{
|
|
|
|
|
auto interpreterAspect = addAspect<InterpreterAspect>();
|
|
|
|
|
interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
|
|
|
|
|
interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
|
|
|
|
|
|
2022-03-30 14:42:33 +02:00
|
|
|
connect(interpreterAspect, &InterpreterAspect::changed,
|
|
|
|
|
this, &PythonRunConfiguration::currentInterpreterChanged);
|
2022-04-13 12:26:54 +02:00
|
|
|
|
|
|
|
|
connect(PythonSettings::instance(), &PythonSettings::interpretersChanged,
|
|
|
|
|
interpreterAspect, &InterpreterAspect::updateInterpreters);
|
|
|
|
|
|
2022-03-30 14:42:33 +02:00
|
|
|
QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(
|
|
|
|
|
project()->projectDirectory());
|
2022-04-13 12:26:54 +02:00
|
|
|
interpreterAspect->updateInterpreters(PythonSettings::interpreters());
|
2022-03-30 14:42:33 +02:00
|
|
|
Interpreter defaultInterpreter = interpreters.isEmpty()
|
|
|
|
|
? PythonSettings::defaultInterpreter()
|
|
|
|
|
: interpreters.first();
|
|
|
|
|
if (!defaultInterpreter.command.isExecutableFile())
|
|
|
|
|
defaultInterpreter = PythonSettings::interpreters().value(0);
|
|
|
|
|
interpreterAspect->setDefaultInterpreter(defaultInterpreter);
|
2022-04-13 12:26:54 +02:00
|
|
|
|
|
|
|
|
auto bufferedAspect = addAspect<BoolAspect>();
|
|
|
|
|
bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered");
|
|
|
|
|
bufferedAspect->setLabel(tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox);
|
|
|
|
|
bufferedAspect->setToolTip(tr("Enabling improves output performance, "
|
|
|
|
|
"but results in delayed output."));
|
|
|
|
|
|
|
|
|
|
auto scriptAspect = addAspect<MainScriptAspect>();
|
|
|
|
|
scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script");
|
|
|
|
|
scriptAspect->setLabelText(tr("Script:"));
|
|
|
|
|
scriptAspect->setDisplayStyle(StringAspect::LabelDisplay);
|
|
|
|
|
|
|
|
|
|
addAspect<LocalEnvironmentAspect>(target);
|
|
|
|
|
|
2022-05-11 16:51:46 +02:00
|
|
|
auto argumentsAspect = addAspect<ArgumentsAspect>(macroExpander());
|
2022-04-13 12:26:54 +02:00
|
|
|
|
2022-05-30 14:56:20 +02:00
|
|
|
addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr);
|
2022-04-13 12:26:54 +02:00
|
|
|
addAspect<TerminalAspect>();
|
|
|
|
|
|
2022-05-11 16:51:46 +02:00
|
|
|
setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] {
|
2022-04-13 12:26:54 +02:00
|
|
|
CommandLine cmd{interpreterAspect->currentInterpreter().command};
|
|
|
|
|
if (!bufferedAspect->value())
|
|
|
|
|
cmd.addArg("-u");
|
|
|
|
|
cmd.addArg(scriptAspect->filePath().fileName());
|
2022-05-11 16:51:46 +02:00
|
|
|
cmd.addArgs(argumentsAspect->arguments(), CommandLine::Raw);
|
2022-04-13 12:26:54 +02:00
|
|
|
return cmd;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setUpdater([this, scriptAspect] {
|
|
|
|
|
const BuildTargetInfo bti = buildTargetInfo();
|
|
|
|
|
const QString script = bti.targetFilePath.toUserOutput();
|
|
|
|
|
setDefaultDisplayName(tr("Run %1").arg(script));
|
|
|
|
|
scriptAspect->setValue(script);
|
|
|
|
|
aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
|
2019-10-01 13:16:17 +02:00
|
|
|
}
|
2022-03-30 14:42:33 +02:00
|
|
|
|
|
|
|
|
void currentInterpreterChanged()
|
|
|
|
|
{
|
|
|
|
|
const FilePath python = aspect<InterpreterAspect>()->currentInterpreter().command;
|
|
|
|
|
|
|
|
|
|
BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps();
|
|
|
|
|
if (auto pySideBuildStep = buildSteps->firstOfType<PySideBuildStep>())
|
|
|
|
|
pySideBuildStep->updateInterpreter(python);
|
|
|
|
|
|
|
|
|
|
for (FilePath &file : project()->files(Project::AllFiles)) {
|
|
|
|
|
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
|
|
|
|
|
if (document->mimeType() == Constants::C_PY_MIMETYPE) {
|
|
|
|
|
PyLSConfigureAssistant::openDocumentWithPython(python, document);
|
|
|
|
|
PySideInstaller::checkPySideInstallation(python, document);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-13 12:26:54 +02:00
|
|
|
};
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
PythonRunConfigurationFactory::PythonRunConfigurationFactory()
|
|
|
|
|
{
|
2022-04-13 12:26:54 +02:00
|
|
|
registerRunConfiguration<PythonRunConfiguration>(Constants::C_PYTHONRUNCONFIGURATION_ID);
|
2019-07-22 14:39:01 +02:00
|
|
|
addSupportedProjectType(PythonProjectId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PythonOutputFormatterFactory::PythonOutputFormatterFactory()
|
|
|
|
|
{
|
2020-04-22 10:46:09 +02:00
|
|
|
setFormatterCreator([](Target *t) -> QList<OutputLineParser *> {
|
2020-04-22 10:20:36 +02:00
|
|
|
if (t && t->project()->mimeType() == Constants::C_PY_MIMETYPE)
|
2020-04-22 10:46:09 +02:00
|
|
|
return {new PythonOutputLineParser};
|
|
|
|
|
return {};
|
2019-07-22 14:39:01 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Python
|