faceswap/update_deps.py
torzdf 79a61002b5 Update Dependencies Script
- Add script `update_deps.py` to automatically update dependencies
- Call update_deps.py when checking for updates from the GUI
- Update setup.py to respect requirements.txt sys_platform tags
- Update INSTALL.md
2019-06-24 00:31:47 +01:00

193 lines
6.4 KiB
Python

#!/usr/bin/env python3
""" Installs any required third party libs for faceswap.py
Checks for installed Conda / Pip packages and updates accordingly
"""
import locale
import os
import re
import sys
import ctypes
from subprocess import CalledProcessError, run, PIPE, Popen
_LOGGER = None
class Environment():
""" Hold information about the running environment """
def __init__(self):
self.is_conda = "conda" in sys.version.lower()
self.encoding = locale.getpreferredencoding()
self.is_admin = self.get_admin_status()
self.is_virtualenv = self.get_virtualenv
required_packages = self.get_required_packages()
self.installed_packages = self.get_installed_packages()
self.get_installed_conda_packages()
self.packages_to_install = self.get_packages_to_install(required_packages)
@staticmethod
def get_admin_status():
""" Check whether user is admin """
try:
retval = os.getuid() == 0
except AttributeError:
retval = ctypes.windll.shell32.IsUserAnAdmin() != 0
return retval
def get_virtualenv(self):
""" Check whether this is a virtual environment """
if not self.is_conda:
retval = (hasattr(sys, "real_prefix") or
(hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix))
else:
prefix = os.path.dirname(sys.prefix)
retval = (os.path.basename(prefix) == "envs")
return retval
@staticmethod
def get_required_packages():
""" Load requirements list """
packages = list()
pypath = os.path.dirname(os.path.realpath(__file__))
requirements_file = os.path.join(pypath, "requirements.txt")
with open(requirements_file) as req:
for package in req.readlines():
package = package.strip()
if package and (not package.startswith("#")):
packages.append(package)
return packages
def get_installed_packages(self):
""" Get currently installed packages """
installed_packages = dict()
chk = Popen("\"{}\" -m pip freeze".format(sys.executable),
shell=True, stdout=PIPE)
installed = chk.communicate()[0].decode(self.encoding).splitlines()
for pkg in installed:
if "==" not in pkg:
continue
item = pkg.split("==")
installed_packages[item[0]] = item[1]
return installed_packages
def get_installed_conda_packages(self):
""" Get currently installed conda packages """
if not self.is_conda:
return
chk = os.popen("conda list").read()
installed = [re.sub(" +", " ", line.strip())
for line in chk.splitlines() if not line.startswith("#")]
for pkg in installed:
item = pkg.split(" ")
self.installed_packages[item[0]] = item[1]
def get_packages_to_install(self, required_packages):
""" Get packages which need installing, upgrading or downgrading """
to_install = list()
for pkg in required_packages:
pkg = self.check_os_requirement(pkg)
if pkg is None:
continue
key = pkg.split("==")[0]
if key not in self.installed_packages:
to_install.append(pkg)
else:
if (len(pkg.split("==")) > 1 and
pkg.split("==")[1] != self.installed_packages.get(key)):
to_install.append(pkg)
return to_install
@staticmethod
def check_os_requirement(package):
""" Check whether this package is required for this OS """
if ";" not in package and "sys_platform" not in package:
return package
package = "".join(package.split())
pkg, tags = package.split(";")
tags = tags.split("==")
sys_platform = tags[tags.index("sys_platform") + 1].replace('"', "").replace("'", "")
if sys_platform == sys.platform:
return pkg
return None
class Installer():
""" Install packages through Conda or Pip """
def __init__(self, environment):
self.packages = environment.packages_to_install
self.env = environment
self.install()
def install(self):
""" Install required pip packages """
success = True
for pkg in self.packages:
output("Installing {}".format(pkg))
if self.env.is_conda and self.conda_install(pkg):
continue
if not self.pip_install(pkg):
success = False
if not success:
output("There were problems updating one or more dependencies.")
else:
output("Dependencies succesfully updated.")
@staticmethod
def conda_install(package):
""" Install a conda package """
success = True
condaexe = ["conda", "install", "-y", package]
try:
with open(os.devnull, "w") as devnull:
run(condaexe, stdout=devnull, stderr=devnull, check=True)
except CalledProcessError:
output("{} not available in Conda. Installing with pip...".format(package))
success = False
return success
def pip_install(self, package):
""" Install a pip package """
success = True
pipexe = [sys.executable, "-m", "pip"]
# hide info/warning and fix cache hang
pipexe.extend(["install", "-qq", "--no-cache-dir"])
# install as user to solve perm restriction
if not self.env.is_admin and not self.env.is_virtualenv:
pipexe.append("--user")
pipexe.append(package)
try:
run(pipexe, check=True)
except CalledProcessError:
output("Couldn't install {}. Please install this package manually".format(package))
success = False
return success
def output(msg):
""" Output to print or logger """
if _LOGGER is not None:
_LOGGER.info(msg)
else:
print(msg)
def main(logger=None):
""" Check for and update dependencies """
if logger is not None:
global _LOGGER # pylint:disable=global-statement
_LOGGER = logger
output("Updating Dependencies...")
update = Environment()
packages = update.packages_to_install
if not packages:
output("All Dependencies are up to date")
else:
Installer(update)
if __name__ == "__main__":
main()