Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ recursive-include octoprint_netconnectd/translations *
include LICENSE
include requirements.txt
include README.md
graft octoprint_netconnectd/scripts
68 changes: 31 additions & 37 deletions octoprint_netconnectd/scripts/update_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@
from octoprint.plugins.softwareupdate import exceptions
from octoprint.settings import _default_basedir

from octoprint_mrbeam.util.pip_util import get_version_of_pip_module, \
get_pip_caller # TODO check how to be independent of mrbeam plugin
from octoprint_netconnectd.util.pip_util import get_version_of_pip_module, \
get_pip_caller

from requests.adapters import HTTPAdapter
from urllib3 import Retry
from urllib3.exceptions import MaxRetryError, ConnectionError

_logger = logging.getLogger("octoprint.plugins.netconnectd.softwareupdate.updatescript")

UPDATE_CONFIG_NAME = "netconnectd_plugin"
UPDATE_CONFIG_NAME = "netconnectd"
REPO_NAME = "OctoPrint-Netconnectd"
MAIN_SRC_FOLDER_NAME = "octoprint_netconnectd"
PLUGIN_NAME = "OctoPrint-Netconnectd"
DEFAULT_OPRINT_VENV = "/home/pi/oprint/bin/pip"
PIP_WHEEL_TEMP_FOLDER = "/tmp/wheelhouse"

"""
copy pasta of mrbeam plugin update script
Expand Down Expand Up @@ -96,14 +97,6 @@ def _parse_arguments():
default=None,
help="Path of target zip file on local system",
)
parser.add_argument(
"--target_version",
action="store",
type=str,
dest="target_version",
default=None,
help="Version number of the target",
)
parser.add_argument(
"folder",
type=str,
Expand Down Expand Up @@ -175,13 +168,25 @@ def build_wheels(build_queue):
None

"""
try:
if not os.path.isdir(PIP_WHEEL_TEMP_FOLDER):
os.mkdir(PIP_WHEEL_TEMP_FOLDER)
except OSError as e:
raise RuntimeError("can't create wheel tmp folder {} - {}".format(PIP_WHEEL_TEMP_FOLDER, e))

for venv, packages in build_queue.items():
tmp_folder = os.path.join(PIP_WHEEL_TEMP_FOLDER, re.search(r"\w+((?=\/venv)|(?=\/bin))", venv).group(0))
if os.path.isdir(tmp_folder):
try:
os.system("sudo rm -r {}".format(tmp_folder))
except Exception as e:
raise RuntimeError("can't delete pip wheel temp folder {} - {}".format(tmp_folder, e))

pip_args = [
"wheel",
"--no-python-version-warning",
"--disable-pip-version-check",
"--wheel-dir=/tmp/wheelhouse", # Build wheels into <dir>, where the default is the current working directory.
"--wheel-dir={}".format(tmp_folder), # Build wheels into <dir>, where the default is the current working directory.
"--no-dependencies", # Don't install package dependencies.
]
for package in packages:
Expand Down Expand Up @@ -213,20 +218,20 @@ def install_wheels(install_queue):
raise RuntimeError("install queue is not a dict")

for venv, packages in install_queue.items():

tmp_folder = os.path.join(PIP_WHEEL_TEMP_FOLDER, re.search(r"\w+((?=\/venv)|(?=\/bin))", venv).group(0))
pip_args = [
"install",
"--no-python-version-warning",
"--disable-pip-version-check",
"--upgrade", # Upgrade all specified packages to the newest available version. The handling of dependencies depends on the upgrade-strategy used.
"--no-index", # Ignore package index (only looking at --find-links URLs instead).
"--find-links=/tmp/wheelhouse", # If a URL or path to an html file, then parse for links to archives such as sdist (.tar.gz) or wheel (.whl) files. If a local path or file:// URL that's a directory, then look for archives in the directory listing. Links to VCS project URLs are not supported.
"--find-links={}".format(tmp_folder), # If a URL or path to an html file, then parse for links to archives such as sdist (.tar.gz) or wheel (.whl) files. If a local path or file:// URL that's a directory, then look for archives in the directory listing. Links to VCS project URLs are not supported.
"--no-dependencies", # Don't install package dependencies.
]
for package in packages:
pip_args.append(
"{package}=={package_version}".format(
package=package["name"], package_version=package["target"]
"{package}".format(
package=package["name"]
)
)

Expand All @@ -239,14 +244,14 @@ def install_wheels(install_queue):
)


