123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- # The following comment should be removed at some point in the future.
- # mypy: strict-optional=False
- from __future__ import absolute_import
- import logging
- import os
- import shutil
- import sys
- import zipfile
- from pip._vendor import pkg_resources, six
- from pip._vendor.packaging.requirements import Requirement
- from pip._vendor.packaging.utils import canonicalize_name
- from pip._vendor.packaging.version import Version
- from pip._vendor.packaging.version import parse as parse_version
- from pip._vendor.pep517.wrappers import Pep517HookCaller
- from pip._internal.build_env import NoOpBuildEnvironment
- from pip._internal.exceptions import InstallationError
- from pip._internal.locations import get_scheme
- from pip._internal.models.link import Link
- from pip._internal.operations.build.metadata import generate_metadata
- from pip._internal.operations.build.metadata_legacy import \
- generate_metadata as generate_metadata_legacy
- from pip._internal.operations.install.editable_legacy import \
- install_editable as install_editable_legacy
- from pip._internal.operations.install.legacy import LegacyInstallFailure
- from pip._internal.operations.install.legacy import install as install_legacy
- from pip._internal.operations.install.wheel import install_wheel
- from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path
- from pip._internal.req.req_uninstall import UninstallPathSet
- from pip._internal.utils.deprecation import deprecated
- from pip._internal.utils.direct_url_helpers import direct_url_from_link
- from pip._internal.utils.hashes import Hashes
- from pip._internal.utils.logging import indent_log
- from pip._internal.utils.misc import (
- ask_path_exists,
- backup_dir,
- display_path,
- dist_in_site_packages,
- dist_in_usersite,
- get_installed_version,
- hide_url,
- redact_auth_from_url,
- )
- from pip._internal.utils.packaging import get_metadata
- from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
- from pip._internal.utils.typing import MYPY_CHECK_RUNNING
- from pip._internal.utils.virtualenv import running_under_virtualenv
- from pip._internal.vcs import vcs
- if MYPY_CHECK_RUNNING:
- from typing import (
- Any, Dict, Iterable, List, Optional, Sequence, Union,
- )
- from pip._internal.build_env import BuildEnvironment
- from pip._vendor.pkg_resources import Distribution
- from pip._vendor.packaging.specifiers import SpecifierSet
- from pip._vendor.packaging.markers import Marker
- logger = logging.getLogger(__name__)
- def _get_dist(metadata_directory):
- # type: (str) -> Distribution
- """Return a pkg_resources.Distribution for the provided
- metadata directory.
- """
- dist_dir = metadata_directory.rstrip(os.sep)
- # Build a PathMetadata object, from path to metadata. :wink:
- base_dir, dist_dir_name = os.path.split(dist_dir)
- metadata = pkg_resources.PathMetadata(base_dir, dist_dir)
- # Determine the correct Distribution object type.
- if dist_dir.endswith(".egg-info"):
- dist_cls = pkg_resources.Distribution
- dist_name = os.path.splitext(dist_dir_name)[0]
- else:
- assert dist_dir.endswith(".dist-info")
- dist_cls = pkg_resources.DistInfoDistribution
- dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0]
- return dist_cls(
- base_dir,
- project_name=dist_name,
- metadata=metadata,
- )
- class InstallRequirement(object):
- """
- Represents something that may be installed later on, may have information
- about where to fetch the relevant requirement and also contains logic for
- installing the said requirement.
- """
- def __init__(
- self,
- req, # type: Optional[Requirement]
- comes_from, # type: Optional[Union[str, InstallRequirement]]
- editable=False, # type: bool
- link=None, # type: Optional[Link]
- markers=None, # type: Optional[Marker]
- use_pep517=None, # type: Optional[bool]
- isolated=False, # type: bool
- install_options=None, # type: Optional[List[str]]
- global_options=None, # type: Optional[List[str]]
- hash_options=None, # type: Optional[Dict[str, List[str]]]
- constraint=False, # type: bool
- extras=() # type: Iterable[str]
- ):
- # type: (...) -> None
- assert req is None or isinstance(req, Requirement), req
- self.req = req
- self.comes_from = comes_from
- self.constraint = constraint
- self.editable = editable
- # source_dir is the local directory where the linked requirement is
- # located, or unpacked. In case unpacking is needed, creating and
- # populating source_dir is done by the RequirementPreparer. Note this
- # is not necessarily the directory where pyproject.toml or setup.py is
- # located - that one is obtained via unpacked_source_directory.
- self.source_dir = None # type: Optional[str]
- if self.editable:
- assert link
- if link.is_file:
- self.source_dir = os.path.normpath(
- os.path.abspath(link.file_path)
- )
- if link is None and req and req.url:
- # PEP 508 URL requirement
- link = Link(req.url)
- self.link = self.original_link = link
- self.original_link_is_in_wheel_cache = False
- # Path to any downloaded or already-existing package.
- self.local_file_path = None # type: Optional[str]
- if self.link and self.link.is_file:
- self.local_file_path = self.link.file_path
- if extras:
- self.extras = extras
- elif req:
- self.extras = {
- pkg_resources.safe_extra(extra) for extra in req.extras
- }
- else:
- self.extras = set()
- if markers is None and req:
- markers = req.marker
- self.markers = markers
- # This holds the pkg_resources.Distribution object if this requirement
- # is already available:
- self.satisfied_by = None # type: Optional[Distribution]
- # Whether the installation process should try to uninstall an existing
- # distribution before installing this requirement.
- self.should_reinstall = False
- # Temporary build location
- self._temp_build_dir = None # type: Optional[TempDirectory]
- # Set to True after successful installation
- self.install_succeeded = None # type: Optional[bool]
- # Supplied options
- self.install_options = install_options if install_options else []
- self.global_options = global_options if global_options else []
- self.hash_options = hash_options if hash_options else {}
- # Set to True after successful preparation of this requirement
- self.prepared = False
- self.is_direct = False
- # Set by the legacy resolver when the requirement has been downloaded
- # TODO: This introduces a strong coupling between the resolver and the
- # requirement (the coupling was previously between the resolver
- # and the requirement set). This should be refactored to allow
- # the requirement to decide for itself when it has been
- # successfully downloaded - but that is more tricky to get right,
- # se we are making the change in stages.
- self.successfully_downloaded = False
- self.isolated = isolated
- self.build_env = NoOpBuildEnvironment() # type: BuildEnvironment
- # For PEP 517, the directory where we request the project metadata
- # gets stored. We need this to pass to build_wheel, so the backend
- # can ensure that the wheel matches the metadata (see the PEP for
- # details).
- self.metadata_directory = None # type: Optional[str]
- # The static build requirements (from pyproject.toml)
- self.pyproject_requires = None # type: Optional[List[str]]
- # Build requirements that we will check are available
- self.requirements_to_check = [] # type: List[str]
- # The PEP 517 backend we should use to build the project
- self.pep517_backend = None # type: Optional[Pep517HookCaller]
- # Are we using PEP 517 for this requirement?
- # After pyproject.toml has been loaded, the only valid values are True
- # and False. Before loading, None is valid (meaning "use the default").
- # Setting an explicit value before loading pyproject.toml is supported,
- # but after loading this flag should be treated as read only.
- self.use_pep517 = use_pep517
- def __str__(self):
- # type: () -> str
- if self.req:
- s = str(self.req)
- if self.link:
- s += ' from {}'.format(redact_auth_from_url(self.link.url))
- elif self.link:
- s = redact_auth_from_url(self.link.url)
- else:
- s = '<InstallRequirement>'
- if self.satisfied_by is not None:
- s += ' in {}'.format(display_path(self.satisfied_by.location))
- if self.comes_from:
- if isinstance(self.comes_from, six.string_types):
- comes_from = self.comes_from # type: Optional[str]
- else:
- comes_from = self.comes_from.from_path()
- if comes_from:
- s += ' (from {})'.format(comes_from)
- return s
- def __repr__(self):
- # type: () -> str
- return '<{} object: {} editable={!r}>'.format(
- self.__class__.__name__, str(self), self.editable)
- def format_debug(self):
- # type: () -> str
- """An un-tested helper for getting state, for debugging.
- """
- attributes = vars(self)
- names = sorted(attributes)
- state = (
- "{}={!r}".format(attr, attributes[attr]) for attr in sorted(names)
- )
- return '<{name} object: {{{state}}}>'.format(
- name=self.__class__.__name__,
- state=", ".join(state),
- )
- # Things that are valid for all kinds of requirements?
- @property
- def name(self):
- # type: () -> Optional[str]
- if self.req is None:
- return None
- return six.ensure_str(pkg_resources.safe_name(self.req.name))
- @property
- def specifier(self):
- # type: () -> SpecifierSet
- return self.req.specifier
- @property
- def is_pinned(self):
- # type: () -> bool
- """Return whether I am pinned to an exact version.
- For example, some-package==1.2 is pinned; some-package>1.2 is not.
- """
- specifiers = self.specifier
- return (len(specifiers) == 1 and
- next(iter(specifiers)).operator in {'==', '==='})
- @property
- def installed_version(self):
- # type: () -> Optional[str]
- return get_installed_version(self.name)
- def match_markers(self, extras_requested=None):
- # type: (Optional[Iterable[str]]) -> bool
- if not extras_requested:
- # Provide an extra to safely evaluate the markers
- # without matching any extra
- extras_requested = ('',)
- if self.markers is not None:
- return any(
- self.markers.evaluate({'extra': extra})
- for extra in extras_requested)
- else:
- return True
- @property
- def has_hash_options(self):
- # type: () -> bool
- """Return whether any known-good hashes are specified as options.
- These activate --require-hashes mode; hashes specified as part of a
- URL do not.
- """
- return bool(self.hash_options)
- def hashes(self, trust_internet=True):
- # type: (bool) -> Hashes
- """Return a hash-comparer that considers my option- and URL-based
- hashes to be known-good.
- Hashes in URLs--ones embedded in the requirements file, not ones
- downloaded from an index server--are almost peers with ones from
- flags. They satisfy --require-hashes (whether it was implicitly or
- explicitly activated) but do not activate it. md5 and sha224 are not
- allowed in flags, which should nudge people toward good algos. We
- always OR all hashes together, even ones from URLs.
- :param trust_internet: Whether to trust URL-based (#md5=...) hashes
- downloaded from the internet, as by populate_link()
- """
- good_hashes = self.hash_options.copy()
- link = self.link if trust_internet else self.original_link
- if link and link.hash:
- good_hashes.setdefault(link.hash_name, []).append(link.hash)
- return Hashes(good_hashes)
- def from_path(self):
- # type: () -> Optional[str]
- """Format a nice indicator to show where this "comes from"
- """
- if self.req is None:
- return None
- s = str(self.req)
- if self.comes_from:
- if isinstance(self.comes_from, six.string_types):
- comes_from = self.comes_from
- else:
- comes_from = self.comes_from.from_path()
- if comes_from:
- s += '->' + comes_from
- return s
- def ensure_build_location(self, build_dir, autodelete):
- # type: (str, bool) -> str
- assert build_dir is not None
- if self._temp_build_dir is not None:
- assert self._temp_build_dir.path
- return self._temp_build_dir.path
- if self.req is None:
- # Some systems have /tmp as a symlink which confuses custom
- # builds (such as numpy). Thus, we ensure that the real path
- # is returned.
- self._temp_build_dir = TempDirectory(
- kind=tempdir_kinds.REQ_BUILD, globally_managed=True
- )
- return self._temp_build_dir.path
- if self.editable:
- name = self.name.lower()
- else:
- name = self.name
- # FIXME: Is there a better place to create the build_dir? (hg and bzr
- # need this)
- if not os.path.exists(build_dir):
- logger.debug('Creating directory %s', build_dir)
- os.makedirs(build_dir)
- actual_build_dir = os.path.join(build_dir, name)
- # `None` indicates that we respect the globally-configured deletion
- # settings, which is what we actually want when auto-deleting.
- delete_arg = None if autodelete else False
- return TempDirectory(
- path=actual_build_dir,
- delete=delete_arg,
- kind=tempdir_kinds.REQ_BUILD,
- globally_managed=True,
- ).path
- def _set_requirement(self):
- # type: () -> None
- """Set requirement after generating metadata.
- """
- assert self.req is None
- assert self.metadata is not None
- assert self.source_dir is not None
- # Construct a Requirement object from the generated metadata
- if isinstance(parse_version(self.metadata["Version"]), Version):
- op = "=="
- else:
- op = "==="
- self.req = Requirement(
- "".join([
- self.metadata["Name"],
- op,
- self.metadata["Version"],
- ])
- )
- def warn_on_mismatching_name(self):
- # type: () -> None
- metadata_name = canonicalize_name(self.metadata["Name"])
- if canonicalize_name(self.req.name) == metadata_name:
- # Everything is fine.
- return
- # If we're here, there's a mismatch. Log a warning about it.
- logger.warning(
- 'Generating metadata for package %s '
- 'produced metadata for project name %s. Fix your '
- '#egg=%s fragments.',
- self.name, metadata_name, self.name
- )
- self.req = Requirement(metadata_name)
- def check_if_exists(self, use_user_site):
- # type: (bool) -> None
- """Find an installed distribution that satisfies or conflicts
- with this requirement, and set self.satisfied_by or
- self.should_reinstall appropriately.
- """
- if self.req is None:
- return
- # get_distribution() will resolve the entire list of requirements
- # anyway, and we've already determined that we need the requirement
- # in question, so strip the marker so that we don't try to
- # evaluate it.
- no_marker = Requirement(str(self.req))
- no_marker.marker = None
- try:
- self.satisfied_by = pkg_resources.get_distribution(str(no_marker))
- except pkg_resources.DistributionNotFound:
- return
- except pkg_resources.VersionConflict:
- existing_dist = pkg_resources.get_distribution(
- self.req.name
- )
- if use_user_site:
- if dist_in_usersite(existing_dist):
- self.should_reinstall = True
- elif (running_under_virtualenv() and
- dist_in_site_packages(existing_dist)):
- raise InstallationError(
- "Will not install to the user site because it will "
- "lack sys.path precedence to {} in {}".format(
- existing_dist.project_name, existing_dist.location)
- )
- else:
- self.should_reinstall = True
- else:
- if self.editable and self.satisfied_by:
- self.should_reinstall = True
- # when installing editables, nothing pre-existing should ever
- # satisfy
- self.satisfied_by = None
- # Things valid for wheels
- @property
- def is_wheel(self):
- # type: () -> bool
- if not self.link:
- return False
- return self.link.is_wheel
- # Things valid for sdists
- @property
- def unpacked_source_directory(self):
- # type: () -> str
- return os.path.join(
- self.source_dir,
- self.link and self.link.subdirectory_fragment or '')
- @property
- def setup_py_path(self):
- # type: () -> str
- assert self.source_dir, "No source dir for {}".format(self)
- setup_py = os.path.join(self.unpacked_source_directory, 'setup.py')
- # Python2 __file__ should not be unicode
- if six.PY2 and isinstance(setup_py, six.text_type):
- setup_py = setup_py.encode(sys.getfilesystemencoding())
- return setup_py
- @property
- def pyproject_toml_path(self):
- # type: () -> str
- assert self.source_dir, "No source dir for {}".format(self)
- return make_pyproject_path(self.unpacked_source_directory)
- def load_pyproject_toml(self):
- # type: () -> None
- """Load the pyproject.toml file.
- After calling this routine, all of the attributes related to PEP 517
- processing for this requirement have been set. In particular, the
- use_pep517 attribute can be used to determine whether we should
- follow the PEP 517 or legacy (setup.py) code path.
- """
- pyproject_toml_data = load_pyproject_toml(
- self.use_pep517,
- self.pyproject_toml_path,
- self.setup_py_path,
- str(self)
- )
- if pyproject_toml_data is None:
- self.use_pep517 = False
- return
- self.use_pep517 = True
- requires, backend, check, backend_path = pyproject_toml_data
- self.requirements_to_check = check
- self.pyproject_requires = requires
- self.pep517_backend = Pep517HookCaller(
- self.unpacked_source_directory, backend, backend_path=backend_path,
- )
- def _generate_metadata(self):
- # type: () -> str
- """Invokes metadata generator functions, with the required arguments.
- """
- if not self.use_pep517:
- assert self.unpacked_source_directory
- return generate_metadata_legacy(
- build_env=self.build_env,
- setup_py_path=self.setup_py_path,
- source_dir=self.unpacked_source_directory,
- isolated=self.isolated,
- details=self.name or "from {}".format(self.link)
- )
- assert self.pep517_backend is not None
- return generate_metadata(
- build_env=self.build_env,
- backend=self.pep517_backend,
- )
- def prepare_metadata(self):
- # type: () -> None
- """Ensure that project metadata is available.
- Under PEP 517, call the backend hook to prepare the metadata.
- Under legacy processing, call setup.py egg-info.
- """
- assert self.source_dir
- with indent_log():
- self.metadata_directory = self._generate_metadata()
- # Act on the newly generated metadata, based on the name and version.
- if not self.name:
- self._set_requirement()
- else:
- self.warn_on_mismatching_name()
- self.assert_source_matches_version()
- @property
- def metadata(self):
- # type: () -> Any
- if not hasattr(self, '_metadata'):
- self._metadata = get_metadata(self.get_dist())
- return self._metadata
- def get_dist(self):
- # type: () -> Distribution
- return _get_dist(self.metadata_directory)
- def assert_source_matches_version(self):
- # type: () -> None
- assert self.source_dir
- version = self.metadata['version']
- if self.req.specifier and version not in self.req.specifier:
- logger.warning(
- 'Requested %s, but installing version %s',
- self,
- version,
- )
- else:
- logger.debug(
- 'Source in %s has version %s, which satisfies requirement %s',
- display_path(self.source_dir),
- version,
- self,
- )
- # For both source distributions and editables
- def ensure_has_source_dir(self, parent_dir, autodelete=False):
- # type: (str, bool) -> None
- """Ensure that a source_dir is set.
- This will create a temporary build dir if the name of the requirement
- isn't known yet.
- :param parent_dir: The ideal pip parent_dir for the source_dir.
- Generally src_dir for editables and build_dir for sdists.
- :return: self.source_dir
- """
- if self.source_dir is None:
- self.source_dir = self.ensure_build_location(
- parent_dir, autodelete
- )
- # For editable installations
- def update_editable(self, obtain=True):
- # type: (bool) -> None
- if not self.link:
- logger.debug(
- "Cannot update repository at %s; repository location is "
- "unknown",
- self.source_dir,
- )
- return
- assert self.editable
- assert self.source_dir
- if self.link.scheme == 'file':
- # Static paths don't get updated
- return
- assert '+' in self.link.url, \
- "bad url: {self.link.url!r}".format(**locals())
- vc_type, url = self.link.url.split('+', 1)
- vcs_backend = vcs.get_backend(vc_type)
- if vcs_backend:
- if not self.link.is_vcs:
- reason = (
- "This form of VCS requirement is being deprecated: {}."
- ).format(
- self.link.url
- )
- replacement = None
- if self.link.url.startswith("git+git@"):
- replacement = (
- "git+https://git@example.com/..., "
- "git+ssh://git@example.com/..., "
- "or the insecure git+git://git@example.com/..."
- )
- deprecated(reason, replacement, gone_in="21.0", issue=7554)
- hidden_url = hide_url(self.link.url)
- if obtain:
- vcs_backend.obtain(self.source_dir, url=hidden_url)
- else:
- vcs_backend.export(self.source_dir, url=hidden_url)
- else:
- assert 0, (
- 'Unexpected version control type (in {}): {}'.format(
- self.link, vc_type))
- # Top-level Actions
- def uninstall(self, auto_confirm=False, verbose=False):
- # type: (bool, bool) -> Optional[UninstallPathSet]
- """
- Uninstall the distribution currently satisfying this requirement.
- Prompts before removing or modifying files unless
- ``auto_confirm`` is True.
- Refuses to delete or modify files outside of ``sys.prefix`` -
- thus uninstallation within a virtual environment can only
- modify that virtual environment, even if the virtualenv is
- linked to global site-packages.
- """
- assert self.req
- try:
- dist = pkg_resources.get_distribution(self.req.name)
- except pkg_resources.DistributionNotFound:
- logger.warning("Skipping %s as it is not installed.", self.name)
- return None
- else:
- logger.info('Found existing installation: %s', dist)
- uninstalled_pathset = UninstallPathSet.from_dist(dist)
- uninstalled_pathset.remove(auto_confirm, verbose)
- return uninstalled_pathset
- def _get_archive_name(self, path, parentdir, rootdir):
- # type: (str, str, str) -> str
- def _clean_zip_name(name, prefix):
- # type: (str, str) -> str
- assert name.startswith(prefix + os.path.sep), (
- "name {name!r} doesn't start with prefix {prefix!r}"
- .format(**locals())
- )
- name = name[len(prefix) + 1:]
- name = name.replace(os.path.sep, '/')
- return name
- path = os.path.join(parentdir, path)
- name = _clean_zip_name(path, rootdir)
- return self.name + '/' + name
- def archive(self, build_dir):
- # type: (str) -> None
- """Saves archive to provided build_dir.
- Used for saving downloaded VCS requirements as part of `pip download`.
- """
- assert self.source_dir
- create_archive = True
- archive_name = '{}-{}.zip'.format(self.name, self.metadata["version"])
- archive_path = os.path.join(build_dir, archive_name)
- if os.path.exists(archive_path):
- response = ask_path_exists(
- 'The file {} exists. (i)gnore, (w)ipe, '
- '(b)ackup, (a)bort '.format(
- display_path(archive_path)),
- ('i', 'w', 'b', 'a'))
- if response == 'i':
- create_archive = False
- elif response == 'w':
- logger.warning('Deleting %s', display_path(archive_path))
- os.remove(archive_path)
- elif response == 'b':
- dest_file = backup_dir(archive_path)
- logger.warning(
- 'Backing up %s to %s',
- display_path(archive_path),
- display_path(dest_file),
- )
- shutil.move(archive_path, dest_file)
- elif response == 'a':
- sys.exit(-1)
- if not create_archive:
- return
- zip_output = zipfile.ZipFile(
- archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True,
- )
- with zip_output:
- dir = os.path.normcase(
- os.path.abspath(self.unpacked_source_directory)
- )
- for dirpath, dirnames, filenames in os.walk(dir):
- for dirname in dirnames:
- dir_arcname = self._get_archive_name(
- dirname, parentdir=dirpath, rootdir=dir,
- )
- zipdir = zipfile.ZipInfo(dir_arcname + '/')
- zipdir.external_attr = 0x1ED << 16 # 0o755
- zip_output.writestr(zipdir, '')
- for filename in filenames:
- file_arcname = self._get_archive_name(
- filename, parentdir=dirpath, rootdir=dir,
- )
- filename = os.path.join(dirpath, filename)
- zip_output.write(filename, file_arcname)
- logger.info('Saved %s', display_path(archive_path))
- def install(
- self,
- install_options, # type: List[str]
- global_options=None, # type: Optional[Sequence[str]]
- root=None, # type: Optional[str]
- home=None, # type: Optional[str]
- prefix=None, # type: Optional[str]
- warn_script_location=True, # type: bool
- use_user_site=False, # type: bool
- pycompile=True # type: bool
- ):
- # type: (...) -> None
- scheme = get_scheme(
- self.name,
- user=use_user_site,
- home=home,
- root=root,
- isolated=self.isolated,
- prefix=prefix,
- )
- global_options = global_options if global_options is not None else []
- if self.editable:
- install_editable_legacy(
- install_options,
- global_options,
- prefix=prefix,
- home=home,
- use_user_site=use_user_site,
- name=self.name,
- setup_py_path=self.setup_py_path,
- isolated=self.isolated,
- build_env=self.build_env,
- unpacked_source_directory=self.unpacked_source_directory,
- )
- self.install_succeeded = True
- return
- if self.is_wheel:
- assert self.local_file_path
- direct_url = None
- if self.original_link:
- direct_url = direct_url_from_link(
- self.original_link,
- self.source_dir,
- self.original_link_is_in_wheel_cache,
- )
- install_wheel(
- self.name,
- self.local_file_path,
- scheme=scheme,
- req_description=str(self.req),
- pycompile=pycompile,
- warn_script_location=warn_script_location,
- direct_url=direct_url,
- )
- self.install_succeeded = True
- return
- # TODO: Why don't we do this for editable installs?
- # Extend the list of global and install options passed on to
- # the setup.py call with the ones from the requirements file.
- # Options specified in requirements file override those
- # specified on the command line, since the last option given
- # to setup.py is the one that is used.
- global_options = list(global_options) + self.global_options
- install_options = list(install_options) + self.install_options
- try:
- success = install_legacy(
- install_options=install_options,
- global_options=global_options,
- root=root,
- home=home,
- prefix=prefix,
- use_user_site=use_user_site,
- pycompile=pycompile,
- scheme=scheme,
- setup_py_path=self.setup_py_path,
- isolated=self.isolated,
- req_name=self.name,
- build_env=self.build_env,
- unpacked_source_directory=self.unpacked_source_directory,
- req_description=str(self.req),
- )
- except LegacyInstallFailure as exc:
- self.install_succeeded = False
- six.reraise(*exc.parent)
- except Exception:
- self.install_succeeded = True
- raise
- self.install_succeeded = success
|