Python: generate Python kits for new interpreter

Change-Id: Ie4b23aae296f66900fba85a9e243bbf656e49ed4
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
David Schulz
2023-10-19 13:00:48 +02:00
parent 20d355e235
commit 28b70672b4
3 changed files with 118 additions and 1 deletions

View File

@@ -5,6 +5,7 @@
#include "pysidebuildconfiguration.h" #include "pysidebuildconfiguration.h"
#include "pythoneditor.h" #include "pythoneditor.h"
#include "pythonkitaspect.h"
#include "pythonproject.h" #include "pythonproject.h"
#include "pythonrunconfiguration.h" #include "pythonrunconfiguration.h"
#include "pythonsettings.h" #include "pythonsettings.h"
@@ -13,6 +14,7 @@
#include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/jsonwizard/jsonwizardfactory.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
#include <projectexplorer/taskhub.h> #include <projectexplorer/taskhub.h>
@@ -60,6 +62,9 @@ void PythonPlugin::initialize()
{ {
d = new PythonPluginPrivate; d = new PythonPluginPrivate;
KitManager::setIrrelevantAspects(KitManager::irrelevantAspects()
+ QSet<Id>{PythonKitAspect::id()});
ProjectManager::registerProjectType<PythonProject>(PythonMimeType); ProjectManager::registerProjectType<PythonProject>(PythonMimeType);
ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy); ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy);
} }

View File

