def check_invalid_constraint_type(req: InstallRequirement) -> str: # Check for unsupported forms problem = "" if not req.name: problem = "Unnamed requirements are not allowed as constraints" elif req.editable: problem = "Editable requirements are not allowed as constraints" elif req.extras: problem = "Constraints cannot have extras" if problem: deprecated( reason=( "Constraints are only allowed to take the form of a package " "name and a version specifier. Other forms were originally " "permitted as an accident of the implementation, but were " "undocumented. The new implementation of the resolver no " "longer supports these forms."), replacement="replacing the constraint with a requirement", # No plan yet for when the new resolver becomes default gone_in=None, issue=8210, ) return problem
def add_dependency_links(self, links): # # FIXME: this shouldn't be global list this, it should only # # apply to requirements of the package that specifies the # # dependency_links value # # FIXME: also, we should track comes_from (i.e., use Link) if self.process_dependency_links: deprecated( "Dependency Links processing has been deprecated and will be " "removed in a future release.", replacement=None, gone_in="18.2", issue=4187, ) self.dependency_links.extend(links)
def get_prefixed_libs(prefix: str) -> List[str]: """Return the lib locations under ``prefix``.""" new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix) if _USE_SYSCONFIG: return _deduplicated(new_pure, new_plat) old_pure, old_plat = _distutils.get_prefixed_libs(prefix) old_lib_paths = _deduplicated(old_pure, old_plat) # Apple's Python (shipped with Xcode and Command Line Tools) hard-code # platlib and purelib to '/Library/Python/X.Y/site-packages'. This will # cause serious build isolation bugs when Apple starts shipping 3.10 because # pip will install build backends to the wrong location. This tells users # who is at fault so Apple may notice it and fix the issue in time. if all(_looks_like_apple_library(p) for p in old_lib_paths): deprecated( reason=( "Python distributed by Apple's Command Line Tools incorrectly " "patches sysconfig to always point to '/Library/Python'. This " "will cause build isolation to operate incorrectly on Python " "3.10 or later. Please help report this to Apple so they can " "fix this. https://developer.apple.com/bug-reporting/"), replacement=None, gone_in=None, ) return old_lib_paths warned = [ _warn_if_mismatch( pathlib.Path(old_pure), pathlib.Path(new_pure), key="prefixed-purelib", ), _warn_if_mismatch( pathlib.Path(old_plat), pathlib.Path(new_plat), key="prefixed-platlib", ), ] if any(warned): _log_context(prefix=prefix) return old_lib_paths
def determine_build_failure_suppression(options: Values) -> bool: """Determines whether build failures should be suppressed and backtracked on.""" if "backtrack-on-build-failures" not in options.deprecated_features_enabled: return False if "legacy-resolver" in options.deprecated_features_enabled: raise CommandError("Cannot backtrack with legacy resolver.") deprecated( reason= ("Backtracking on build failures can mask issues related to how " "a package generates metadata or builds a wheel. This flag will " "be removed in pip 22.2."), gone_in=None, replacement= ("avoiding known-bad versions by explicitly telling pip to ignore them " "(either directly as requirements, or via a constraints file)"), feature_flag=None, issue=10655, ) return True
def warn_deprecated_install_options(requirement_set, options): # type: (RequirementSet, Optional[List[str]]) -> None """If any location-changing --install-option arguments were passed for requirements or on the command-line, then show a deprecation warning. """ def format_options(option_names): # type: (Iterable[str]) -> List[str] return ["--{}".format(name.replace("_", "-")) for name in option_names] requirements = (requirement_set.unnamed_requirements + list(requirement_set.requirements.values())) offenders = [] for requirement in requirements: install_options = requirement.options.get("install_options", []) location_options = parse_distutils_args(install_options) if location_options: offenders.append("{!r} from {}".format( format_options(location_options.keys()), requirement)) if options: location_options = parse_distutils_args(options) if location_options: offenders.append("{!r} from command line".format( format_options(location_options.keys()))) if not offenders: return deprecated( reason=("Location-changing options found in --install-option: {}. " "This configuration may cause unexpected behavior and is " "unsupported.".format("; ".join(offenders))), replacement=( "using pip-level options like --user, --prefix, --root, and " "--target"), gone_in="20.2", issue=7309, )
def install( self, install_options: List[str], global_options: Optional[Sequence[str]] = None, root: Optional[str] = None, home: Optional[str] = None, prefix: Optional[str] = None, warn_script_location: bool = True, use_user_site: bool = False, pycompile: bool = True, ) -> 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 and not self.is_wheel: 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.editable: direct_url = direct_url_for_editable( self.unpacked_source_directory) elif 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, requested=self.user_supplied, ) 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 raise exc except Exception: self.install_succeeded = True raise self.install_succeeded = success if success and self.legacy_install_reason == 8368: deprecated( reason=("{} was installed using the legacy 'setup.py install' " "method, because a wheel could not be built for it.". format(self.name)), replacement="to fix the wheel build issue reported above", gone_in=None, issue=8368, )
def main(self, args): # type: (List[str]) -> int options, args = self.parse_args(args) # Set verbosity so that it can be used elsewhere. self.verbosity = options.verbose - options.quiet level_number = setup_logging( verbosity=self.verbosity, no_color=options.no_color, user_log_file=options.log, ) if sys.version_info[:2] == (3, 4): deprecated( "Python 3.4 support has been deprecated. pip 19.1 will be the " "last one supporting it. Please upgrade your Python as Python " "3.4 won't be maintained after March 2019 (cf PEP 429).", replacement=None, gone_in='19.2', ) elif sys.version_info[:2] == (2, 7): message = ( "A future version of pip will drop support for Python 2.7.") if platform.python_implementation() == "CPython": message = ( "Python 2.7 will reach the end of its life on January " "1st, 2020. Please upgrade your Python as Python 2.7 " "won't be maintained after that date. ") + message deprecated(message, replacement=None, gone_in=None) # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. if options.no_input: os.environ['PIP_NO_INPUT'] = '1' if options.exists_action: os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): logger.critical( 'Could not find an activated virtualenv (required).') sys.exit(VIRTUALENV_NOT_FOUND) try: status = self.run(options, args) # FIXME: all commands should return an exit status # and when it is done, isinstance is not needed anymore if isinstance(status, int): return status except PreviousBuildDirError as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return PREVIOUS_BUILD_DIR_ERROR except (InstallationError, UninstallationError, BadCommand) as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return ERROR except CommandError as exc: logger.critical('ERROR: %s', exc) logger.debug('Exception information:', exc_info=True) return ERROR except BrokenStdoutLoggingError: # Bypass our logger and write any remaining messages to stderr # because stdout no longer works. print('ERROR: Pipe to stdout was broken', file=sys.stderr) if level_number <= logging.DEBUG: traceback.print_exc(file=sys.stderr) return ERROR except KeyboardInterrupt: logger.critical('Operation cancelled by user') logger.debug('Exception information:', exc_info=True) return ERROR except BaseException: logger.critical('Exception:', exc_info=True) return UNKNOWN_ERROR finally: allow_version_check = ( # Does this command have the index_group options? hasattr(options, "no_index") and # Is this command allowed to perform this check? not (options.disable_pip_version_check or options.no_index)) # Check if we're using the latest version of pip available if allow_version_check: session = self._build_session(options, retries=0, timeout=min(5, options.timeout)) with session: pip_version_check(session, options) # Shutdown the logging module logging.shutdown() return SUCCESS
def resolve( self, root_reqs: List[InstallRequirement], check_supported_wheels: bool ) -> RequirementSet: collected = self.factory.collect_root_requirements(root_reqs) provider = PipProvider( factory=self.factory, constraints=collected.constraints, ignore_dependencies=self.ignore_dependencies, upgrade_strategy=self.upgrade_strategy, user_requested=collected.user_requested, ) if "PIP_RESOLVER_DEBUG" in os.environ: reporter: BaseReporter = PipDebuggingReporter() else: reporter = PipReporter() resolver: RLResolver[Requirement, Candidate, str] = RLResolver( provider, reporter, ) try: try_to_avoid_resolution_too_deep = 2000000 result = self._result = resolver.resolve( collected.requirements, max_rounds=try_to_avoid_resolution_too_deep ) except ResolutionImpossible as e: error = self.factory.get_installation_error( cast("ResolutionImpossible[Requirement, Candidate]", e), collected.constraints, ) raise error from e req_set = RequirementSet(check_supported_wheels=check_supported_wheels) for candidate in result.mapping.values(): ireq = candidate.get_install_requirement() if ireq is None: continue # Check if there is already an installation under the same name, # and set a flag for later stages to uninstall it, if needed. installed_dist = self.factory.get_dist_to_uninstall(candidate) if installed_dist is None: # There is no existing installation -- nothing to uninstall. ireq.should_reinstall = False elif self.factory.force_reinstall: # The --force-reinstall flag is set -- reinstall. ireq.should_reinstall = True elif installed_dist.version != candidate.version: # The installation is different in version -- reinstall. ireq.should_reinstall = True elif candidate.is_editable or installed_dist.editable: # The incoming distribution is editable, or different in # editable-ness to installation -- reinstall. ireq.should_reinstall = True elif candidate.source_link and candidate.source_link.is_file: # The incoming distribution is under file:// if candidate.source_link.is_wheel: # is a local wheel -- do nothing. logger.info( "%s is already installed with the same version as the " "provided wheel. Use --force-reinstall to force an " "installation of the wheel.", ireq.name, ) continue looks_like_sdist = ( is_archive_file(candidate.source_link.file_path) and candidate.source_link.ext != ".zip" ) if looks_like_sdist: # is a local sdist -- show a deprecation warning! reason = ( "Source distribution is being reinstalled despite an " "installed package having the same name and version as " "the installed package." ) replacement = "use --force-reinstall" deprecated( reason=reason, replacement=replacement, gone_in="21.3", issue=8711, ) # is a local sdist or path -- reinstall ireq.should_reinstall = True else: continue link = candidate.source_link if link and link.is_yanked: # The reason can contain non-ASCII characters, Unicode # is required for Python 2. msg = ( "The candidate selected for download or install is a " "yanked version: {name!r} candidate (version {version} " "at {link})\nReason for being yanked: {reason}" ).format( name=candidate.name, version=candidate.version, link=link, reason=link.yanked_reason or "<none given>", ) logger.warning(msg) req_set.add_named_requirement(ireq) reqs = req_set.all_requirements self.factory.preparer.prepare_linked_requirements_more(reqs) return req_set
def get_scheme( dist_name: str, user: bool = False, home: Optional[str] = None, root: Optional[str] = None, isolated: bool = False, prefix: Optional[str] = None, ) -> Scheme: old = _distutils.get_scheme( dist_name, user=user, home=home, root=root, isolated=isolated, prefix=prefix, ) new = _sysconfig.get_scheme( dist_name, user=user, home=home, root=root, isolated=isolated, prefix=prefix, ) base = prefix or home or _default_base(user=user) warning_contexts = [] for k in SCHEME_KEYS: # Extra join because distutils can return relative paths. old_v = pathlib.Path(base, getattr(old, k)) new_v = pathlib.Path(getattr(new, k)) if old_v == new_v: continue # distutils incorrectly put PyPy packages under ``site-packages/python`` # in the ``posix_home`` scheme, but PyPy devs said they expect the # directory name to be ``pypy`` instead. So we treat this as a bug fix # and not warn about it. See bpo-43307 and python/cpython#24628. skip_pypy_special_case = (sys.implementation.name == "pypy" and home is not None and k in ("platlib", "purelib") and old_v.parent == new_v.parent and old_v.name.startswith("python") and new_v.name.startswith("pypy")) if skip_pypy_special_case: continue # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in # the ``include`` value, but distutils's ``headers`` does. We'll let # CPython decide whether this is a bug or feature. See bpo-43948. skip_osx_framework_user_special_case = ( user and is_osx_framework() and k == "headers" and old_v.parent.parent == new_v.parent and old_v.parent.name.startswith("python")) if skip_osx_framework_user_special_case: continue # On Red Hat and derived Linux distributions, distutils is patched to # use "lib64" instead of "lib" for platlib. if k == "platlib" and _looks_like_red_hat_patched(): continue # Both Debian and Red Hat patch Python to place the system site under # /usr/local instead of /usr. Debian also places lib in dist-packages # instead of site-packages, but the /usr/local check should cover it. skip_linux_system_special_case = ( not (user or home or prefix) and old_v.parts[1:3] == ("usr", "local") and len(new_v.parts) > 1 and new_v.parts[1] == "usr" and (len(new_v.parts) < 3 or new_v.parts[2] != "local") and (_looks_like_red_hat_patched() or _looks_like_debian_patched())) if skip_linux_system_special_case: continue # On Python 3.7 and earlier, sysconfig does not include sys.abiflags in # the "pythonX.Y" part of the path, but distutils does. skip_sysconfig_abiflag_bug = ( sys.version_info < (3, 8) and not WINDOWS and k in ("headers", "platlib", "purelib") and tuple(_fix_abiflags(old_v.parts)) == new_v.parts) if skip_sysconfig_abiflag_bug: continue warning_contexts.append((old_v, new_v, f"scheme.{k}")) if not warning_contexts: return old # Check if this path mismatch is caused by distutils config files. Those # files will no longer work once we switch to sysconfig, so this raises a # deprecation message for them. default_old = _distutils.distutils_scheme( dist_name, user, home, root, isolated, prefix, ignore_config_files=True, ) if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS): deprecated( "Configuring installation scheme with distutils config files " "is deprecated and will no longer work in the near future. If you " "are using a Homebrew or Linuxbrew Python, please see discussion " "at https://github.com/Homebrew/homebrew-core/issues/76621", replacement=None, gone_in=None, ) return old # Post warnings about this mismatch so user can report them back. for old_v, new_v, key in warning_contexts: _warn_mismatched(old_v, new_v, key=key) _log_context(user=user, home=home, root=root, prefix=prefix) return old
def make_requirement_preparer( cls, temp_build_dir: TempDirectory, options: Values, req_tracker: RequirementTracker, session: PipSession, finder: PackageFinder, use_user_site: bool, download_dir: Optional[str] = None, verbosity: int = 0, ) -> RequirementPreparer: """ Create a RequirementPreparer instance for the given parameters. """ temp_build_dir_path = temp_build_dir.path assert temp_build_dir_path is not None resolver_variant = cls.determine_resolver_variant(options) if resolver_variant == "2020-resolver": lazy_wheel = "fast-deps" in options.features_enabled if lazy_wheel: logger.warning( "pip is using lazily downloaded wheels using HTTP " "range requests to obtain dependency information. " "This experimental feature is enabled through " "--use-feature=fast-deps and it is not ready for " "production.") else: lazy_wheel = False if "fast-deps" in options.features_enabled: logger.warning( "fast-deps has no effect when used with the legacy resolver." ) in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled if "in-tree-build" in options.features_enabled: deprecated( reason="In-tree builds are now the default.", replacement="to remove the --use-feature=in-tree-build flag", gone_in="22.1", ) if "out-of-tree-build" in options.deprecated_features_enabled: deprecated( reason="Out-of-tree builds are deprecated.", replacement=None, gone_in="22.1", ) if options.progress_bar not in {"on", "off"}: deprecated( reason="Custom progress bar styles are deprecated", replacement="to use the default progress bar style.", gone_in="22.1", ) return RequirementPreparer( build_dir=temp_build_dir_path, src_dir=options.src_dir, download_dir=download_dir, build_isolation=options.build_isolation, req_tracker=req_tracker, session=session, progress_bar=options.progress_bar, finder=finder, require_hashes=options.require_hashes, use_user_site=use_user_site, lazy_wheel=lazy_wheel, verbosity=verbosity, in_tree_build=in_tree_build, )
def _main(self, args: List[str]) -> int: # We must initialize this before the tempdir manager, otherwise the # configuration would not be accessible by the time we clean up the # tempdir manager. self.tempdir_registry = self.enter_context(tempdir_registry()) # Intentionally set as early as possible so globally-managed temporary # directories are available to the rest of the code. self.enter_context(global_tempdir_manager()) options, args = self.parse_args(args) # Set verbosity so that it can be used elsewhere. self.verbosity = options.verbose - options.quiet level_number = setup_logging( verbosity=self.verbosity, no_color=options.no_color, user_log_file=options.log, ) # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. if options.no_input: os.environ["PIP_NO_INPUT"] = "1" if options.exists_action: os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): logger.critical("Could not find an activated virtualenv (required).") sys.exit(VIRTUALENV_NOT_FOUND) if options.cache_dir: options.cache_dir = normalize_path(options.cache_dir) if not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "or is not writable by the current user. The cache " "has been disabled. Check the permissions and owner of " "that directory. If executing pip with sudo, you should " "use sudo's -H flag.", options.cache_dir, ) options.cache_dir = None if getattr(options, "build_dir", None): deprecated( reason=( "The -b/--build/--build-dir/--build-directory " "option is deprecated and has no effect anymore." ), replacement=( "use the TMPDIR/TEMP/TMP environment variable, " "possibly combined with --no-clean" ), gone_in="21.3", issue=8333, ) if "2020-resolver" in options.features_enabled: logger.warning( "--use-feature=2020-resolver no longer has any effect, " "since it is now the default dependency resolver in pip. " "This will become an error in pip 21.0." ) try: status = self.run(options, args) assert isinstance(status, int) return status except PreviousBuildDirError as exc: logger.critical(str(exc)) logger.debug("Exception information:", exc_info=True) return PREVIOUS_BUILD_DIR_ERROR except ( InstallationError, UninstallationError, BadCommand, NetworkConnectionError, ) as exc: logger.critical(str(exc)) logger.debug("Exception information:", exc_info=True) return ERROR except CommandError as exc: logger.critical("%s", exc) logger.debug("Exception information:", exc_info=True) return ERROR except BrokenStdoutLoggingError: # Bypass our logger and write any remaining messages to stderr # because stdout no longer works. print("ERROR: Pipe to stdout was broken", file=sys.stderr) if level_number <= logging.DEBUG: traceback.print_exc(file=sys.stderr) return ERROR except KeyboardInterrupt: logger.critical("Operation cancelled by user") logger.debug("Exception information:", exc_info=True) return ERROR except BaseException: logger.critical("Exception:", exc_info=True) return UNKNOWN_ERROR finally: self.handle_pip_version_check(options)
def from_dist(cls, dist, dependency_links): location = os.path.normcase(os.path.abspath(dist.location)) comments = [] from pipenv.patched.notpip._internal.vcs import vcs, get_src_requirement if dist_is_editable(dist) and vcs.get_backend_name(location): editable = True try: req = get_src_requirement(dist, location) except InstallationError as exc: logger.warning( "Error when trying to get requirement for VCS system %s, " "falling back to uneditable format", exc ) req = None if req is None: logger.warning( 'Could not determine repository location of %s', location ) comments.append( '## !! Could not determine repository location' ) req = dist.as_requirement() editable = False else: editable = False req = dist.as_requirement() specs = req.specs assert len(specs) == 1 and specs[0][0] in ["==", "==="], \ 'Expected 1 spec with == or ===; specs = %r; dist = %r' % \ (specs, dist) version = specs[0][1] ver_match = cls._rev_re.search(version) date_match = cls._date_re.search(version) if ver_match or date_match: svn_backend = vcs.get_backend('svn') if svn_backend: svn_location = svn_backend().get_location( dist, dependency_links, ) if not svn_location: logger.warning( 'Warning: cannot find svn location for %s', req, ) comments.append( '## FIXME: could not find svn URL in dependency_links ' 'for this package:' ) else: deprecated( "SVN editable detection based on dependency links " "will be dropped in the future.", replacement=None, gone_in="18.2", issue=4187, ) comments.append( '# Installing as editable to satisfy requirement %s:' % req ) if ver_match: rev = ver_match.group(1) else: rev = '{%s}' % date_match.group(1) editable = True req = '%s@%s#egg=%s' % ( svn_location, rev, cls.egg_name(dist) ) return cls(dist.project_name, req, editable, comments)
def install( install_req, # type: InstallRequirement install_options, # type: List[str] global_options, # type: Sequence[str] root, # type: Optional[str] home, # type: Optional[str] prefix, # type: Optional[str] use_user_site, # type: bool pycompile, # type: bool scheme, # type: Scheme ): # type: (...) -> None # 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) + \ install_req.options.get('global_options', []) install_options = list(install_options) + \ install_req.options.get('install_options', []) header_dir = scheme.headers with TempDirectory(kind="record") as temp_dir: record_filename = os.path.join(temp_dir.path, 'install-record.txt') install_args = make_setuptools_install_args( install_req.setup_py_path, global_options=global_options, install_options=install_options, record_filename=record_filename, root=root, prefix=prefix, header_dir=header_dir, home=home, use_user_site=use_user_site, no_user_config=install_req.isolated, pycompile=pycompile, ) runner = runner_with_spinner_message( "Running setup.py install for {}".format(install_req.name)) with indent_log(), install_req.build_env: runner( cmd=install_args, cwd=install_req.unpacked_source_directory, ) if not os.path.exists(record_filename): logger.debug('Record file %s not found', record_filename) return install_req.install_succeeded = True # We intentionally do not use any encoding to read the file because # setuptools writes the file using distutils.file_util.write_file, # which does not specify an encoding. with open(record_filename) as f: record_lines = f.read().splitlines() def prepend_root(path): # type: (str) -> str if root is None or not os.path.isabs(path): return path else: return change_root(root, path) for line in record_lines: directory = os.path.dirname(line) if directory.endswith('.egg-info'): egg_info_dir = prepend_root(directory) break else: deprecated( reason=("{} did not indicate that it installed an " ".egg-info directory. Only setup.py projects " "generating .egg-info directories are supported." ).format(install_req), replacement=("for maintainers: updating the setup.py of {0}. " "For users: contact the maintainers of {0} to let " "them know to update their setup.py.".format( install_req.name)), gone_in="20.2", issue=6998, ) # FIXME: put the record somewhere return new_lines = [] for line in record_lines: filename = line.strip() if os.path.isdir(filename): filename += os.path.sep new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir)) new_lines.sort() ensure_dir(egg_info_dir) inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') with open(inst_files_path, 'w') as f: f.write('\n'.join(new_lines) + '\n')
def _main(self, args): # type: (List[str]) -> int # Intentionally set as early as possible so globally-managed temporary # directories are available to the rest of the code. self.enter_context(global_tempdir_manager()) options, args = self.parse_args(args) # Set verbosity so that it can be used elsewhere. self.verbosity = options.verbose - options.quiet level_number = setup_logging( verbosity=self.verbosity, no_color=options.no_color, user_log_file=options.log, ) if (sys.version_info[:2] == (2, 7) and not options.no_python_version_warning): message = ( "A future version of pip will drop support for Python 2.7. " "More details about Python 2 support in pip, can be found at " "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa ) if platform.python_implementation() == "CPython": message = ( "Python 2.7 reached the end of its life on January " "1st, 2020. Please upgrade your Python as Python 2.7 " "is no longer maintained. ") + message deprecated(message, replacement=None, gone_in=None) if options.skip_requirements_regex: deprecated( "--skip-requirements-regex is unsupported and will be removed", replacement=( "manage requirements/constraints files explicitly, " "possibly generating them from metadata"), gone_in="20.1", issue=7297, ) # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. if options.no_input: os.environ['PIP_NO_INPUT'] = '1' if options.exists_action: os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): logger.critical( 'Could not find an activated virtualenv (required).') sys.exit(VIRTUALENV_NOT_FOUND) if options.cache_dir: options.cache_dir = normalize_path(options.cache_dir) if not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "or is not writable by the current user. The cache " "has been disabled. Check the permissions and owner of " "that directory. If executing pip with sudo, you may want " "sudo's -H flag.", options.cache_dir, ) options.cache_dir = None try: status = self.run(options, args) # FIXME: all commands should return an exit status # and when it is done, isinstance is not needed anymore if isinstance(status, int): return status except PreviousBuildDirError as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return PREVIOUS_BUILD_DIR_ERROR except (InstallationError, UninstallationError, BadCommand) as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return ERROR except CommandError as exc: logger.critical('%s', exc) logger.debug('Exception information:', exc_info=True) return ERROR except BrokenStdoutLoggingError: # Bypass our logger and write any remaining messages to stderr # because stdout no longer works. print('ERROR: Pipe to stdout was broken', file=sys.stderr) if level_number <= logging.DEBUG: traceback.print_exc(file=sys.stderr) return ERROR except KeyboardInterrupt: logger.critical('Operation cancelled by user') logger.debug('Exception information:', exc_info=True) return ERROR except BaseException: logger.critical('Exception:', exc_info=True) return UNKNOWN_ERROR finally: self.handle_pip_version_check(options) return SUCCESS
def unpack_url( link, # type: Link location, # type: str download, # type: Downloader download_dir=None, # type: Optional[str] hashes=None, # type: Optional[Hashes] ): # type: (...) -> Optional[File] """Unpack link into location, downloading if required. :param hashes: A Hashes object, one of whose embedded hashes must match, or HashMismatch will be raised. If the Hashes is empty, no matches are required, and unhashable types of requirements (like VCS ones, which would ordinarily raise HashUnsupported) are allowed. """ # non-editable vcs urls if link.is_vcs: unpack_vcs_link(link, location) return None # Once out-of-tree-builds are no longer supported, could potentially # replace the below condition with `assert not link.is_existing_dir` # - unpack_url does not need to be called for in-tree-builds. # # As further cleanup, _copy_source_tree and accompanying tests can # be removed. if link.is_existing_dir(): deprecated( "A future pip version will change local packages to be built " "in-place without first copying to a temporary directory. " "We recommend you use --use-feature=in-tree-build to test " "your packages with this new behavior before it becomes the " "default.\n", replacement=None, gone_in="21.3", issue=7555 ) if os.path.isdir(location): rmtree(location) _copy_source_tree(link.file_path, location) return None # file urls if link.is_file: file = get_file_url(link, download_dir, hashes=hashes) # http urls else: file = get_http_url( link, download, download_dir, hashes=hashes, ) # unpack the archive to the build dir location. even when only downloading # archives, they have to be unpacked to parse dependencies, except wheels if not link.is_wheel: unpack_file(file.path, location, file.content_type) return file
def _main(self, args): # type: (List[str]) -> int options, args = self.parse_args(args) # Set verbosity so that it can be used elsewhere. self.verbosity = options.verbose - options.quiet level_number = setup_logging( verbosity=self.verbosity, no_color=options.no_color, user_log_file=options.log, ) if sys.version_info[:2] == (2, 7): message = ( "A future version of pip will drop support for Python 2.7. " "More details about Python 2 support in pip, can be found at " "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa ) if platform.python_implementation() == "CPython": message = ( "Python 2.7 will reach the end of its life on January " "1st, 2020. Please upgrade your Python as Python 2.7 " "won't be maintained after that date. ") + message deprecated(message, replacement=None, gone_in=None) # TODO: Try to get these passing down from the command? # without resorting to os.environ to hold these. # This also affects isolated builds and it should. if options.no_input: os.environ['PIP_NO_INPUT'] = '1' if options.exists_action: os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) if options.require_venv and not self.ignore_require_venv: # If a venv is required check if it can really be found if not running_under_virtualenv(): logger.critical( 'Could not find an activated virtualenv (required).') sys.exit(VIRTUALENV_NOT_FOUND) try: status = self.run(options, args) # FIXME: all commands should return an exit status # and when it is done, isinstance is not needed anymore if isinstance(status, int): return status except PreviousBuildDirError as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return PREVIOUS_BUILD_DIR_ERROR except (InstallationError, UninstallationError, BadCommand) as exc: logger.critical(str(exc)) logger.debug('Exception information:', exc_info=True) return ERROR except CommandError as exc: logger.critical('%s', exc) logger.debug('Exception information:', exc_info=True) return ERROR except BrokenStdoutLoggingError: # Bypass our logger and write any remaining messages to stderr # because stdout no longer works. print('ERROR: Pipe to stdout was broken', file=sys.stderr) if level_number <= logging.DEBUG: traceback.print_exc(file=sys.stderr) return ERROR except KeyboardInterrupt: logger.critical('Operation cancelled by user') logger.debug('Exception information:', exc_info=True) return ERROR except BaseException: logger.critical('Exception:', exc_info=True) return UNKNOWN_ERROR finally: self.handle_pip_version_check(options) return SUCCESS
def get_scheme( dist_name: str, user: bool = False, home: Optional[str] = None, root: Optional[str] = None, isolated: bool = False, prefix: Optional[str] = None, ) -> Scheme: new = _sysconfig.get_scheme( dist_name, user=user, home=home, root=root, isolated=isolated, prefix=prefix, ) if _USE_SYSCONFIG: return new old = _distutils.get_scheme( dist_name, user=user, home=home, root=root, isolated=isolated, prefix=prefix, ) warning_contexts = [] for k in SCHEME_KEYS: old_v = pathlib.Path(getattr(old, k)) new_v = pathlib.Path(getattr(new, k)) if old_v == new_v: continue # distutils incorrectly put PyPy packages under ``site-packages/python`` # in the ``posix_home`` scheme, but PyPy devs said they expect the # directory name to be ``pypy`` instead. So we treat this as a bug fix # and not warn about it. See bpo-43307 and python/cpython#24628. skip_pypy_special_case = (sys.implementation.name == "pypy" and home is not None and k in ("platlib", "purelib") and old_v.parent == new_v.parent and old_v.name.startswith("python") and new_v.name.startswith("pypy")) if skip_pypy_special_case: continue # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in # the ``include`` value, but distutils's ``headers`` does. We'll let # CPython decide whether this is a bug or feature. See bpo-43948. skip_osx_framework_user_special_case = ( user and is_osx_framework() and k == "headers" and old_v.parent.parent == new_v.parent and old_v.parent.name.startswith("python")) if skip_osx_framework_user_special_case: continue # On Red Hat and derived Linux distributions, distutils is patched to # use "lib64" instead of "lib" for platlib. if k == "platlib" and _looks_like_red_hat_lib(): continue # On Python 3.9+, sysconfig's posix_user scheme sets platlib against # sys.platlibdir, but distutils's unix_user incorrectly coninutes # using the same $usersite for both platlib and purelib. This creates a # mismatch when sys.platlibdir is not "lib". skip_bpo_44860 = (user and k == "platlib" and not WINDOWS and sys.version_info >= (3, 9) and _PLATLIBDIR != "lib" and _looks_like_bpo_44860()) if skip_bpo_44860: continue # Slackware incorrectly patches posix_user to use lib64 instead of lib, # but not usersite to match the location. skip_slackware_user_scheme = (user and k in ("platlib", "purelib") and not WINDOWS and _looks_like_slackware_scheme()) if skip_slackware_user_scheme: continue # Both Debian and Red Hat patch Python to place the system site under # /usr/local instead of /usr. Debian also places lib in dist-packages # instead of site-packages, but the /usr/local check should cover it. skip_linux_system_special_case = ( not (user or home or prefix or running_under_virtualenv()) and old_v.parts[1:3] == ("usr", "local") and len(new_v.parts) > 1 and new_v.parts[1] == "usr" and (len(new_v.parts) < 3 or new_v.parts[2] != "local") and (_looks_like_red_hat_scheme() or _looks_like_debian_scheme())) if skip_linux_system_special_case: continue # On Python 3.7 and earlier, sysconfig does not include sys.abiflags in # the "pythonX.Y" part of the path, but distutils does. skip_sysconfig_abiflag_bug = ( sys.version_info < (3, 8) and not WINDOWS and k in ("headers", "platlib", "purelib") and tuple(_fix_abiflags(old_v.parts)) == new_v.parts) if skip_sysconfig_abiflag_bug: continue # MSYS2 MINGW's sysconfig patch does not include the "site-packages" # part of the path. This is incorrect and will be fixed in MSYS. skip_msys2_mingw_bug = (WINDOWS and k in ("platlib", "purelib") and _looks_like_msys2_mingw_scheme()) if skip_msys2_mingw_bug: continue # CPython's POSIX install script invokes pip (via ensurepip) against the # interpreter located in the source tree, not the install site. This # triggers special logic in sysconfig that's not present in distutils. # https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194 skip_cpython_build = (sysconfig.is_python_build(check_home=True) and not WINDOWS and k in ("headers", "include", "platinclude")) if skip_cpython_build: continue warning_contexts.append((old_v, new_v, f"scheme.{k}")) if not warning_contexts: return old # Check if this path mismatch is caused by distutils config files. Those # files will no longer work once we switch to sysconfig, so this raises a # deprecation message for them. default_old = _distutils.distutils_scheme( dist_name, user, home, root, isolated, prefix, ignore_config_files=True, ) if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS): deprecated( reason= ("Configuring installation scheme with distutils config files " "is deprecated and will no longer work in the near future. If you " "are using a Homebrew or Linuxbrew Python, please see discussion " "at https://github.com/Homebrew/homebrew-core/issues/76621"), replacement=None, gone_in=None, ) return old # Post warnings about this mismatch so user can report them back. for old_v, new_v, key in warning_contexts: _warn_mismatched(old_v, new_v, key=key) _log_context(user=user, home=home, root=root, prefix=prefix) return old