Python: detect pythons async

Change-Id: I74484a4f2c33c4fd7754f87bfbf3b9d711542741
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-09-20 14:28:21 +02:00
parent 4e90ca1b59
commit 2c298097e9

View File

@@ -21,13 +21,14 @@
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/async.h>
#include <utils/detailswidget.h> #include <utils/detailswidget.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/listmodel.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/listmodel.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/process.h> #include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h> #include <utils/treemodel.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
@@ -366,14 +367,6 @@ private:
InterpreterOptionsWidget *m_widget = nullptr; InterpreterOptionsWidget *m_widget = nullptr;
}; };
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
{
return Utils::anyOf(pythons, [pythonExecutable](const Interpreter &interpreter) {
return interpreter.command.toFileInfo().canonicalFilePath()
== pythonExecutable.toFileInfo().canonicalFilePath();
});
}
static InterpreterOptionsPage &interpreterOptionsPage() static InterpreterOptionsPage &interpreterOptionsPage()
{ {
static InterpreterOptionsPage page; static InterpreterOptionsPage page;
@@ -626,8 +619,9 @@ static void disableOutdatedPyls()
} }
} }
static void addPythonsFromRegistry(QList<Interpreter> &pythons) static QList<Interpreter> pythonsFromRegistry()
{ {
QList<Interpreter> pythons;
QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore",
QSettings::NativeFormat); QSettings::NativeFormat);
for (const QString &versionGroup : pythonRegistry.childGroups()) { for (const QString &versionGroup : pythonRegistry.childGroups()) {
@@ -636,7 +630,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath"); QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath");
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &executable = FilePath::fromUserInput(regVal.toString()); const FilePath &executable = FilePath::fromUserInput(regVal.toString());
if (executable.exists() && !alreadyRegistered(pythons, executable)) { if (executable.exists()) {
pythons << Interpreter{QUuid::createUuid().toString(), pythons << Interpreter{QUuid::createUuid().toString(),
name, name,
FilePath::fromUserInput(regVal.toString())}; FilePath::fromUserInput(regVal.toString())};
@@ -645,7 +639,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath"); regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath");
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &executable = FilePath::fromUserInput(regVal.toString()); const FilePath &executable = FilePath::fromUserInput(regVal.toString());
if (executable.exists() && !alreadyRegistered(pythons, executable)) { if (executable.exists()) {
pythons << Interpreter{QUuid::createUuid().toString(), pythons << Interpreter{QUuid::createUuid().toString(),
//: <python display name> (Windowed) //: <python display name> (Windowed)
Tr::tr("%1 (Windowed)").arg(name), Tr::tr("%1 (Windowed)").arg(name),
@@ -656,28 +650,30 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath &path = FilePath::fromUserInput(regVal.toString());
const FilePath python = path.pathAppended("python").withExecutableSuffix(); const FilePath python = path.pathAppended("python").withExecutableSuffix();
if (python.exists() && !alreadyRegistered(pythons, python)) if (python.exists())
pythons << createInterpreter(python, "Python " + versionGroup); pythons << createInterpreter(python, "Python " + versionGroup);
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) if (pythonw.exists())
pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)"); pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
} }
pythonRegistry.endGroup(); pythonRegistry.endGroup();
} }
return pythons;
} }
static void addPythonsFromPath(QList<Interpreter> &pythons) static QList<Interpreter> pythonsFromPath()
{ {
QList<Interpreter> pythons;
if (HostOsInfo::isWindowsHost()) { if (HostOsInfo::isWindowsHost()) {
for (const FilePath &executable : FilePath("python").searchAllInPath()) { for (const FilePath &executable : FilePath("python").searchAllInPath()) {
// Windows creates empty redirector files that may interfere // Windows creates empty redirector files that may interfere
if (executable.toFileInfo().size() == 0) if (executable.toFileInfo().size() == 0)
continue; continue;
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path"); pythons << createInterpreter(executable, "Python from Path");
} }
for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) { for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path", "(Windowed)"); pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
} }
} else { } else {
@@ -690,11 +686,12 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
const QDir dir(path.toString()); const QDir dir(path.toString());
for (const QFileInfo &fi : dir.entryInfoList(filters)) { for (const QFileInfo &fi : dir.entryInfoList(filters)) {
const FilePath executable = Utils::FilePath::fromFileInfo(fi); const FilePath executable = Utils::FilePath::fromFileInfo(fi);
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path"); pythons << createInterpreter(executable, "Python from Path");
} }
} }
} }
return pythons;
} }
static QString idForPythonFromPath(const QList<Interpreter> &pythons) static QString idForPythonFromPath(const QList<Interpreter> &pythons)
@@ -713,6 +710,51 @@ static QString idForPythonFromPath(const QList<Interpreter> &pythons)
static PythonSettings *settingsInstance = nullptr; static PythonSettings *settingsInstance = nullptr;
static bool alreadyRegistered(const Interpreter &candidate)
{
return Utils::anyOf(settingsInstance->interpreters(),
[candidate = candidate.command](const Interpreter &interpreter) {
return interpreter.command.isSameDevice(candidate)
&& interpreter.command.resolveSymlinks()
== candidate.resolveSymlinks();
});
}
static void scanPath()
{
auto watcher = new QFutureWatcher<QList<Interpreter>>();
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
for (const Interpreter &interpreter : watcher->result()) {
if (!alreadyRegistered(interpreter))
settingsInstance->addInterpreter(interpreter);
}
watcher->deleteLater();
});
watcher->setFuture(Utils::asyncRun(pythonsFromPath));
}
static void scanRegistry()
{
auto watcher = new QFutureWatcher<QList<Interpreter>>();
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
for (const Interpreter &interpreter : watcher->result()) {
if (!alreadyRegistered(interpreter))
settingsInstance->addInterpreter(interpreter);
}
watcher->deleteLater();
scanPath();
});
watcher->setFuture(Utils::asyncRun(pythonsFromRegistry));
}
static void scanSystemForInterpreters()
{
if (Utils::HostOsInfo::isWindowsHost())
scanRegistry();
else
scanPath();
}
PythonSettings::PythonSettings() PythonSettings::PythonSettings()
{ {
QTC_ASSERT(!settingsInstance, return); QTC_ASSERT(!settingsInstance, return);
@@ -723,9 +765,7 @@ PythonSettings::PythonSettings()
initFromSettings(Core::ICore::settings()); initFromSettings(Core::ICore::settings());
if (HostOsInfo::isWindowsHost()) scanSystemForInterpreters();
addPythonsFromRegistry(m_interpreters);
addPythonsFromPath(m_interpreters);
if (m_defaultInterpreterId.isEmpty()) if (m_defaultInterpreterId.isEmpty())
m_defaultInterpreterId = idForPythonFromPath(m_interpreters); m_defaultInterpreterId = idForPythonFromPath(m_interpreters);