diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index 020aad52259..4aa8ef6181f 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -21,13 +21,14 @@ #include #include -#include +#include #include #include -#include #include +#include #include #include +#include #include #include @@ -366,14 +367,6 @@ private: InterpreterOptionsWidget *m_widget = nullptr; }; -static bool alreadyRegistered(const QList &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 page; @@ -626,8 +619,9 @@ static void disableOutdatedPyls() } } -static void addPythonsFromRegistry(QList &pythons) +static QList pythonsFromRegistry() { + QList pythons; QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", QSettings::NativeFormat); for (const QString &versionGroup : pythonRegistry.childGroups()) { @@ -636,7 +630,7 @@ static void addPythonsFromRegistry(QList &pythons) QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath"); if (regVal.isValid()) { const FilePath &executable = FilePath::fromUserInput(regVal.toString()); - if (executable.exists() && !alreadyRegistered(pythons, executable)) { + if (executable.exists()) { pythons << Interpreter{QUuid::createUuid().toString(), name, FilePath::fromUserInput(regVal.toString())}; @@ -645,7 +639,7 @@ static void addPythonsFromRegistry(QList &pythons) regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath"); if (regVal.isValid()) { const FilePath &executable = FilePath::fromUserInput(regVal.toString()); - if (executable.exists() && !alreadyRegistered(pythons, executable)) { + if (executable.exists()) { pythons << Interpreter{QUuid::createUuid().toString(), //: (Windowed) Tr::tr("%1 (Windowed)").arg(name), @@ -656,28 +650,30 @@ static void addPythonsFromRegistry(QList &pythons) if (regVal.isValid()) { const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath python = path.pathAppended("python").withExecutableSuffix(); - if (python.exists() && !alreadyRegistered(pythons, python)) + if (python.exists()) pythons << createInterpreter(python, "Python " + versionGroup); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); - if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) + if (pythonw.exists()) pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)"); } pythonRegistry.endGroup(); } + return pythons; } -static void addPythonsFromPath(QList &pythons) +static QList pythonsFromPath() { + QList pythons; if (HostOsInfo::isWindowsHost()) { for (const FilePath &executable : FilePath("python").searchAllInPath()) { // Windows creates empty redirector files that may interfere if (executable.toFileInfo().size() == 0) continue; - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path"); } for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) { - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path", "(Windowed)"); } } else { @@ -690,11 +686,12 @@ static void addPythonsFromPath(QList &pythons) const QDir dir(path.toString()); for (const QFileInfo &fi : dir.entryInfoList(filters)) { const FilePath executable = Utils::FilePath::fromFileInfo(fi); - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path"); } } } + return pythons; } static QString idForPythonFromPath(const QList &pythons) @@ -713,6 +710,51 @@ static QString idForPythonFromPath(const QList &pythons) 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>(); + QObject::connect(watcher, &QFutureWatcher>::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>(); + QObject::connect(watcher, &QFutureWatcher>::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() { QTC_ASSERT(!settingsInstance, return); @@ -723,9 +765,7 @@ PythonSettings::PythonSettings() initFromSettings(Core::ICore::settings()); - if (HostOsInfo::isWindowsHost()) - addPythonsFromRegistry(m_interpreters); - addPythonsFromPath(m_interpreters); + scanSystemForInterpreters(); if (m_defaultInterpreterId.isEmpty()) m_defaultInterpreterId = idForPythonFromPath(m_interpreters);