| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- """Metadata generation logic for source distributions.
- """
- import logging
- import os
- from pip._internal.exceptions import InstallationError
- from pip._internal.utils.misc import ensure_dir
- from pip._internal.utils.setuptools_build import make_setuptools_shim_args
- from pip._internal.utils.subprocess import call_subprocess
- from pip._internal.utils.typing import MYPY_CHECK_RUNNING
- from pip._internal.vcs import vcs
- if MYPY_CHECK_RUNNING:
- from typing import Callable, List
- from pip._internal.req.req_install import InstallRequirement
- logger = logging.getLogger(__name__)
- def get_metadata_generator(install_req):
- # type: (InstallRequirement) -> Callable[[InstallRequirement], str]
- """Return a callable metadata generator for this InstallRequirement.
- A metadata generator takes an InstallRequirement (install_req) as an input,
- generates metadata via the appropriate process for that install_req and
- returns the generated metadata directory.
- """
- if not install_req.use_pep517:
- return _generate_metadata_legacy
- return _generate_metadata
- def _find_egg_info(source_directory, is_editable):
- # type: (str, bool) -> str
- """Find an .egg-info in `source_directory`, based on `is_editable`.
- """
- def looks_like_virtual_env(path):
- # type: (str) -> bool
- return (
- os.path.lexists(os.path.join(path, 'bin', 'python')) or
- os.path.exists(os.path.join(path, 'Scripts', 'Python.exe'))
- )
- def locate_editable_egg_info(base):
- # type: (str) -> List[str]
- candidates = [] # type: List[str]
- for root, dirs, files in os.walk(base):
- for dir_ in vcs.dirnames:
- if dir_ in dirs:
- dirs.remove(dir_)
- # Iterate over a copy of ``dirs``, since mutating
- # a list while iterating over it can cause trouble.
- # (See https://github.com/pypa/pip/pull/462.)
- for dir_ in list(dirs):
- if looks_like_virtual_env(os.path.join(root, dir_)):
- dirs.remove(dir_)
- # Also don't search through tests
- elif dir_ == 'test' or dir_ == 'tests':
- dirs.remove(dir_)
- candidates.extend(os.path.join(root, dir_) for dir_ in dirs)
- return [f for f in candidates if f.endswith('.egg-info')]
- def depth_of_directory(dir_):
- # type: (str) -> int
- return (
- dir_.count(os.path.sep) +
- (os.path.altsep and dir_.count(os.path.altsep) or 0)
- )
- base = source_directory
- if is_editable:
- filenames = locate_editable_egg_info(base)
- else:
- base = os.path.join(base, 'pip-egg-info')
- filenames = os.listdir(base)
- if not filenames:
- raise InstallationError(
- "Files/directories not found in %s" % base
- )
- # If we have more than one match, we pick the toplevel one. This
- # can easily be the case if there is a dist folder which contains
- # an extracted tarball for testing purposes.
- if len(filenames) > 1:
- filenames.sort(key=depth_of_directory)
- return os.path.join(base, filenames[0])
- def _generate_metadata_legacy(install_req):
- # type: (InstallRequirement) -> str
- req_details_str = install_req.name or "from {}".format(install_req.link)
- logger.debug(
- 'Running setup.py (path:%s) egg_info for package %s',
- install_req.setup_py_path, req_details_str,
- )
- # Compose arguments for subprocess call
- base_cmd = make_setuptools_shim_args(install_req.setup_py_path)
- if install_req.isolated:
- base_cmd += ["--no-user-cfg"]
- # For non-editable installs, don't put the .egg-info files at the root,
- # to avoid confusion due to the source code being considered an installed
- # egg.
- egg_base_option = [] # type: List[str]
- if not install_req.editable:
- egg_info_dir = os.path.join(
- install_req.unpacked_source_directory, 'pip-egg-info',
- )
- egg_base_option = ['--egg-base', egg_info_dir]
- # setuptools complains if the target directory does not exist.
- ensure_dir(egg_info_dir)
- with install_req.build_env:
- call_subprocess(
- base_cmd + ["egg_info"] + egg_base_option,
- cwd=install_req.unpacked_source_directory,
- command_desc='python setup.py egg_info',
- )
- # Return the .egg-info directory.
- return _find_egg_info(
- install_req.unpacked_source_directory,
- install_req.editable,
- )
- def _generate_metadata(install_req):
- # type: (InstallRequirement) -> str
- return install_req.prepare_pep517_metadata()
|