Fix pip installation issues.

This commit is to fix issue #325.
There were three issues with the PIP installations.

1) If multiple instances of the same platform were found, pip could
attempt to install the same dependency multiple times at once by being
run simultaneously in different processes. This would cause pip
failures due to race conditions. This has been fixed by using a thread
lock to allow only one instance of PIP to run at a time.

2) PIP would not check the target if the dependency was already met.
This would lead to PIP attempting to reinstall every dependency on
every boot. This would eventually fail because the package was already
installed, but it significantly increased boot time, especially on
Raspberry Pis.

3) PIP would not upgrade packages that were already installed. Usually,
when a version is specified to PIP, it will install the specified
version if it is not already installed, even without the \-\-upgrade
flag. This behavior did not work when using the \-\-target flag. When
using the target flag, a new install is always attempted, but nothing
will be overwritten unless the \-\-upgrade flag is also given. This
caused new packages to not be installed when their dependencies were
increased. This is fixed by defaulting towards using the
\-\-upgrade flag.
This commit is contained in:
Ryan Kraus 2015-09-05 04:50:35 -04:00
parent 97e19908be
commit 34c4bb585a

View File

@ -1,19 +1,54 @@
"""Helpers to install PyPi packages."""
import os
import logging
import pkg_resources
import subprocess
import sys
import threading
_LOGGER = logging.getLogger(__name__)
INSTALL_LOCK = threading.Lock()
def install_package(package, upgrade=False, target=None):
def install_package(package, upgrade=True, target=None):
"""Install a package on PyPi. Accepts pip compatible package strings.
Return boolean if install successfull."""
# Not using 'import pip; pip.main([])' because it breaks the logger
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
if upgrade:
args.append('--upgrade')
if target:
args += ['--target', os.path.abspath(target)]
target = os.path.abspath(target)
args += ['--target', target]
with INSTALL_LOCK:
if check_package_exists(package, target):
return True
_LOGGER.info('Attempting install of %s', package)
try:
return 0 == subprocess.call(args)
except subprocess.SubprocessError:
return False
def check_package_exists(package, target=None):
"""Check if a package exists.
Returns True when the requirement is met.
Returns False when the package is not installed or doesn't meet req."""
req = pkg_resources.Requirement.parse(package)
if target:
work_set = pkg_resources.WorkingSet([target])
search_fun = work_set.find
else:
search_fun = pkg_resources.get_distribution
try:
return 0 == subprocess.call(args)
except subprocess.SubprocessError:
result = search_fun(req)
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
return False
return bool(result)