def build_queue(update_info, dependencies, target, plugin_archive):
def build_queue(update_info, dependencies, plugin_archive):
"""
build the queue of packages to install

Args:
update_info: a dict of informations how to update the packages
dependencies: a list dicts of dependencies [{"name", "version"}]
target: target of the Mr Beam Plugin to update to
plugin_archive: path to archive of the plugin

Returns:
install_queue: dict of venvs with a list of package dicts {"<venv path>": [{"name", "archive", "target"}]
Expand All @@ -259,7 +264,7 @@ def build_queue(update_info, dependencies, target, plugin_archive):
{
"name": PLUGIN_NAME,
"archive": plugin_archive,
"target": target,
"target": '',
}
)
print("dependencies - {}".format(dependencies))
Expand Down Expand Up @@ -322,7 +327,7 @@ def run_update():
update_info = get_update_info()

install_queue = build_queue(
update_info, dependencies, args.target_version, args.archive
update_info, dependencies, args.archive
)

print("install_queue", install_queue)
Expand Down Expand Up @@ -367,7 +372,7 @@ def loadPluginTarget(archive, folder):
folder: working directory

Returns:
(zip_file_path, target_version) - path of the downloaded zip file and target version string
zip_file_path - path of the downloaded zip file
"""

# download target repo zip
Expand Down Expand Up @@ -421,17 +426,7 @@ def loadPluginTarget(archive, folder):
except IOError:
raise RuntimeError("Could not copy update_script to working directory")

# get target version
exec(
open(
os.path.join(
plugin_extracted_path_folder, MAIN_SRC_FOLDER_NAME, "__version.py"
)
).read()
)
target_version = __version__

return zip_file_path, target_version
return zip_file_path


def main():
Expand All @@ -445,9 +440,9 @@ def main():

args = _parse_arguments()
if args.call:
if args.archive is None or args.target_version is None:
if args.archive is None:
raise RuntimeError(
"Could not run update archive or target_version is missing"
"Could not run update archive is missing"
)
run_update()
else:
Expand All @@ -460,7 +455,7 @@ def main():
raise RuntimeError("Could not update, base folder is not writable")

