Source code for hbutils.system.python.package

import importlib
import os
import pathlib
import subprocess
import sys
from typing import Dict, Optional
from typing import List

import pkg_resources
from packaging.version import Version

__all__ = [
    'package_version',
    'load_req_file', 'pip',
    'check_reqs', 'check_req_file',
    'pip_install', 'pip_install_req_file',
]

PIP_PACKAGES: Dict[str, str] = {}


def _init_pip_packages():
    global PIP_PACKAGES
    PIP_PACKAGES.clear()
    for i in pkg_resources.working_set:
        PIP_PACKAGES[i.key.lower()] = i.version


_init_pip_packages()


[docs]def package_version(name: str) -> Optional[Version]: """ Overview: Get version of package with given ``name``. :param name: Name of the package, case is not sensitive. :return: A :class:`packing.version.Version` object. If the package is not installed, return ``None``. Examples:: >>> from hbutils.system import package_version >>> >>> package_version('pip') <Version('21.3.1')> >>> package_version('setuptools') <Version('59.6.0')> >>> package_version('not_a_package') None """ _lower_name = name.lower() if _lower_name in PIP_PACKAGES: return pkg_resources.parse_version(PIP_PACKAGES[_lower_name]) else: return None
[docs]def load_req_file(requirements_file: str) -> List[str]: """ Overview: Load requirements items from a ``requirements.txt`` file. :param requirements_file: Requirements file. :return requirements: List of requirements. Examples:: >>> from hbutils.system import load_req_file >>> load_req_file('requirements.txt') ['packaging>=21.3', 'setuptools>=50.0'] """ with pathlib.Path(requirements_file).open() as reqfile: return list(map(str, pkg_resources.parse_requirements(reqfile)))
[docs]def pip(*args, silent: bool = False): """ Overview: Run pip command with code. :param args: Command line arguments for ``pip`` command. :param silent: Do not print anything. Default is false, which means print the output to ``sys.stdout`` \ and ``sys.stderr``. Examples:: >>> from hbutils.system import pip >>> pip('-V') pip 22.3.1 from /home/user/myproject/venv/lib/python3.7/site-packages/pip (python 3.7) >>> pip('-V', silent=True) # nothing will be printed """ with open(os.devnull, 'w') as sout: try: process = subprocess.run( [sys.executable, '-m', 'pip', *args], stdin=sys.stdin if not silent else None, stdout=sys.stdout if not silent else sout, stderr=sys.stderr if not silent else sout, ) process.check_returncode() finally: if args and args[0] in {'install', 'uninstall'}: importlib.reload(pkg_resources) _init_pip_packages()
[docs]def check_reqs(reqs: List[str]) -> bool: """ Overview: Check if the given requirements are all satisfied. :param reqs: List of requirements. :return satisfied: All the requirements in ``reqs`` satisfied or not. Examples:: >>> from hbutils.system import check_reqs >>> check_reqs(['pip>=20.0']) True >>> check_reqs(['pip~=19.2']) False >>> check_reqs(['pip>=20.0', 'setuptools>=50.0']) True """ try: pkg_resources.require(reqs) except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict): return False else: return True
[docs]def check_req_file(requirements_file: str) -> bool: """ Overview: Check if the requirements in the given ``requirements_file`` is satisfied. :param requirements_file: Requirements file, such as ``requirements.txt``. :return satisfied: All the requirements in ``requirements_file`` satisfied or not. Examples:: >>> from hbutils.system import check_req_file >>> >>> check_req_file('requirements.txt') True >>> check_req_file('requirements-test.txt') True """ return check_reqs(load_req_file(requirements_file))
[docs]def pip_install(reqs: List[str], silent: bool = False, force: bool = False, user: bool = False): """ Overview: Pip install requirements with code. Similar to ``pip install req1 req2 ...``. :param reqs: Requirement items to install. :param silent: Do not print anything. Default is ``False``. :param force: Force execute the ``pip install`` command. Default is ``False`` which means the requirements \ will be checked before installation, and the installation will be only executed when \ some requirements not installed. :param user: User mode, represents ``--user`` option in ``pip``. Examples:: >>> from hbutils.system import pip_install >>> pip_install(['scikit-learn']) # not installed Looking in indexes: https://xxx/simple Collecting scikit-learn Using cached https://xxx/scikit_learn-1.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (24.8 MB) Installing collected packages: threadpoolctl, scipy, joblib, scikit-learn Successfully installed joblib-1.2.0 scikit-learn-1.0.2 scipy-1.7.3 threadpoolctl-3.1.0 >>> pip_install(['numpy>=1.10.0']) # installed >>> pip_install(['numpy>=1.10.0'], force=True) # force execute Looking in indexes: https://xxx/simple Requirement already satisfied: numpy>=1.10.0 in ./venv/lib/python3.7/site-packages (1.21.6) """ if force or not check_reqs(reqs): pip('install', *(('--user',) if user else ()), *reqs, silent=silent)
[docs]def pip_install_req_file(requirements_file: str, silent: bool = False, force: bool = False, user: bool = False): """ Overview: Pip install requirements from file with code. Similar to ``pip install -r requirements.txt``. :param requirements_file: Requirements file, such as ``requirements.txt``. :param silent: Do not print anything. Default is ``False``. :param force: Force execute the ``pip install`` command. Default is ``False`` which means the requirements \ will be checked before installation, and the installation will be only executed when \ some requirements not installed. :param user: User mode, represents ``--user`` option in ``pip``. Examples:: >>> from hbutils.system import pip_install_req_file >>> pip_install_req_file('requirements.txt') # pip install -r requirements.txt """ if force or not check_req_file(requirements_file): pip('install', *(('--user',) if user else ()), '-r', requirements_file, silent=silent)