Plugin Manager: Use new PipCaller class and only provide dependency-links parameter if supported by pip version
This commit is contained in:
parent
b32f9bddd2
commit
71ccc47717
1 changed files with 18 additions and 65 deletions
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue