This repository has been archived on 2025-09-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Employee-Management-Portal/Lib/site-packages/pipenv/installers.py
Alicja Cięciwa cb8886666c login page
2020-10-27 12:57:58 +01:00

225 lines
7.4 KiB
Python

import os
import operator
import re
import six
from abc import ABCMeta, abstractmethod
from .environments import PIPENV_INSTALL_TIMEOUT
from .vendor import attr, delegator
from .utils import find_windows_executable
@attr.s
class Version(object):
major = attr.ib()
minor = attr.ib()
patch = attr.ib()
def __str__(self):
parts = [self.major, self.minor]
if self.patch is not None:
parts.append(self.patch)
return '.'.join(str(p) for p in parts)
@classmethod
def parse(cls, name):
"""Parse an X.Y.Z or X.Y string into a version tuple.
"""
match = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?$', name)
if not match:
raise ValueError('invalid version name {0!r}'.format(name))
major = int(match.group(1))
minor = int(match.group(2))
patch = match.group(3)
if patch is not None:
patch = int(patch)
return cls(major, minor, patch)
@property
def cmpkey(self):
"""Make the version a comparable tuple.
Some old Python versions does not have a patch part, e.g. 2.7.0 is
named "2.7" in pyenv. Fix that, otherwise `None` will fail to compare
with int.
"""
return (self.major, self.minor, self.patch or 0)
def matches_minor(self, other):
"""Check whether this version matches the other in (major, minor).
"""
return (self.major, self.minor) == (other.major, other.minor)
class InstallerNotFound(RuntimeError):
pass
class InstallerError(RuntimeError):
def __init__(self, desc, c):
super(InstallerError, self).__init__(desc)
self.out = c.out
self.err = c.err
@six.add_metaclass(ABCMeta)
class Installer(object):
def __init__(self):
self.cmd = self._find_installer()
super(Installer, self).__init__()
def __str__(self):
return self.__class__.__name__
@abstractmethod
def _find_installer(self):
pass
@staticmethod
def _find_python_installer_by_name_and_env(name, env_var):
"""
Given a python installer (pyenv or asdf), try to locate the binary for that
installer.
pyenv/asdf are not always present on PATH. Both installers also support a
custom environment variable (PYENV_ROOT or ASDF_DIR) which alows them to
be installed into a non-default location (the default/suggested source
install location is in ~/.pyenv or ~/.asdf).
For systems without the installers on PATH, and with a custom location
(e.g. /opt/pyenv), Pipenv can use those installers without modifications to
PATH, if an installer's respective environment variable is present in an
environment's .env file.
This function searches for installer binaries in the following locations,
by precedence:
1. On PATH, equivalent to which(1).
2. In the "bin" subdirectory of PYENV_ROOT or ASDF_DIR, depending on the
installer.
3. In ~/.pyenv/bin or ~/.asdf/bin, depending on the installer.
"""
for candidate in (
# Look for the Python installer using the equivalent of 'which'. On
# Homebrew-installed systems, the env var may not be set, but this
# strategy will work.
find_windows_executable('', name),
# Check for explicitly set install locations (e.g. PYENV_ROOT, ASDF_DIR).
os.path.join(os.path.expanduser(os.getenv(env_var, '/dev/null')), 'bin', name),
# Check the pyenv/asdf-recommended from-source install locations
os.path.join(os.path.expanduser('~/.{}'.format(name)), 'bin', name),
):
if candidate is not None and os.path.isfile(candidate) and os.access(candidate, os.X_OK):
return candidate
raise InstallerNotFound()
def _run(self, *args, **kwargs):
timeout = kwargs.pop('timeout', delegator.TIMEOUT)
if kwargs:
k = list(kwargs.keys())[0]
raise TypeError('unexpected keyword argument {0!r}'.format(k))
args = (self.cmd,) + tuple(args)
c = delegator.run(args, block=False, timeout=timeout)
c.block()
if c.return_code != 0:
raise InstallerError('failed to run {0}'.format(args), c)
return c
@abstractmethod
def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install.
"""
pass
def find_version_to_install(self, name):
"""Find a version in the installer from the version supplied.
A ValueError is raised if a matching version cannot be found.
"""
version = Version.parse(name)
if version.patch is not None:
return name
try:
best_match = max((
inst_version
for inst_version in self.iter_installable_versions()
if inst_version.matches_minor(version)
), key=operator.attrgetter('cmpkey'))
except ValueError:
raise ValueError(
'no installable version found for {0!r}'.format(name),
)
return best_match
@abstractmethod
def install(self, version):
"""Install the given version with runner implementation.
The version must be a ``Version`` instance representing a version
found in the Installer.
A ValueError is raised if the given version does not have a match in
the runner. A InstallerError is raised if the runner command fails.
"""
pass
class Pyenv(Installer):
def _find_installer(self):
return self._find_python_installer_by_name_and_env('pyenv', 'PYENV_ROOT')
def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install.
"""
for name in self._run('install', '--list').out.splitlines():
try:
version = Version.parse(name.strip())
except ValueError:
continue
yield version
def install(self, version):
"""Install the given version with pyenv.
The version must be a ``Version`` instance representing a version
found in pyenv.
A ValueError is raised if the given version does not have a match in
pyenv. A InstallerError is raised if the pyenv command fails.
"""
c = self._run(
'install', '-s', str(version),
timeout=PIPENV_INSTALL_TIMEOUT,
)
return c
class Asdf(Installer):
def _find_installer(self):
return self._find_python_installer_by_name_and_env('asdf', 'ASDF_DIR')
def iter_installable_versions(self):
"""Iterate through CPython versions available for asdf to install.
"""
for name in self._run('list-all', 'python').out.splitlines():
try:
version = Version.parse(name.strip())
except ValueError:
continue
yield version
def install(self, version):
"""Install the given version with asdf.
The version must be a ``Version`` instance representing a version
found in asdf.
A ValueError is raised if the given version does not have a match in
asdf. A InstallerError is raised if the asdf command fails.
"""
c = self._run(
'install', 'python', str(version),
timeout=PIPENV_INSTALL_TIMEOUT,
)
return c