Software Update: Use new PipCaller class and only provide dependency-links parameter if supported by pip version
This commit is contained in:
parent
71ccc47717
commit
ac151d9019
1 changed files with 38 additions and 79 deletions
|
|
@ -7,114 +7,73 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
|
|||
|
||||
|
||||
import logging
|
||||
import sarge
|
||||
import sys
|
||||
import pkg_resources
|
||||
|
||||
from octoprint.util.pip import PipCaller, UnknownPip
|
||||
|
||||
logger = logging.getLogger("octoprint.plugins.softwareupdate.updaters.pip")
|
||||
console_logger = logging.getLogger("octoprint.plugins.softwareupdate.updaters.pip.console")
|
||||
|
||||
_pip_callers = dict()
|
||||
_pip_version_dependency_links = pkg_resources.parse_version("1.5")
|
||||
|
||||
def can_perform_update(target, check):
|
||||
return "pip" in check
|
||||
pip_caller = _get_pip_caller(command=check["pip_command"] if "pip_command" in check else None)
|
||||
return "pip" in check and pip_caller is not None and pip_caller.available
|
||||
|
||||
def _get_pip_caller(command=None):
|
||||
key = command
|
||||
if command is None:
|
||||
key = "__default"
|
||||
|
||||
if not key in _pip_callers:
|
||||
try:
|
||||
_pip_callers[key] = PipCaller(configured=command)
|
||||
_pip_callers[key].on_log_call = _log_call
|
||||
_pip_callers[key].on_log_stdout = _log_stdout
|
||||
_pip_callers[key].on_log_stderr = _log_stderr
|
||||
except UnknownPip:
|
||||
_pip_callers[key] = None
|
||||
|
||||
return _pip_callers[key]
|
||||
|
||||
def perform_update(target, check, target_version):
|
||||
pip_command = None
|
||||
if "pip_command" in check:
|
||||
pip_command = check["pip_command"]
|
||||
|
||||
pip_caller = _get_pip_caller(command=pip_command)
|
||||
if pip_caller is None:
|
||||
raise RuntimeError("Can't run pip")
|
||||
|
||||
install_arg = check["pip"].format(target_version=target_version)
|
||||
|
||||
logger.debug("Target: %s, executing pip install %s" % (target, install_arg))
|
||||
pip_args = ["install", check["pip"].format(target_version=target_version, target=target_version)]
|
||||
|
||||
if "dependency_links" in check and check["dependency_links"]:
|
||||
if "dependency_links" in check and check["dependency_links"] and pip_caller >= _pip_version_dependency_links:
|
||||
pip_args += ["--process-dependency-links"]
|
||||
|
||||
_call_pip(pip_args, pip_command=pip_command)
|
||||
pip_caller.execute(*pip_args)
|
||||
|
||||
logger.debug("Target. %s, executing pip install %s --ignore-reinstalled --force-reinstall --no-deps" % (target, install_arg))
|
||||
pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
|
||||
_call_pip(pip_args, pip_command=pip_command)
|
||||
|
||||
pip_caller.execute(*pip_args)
|
||||
|
||||
return "ok"
|
||||
|
||||
def _call_pip(args, pip_command=None):
|
||||
if pip_command is None:
|
||||
import os
|
||||
python_command = sys.executable
|
||||
binary_dir = os.path.dirname(python_command)
|
||||
|
||||
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
|
||||
|
||||
# virtual env?
|
||||
pip_command = os.path.join(binary_dir, "pip.exe")
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
_log_stderr(line)
|
||||
all_stderr.append(line)
|
||||
|
||||
line = p.stdout.readline(timeout=0.5)
|
||||
if line:
|
||||
_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")
|
||||
_log_stderr(*split_lines)
|
||||
all_stderr += split_lines
|
||||
|
||||
stdout = p.stdout.text
|
||||
if stdout:
|
||||
split_lines = stdout.split("\n")
|
||||
_log_stdout(*split_lines)
|
||||
all_stdout += split_lines
|
||||
|
||||
return p.returncode, all_stdout, all_stderr
|
||||
def _log_call(*lines):
|
||||
_log(lines, prefix=" ")
|
||||
|
||||
def _log_stdout(*lines):
|
||||
_log(lines, prefix=">", stream="stdout")
|
||||
_log(lines, prefix=">")
|
||||
|
||||
def _log_stderr(*lines):
|
||||
_log(lines, prefix="!", stream="stderr")
|
||||
_log(lines, prefix="!")
|
||||
|
||||
def _log(lines, prefix=None, stream=None, strip=True):
|
||||
if strip:
|
||||
lines = map(lambda x: x.strip(), lines)
|
||||
def _log(lines, prefix=None):
|
||||
lines = map(lambda x: x.strip(), lines)
|
||||
for line in lines:
|
||||
console_logger.debug(u"{prefix} {line}".format(**locals()))
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue