Skip to content

Commit

Permalink
Merge pull request #45886 from qgis/backport-45856-to-release-3_22
Browse files Browse the repository at this point in the history
[Backport release-3_22] [plugins] Add support for experimental dependencies
  • Loading branch information
m-kuhn committed Nov 22, 2021
2 parents 58b0234 + e4a7c95 commit e86e559
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 22 deletions.
8 changes: 4 additions & 4 deletions python/pyplugin_installer/installer.py
Expand Up @@ -682,17 +682,17 @@ def processDependencies(self, plugin_id):
dlg = QgsPluginDependenciesDialog(plugin_id, to_install, to_upgrade, not_found)
if dlg.exec_() == QgsPluginDependenciesDialog.Accepted:
actions = dlg.actions()
for dependency_plugin_id, action in actions.items():
for dependency_plugin_id, action_data in actions.items():
try:
self.installPlugin(dependency_plugin_id)
if action == 'install':
self.installPlugin(dependency_plugin_id, stable=action_data['use_stable_version'])
if action_data['action'] == 'install':
iface.pluginManagerInterface().pushMessage(self.tr("Plugin dependency <b>%s</b> successfully installed") %
dependency_plugin_id, Qgis.Info)
else:
iface.pluginManagerInterface().pushMessage(self.tr("Plugin dependency <b>%s</b> successfully upgraded") %
dependency_plugin_id, Qgis.Info)
except Exception as ex:
if action == 'install':
if action_data['action'] == 'install':
iface.pluginManagerInterface().pushMessage(self.tr("Error installing plugin dependency <b>%s</b>: %s") %
(dependency_plugin_id, ex), Qgis.Warning)
else:
Expand Down
7 changes: 6 additions & 1 deletion python/pyplugin_installer/installer_data.py
Expand Up @@ -50,7 +50,7 @@
mRepositories = dict of dicts: {repoName : {"url" unicode,
"enabled" bool,
"valid" bool,
"Relay" Relay, # Relay object for transmitting signals from QPHttp with adding the repoName information
"Relay" Relay, # Relay object for transmitting signals from QPHttp with adding the repoName information
"Request" QNetworkRequest,
"xmlData" QNetworkReply,
"state" int, (0 - disabled, 1-loading, 2-loaded ok, 3-error (to be retried), 4-rejected)
Expand Down Expand Up @@ -78,14 +78,19 @@
"installed" boolean, # True if installed
"available" boolean, # True if available in repositories
"status" unicode, # ( not installed | new ) | ( installed | upgradeable | orphan | newer )
"status_exp" unicode, # ( not installed | new ) | ( installed | upgradeable | orphan | newer )
"error" unicode, # NULL | broken | incompatible | dependent
"error_details" unicode, # error description
"experimental" boolean, # true if experimental, false if stable
"deprecated" boolean, # true if deprecated, false if actual
"trusted" boolean, # true if trusted, false if not trusted
"version_available" unicode, # available version
"version_available_stable" unicode, # available stable version
"version_available_experimental" unicode, # available experimental version
"zip_repository" unicode, # the remote repository id
"download_url" unicode, # url for downloading the plugin
"download_url_stable" unicode, # url for downloading the plugin's stable version
"download_url_experimental" unicode, # url for downloading the plugin's experimental version
"filename" unicode, # the zip file name to be unzipped after downloaded
"downloads" unicode, # number of downloads
"average_vote" unicode, # average vote
Expand Down
25 changes: 22 additions & 3 deletions python/pyplugin_installer/plugindependencies.py
Expand Up @@ -44,12 +44,13 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
to_upgrade = {}
not_found = {}

if plugin_deps is None:
if plugin_deps is None or installed_plugins is None:
updateAvailablePlugins()

if plugin_deps is None:
plugin_deps = get_plugin_deps(plugin_id)

if installed_plugins is None:
updateAvailablePlugins()
metadata_parser = metadataParser()
installed_plugins = {metadata_parser[k].get('general', 'name'): metadata_parser[k].get('general', 'version') for k, v in metadata_parser.items()}

Expand Down Expand Up @@ -79,8 +80,26 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
"version_installed": installed_plugins.get(name, None),
"version_required": version_required,
"version_available": plugin_data[p_id].get('version_available', None),
"use_stable_version": True, # Prefer stable by default
"action": None,
})
version_available_stable = plugin_data[p_id].get('version_available_stable', None)
version_available_experimental = plugin_data[p_id].get('version_available_experimental', None)

