mirror of
https://github.com/deepfakes/faceswap.git
synced 2026-10-17 05:21:10 +08:00
fa79d4dcee
* Update requirements * Remove docker * ICNR init to torch * conv-aware: Output as Tensor rather than Variable * Bugfix: Omit type object from automodsumm * bugfix: Tensorboard unit test. Switch Sequential for Functional * Suppress broken Keras test * revert failing test
289 lines
11 KiB
Python
289 lines
11 KiB
Python
#! /usr/env/bin/python3
|
|
"""Holds information about the running system. Used in setup.py and lib.sysinfo
|
|
NOTE: Only packages from Python's Standard Library should be imported in this module
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import ctypes
|
|
import locale
|
|
import logging
|
|
import os
|
|
import platform
|
|
import re
|
|
import sys
|
|
import typing as T
|
|
|
|
from shutil import which
|
|
from subprocess import CalledProcessError, run
|
|
|
|
from lib.utils import get_module_objects
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
VALID_PYTHON = ((3, 11), (3, 13))
|
|
"""The minimum and maximum versions of Python that can run Faceswap"""
|
|
VALID_TORCH = ((2, 3), (2, 12))
|
|
"""The minimum and maximum versions of Torch that can run Faceswap"""
|
|
VALID_KERAS = ((3, 14), (3, 14))
|
|
"""The minimum and maximum versions of Keras that can run Faceswap"""
|
|
|
|
|
|
def _lines_from_command(command: list[str]) -> list[str]:
|
|
"""Output stdout lines from an executed command.
|
|
|
|
Parameters
|
|
----------
|
|
command
|
|
The command to run
|
|
|
|
Returns
|
|
-------
|
|
The output lines from the given command
|
|
"""
|
|
logger.debug("Running command %s", command)
|
|
try:
|
|
proc = run(command,
|
|
capture_output=True,
|
|
check=True,
|
|
encoding=locale.getpreferredencoding(),
|
|
errors="replace")
|
|
except (FileNotFoundError, CalledProcessError) as err:
|
|
logger.debug("Error from command: %s", str(err))
|
|
return []
|
|
return proc.stdout.splitlines()
|
|
|
|
|
|
class System: # pylint:disable=too-many-instance-attributes
|
|
"""Holds information about the currently running system and environment"""
|
|
def __init__(self) -> None:
|
|
self.platform = platform.platform()
|
|
"""Human readable platform identifier"""
|
|
self.system: T.Literal["darwin", "linux", "windows"] = T.cast(
|
|
T.Literal["darwin", "linux", "windows"], platform.system().lower())
|
|
"""The system (OS type) that this code is running on. Always lowercase"""
|
|
self.machine = platform.machine()
|
|
"""The machine type (eg: "x86_64")"""
|
|
self.release = platform.release()
|
|
"""The OS Release that this code is running on"""
|
|
self.processor = platform.processor()
|
|
"""The processor in use, if detected"""
|
|
self.cpu_count = os.cpu_count()
|
|
"""The number of CPU cores on the system"""
|
|
self.python_implementation = platform.python_implementation()
|
|
"""The python implementation in use"""
|
|
self.python_version = platform.python_version()
|
|
"""The <major>.<minor>.<release> version of Python that is running"""
|
|
self.python_architecture = platform.architecture()[0]
|
|
"""The Python architecture that is running (eg: 64bit/32bit)"""
|
|
self.encoding = locale.getpreferredencoding()
|
|
"""The system encoding"""
|
|
self.is_conda = ("conda" in sys.version.lower() or
|
|
os.path.exists(os.path.join(sys.prefix, 'conda-meta')))
|
|
"""``True`` if running under Conda otherwise ``False``"""
|
|
self.is_admin = self._get_permissions()
|
|
"""``True`` if we are running with Admin privileges"""
|
|
self.is_virtual_env = self._check_virtual_env()
|
|
"""``True`` if Python is being run inside a virtual environment"""
|
|
|
|
@property
|
|
def is_linux(self) -> bool:
|
|
"""``True`` if running on a Linux system otherwise ``False``."""
|
|
return self.system == "linux"
|
|
|
|
@property
|
|
def is_macos(self) -> bool:
|
|
"""``True`` if running on a macOS system otherwise ``False``."""
|
|
return self.system == "darwin"
|
|
|
|
@property
|
|
def is_windows(self) -> bool:
|
|
"""``True`` if running on a Windows system otherwise ``False``."""
|
|
return self.system == "windows"
|
|
|
|
def __repr__(self) -> str:
|
|
"""Pretty print the system information for logging"""
|
|
attrs = ", ".join(f"{k}={repr(v)}" for k, v in self.__dict__.items()
|
|
if not k.startswith("_"))
|
|
return f"{self.__class__.__name__}({attrs})"
|
|
|
|
def _get_permissions(self) -> bool:
|
|
"""Check whether user is admin
|
|
|
|
Returns
|
|
-------
|
|
``True`` if we are running with Admin privileges
|
|
"""
|
|
if self.is_windows:
|
|
retval = ctypes.windll.shell32.IsUserAnAdmin() != 0 # type:ignore[attr-defined]
|
|
else:
|
|
retval = os.getuid() == 0 # type:ignore[attr-defined] # pylint:disable=no-member
|
|
return retval
|
|
|
|
def _check_virtual_env(self) -> bool:
|
|
"""Check whether we are in a virtual environment
|
|
|
|
Returns
|
|
-------
|
|
``True`` if Python is being run inside 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
|
|
|
|
def validate_python(self, max_version: tuple[int, int] | None = None) -> bool:
|
|
"""Check that the running Python version is valid
|
|
|
|
Parameters
|
|
----------
|
|
max_version
|
|
The max version to validate Python against. ``None`` for the project Maximum.
|
|
Default: ``None`` (project maximum)
|
|
|
|
Returns
|
|
-------
|
|
``True`` if the running Python version is valid, otherwise logs an error and exits
|
|
"""
|
|
max_python = VALID_PYTHON[1] if max_version is None else max_version
|
|
retval = (VALID_PYTHON[0] <= sys.version_info[:2] <= max_python
|
|
and self.python_architecture == "64bit")
|
|
logger.debug("Python version %s(%s) within %s - %s(64bit): %s",
|
|
self.python_version,
|
|
self.python_architecture,
|
|
VALID_PYTHON[0],
|
|
max_python,
|
|
retval)
|
|
if not retval:
|
|
print()
|
|
logger.error("Your Python version %s(%s) is unsupported. Please run with Python "
|
|
"version %s to %s 64bit.",
|
|
self.python_version,
|
|
self.python_architecture,
|
|
".".join(str(x) for x in VALID_PYTHON[0]),
|
|
".".join(str(x) for x in max_python))
|
|
print()
|
|
logger.error("If you have recently upgraded faceswap, then you will need to create a "
|
|
"new virtual environment.")
|
|
logger.error("The easiest way to do this is to run the latest version of the Faceswap "
|
|
"installer from:")
|
|
logger.error("https://github.com/deepfakes/faceswap/releases")
|
|
print()
|
|
input("Press <Enter> to close")
|
|
sys.exit(1)
|
|
|
|
return retval
|
|
|
|
def validate(self) -> None:
|
|
"""Perform validation that the running system can be used for faceswap. Log an error and
|
|
exit if it cannot"""
|
|
if not any((self.is_linux, self.is_macos, self.is_windows)):
|
|
logger.error("Your system %s is not supported!", self.system.title())
|
|
sys.exit(1)
|
|
if self.is_macos and self.machine == "arm64" and not self.is_conda:
|
|
logger.error("Setting up Faceswap for Apple Silicon outside of a Conda "
|
|
"environment is unsupported")
|
|
sys.exit(1)
|
|
self.validate_python()
|
|
|
|
|
|
class Packages():
|
|
"""Holds information about installed python and conda packages.
|
|
|
|
Note: Packaging library is lazy loaded as it may not be available during setup.py
|
|
"""
|
|
def __init__(self) -> None:
|
|
self._conda_exe = which("conda")
|
|
self._installed_python = self._get_installed_python()
|
|
self._installed_conda: list[str] | None = None
|
|
self._get_installed_conda()
|
|
|
|
@property
|
|
def installed_python(self) -> dict[str, str]:
|
|
"""Installed Python package names to Python package versions"""
|
|
return self._installed_python
|
|
|
|
@property
|
|
def installed_python_pretty(self) -> str:
|
|
"""A pretty printed representation of installed Python packages"""
|
|
pkgs = self._installed_python
|
|
align = max(len(x) for x in pkgs) + 1
|
|
return "\n".join(f"{k.ljust(align)} {v}" for k, v in pkgs.items())
|
|
|
|
@property
|
|
def installed_conda(self) -> dict[str, tuple[str, str, str]]:
|
|
"""Installed Conda package names to the version and channel"""
|
|
if not self._installed_conda:
|
|
return {}
|
|
|
|
installed = [re.sub(" +", " ", line.strip())
|
|
for line in self._installed_conda if not line.startswith("#")]
|
|
retval = {}
|
|
for pkg in installed:
|
|
item = pkg.split(" ")
|
|
assert len(item) == 4
|
|
retval[item[0]] = T.cast(tuple[str, str, str], tuple(item[1:]))
|
|
return retval
|
|
|
|
@property
|
|
def installed_conda_pretty(self) -> str:
|
|
"""A pretty printed representation of installed conda packages"""
|
|
if not self._installed_conda:
|
|
return "Could not get Conda package list"
|
|
return "\n".join(self._installed_conda)
|
|
|
|
def __repr__(self) -> str:
|
|
"""Pretty print the installed packages for logging"""
|
|
props = ", ".join(
|
|
f"{k}={repr(getattr(self, k))}"
|
|
for k, v in self.__class__.__dict__.items()
|
|
if isinstance(v, property) and not k.startswith("_") and "pretty" not in k)
|
|
return f"{self.__class__.__name__}({props})"
|
|
|
|
def _get_installed_python(self) -> dict[str, str]:
|
|
"""Parse the installed python modules
|
|
|
|
Returns
|
|
-------
|
|
Installed Python package names to Python package versions
|
|
"""
|
|
installed = _lines_from_command([sys.executable, "-m", "pip", "freeze", "--local"])
|
|
retval = {}
|
|
for pkg in installed:
|
|
if "==" not in pkg:
|
|
continue
|
|
item = pkg.split("==")
|
|
retval[item[0].lower()] = item[1]
|
|
logger.debug("Installed Python packages: %s", retval)
|
|
return retval
|
|
|
|
def _get_installed_conda(self) -> None:
|
|
"""Collect the output from 'conda list' for the installed Conda packages and
|
|
populate :attr:`_installed_conda`
|
|
|
|
Returns
|
|
-------
|
|
Each line of output from the 'conda list' command
|
|
"""
|
|
if not self._conda_exe:
|
|
logger.debug("Conda not found. Not collecting packages")
|
|
return
|
|
|
|
lines = _lines_from_command([self._conda_exe, "list", "--show-channel-urls"])
|
|
if not lines:
|
|
self._installed_conda = ["Could not get Conda package list"]
|
|
return
|
|
self._installed_conda = lines
|
|
logger.debug("Installed Conda packages: %s", self.installed_conda)
|
|
|
|
|
|
__all__ = get_module_objects(__name__)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(System())
|
|
print(Packages())
|