New approach to matching up installed packages with plugins

Match against entry_point origins using both package name and package name plus version. If one of those matches (depends on pip version if output will include version or not), it's the plugin we are looking for.

This way we don't need to try to strip a version number from the package name as output by pip without knowing if a version number is included or not.
This commit is contained in:
Gina Häußge 2015-06-03 10:46:58 +02:00
parent ba5e99c187
commit 27dcef3e23

View file

@ -174,7 +174,8 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
all_plugins_before = self._plugin_manager.find_plugins()
success_string = "Successfully installed "
success_string = "Successfully installed"
failure_string = "Could not install"
try:
returncode, stdout, stderr = self._call_pip(pip_args)
except:
@ -190,39 +191,49 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
return make_response("Could not install plugin from url, see the log for more details", 500)
try:
last_line = filter(lambda x: x.startswith(success_string) or x.startswith("Could not install"), stdout)[-1]
result_line = filter(lambda x: x.startswith(success_string) or x.startswith(failure_string), stdout)[-1]
except IndexError:
result = dict(result=False, reason="Could not parse output from pip")
self._send_result_notification("install", result)
return jsonify(result)
# The success line of a pip install command looks something like this:
# The final output of a pip install command looks something like this:
#
# Successfully installed OctoPrint-Plugin-1.0 Dependency-One-0.1 Dependency-Two-9.3
#
# So we'll first strip the first part, then split by whitespace and remove the version to get the name of
# the installed package. By then matching up the package names stored for all our entry point plugins against
# the list of installed packages from our pip install run, we'll find the plugin that was installed.
# or this:
#
# Successfully installed OctoPrint-Plugin Dependency-One Dependency-Two
# Cleaning up...
#
# So we'll need to fetch the "Successfully installed" line, strip the "Successfully" part, then split by whitespace
# and strip to get all installed packages.
#
# We then need to iterate over all known plugins and see if either the package name or the package name plus
# version number matches one of our installed packages. If it does, that's our installed plugin.
#
# Known issue: This might return the wrong plugin if more than one plugin was installed through this
# command (e.g. due to pulling in another plugin as dependency). It should be safe for now though to
# consider this a rare corner case. Once it becomes a real problem we'll just extend the plugin manager
# so that it can report on more than one installed plugin.
last_line = last_line.strip()
if not last_line.startswith(success_string):
result_line = result_line.strip()
if not result_line.startswith(success_string):
result = dict(result=False, reason="Pip did not report successful installation")
self._send_result_notification("install", result)
return jsonify(result)
def strip_version(s):
l = s.strip().split("-")
if len(l) > 0 and s[-1][:1].isdigit():
del l[-1]
return "-".join(l)
installed = map(strip_version, last_line[len(success_string):].split(" "))
installed = map(lambda x: x.strip(), result_line[len(success_string):].split(" "))
all_plugins_after = self._plugin_manager.find_plugins(existing=dict(), ignore_uninstalled=False)
for key, plugin in all_plugins_after.items():
if plugin.origin is None or plugin.origin[0] != "entry_point":
if plugin.origin is None or plugin.origin.type != "entry_point":
continue
if plugin.origin[3] in installed:
package_name = plugin.origin.package_name
package_version = plugin.origin.package_version
versioned_package = "{package_name}-{package_version}".format(**locals())
if package_name in installed or versioned_package in installed:
new_plugin_key = key
new_plugin = plugin
break
@ -248,7 +259,11 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
if plugin.bundled:
return make_response("Bundled plugins cannot be uninstalled", 400)
if plugin.origin[0] == "entry_point":
if plugin.origin is None:
self._logger.warn(u"Trying to uninstall plugin {plugin} but origin is unknown".format(**locals()))
return make_response("Could not uninstall plugin, its origin is unknown")
if plugin.origin.type == "entry_point":
# plugin is installed through entry point, need to use pip to uninstall it
origin = plugin.origin[3]
if origin is None:
@ -261,7 +276,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
self._logger.exception(u"Could not uninstall plugin via pip")
return make_response("Could not uninstall plugin via pip, see the log for more details", 500)
elif plugin.origin[0] == "folder":
elif plugin.origin.type == "folder":
import os
import shutil
full_path = os.path.realpath(plugin.location)
@ -279,6 +294,10 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
if os.path.isfile(pyc_file):
os.remove(pyc_file)
else:
self._logger.warn(u"Trying to uninstall plugin {plugin} but origin is unknown ({plugin.origin.type})".format(**locals()))
return make_response("Could not uninstall plugin, its origin is unknown")
needs_restart = self._plugin_manager.is_restart_needing_plugin(plugin)
needs_refresh = plugin.implementation and isinstance(plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)