diff options
-rw-r--r-- | src/plugins/python/pipsupport.cpp | 83 | ||||
-rw-r--r-- | src/plugins/python/pipsupport.h | 44 | ||||
-rw-r--r-- | src/plugins/python/pythonrunconfiguration.cpp | 100 | ||||
-rw-r--r-- | src/plugins/python/pythonrunconfiguration.h | 9 |
4 files changed, 161 insertions, 75 deletions
diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp index 8c98bd423d..848184b462 100644 --- a/src/plugins/python/pipsupport.cpp +++ b/src/plugins/python/pipsupport.cpp @@ -3,6 +3,7 @@ #include "pipsupport.h" +#include "pythonplugin.h" #include "pythontr.h" #include <coreplugin/messagemanager.h> @@ -15,6 +16,7 @@ #include <utils/algorithm.h> #include <utils/mimeutils.h> #include <utils/qtcprocess.h> +#include <utils/runextensions.h> using namespace Utils; @@ -100,36 +102,6 @@ void PipInstallTask::handleError() Core::MessageManager::writeSilently(stdErr); } -PipPackageInfo PipPackage::info(const FilePath &python) const -{ - PipPackageInfo result; - - QtcProcess pip; - pip.setCommand(CommandLine(python, {"-m", "pip", "show", "-f", packageName})); - pip.runBlocking(); - QString fieldName; - QStringList data; - const QString pipOutput = pip.allOutput(); - for (const QString &line : pipOutput.split('\n')) { - if (line.isEmpty()) - continue; - if (line.front().isSpace()) { - data.append(line.trimmed()); - } else { - result.parseField(fieldName, data); - if (auto colonPos = line.indexOf(':'); colonPos >= 0) { - fieldName = line.left(colonPos); - data = QStringList(line.mid(colonPos + 1).trimmed()); - } else { - fieldName.clear(); - data.clear(); - } - } - } - result.parseField(fieldName, data); - return result; -} - void PipPackageInfo::parseField(const QString &field, const QStringList &data) { if (field.isEmpty()) @@ -162,4 +134,55 @@ void PipPackageInfo::parseField(const QString &field, const QStringList &data) } } +Pip *Pip::instance(const FilePath &python) +{ + static QMap<FilePath, Pip *> pips; + auto it = pips.find(python); + if (it == pips.end()) + it = pips.insert(python, new Pip(python)); + return it.value(); +} + +QFuture<PipPackageInfo> Pip::info(const PipPackage &package) +{ + return Utils::runAsync(&Pip::infoImpl, this, package); +} + +PipPackageInfo Pip::infoImpl(const PipPackage &package) +{ + PipPackageInfo result; + + QtcProcess pip; + pip.setCommand(CommandLine(m_python, {"-m", "pip", "show", "-f", package.packageName})); + m_lock.lock(); + pip.runBlocking(); + m_lock.unlock(); + QString fieldName; + QStringList data; + const QString pipOutput = pip.allOutput(); + for (const QString &line : pipOutput.split('\n')) { + if (line.isEmpty()) + continue; + if (line.front().isSpace()) { + data.append(line.trimmed()); + } else { + result.parseField(fieldName, data); + if (auto colonPos = line.indexOf(':'); colonPos >= 0) { + fieldName = line.left(colonPos); + data = QStringList(line.mid(colonPos + 1).trimmed()); + } else { + fieldName.clear(); + data.clear(); + } + } + } + result.parseField(fieldName, data); + return result; +} + +Pip::Pip(const Utils::FilePath &python) + : QObject(PythonPlugin::instance()) + , m_python(python) +{} + } // Python::Internal diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h index a26360392d..e5f53768ce 100644 --- a/src/plugins/python/pipsupport.h +++ b/src/plugins/python/pipsupport.h @@ -12,7 +12,23 @@ namespace Python::Internal { -class PipPackageInfo; +class PipPackageInfo +{ +public: + QString name; + QString version; + QString summary; + QUrl homePage; + QString author; + QString authorEmail; + QString license; + Utils::FilePath location; + QStringList requiresPackage; + QStringList requiredByPackage; + Utils::FilePaths files; + + void parseField(const QString &field, const QStringList &value); +}; class PipPackage { @@ -27,26 +43,22 @@ public: QString packageName; QString displayName; QString version; - - PipPackageInfo info(const Utils::FilePath &python) const; }; -class PipPackageInfo +class Pip : public QObject { public: - QString name; - QString version; - QString summary; - QUrl homePage; - QString author; - QString authorEmail; - QString license; - Utils::FilePath location; - QStringList requiresPackage; - QStringList requiredByPackage; - Utils::FilePaths files; + static Pip *instance(const Utils::FilePath &python); - void parseField(const QString &field, const QStringList &value); + QFuture<PipPackageInfo> info(const PipPackage &package); + +private: + Pip(const Utils::FilePath &python); + + PipPackageInfo infoImpl(const PipPackage &package); + + QMutex m_lock; + Utils::FilePath m_python; }; class PipInstallTask : public QObject diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 480f3f153f..2163e2b7ed 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -117,8 +117,35 @@ private: //////////////////////////////////////////////////////////////// +class PythonRunConfigurationPrivate +{ +public: + PythonRunConfigurationPrivate(PythonRunConfiguration *rc) + : q(rc) + {} + ~PythonRunConfigurationPrivate() + { + qDeleteAll(m_extraCompilers); + } + + void checkForPySide(const Utils::FilePath &python); + void checkForPySide(const Utils::FilePath &python, const QString &pySidePackageName); + void handlePySidePackageInfo(const PipPackageInfo &pySideInfo, + const Utils::FilePath &python, + const QString &requestedPackageName); + void updateExtraCompilers(); + Utils::FilePath m_pySideUicPath; + + PythonRunConfiguration *q; + QList<PySideUicExtraCompiler *> m_extraCompilers; + QFutureWatcher<PipPackageInfo> m_watcher; + QMetaObject::Connection m_watcherConnection; + +}; + PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) + , d(new PythonRunConfigurationPrivate(this)) { auto interpreterAspect = addAspect<InterpreterAspect>(); interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter"); @@ -185,7 +212,7 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - connect(target, &Target::buildSystemUpdated, this, &PythonRunConfiguration::updateExtraCompilers); + connect(target, &Target::buildSystemUpdated, this, [this]() { d->updateExtraCompilers(); }); currentInterpreterChanged(); setRunnableModifier([](Runnable &r) { @@ -195,25 +222,53 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this, [this](const FilePath &python) { if (python == aspect<InterpreterAspect>()->currentInterpreter().command) - checkForPySide(python); + d->checkForPySide(python); }); } PythonRunConfiguration::~PythonRunConfiguration() { - qDeleteAll(m_extraCompilers); + delete d; } -struct PythonTools +void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python) { - FilePath pySideProjectPath; - FilePath pySideUicPath; -}; + checkForPySide(python, "PySide6-Essentials"); +} + +void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python, + const QString &pySidePackageName) +{ + const PipPackage package(pySidePackageName); + QObject::disconnect(m_watcherConnection); + m_watcherConnection = QObject::connect(&m_watcher, + &QFutureWatcher<PipPackageInfo>::finished, + q, + [=]() { + handlePySidePackageInfo(m_watcher.result(), + python, + pySidePackageName); + }); + m_watcher.setFuture(Pip::instance(python)->info(package)); +} -void PythonRunConfiguration::checkForPySide(const FilePath &python) +void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo, + const Utils::FilePath &python, + const QString &requestedPackageName) { - BuildStepList *buildSteps = target()->activeBuildConfiguration()->buildSteps(); + struct PythonTools + { + FilePath pySideProjectPath; + FilePath pySideUicPath; + }; + BuildStepList *buildSteps = nullptr; + if (Target *target = q->target()) { + if (auto buildConfiguration = target->activeBuildConfiguration()) + buildSteps = buildConfiguration->buildSteps(); + } + if (!buildSteps) + return; const auto findPythonTools = [](const FilePaths &files, const FilePath &location, @@ -239,13 +294,10 @@ void PythonRunConfiguration::checkForPySide(const FilePath &python) return {}; }; - const PipPackage pySide6EssentialPackage("PySide6-Essentials"); - PipPackageInfo info = pySide6EssentialPackage.info(python); - PythonTools pythonTools = findPythonTools(info.files, info.location, python); - if (!pythonTools.pySideProjectPath.isExecutableFile()) { - const PipPackage pySide6Package("PySide6"); - info = pySide6Package.info(python); - pythonTools = findPythonTools(info.files, info.location, python); + PythonTools pythonTools = findPythonTools(pySideInfo.files, pySideInfo.location, python); + if (!pythonTools.pySideProjectPath.isExecutableFile() && requestedPackageName != "PySide6") { + checkForPySide(python, "PySide6"); + return; } m_pySideUicPath = pythonTools.pySideUicPath; @@ -259,7 +311,7 @@ void PythonRunConfiguration::checkForPySide(const FilePath &python) void PythonRunConfiguration::currentInterpreterChanged() { const FilePath python = aspect<InterpreterAspect>()->currentInterpreter().command; - checkForPySide(python); + d->checkForPySide(python); for (FilePath &file : project()->files(Project::AllFiles)) { if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { @@ -274,10 +326,10 @@ void PythonRunConfiguration::currentInterpreterChanged() QList<PySideUicExtraCompiler *> PythonRunConfiguration::extraCompilers() const { - return m_extraCompilers; + return d->m_extraCompilers; } -void PythonRunConfiguration::updateExtraCompilers() +void PythonRunConfigurationPrivate::updateExtraCompilers() { QList<PySideUicExtraCompiler *> oldCompilers = m_extraCompilers; m_extraCompilers.clear(); @@ -288,21 +340,21 @@ void PythonRunConfiguration::updateExtraCompilers() return fileNode->fileType() == ProjectExplorer::FileType::Form; return false; }; - const FilePaths uiFiles = project()->files(uiMatcher); + const FilePaths uiFiles = q->project()->files(uiMatcher); for (const FilePath &uiFile : uiFiles) { FilePath generated = uiFile.parentDir(); generated = generated.pathAppended("/ui_" + uiFile.baseName() + ".py"); int index = Utils::indexOf(oldCompilers, [&](PySideUicExtraCompiler *oldCompiler) { return oldCompiler->pySideUicPath() == m_pySideUicPath - && oldCompiler->project() == project() && oldCompiler->source() == uiFile + && oldCompiler->project() == q->project() && oldCompiler->source() == uiFile && oldCompiler->targets() == FilePaths{generated}; }); if (index < 0) { m_extraCompilers << new PySideUicExtraCompiler(m_pySideUicPath, - project(), + q->project(), uiFile, {generated}, - this); + q); } else { m_extraCompilers << oldCompilers.takeAt(index); } @@ -310,7 +362,7 @@ void PythonRunConfiguration::updateExtraCompilers() } for (LanguageClient::Client *client : LanguageClient::LanguageClientManager::clients()) { if (auto pylsClient = qobject_cast<PyLSClient *>(client)) - pylsClient->updateExtraCompilers(project(), m_extraCompilers); + pylsClient->updateExtraCompilers(q->project(), m_extraCompilers); } qDeleteAll(oldCompilers); } diff --git a/src/plugins/python/pythonrunconfiguration.h b/src/plugins/python/pythonrunconfiguration.h index 0def9af0a2..605e3c42aa 100644 --- a/src/plugins/python/pythonrunconfiguration.h +++ b/src/plugins/python/pythonrunconfiguration.h @@ -6,8 +6,11 @@ #include <projectexplorer/runconfiguration.h> #include <projectexplorer/runcontrol.h> +#include <QFutureWatcher> + namespace Python::Internal { +class PythonRunConfigurationPrivate; class PySideUicExtraCompiler; class PythonRunConfiguration : public ProjectExplorer::RunConfiguration @@ -20,11 +23,7 @@ public: QList<PySideUicExtraCompiler *> extraCompilers() const; private: - void checkForPySide(const Utils::FilePath &python); - void updateExtraCompilers(); - Utils::FilePath m_pySideUicPath; - - QList<PySideUicExtraCompiler *> m_extraCompilers; + PythonRunConfigurationPrivate *d = nullptr; }; class PythonRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory |