From 3e6c8a05ddfbdc903de06470156270a5c7d6a7b8 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Tue, 19 Jun 2018 20:16:15 +0800 Subject: [PATCH] Reduce queries to python version Instead of doing at least 2 queries for python infos, now requires only 1 query (in ideal condition), and the result is cached. --- src/base/search/searchdownloadhandler.cpp | 2 +- src/base/search/searchhandler.cpp | 2 +- src/base/search/searchpluginmanager.cpp | 8 +- src/base/utils/foreignapps.cpp | 133 ++++++++-------------- src/base/utils/foreignapps.h | 17 ++- src/base/utils/version.h | 7 +- src/gui/mainwindow.cpp | 47 ++++---- src/gui/search/searchwidget.cpp | 2 +- 8 files changed, 97 insertions(+), 121 deletions(-) diff --git a/src/base/search/searchdownloadhandler.cpp b/src/base/search/searchdownloadhandler.cpp index 33f267db2..6dfdb75f8 100644 --- a/src/base/search/searchdownloadhandler.cpp +++ b/src/base/search/searchdownloadhandler.cpp @@ -48,7 +48,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QStri url }; // Launch search - m_downloadProcess->start(Utils::ForeignApps::Python::pythonExecutable(), params, QIODevice::ReadOnly); + m_downloadProcess->start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly); } void SearchDownloadHandler::downloadProcessFinished(int exitcode) diff --git a/src/base/search/searchhandler.cpp b/src/base/search/searchhandler.cpp index d1fb72157..8547087be 100644 --- a/src/base/search/searchhandler.cpp +++ b/src/base/search/searchhandler.cpp @@ -70,7 +70,7 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co }; // Launch search - m_searchProcess->setProgram(Utils::ForeignApps::Python::pythonExecutable()); + m_searchProcess->setProgram(Utils::ForeignApps::pythonInfo().executableName); m_searchProcess->setArguments(params + m_pattern.split(" ")); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) diff --git a/src/base/search/searchpluginmanager.cpp b/src/base/search/searchpluginmanager.cpp index 448793245..892b3186a 100644 --- a/src/base/search/searchpluginmanager.cpp +++ b/src/base/search/searchpluginmanager.cpp @@ -64,7 +64,7 @@ namespace QPointer SearchPluginManager::m_instance = nullptr; SearchPluginManager::SearchPluginManager() - : m_updateUrl(QString("http://searchplugins.qbittorrent.org/%1/engines/").arg(Utils::ForeignApps::Python::pythonVersion() >= 3 ? "nova3" : "nova")) + : m_updateUrl(QString("http://searchplugins.qbittorrent.org/%1/engines/").arg(Utils::ForeignApps::pythonInfo().version.majorNumber() >= 3 ? "nova3" : "nova")) { Q_ASSERT(!m_instance); // only one instance is allowed m_instance = this; @@ -323,7 +323,7 @@ QString SearchPluginManager::pluginsLocation() QString SearchPluginManager::engineLocation() { QString folder = "nova"; - if (Utils::ForeignApps::Python::pythonVersion() >= 3) + if (Utils::ForeignApps::pythonInfo().version.majorNumber() >= 3) folder = "nova3"; const QString location = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + folder); QDir locationDir(location); @@ -371,7 +371,7 @@ void SearchPluginManager::updateNova() // create nova directory if necessary QDir searchDir(engineLocation()); - QString novaFolder = Utils::ForeignApps::Python::pythonVersion() >= 3 ? "searchengine/nova3" : "searchengine/nova"; + QString novaFolder = Utils::ForeignApps::pythonInfo().version.majorNumber() >= 3 ? "searchengine/nova3" : "searchengine/nova"; QFile packageFile(searchDir.absoluteFilePath("__init__.py")); packageFile.open(QIODevice::WriteOnly | QIODevice::Text); packageFile.close(); @@ -437,7 +437,7 @@ void SearchPluginManager::update() QStringList params; params << Utils::Fs::toNativePath(engineLocation() + "/nova2.py"); params << "--capabilities"; - nova.start(Utils::ForeignApps::Python::pythonExecutable(), params, QIODevice::ReadOnly); + nova.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly); nova.waitForStarted(); nova.waitForFinished(); diff --git a/src/base/utils/foreignapps.cpp b/src/base/utils/foreignapps.cpp index 6a26197b9..af45243e1 100644 --- a/src/base/utils/foreignapps.cpp +++ b/src/base/utils/foreignapps.cpp @@ -35,99 +35,66 @@ #include "base/logger.h" -/** - * Detects the python version. - */ -int Utils::ForeignApps::Python::pythonVersion() +using namespace Utils::ForeignApps; + +namespace { - static int version = -1; - if (version < 0) { - QString versionComplete = pythonVersionComplete().trimmed(); - QStringList splitted = versionComplete.split('.'); - if (splitted.size() > 1) { - int highVer = splitted.at(0).toInt(); - if ((highVer == 2) || (highVer == 3)) - version = highVer; + bool testPythonInstallation(const QString &exeName, PythonInfo &info) + { + QProcess proc; + proc.start(exeName, {"--version"}, QIODevice::ReadOnly); + if (proc.waitForFinished() && (proc.exitCode() == QProcess::NormalExit)) { + QByteArray procOutput = proc.readAllStandardOutput(); + if (procOutput.isEmpty()) + procOutput = proc.readAllStandardError(); + procOutput = procOutput.simplified(); + + // Software 'Anaconda' installs its own python interpreter + // and `python --version` returns a string like this: + // "Python 3.4.3 :: Anaconda 2.3.0 (64-bit)" + const QList outputSplit = procOutput.split(' '); + if (outputSplit.size() <= 1) + return false; + + try { + info = {exeName, outputSplit[1]}; + } + catch (const std::runtime_error &err) { + return false; + } + + LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python detected, version: %1").arg(info.version), Log::INFO); + return true; } + + return false; } - return version; } -/** - * Detects the python executable by calling "python --version". - */ -QString Utils::ForeignApps::Python::pythonExecutable() +bool Utils::ForeignApps::PythonInfo::isValid() const +{ + return (!executableName.isEmpty() && version.isValid()); +} + +PythonInfo Utils::ForeignApps::pythonInfo() { - static QString executable; - if (executable.isEmpty()) { - QProcess pythonProc; + static PythonInfo pyInfo; + if (!pyInfo.isValid()) { #if defined(Q_OS_UNIX) - /* - * On Unix-Like Systems python2 and python3 should always exist - * http://legacy.python.org/dev/peps/pep-0394/ - */ - pythonProc.start("python3", {"--version"}, QIODevice::ReadOnly); - if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) { - executable = "python3"; - return executable; - } - pythonProc.start("python2", {"--version"}, QIODevice::ReadOnly); - if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) { - executable = "python2"; - return executable; - } + // On Unix-Like Systems python2 and python3 should always exist + // https://legacy.python.org/dev/peps/pep-0394/ + if (testPythonInstallation("python3", pyInfo)) + return pyInfo; + if (testPythonInstallation("python2", pyInfo)) + return pyInfo; #endif // Look for "python" in Windows and in UNIX if "python2" and "python3" are // not detected. - pythonProc.start("python", {"--version"}, QIODevice::ReadOnly); - if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) - executable = "python"; - else - Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python not detected"), Log::INFO); - } - return executable; -} - -/** - * Returns the complete python version - * eg 2.7.9 - * Make sure to have setup python first - */ -QString Utils::ForeignApps::Python::pythonVersionComplete() -{ - static QString version; - if (version.isEmpty()) { - if (pythonExecutable().isEmpty()) - return version; - QProcess pythonProc; - pythonProc.start(pythonExecutable(), {"--version"}, QIODevice::ReadOnly); - if (pythonProc.waitForFinished() && (pythonProc.exitCode() == 0)) { - QByteArray output = pythonProc.readAllStandardOutput(); - if (output.isEmpty()) - output = pythonProc.readAllStandardError(); + if (testPythonInstallation("python", pyInfo)) + return pyInfo; - // Software 'Anaconda' installs its own python interpreter - // and `python --version` returns a string like this: - // `Python 3.4.3 :: Anaconda 2.3.0 (64-bit)` - const QList outSplit = output.split(' '); - if (outSplit.size() > 1) { - version = outSplit.at(1).trimmed(); - Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python version: %1").arg(version), Log::INFO); - } - - // If python doesn't report a 3-piece version e.g. 3.6.1 - // then fill the missing pieces with zero - const QStringList verSplit = version.split('.', QString::SkipEmptyParts); - if (verSplit.size() < 3) { - for (int i = verSplit.size(); i < 3; ++i) { - if (version.endsWith('.')) - version.append('0'); - else - version.append(".0"); - } - Logger::instance()->addMessage(QCoreApplication::translate("misc", "Normalized Python version: %1").arg(version), Log::INFO); - } - } + LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python not detected"), Log::INFO); } - return version; + + return pyInfo; } diff --git a/src/base/utils/foreignapps.h b/src/base/utils/foreignapps.h index 40c2eeddd..ca427c4a6 100644 --- a/src/base/utils/foreignapps.h +++ b/src/base/utils/foreignapps.h @@ -31,15 +31,22 @@ #include +#include "base/utils/version.h" + namespace Utils { namespace ForeignApps { - namespace Python + struct PythonInfo { - int pythonVersion(); - QString pythonExecutable(); - QString pythonVersionComplete(); - } + using Version = Utils::Version; + + bool isValid() const; + + QString executableName; + Version version; + }; + + PythonInfo pythonInfo(); } } diff --git a/src/base/utils/version.h b/src/base/utils/version.h index 19213cb35..71b797eb4 100644 --- a/src/base/utils/version.h +++ b/src/base/utils/version.h @@ -58,7 +58,7 @@ namespace Utils template constexpr Version(Other ... components) - : m_components {{components ...}} + : m_components {{static_cast(components) ...}} { } @@ -129,6 +129,11 @@ namespace Utils return res; } + constexpr bool isValid() const + { + return (*this != ThisType {}); + } + constexpr bool operator==(const ThisType &other) const { return (m_components == other.m_components); diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 9bd284725..dbe5a85ef 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1743,45 +1743,42 @@ void MainWindow::on_actionRSSReader_triggered() void MainWindow::on_actionSearchWidget_triggered() { if (!m_hasPython && m_ui->actionSearchWidget->isChecked()) { - int pythonVersion = Utils::ForeignApps::Python::pythonVersion(); + int majorVersion = Utils::ForeignApps::pythonInfo().version.majorNumber(); // Check if python is already in PATH - if (pythonVersion > 0) + if (majorVersion > 0) { // Prevent translators from messing with PATH Logger::instance()->addMessage(tr("Python found in %1: %2", "Python found in PATH: /usr/local/bin:/usr/bin:/etc/bin") .arg("PATH", qgetenv("PATH").constData()), Log::INFO); + } #ifdef Q_OS_WIN - else if (addPythonPathToEnv()) - pythonVersion = Utils::ForeignApps::Python::pythonVersion(); + else if (addPythonPathToEnv()) { + majorVersion = Utils::ForeignApps::pythonInfo().version.majorNumber(); + } #endif + else { + QMessageBox::information(this, tr("Undetermined Python version"), tr("Couldn't determine your Python version. Search engine disabled.")); + m_ui->actionSearchWidget->setChecked(false); + Preferences::instance()->setSearchEnabled(false); + return; + } bool res = false; - if ((pythonVersion == 2) || (pythonVersion == 3)) { + if ((majorVersion == 2) || (majorVersion == 3)) { // Check Python minimum requirement: 2.7.9 / 3.3.0 - QString version = Utils::ForeignApps::Python::pythonVersionComplete(); - QStringList splitted = version.split('.'); - if (splitted.size() > 2) { - int middleVer = splitted.at(1).toInt(); - int lowerVer = splitted.at(2).toInt(); - if (((pythonVersion == 2) && (middleVer < 7)) - || ((pythonVersion == 2) && (middleVer == 7) && (lowerVer < 9)) - || ((pythonVersion == 3) && (middleVer < 3))) { - QMessageBox::information(this, tr("Old Python Interpreter"), tr("Your Python version (%1) is outdated. Please upgrade to latest version for search engines to work.\nMinimum requirement: 2.7.9 / 3.3.0.").arg(version)); - m_ui->actionSearchWidget->setChecked(false); - Preferences::instance()->setSearchEnabled(false); - return; - } - else { - res = true; - } - } - else { - QMessageBox::information(this, tr("Undetermined Python version"), tr("Couldn't determine your Python version (%1). Search engine disabled.").arg(version)); + using Version = Utils::ForeignApps::PythonInfo::Version; + const Version pyVersion = Utils::ForeignApps::pythonInfo().version; + + if (((majorVersion == 2) && (pyVersion < Version {2, 7, 9})) + || ((majorVersion == 3) && (pyVersion < Version {3, 3, 0}))) { + QMessageBox::information(this, tr("Old Python Interpreter"), tr("Your Python version (%1) is outdated. Please upgrade to latest version for search engines to work.\nMinimum requirement: 2.7.9 / 3.3.0.").arg(pyVersion)); m_ui->actionSearchWidget->setChecked(false); Preferences::instance()->setSearchEnabled(false); return; } + + res = true; } if (res) { @@ -2086,7 +2083,7 @@ void MainWindow::pythonDownloadSuccess(const QString &url, const QString &filePa m_hasPython = addPythonPathToEnv(); if (m_hasPython) { // Make it print the version to Log - Utils::ForeignApps::Python::pythonVersion(); + Utils::ForeignApps::pythonInfo(); m_ui->actionSearchWidget->setChecked(true); displaySearchTab(true); } diff --git a/src/gui/search/searchwidget.cpp b/src/gui/search/searchwidget.cpp index 45894debd..ccff90752 100644 --- a/src/gui/search/searchwidget.cpp +++ b/src/gui/search/searchwidget.cpp @@ -285,7 +285,7 @@ void SearchWidget::giveFocusToSearchInput() // Function called when we click on search button void SearchWidget::on_searchButton_clicked() { - if (Utils::ForeignApps::Python::pythonVersion() < 0) { + if (Utils::ForeignApps::pythonInfo().version.majorNumber() <= 0) { m_mainWindow->showNotificationBaloon(tr("Search Engine"), tr("Please install Python to use the Search Engine.")); return; }