update_info = get_update_info()
archive, target_version = loadPluginTarget(
archive = loadPluginTarget(
update_info.get(UPDATE_CONFIG_NAME)
.get("pip")
.format(target_version=args.target),
Expand All @@ -470,8 +465,7 @@ def main():
# call new update script with args
sys.argv = [
"--call=true",
"--archive={}".format(archive),
"--target_version={}".format(target_version),
"--archive={}".format(archive)
] + sys.argv[1:]
try:
result = subprocess.call(
Expand Down
Empty file.
78 changes: 78 additions & 0 deletions octoprint_netconnectd/util/cmd_exec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
copy pasta of octoprint_mrbeam/util/cmd_exec.py
only changed the logging to be mrbeam plugin independend
"""
import logging
import subprocess
from logging import DEBUG


def exec_cmd(cmd, log=True, shell=True, loglvl=DEBUG):
"""
Executes a system command
:param cmd:
:return: True if system returncode was 0,
False if the command returned with an error,
None if there was an exception.
"""
_logger = logging.getLogger(__name__ + ".exec_cmd")
code = None
if log:
_logger.log(loglvl, "cmd=%s", cmd)
try:
code = subprocess.call(cmd, shell=shell)
except Exception as e:
_logger.debug(
"Failed to execute command '%s', return code: %s, Exception: %s",
cmd,
code,
e,
)
return None
if code != 0 and log:
_logger.info("cmd= '%s', return code: '%s'", code)
return code == 0


def exec_cmd_output(cmd, log=True, shell=False, loglvl=DEBUG):
"""
Executes a system command and returns its output.
:param cmd:
:return: Tuple(String:output , int return_code)
"""
_logger = logging.getLogger(__name__ + "exec_cmd_output")
output = None
code = 0
if log:
_logger.log(loglvl, "cmd='%s'", cmd)
try:
output = subprocess.check_output(cmd, shell=shell, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
code = e.returncode

if not log:
cmd = cmd[:50] + "..." if len(cmd) > 30 else cmd
if e.output is not None:
output = e.output[:30] + "..." if len(e.output) > 30 else e.output
else:
output = e.output
_logger.log(
loglvl,
"Failed to execute command '%s', return code: %s, output: '%s'",
cmd,
e.returncode,
output,
)

except Exception as e:
code = 99
output = "{e}: {o}".format(e=e, o=output)
_logger.log(
loglvl,
"Failed to execute command '%s', return code: %s, output: '%s'",
cmd,
None,
output,
)

return output, code
109 changes: 109 additions & 0 deletions octoprint_netconnectd/util/pip_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
copy pasta of octoprint_mrbeam/util/pip_util.py
only changed the logging to be mrbeam plugin independend
"""
import logging

from octoprint.plugins.softwareupdate.updaters.pip import _get_pip_caller
from octoprint.util.pip import PipCaller
from cmd_exec import exec_cmd_output

DISABLE_PIP_CHECK = "--disable-pip-version-check"
DISABLE_PY_WARNING = "--no-python-version-warning"

# Dictionary of package versions available at different locations
# {
# /home/pi/oprint/bin/pip : {
# "OctoPrint x.x.x",
# ...
# },
# /usr/share/iobeam/venv/bin/pip : {
# "iobeam y.y.y",
# ...
# }
# }
_pip_package_version_lists = {}


def get_version_of_pip_module(pip_name, pip_command=None, disable_pip_ver_check=True):
_logger = logging.getLogger(__name__ + ".get_version_of_pip_module")
global _pip_package_version_lists
version = None
returncode = -1
if pip_command is None:
pip_command = "pip"
elif isinstance(pip_command, list):
pip_command = " ".join(pip_command)
# Checking for pip version outdate takes extra time and text output.
# NOTE: Older versions of pip do not have the --no-python-version-warning flag
for disabled in [
DISABLE_PIP_CHECK,
]: # DISABLE_PY_WARNING]:
if disable_pip_ver_check and not disabled in pip_command:
pip_command += " " + disabled
venv_packages = _pip_package_version_lists.get(pip_command, None)

if venv_packages is None:
# perform a pip discovery and remember it for next time
command = "{pip_command} list".format(pip_command=pip_command)
_logger.debug("refreshing list of installed packages (%s list)", pip_command)
output, returncode = exec_cmd_output(command, shell=True, log=False)
if returncode == 0:
venv_packages = output.splitlines()
_pip_package_version_lists[pip_command] = venv_packages
elif returncode == 127:
_logger.error(
"`%s` was not found in local $PATH (returncode %s)",
pip_command,
returncode,
)
return None
else:
_logger.warning("`%s list` returned code %s", pip_command, returncode)
return None
# Go through the package list available in our venv
for line in venv_packages:
token = line.split()
if len(token) >= 2 and token[0] == pip_name:
version = token[1]
break
_logger.debug("%s==%s", pip_name, version)
return version


def get_pip_caller(venv, _logger=None):
"""
gets the pip caller of the givenv venv

Args:
venv: path to venv
_logger: logger to log call, stdout and stderr of the pip caller

Returns:
PipCaller of the venv
"""
pip_caller = _get_pip_caller(command=venv)
if not isinstance(pip_caller, PipCaller):
raise RuntimeError("Can't run pip", None)

def _log_call(*lines):
_log(lines, prefix=" ", stream="call")

def _log_stdout(*lines):
_log(lines, prefix=">", stream="stdout")

def _log_stderr(*lines):
_log(lines, prefix="!", stream="stderr")

def _log(lines, prefix=None, stream=None, strip=True):
if strip:
lines = map(lambda x: x.strip(), lines)
for line in lines:
print(u"{} {}".format(prefix, line))

if _logger is not None:
pip_caller.on_log_call = _log_call
pip_caller.on_log_stdout = _log_stdout
pip_caller.on_log_stderr = _log_stderr

return pip_caller