@@ -4,6 +4,7 @@
#include "pythonsettings.h" #include "pythonsettings.h"
#include "pythonconstants.h" #include "pythonconstants.h"
#include "pythonkitaspect.h"
#include "pythonplugin.h" #include "pythonplugin.h"
#include "pythontr.h" #include "pythontr.h"
#include "pythonutils.h" #include "pythonutils.h"
@@ -11,6 +12,9 @@
#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/kitmanager.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <languageclient/languageclient_global.h> #include <languageclient/languageclient_global.h>
@@ -144,6 +148,7 @@ private:
InterpreterDetailsWidget *m_detailsWidget = nullptr; InterpreterDetailsWidget *m_detailsWidget = nullptr;
QPushButton *m_deleteButton = nullptr; QPushButton *m_deleteButton = nullptr;
QPushButton *m_makeDefaultButton = nullptr; QPushButton *m_makeDefaultButton = nullptr;
QPushButton *m_generateKitButton = nullptr;
QPushButton *m_cleanButton = nullptr; QPushButton *m_cleanButton = nullptr;
QString m_defaultId; QString m_defaultId;
@@ -153,6 +158,7 @@ private:
void addItem(); void addItem();
void deleteItem(); void deleteItem();
void makeDefault(); void makeDefault();
void generateKit();
void cleanUp(); void cleanUp();
}; };
@@ -199,6 +205,8 @@ InterpreterOptionsWidget::InterpreterOptionsWidget()
m_deleteButton->setEnabled(false); m_deleteButton->setEnabled(false);
m_makeDefaultButton = new QPushButton(Tr::tr("&Make Default")); m_makeDefaultButton = new QPushButton(Tr::tr("&Make Default"));
m_makeDefaultButton->setEnabled(false); m_makeDefaultButton->setEnabled(false);
m_generateKitButton = new QPushButton(Tr::tr("&Generate Kit"));
m_generateKitButton->setEnabled(false);
m_cleanButton = new QPushButton(Tr::tr("&Clean Up"), this); m_cleanButton = new QPushButton(Tr::tr("&Clean Up"), this);
m_cleanButton->setToolTip(Tr::tr("Remove all Python interpreters without a valid executable.")); m_cleanButton->setToolTip(Tr::tr("Remove all Python interpreters without a valid executable."));
@@ -209,6 +217,7 @@ InterpreterOptionsWidget::InterpreterOptionsWidget()
addButton, addButton,
m_deleteButton, m_deleteButton,
m_makeDefaultButton, m_makeDefaultButton,
m_generateKitButton,
m_cleanButton, m_cleanButton,
st st
}; };
@@ -230,6 +239,7 @@ InterpreterOptionsWidget::InterpreterOptionsWidget()
connect(addButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::addItem); connect(addButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::addItem);
connect(m_deleteButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::deleteItem); connect(m_deleteButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::deleteItem);
connect(m_makeDefaultButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::makeDefault); connect(m_makeDefaultButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::makeDefault);
connect(m_generateKitButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::generateKit);
connect(m_cleanButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::cleanUp); connect(m_cleanButton, &QPushButton::pressed, this, &InterpreterOptionsWidget::cleanUp);
connect(m_detailsWidget, &InterpreterDetailsWidget::changed, connect(m_detailsWidget, &InterpreterDetailsWidget::changed,
@@ -275,8 +285,11 @@ void InterpreterOptionsWidget::currentChanged(const QModelIndex &index, const QM
if (index.isValid()) { if (index.isValid()) {
m_detailsWidget->updateInterpreter(m_model.itemAt(index.row())->itemData); m_detailsWidget->updateInterpreter(m_model.itemAt(index.row())->itemData);
m_detailsWidget->show(); m_detailsWidget->show();
m_generateKitButton->setEnabled(
!KitManager::kit(Id::fromString(m_model.itemAt(index.row())->itemData.id)));
} else { } else {
m_detailsWidget->hide(); m_detailsWidget->hide();
m_generateKitButton->setEnabled(false);
} }
m_deleteButton->setEnabled(index.isValid()); m_deleteButton->setEnabled(index.isValid());
m_makeDefaultButton->setEnabled(index.isValid()); m_makeDefaultButton->setEnabled(index.isValid());
@@ -553,6 +566,13 @@ void InterpreterOptionsWidget::makeDefault()
} }
} }
void InterpreterOptionsWidget::generateKit()
{
const QModelIndex &index = m_view->currentIndex();
if (index.isValid())
PythonSettings::addKitsForInterpreter(m_model.itemAt(index.row())->itemData);
}
void InterpreterOptionsWidget::cleanUp() void InterpreterOptionsWidget::cleanUp()
{ {
m_model.destroyItems( m_model.destroyItems(
@@ -563,6 +583,7 @@ void InterpreterOptionsWidget::cleanUp()
constexpr char settingsGroupKey[] = "Python"; constexpr char settingsGroupKey[] = "Python";
constexpr char interpreterKey[] = "Interpeter"; constexpr char interpreterKey[] = "Interpeter";
constexpr char defaultKey[] = "DefaultInterpeter"; constexpr char defaultKey[] = "DefaultInterpeter";
constexpr char kitsGeneratedKey[] = "KitsGenerated";
constexpr char pylsEnabledKey[] = "PylsEnabled"; constexpr char pylsEnabledKey[] = "PylsEnabled";
constexpr char pylsConfigurationKey[] = "PylsConfiguration"; constexpr char pylsConfigurationKey[] = "PylsConfiguration";
@@ -786,12 +807,65 @@ PythonSettings::~PythonSettings()
settingsInstance = nullptr; settingsInstance = nullptr;
} }
static void setRelevantAspectsToKit(Kit *k)
{
QTC_ASSERT(k, return);
QSet<Utils::Id> relevantAspects = k->relevantAspects();
relevantAspects.unite({PythonKitAspect::id(), EnvironmentKitAspect::id()});
k->setRelevantAspects(relevantAspects);
}
void PythonSettings::addKitsForInterpreter(const Interpreter &interpreter)
{
if (!KitManager::isLoaded()) {
connect(KitManager::instance(), &KitManager::kitsLoaded, settingsInstance, [interpreter]() {
addKitsForInterpreter(interpreter);
});
return;
}
const Id kitId = Id::fromString(interpreter.id);
if (Kit *k = KitManager::kit(kitId)) {
setRelevantAspectsToKit(k);
} else {
KitManager::registerKit(
[interpreter](Kit *k) {
k->setAutoDetected(true);
k->setAutoDetectionSource("Python");
k->setUnexpandedDisplayName(interpreter.name);
setRelevantAspectsToKit(k);
PythonKitAspect::setPython(k, interpreter.id);
},
kitId);
}
}
void PythonSettings::removeKitsForInterpreter(const Interpreter &interpreter)
{
if (!KitManager::isLoaded()) {
connect(KitManager::instance(), &KitManager::kitsLoaded, settingsInstance, [interpreter]() {
removeKitsForInterpreter(interpreter);
});
return;
}
if (Kit *k = KitManager::kit(Id::fromString(interpreter.id)))
KitManager::deregisterKit(k);
}
void PythonSettings::setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId) void PythonSettings::setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId)
{ {
if (defaultId == settingsInstance->m_defaultInterpreterId if (defaultId == settingsInstance->m_defaultInterpreterId
&& interpreters == settingsInstance->m_interpreters) { && interpreters == settingsInstance->m_interpreters) {
return; return;
} }
QList<Interpreter> toRemove = settingsInstance->m_interpreters;
for (const Interpreter &interpreter : interpreters) {
if (!Utils::eraseOne(toRemove, Utils::equal(&Interpreter::id, interpreter.id)))
addKitsForInterpreter(interpreter);
}
for (const Interpreter &interpreter : toRemove)
removeKitsForInterpreter(interpreter);
settingsInstance->m_interpreters = interpreters; settingsInstance->m_interpreters = interpreters;
settingsInstance->m_defaultInterpreterId = defaultId; settingsInstance->m_defaultInterpreterId = defaultId;
saveSettings(); saveSettings();
@@ -833,6 +907,7 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau
if (isDefault) if (isDefault)
settingsInstance->m_defaultInterpreterId = interpreter.id; settingsInstance->m_defaultInterpreterId = interpreter.id;
saveSettings(); saveSettings();
addKitsForInterpreter(interpreter);
} }
Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath, Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath,
@@ -981,7 +1056,24 @@ void PythonSettings::initFromSettings(QtcSettings *settings)
|| interpreter.command.isExecutableFile(); || interpreter.command.isExecutableFile();
}; };
m_interpreters = Utils::filtered(m_interpreters, keepInterpreter); const auto [valid, outdatedInterpreters] = Utils::partition(m_interpreters, keepInterpreter);
m_interpreters = valid;
if (!settings->value(kitsGeneratedKey, false).toBool()) {
for (const Interpreter &interpreter : m_interpreters) {
if (interpreter.autoDetected) {
const FilePath &cmd = interpreter.command;
if (cmd.needsDevice() || cmd.parentDir().pathAppended("activate").exists())
continue;
}
addKitsForInterpreter(interpreter);
}
} else {
fixupPythonKits();
}
for (const Interpreter &outdated : outdatedInterpreters)
removeKitsForInterpreter(outdated);
m_defaultInterpreterId = settings->value(defaultKey).toString(); m_defaultInterpreterId = settings->value(defaultKey).toString();
@@ -1014,6 +1106,7 @@ void PythonSettings::writeToSettings(QtcSettings *settings)
settings->setValue(defaultKey, m_defaultInterpreterId); settings->setValue(defaultKey, m_defaultInterpreterId);
settings->setValue(pylsConfigurationKey, m_pylsConfiguration); settings->setValue(pylsConfigurationKey, m_pylsConfiguration);
settings->setValue(pylsEnabledKey, m_pylsEnabled); settings->setValue(pylsEnabledKey, m_pylsEnabled);
settings->setValue(kitsGeneratedKey, true);
settings->endGroup(); settings->endGroup();
} }
@@ -1056,6 +1149,22 @@ void PythonSettings::listDetectedPython(const QString &detectionSource, QString
logMessage->append(interpreter.name + '\n'); logMessage->append(interpreter.name + '\n');
} }
void PythonSettings::fixupPythonKits()
{
if (!KitManager::isLoaded()) {
connect(KitManager::instance(),
&KitManager::kitsLoaded,
settingsInstance,
&PythonSettings::fixupPythonKits,
Qt::UniqueConnection);
return;
}
for (const Interpreter &interpreter : m_interpreters) {
if (auto k = KitManager::kit(Id::fromString(interpreter.id)))
setRelevantAspectsToKit(k);
}
}
void PythonSettings::saveSettings() void PythonSettings::saveSettings()
{ {
QTC_ASSERT(settingsInstance, return); QTC_ASSERT(settingsInstance, return);

View File

@@ -42,6 +42,8 @@ public:
const std::function<void(std::optional<Interpreter>)> &callback, const std::function<void(std::optional<Interpreter>)> &callback,
const QString &nameSuffix = {}); const QString &nameSuffix = {});
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path); static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
static void addKitsForInterpreter(const Interpreter &interpreter);
static void removeKitsForInterpreter(const Interpreter &interpreter);
signals: signals:
void interpretersChanged(const QList<Interpreter> &interpreters, const QString &defaultId); void interpretersChanged(const QList<Interpreter> &interpreters, const QString &defaultId);
@@ -57,6 +59,7 @@ public slots:
void listDetectedPython(const QString &detectionSource, QString *logMessage); void listDetectedPython(const QString &detectionSource, QString *logMessage);
private: private:
void fixupPythonKits();
void initFromSettings(Utils::QtcSettings *settings); void initFromSettings(Utils::QtcSettings *settings);
void writeToSettings(Utils::QtcSettings *settings); void writeToSettings(Utils::QtcSettings *settings);