2021-04-09 20:07:38 +02:00
|
|
|
import os
|
2021-03-13 22:53:28 +01:00
|
|
|
import subprocess
|
|
|
|
import sys
|
2021-04-09 20:07:38 +02:00
|
|
|
import shlex
|
2021-03-14 10:31:45 +01:00
|
|
|
from contextlib import suppress
|
2021-04-09 20:07:38 +02:00
|
|
|
from textwrap import dedent
|
2021-03-14 10:31:45 +01:00
|
|
|
from typing import Optional
|
2021-03-13 22:53:28 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Pip:
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Executes pip commands
|
|
|
|
"""
|
2021-03-14 13:22:10 +01:00
|
|
|
_executable = sys.executable
|
2021-04-09 20:07:38 +02:00
|
|
|
_env = os.environ
|
|
|
|
_is_venv = False
|
2021-03-13 22:53:28 +01:00
|
|
|
|
2021-03-14 13:22:10 +01:00
|
|
|
"""
|
|
|
|
Getter
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_executable(cls) -> str:
|
|
|
|
return cls._executable
|
|
|
|
|
|
|
|
"""
|
|
|
|
Setter
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def set_executable(cls, executable: str):
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Sets the executable
|
|
|
|
:param executable:
|
|
|
|
:return:
|
|
|
|
"""
|
2021-04-09 20:07:38 +02:00
|
|
|
if executable is not None and executable != sys.executable:
|
2021-03-14 13:22:10 +01:00
|
|
|
cls._executable = executable
|
2021-04-09 20:07:38 +02:00
|
|
|
if os.path.islink(cls._executable):
|
|
|
|
cls._is_venv = True
|
|
|
|
path = os.path.dirname(os.path.dirname(cls._executable))
|
|
|
|
cls._env = os.environ
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
cls._env['PATH'] = f'{path}\\bin' + os.pathsep + os.environ.get('PATH', '')
|
|
|
|
else:
|
|
|
|
cls._env['PATH'] = f'{path}/bin' + os.pathsep + os.environ.get('PATH', '')
|
|
|
|
cls._env['VIRTUAL_ENV'] = path
|
2021-03-14 13:22:10 +01:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def reset_executable(cls):
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Resets the executable to system standard
|
|
|
|
:return:
|
|
|
|
"""
|
2021-03-14 13:22:10 +01:00
|
|
|
cls._executable = sys.executable
|
2021-04-09 20:07:38 +02:00
|
|
|
cls._is_venv = False
|
2021-03-14 13:22:10 +01:00
|
|
|
|
|
|
|
"""
|
|
|
|
Public utils functions
|
|
|
|
"""
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_package(cls, package: str) -> Optional[str]:
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Gets given package py local pip list
|
|
|
|
:param package:
|
|
|
|
:return:
|
|
|
|
"""
|
2021-03-14 10:31:45 +01:00
|
|
|
result = None
|
|
|
|
with suppress(Exception):
|
2021-04-09 20:07:38 +02:00
|
|
|
args = [cls._executable, "-m", "pip", "show", package]
|
|
|
|
if cls._is_venv:
|
|
|
|
args = ["pip", "show", package]
|
|
|
|
|
|
|
|
result = subprocess.check_output(
|
|
|
|
args,
|
|
|
|
stderr=subprocess.DEVNULL, env=cls._env
|
|
|
|
)
|
2021-03-14 10:31:45 +01:00
|
|
|
|
|
|
|
if result is None:
|
|
|
|
return None
|
2021-03-13 22:53:28 +01:00
|
|
|
|
|
|
|
new_package: list[str] = str(result, 'utf-8').lower().split('\n')
|
|
|
|
new_version = ''
|
|
|
|
|
|
|
|
for atr in new_package:
|
|
|
|
if 'version' in atr:
|
|
|
|
new_version = atr.split(': ')[1]
|
|
|
|
|
2021-03-16 22:46:04 +01:00
|
|
|
if new_version != '':
|
|
|
|
return f'{package}=={new_version}'
|
|
|
|
|
|
|
|
return package
|
2021-03-13 22:53:28 +01:00
|
|
|
|
2021-03-14 13:22:10 +01:00
|
|
|
@classmethod
|
|
|
|
def get_outdated(cls) -> bytes:
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Gets table of outdated packages
|
|
|
|
:return:
|
|
|
|
"""
|
2021-04-09 20:07:38 +02:00
|
|
|
args = [cls._executable, "-m", "pip", "list", "--outdated"]
|
|
|
|
if cls._is_venv:
|
|
|
|
args = ["pip", "list", "--outdated"]
|
|
|
|
|
|
|
|
return subprocess.check_output(args, env=cls._env)
|
2021-03-14 13:22:10 +01:00
|
|
|
|
|
|
|
@classmethod
|
2021-03-14 14:47:53 +01:00
|
|
|
def install(cls, package: str, *args, source: str = None, stdout=None, stderr=None):
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Installs given package
|
|
|
|
:param package:
|
|
|
|
:param args:
|
|
|
|
:param source:
|
|
|
|
:param stdout:
|
|
|
|
:param stderr:
|
|
|
|
:return:
|
|
|
|
"""
|
2021-03-14 13:22:10 +01:00
|
|
|
pip_args = [cls._executable, "-m", "pip", "install"]
|
2021-04-09 20:07:38 +02:00
|
|
|
if cls._is_venv:
|
|
|
|
pip_args = ["pip", "install"]
|
2021-03-13 22:53:28 +01:00
|
|
|
|
|
|
|
for arg in args:
|
|
|
|
pip_args.append(arg)
|
|
|
|
|
|
|
|
if source is not None:
|
|
|
|
pip_args.append(f'--extra-index-url')
|
|
|
|
pip_args.append(source)
|
|
|
|
|
|
|
|
pip_args.append(package)
|
2021-04-09 20:07:38 +02:00
|
|
|
subprocess.run(pip_args, stdout=stdout, stderr=stderr, env=cls._env)
|
2021-03-14 10:58:59 +01:00
|
|
|
|
2021-03-14 13:22:10 +01:00
|
|
|
@classmethod
|
|
|
|
def uninstall(cls, package: str, stdout=None, stderr=None):
|
2021-03-14 16:06:23 +01:00
|
|
|
"""
|
|
|
|
Uninstalls given package
|
|
|
|
:param package:
|
|
|
|
:param stdout:
|
|
|
|
:param stderr:
|
|
|
|
:return:
|
|
|
|
"""
|
2021-04-09 20:07:38 +02:00
|
|
|
args = [cls._executable, "-m", "pip", "uninstall", "--yes", package]
|
|
|
|
if cls._is_venv:
|
|
|
|
args = ["pip", "uninstall", "--yes", package]
|
|
|
|
|
|
|
|
subprocess.run(
|
|
|
|
args,
|
|
|
|
stdout=stdout, stderr=stderr, env=cls._env
|
|
|
|
)
|