if version_required is not None and version_required == version_available_stable:
affected_plugin["version_available"] = version_available_stable
affected_plugin["use_stable_version"] = True
elif version_required is not None and version_required == version_available_experimental:
affected_plugin["version_available"] = version_available_experimental
affected_plugin["use_stable_version"] = False
elif version_required is None:
if version_available_stable: # None if not found, "" if not offered
# Prefer the stable version, if any
affected_plugin["version_available"] = version_available_stable
affected_plugin["use_stable_version"] = True
else: # The only available version is experimental
affected_plugin["version_available"] = version_available_experimental
affected_plugin["use_stable_version"] = False

# Install is needed
if name not in installed_plugins:
Expand All @@ -95,7 +114,7 @@ def find_dependencies(plugin_id, plugin_data=None, plugin_deps=None, installed_p
else:
continue

if affected_plugin['version_required'] == affected_plugin['version_available'] or affected_plugin['version_required'] is None:
if version_required == affected_plugin['version_available'] or version_required is None:
destination_list.update({name: affected_plugin})
else:
affected_plugin['error'] = 'unavailable {}'.format(affected_plugin['action'])
Expand Down
18 changes: 11 additions & 7 deletions python/pyplugin_installer/qgsplugindependenciesdialog.py
Expand Up @@ -30,12 +30,12 @@ def __init__(self, plugin_name, to_install, to_upgrade, not_found, parent=None):
:param plugin_name: the name of the parent plugin
:type plugin_name: str
:param to_install: list of plugin IDs that needs to be installed
:type to_install: list
:param to_upgrade: list of plugin IDs that needs to be upgraded
:type to_upgrade: list
:param not_found: list of plugin IDs that are not found (unavailable)
:type not_found: list
:param to_install: dict of plugins that need to be installed, keyed by plugin name
:type to_install: dict
:param to_upgrade: dict of plugins that need to be upgraded, keyed by plugin name
:type to_upgrade: dict
:param not_found: dict of plugins that were not found (unavailable), keyed by plugin name
:type not_found: dict
:param parent: parent object, defaults to None
:param parent: QWidget, optional
"""
Expand All @@ -60,7 +60,9 @@ def _make_row(data, i, name):
widget = QtWidgets.QLabel("<b>%s</b>" % name)
widget.p_id = data['id']
widget.action = data['action']
widget.use_stable_version = data['use_stable_version']
self.pluginList.setCellWidget(i, 0, widget)
self.pluginList.resizeColumnToContents(0)
widget = QtWidgets.QTableWidgetItem(_display(data['version_installed']))
widget.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
self.pluginList.setItem(i, 1, widget)
Expand Down Expand Up @@ -106,7 +108,9 @@ def accept(self):
for i in range(self.pluginList.rowCount()):
try:
if self.pluginList.cellWidget(i, 4).isChecked():
self.__actions[self.pluginList.cellWidget(i, 0).p_id] = self.pluginList.cellWidget(i, 0).action
self.__actions[self.pluginList.cellWidget(i, 0).p_id] = {
'action': self.pluginList.cellWidget(i, 0).action,
'use_stable_version': self.pluginList.cellWidget(i, 0).use_stable_version}
except:
pass
super().accept()
90 changes: 84 additions & 6 deletions tests/src/python/test_plugindependencies.py
Expand Up @@ -71,32 +71,52 @@ def tearDown(self):

def test_find_dependencies(self):

to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'InaSAFE': None}, installed_plugins=self.installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'InaSAFE': None},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install, {})
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'InaSAFE': '110.1'}, installed_plugins=self.installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'InaSAFE': '110.1'},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install, {})
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found['InaSAFE']['version_installed'], '5.0.0')

# QuickWkt is installed, version is not specified: ignore
installed_plugins = self.installed_plugins
installed_plugins['QuickWKT'] = '2.1'
to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'QuickMapServices': '0.19.10.1', 'QuickWKT': None}, installed_plugins=self.installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'QuickMapServices': '0.19.10.1',
'QuickWKT': None},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['QuickMapServices']['version_required'], '0.19.10.1')
self.assertEqual(to_install['QuickMapServices']['version_available'], '0.19.10.1')
self.assertEqual(to_install['QuickMapServices']['use_stable_version'], True)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# QuickWkt is installed, version requires upgrade and it's in the repo: upgrade
to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'QuickWKT': '3.1'}, installed_plugins=installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'QuickWKT': '3.1'},
installed_plugins=installed_plugins)
self.assertEqual(to_install, {})
self.assertEqual(to_upgrade['QuickWKT']['version_required'], '3.1')
self.assertEqual(to_upgrade['QuickWKT']['version_available'], '3.1')
self.assertEqual(to_upgrade['QuickWKT']['use_stable_version'], True)
self.assertEqual(not_found, {})

# QuickWkt is installed, version requires upgrade and it's NOT in the repo: not found
to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'QuickWKT': '300.11234'}, installed_plugins=installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'QuickWKT': '300.11234'},
installed_plugins=installed_plugins)
self.assertEqual(to_install, {})
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found['QuickWKT']['version_required'], '300.11234')
Expand All @@ -105,11 +125,69 @@ def test_find_dependencies(self):

# Installed version is > than required: ignore (no downgrade is currently possible)
installed_plugins['QuickWKT'] = '300.1'
to_install, to_upgrade, not_found = find_dependencies('qgisce', self.plugin_data, plugin_deps={'QuickWKT': '1.2'}, installed_plugins=installed_plugins)
to_install, to_upgrade, not_found = find_dependencies('qgisce',
self.plugin_data,
plugin_deps={'QuickWKT': '1.2'},
installed_plugins=installed_plugins)
self.assertEqual(to_install, {})
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# A plugin offers both stable and experimental versions. A dependent plugin requires the experimental one.
to_install, to_upgrade, not_found = find_dependencies('LADM-COL-Add-on-Ambiente',
self.plugin_data,
plugin_deps={'Asistente LADM-COL': '3.2.0-beta-1'},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['Asistente LADM-COL']['version_required'], '3.2.0-beta-1')
self.assertEqual(to_install['Asistente LADM-COL']['version_available'], '3.2.0-beta-1')
self.assertEqual(to_install['Asistente LADM-COL']['use_stable_version'], False)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# A plugin offers both stable and experimental versions. A dependent plugin requires the stable one.
to_install, to_upgrade, not_found = find_dependencies('LADM-COL-Add-on-Ambiente',
self.plugin_data,
plugin_deps={'Asistente LADM-COL': '3.1.9'},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['Asistente LADM-COL']['version_required'], '3.1.9')
self.assertEqual(to_install['Asistente LADM-COL']['version_available'], '3.1.9')
self.assertEqual(to_install['Asistente LADM-COL']['use_stable_version'], True)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# A plugin offers both stable and experimental versions. If no version is required, choose the stable one.
to_install, to_upgrade, not_found = find_dependencies('LADM-COL-Add-on-Ambiente',
self.plugin_data,
plugin_deps={'Asistente LADM-COL': None},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['Asistente LADM-COL']['version_required'], None)
self.assertEqual(to_install['Asistente LADM-COL']['version_available'], '3.1.9')
self.assertEqual(to_install['Asistente LADM-COL']['use_stable_version'], True)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# A plugin only offers experimental version. If the experimental version is required, give it to him.
to_install, to_upgrade, not_found = find_dependencies('dependent-on-unique_values_viewer',
self.plugin_data,
plugin_deps={'UniqueValuesViewer': '0.2'},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['UniqueValuesViewer']['version_required'], '0.2')
self.assertEqual(to_install['UniqueValuesViewer']['version_available'], '0.2')
self.assertEqual(to_install['UniqueValuesViewer']['use_stable_version'], False)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})

# A plugin only offers experimental version. If no version is required, choose the experimental one.
to_install, to_upgrade, not_found = find_dependencies('dependent-on-unique_values_viewer',
self.plugin_data,
plugin_deps={'UniqueValuesViewer': None},
installed_plugins=self.installed_plugins)
self.assertEqual(to_install['UniqueValuesViewer']['version_required'], None)
self.assertEqual(to_install['UniqueValuesViewer']['version_available'], '0.2')
self.assertEqual(to_install['UniqueValuesViewer']['use_stable_version'], False)
self.assertEqual(to_upgrade, {})
self.assertEqual(not_found, {})


def pluginSuite():
return unittest.makeSuite(PluginDependenciesTest, 'test')
Expand Down
2 changes: 1 addition & 1 deletion tests/testdata/plugindependencies_data.json

Large diffs are not rendered by default.

0 comments on commit e86e559

Please sign in to comment.