Plugin Manager: Use new PipCaller class and only provide dependency-links parameter if supported by pip version

This commit is contained in:
Gina Häußge 2015-06-23 11:36:27 +02:00
parent b32f9bddd2
commit 71ccc47717

View file

@ -12,6 +12,7 @@ import octoprint.plugin.core
from octoprint.settings import valid_boolean_trues
from octoprint.server.util.flask import restricted_access
from octoprint.server import admin_permission
from octoprint.util.pip import PipCaller, UnknownPip
from flask import jsonify, make_response
from flask.ext.babel import gettext
@ -22,6 +23,7 @@ import sys
import requests
import re
import os
import pkg_resources
class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
octoprint.plugin.TemplatePlugin,
@ -36,6 +38,9 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
self._pending_install = set()
self._pending_uninstall = set()
self._pip_caller = None
self._pip_version_dependency_links = pkg_resources.parse_version("1.5")
self._repository_available = False
self._repository_plugins = []
self._repository_cache_path = None
@ -46,6 +51,11 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
self._repository_cache_path = os.path.join(self._settings.get_plugin_data_folder(), "plugins.json")
self._repository_cache_ttl = self._settings.get_int(["repository_ttl"]) * 60
self._pip_caller = PipCaller(configured=self._settings.get(["pip"]))
self._pip_caller.on_log_call = self._log_call
self._pip_caller.on_log_stdout = self._log_stdout
self._pip_caller.on_log_stderr = self._log_stderr
##~~ StartupPlugin
def on_startup(self, host, port):
@ -72,6 +82,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
def on_settings_save(self, data):
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
self._repository_cache_ttl = self._settings.get_int(["repository_ttl"]) * 60
self._pip_caller.refresh = True
##~~ AssetPlugin
@ -396,74 +407,16 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin,
self._plugin_manager.send_plugin_message(self._identifier, notification)
def _call_pip(self, args):
pip_command = self._settings.get(["pip"])
if pip_command is None:
import os
python_command = sys.executable
binary_dir = os.path.dirname(python_command)
if self._pip_caller is None or not self._pip_caller.available:
raise RuntimeError(u"No pip available, can't operate".format(**locals()))
pip_command = os.path.join(binary_dir, "pip")
if sys.platform == "win32":
# Windows is a bit special... first of all the file will be called pip.exe, not just pip, and secondly
# for a non-virtualenv install (e.g. global install) the pip binary will not be located in the
# same folder as python.exe, but in a subfolder Scripts, e.g.
#
# C:\Python2.7\
# |- python.exe
# `- Scripts
# `- pip.exe
if "--process-dependency-links" in args and self._pip_caller < self._pip_version_dependency_links:
args.remove("--process-dependency-links")
# virtual env?
pip_command = os.path.join(binary_dir, "pip.exe")
return self._pip_caller.execute(*args)
if not os.path.isfile(pip_command):
# nope, let's try the Scripts folder then
scripts_dir = os.path.join(binary_dir, "Scripts")
if os.path.isdir(scripts_dir):
pip_command = os.path.join(scripts_dir, "pip.exe")
if not os.path.isfile(pip_command) or not os.access(pip_command, os.X_OK):
raise RuntimeError(u"No pip path configured and {pip_command} does not exist or is not executable, can't install".format(**locals()))
command = [pip_command] + args
self._logger.debug(u"Calling: {}".format(" ".join(command)))
p = sarge.run(" ".join(command), shell=True, async=True, stdout=sarge.Capture(), stderr=sarge.Capture())
p.wait_events()
all_stdout = []
all_stderr = []
try:
while p.returncode is None:
line = p.stderr.readline(timeout=0.5)
if line:
self._log_stderr(line)
all_stderr.append(line)
line = p.stdout.readline(timeout=0.5)
if line:
self._log_stdout(line)
all_stdout.append(line)
p.commands[0].poll()
finally:
p.close()
stderr = p.stderr.text
if stderr:
split_lines = stderr.split("\n")
self._log_stderr(*split_lines)
all_stderr += split_lines
stdout = p.stdout.text
if stdout:
split_lines = stdout.split("\n")
self._log_stdout(*split_lines)
all_stdout += split_lines
return p.returncode, all_stdout, all_stderr
def _log_call(self, *lines):
self._log(lines, prefix=" ", stream="call")
def _log_stdout(self, *lines):
self._log(lines, prefix=">", stream="stdout")