def test_resolve_symlinks(self, tmpdir): print(type(tmpdir)) print(dir(tmpdir)) orig_working_dir = os.getcwd() os.chdir(tmpdir) try: d = os.path.join('foo', 'bar') f = os.path.join(d, 'file1') os.makedirs(d) with open(f, 'w'): # Create the file pass os.symlink(d, 'dir_link') os.symlink(f, 'file_link') assert normalize_path( 'dir_link/file1', resolve_symlinks=True ) == os.path.join(tmpdir, f) assert normalize_path( 'dir_link/file1', resolve_symlinks=False ) == os.path.join(tmpdir, 'dir_link', 'file1') assert normalize_path( 'file_link', resolve_symlinks=True ) == os.path.join(tmpdir, f) assert normalize_path( 'file_link', resolve_symlinks=False ) == os.path.join(tmpdir, 'file_link') finally: os.chdir(orig_working_dir)
def __init__(self, build_dir, download_dir, src_dir, wheel_download_dir, progress_bar, build_isolation): super(RequirementPreparer, self).__init__() self.src_dir = src_dir self.build_dir = build_dir # Where still packed archives should be written to. If None, they are # not saved, and are deleted immediately after unpacking. self.download_dir = download_dir # Where still-packed .whl files should be written to. If None, they are # written to the download_dir parameter. Separate to download_dir to # permit only keeping wheel archives for pip wheel. if wheel_download_dir: wheel_download_dir = normalize_path(wheel_download_dir) self.wheel_download_dir = wheel_download_dir # NOTE # download_dir and wheel_download_dir overlap semantically and may # be combined if we're willing to have non-wheel archives present in # the wheelhouse output by 'pip wheel'. self.progress_bar = progress_bar # Is build isolation allowed? self.build_isolation = build_isolation
def _build_session(self, options, retries=None, timeout=None): session = PipSession( cache=( normalize_path(os.path.join(options.cache_dir, "http")) if options.cache_dir else None ), retries=retries if retries is not None else options.retries, insecure_hosts=options.trusted_hosts, ) # Handle custom ca-bundles from the user if options.cert: session.verify = options.cert # Handle SSL client certificate if options.client_cert: session.cert = options.client_cert # Handle timeouts if options.timeout or timeout: session.timeout = ( timeout if timeout is not None else options.timeout ) # Handle configured proxies if options.proxy: session.proxies = { "http": options.proxy, "https": options.proxy, } # Determine if we can prompt the user for authentication or not session.auth.prompting = not options.no_input return session
def _build_session(options, retries=None, timeout=None): session = PipSession( cache=( normalize_path(os.path.join(options.get('cache_dir'), 'http')) if options.get('cache_dir') else None ), retries=retries if retries is not None else options.get('retries'), insecure_hosts=options.get('trusted_hosts'), ) # Handle custom ca-bundles from the user if options.get('cert'): session.verify = options.get('cert') # Handle SSL client certificate if options.get('client_cert'): session.cert = options.get('client_cert') # Handle timeouts if options.get('timeout') or timeout: session.timeout = ( timeout if timeout is not None else options.get('timeout') ) # Handle configured proxies if options.get('proxy'): session.proxies = { 'http': options.get('proxy'), 'https': options.get('proxy'), } # Determine if we can prompt the user for authentication or not session.auth.prompting = not options.get('no_input') return session
def add_pth(self, pth_file, entry): pth_file = normalize_path(pth_file) if self._permitted(pth_file): if pth_file not in self.pth: self.pth[pth_file] = UninstallPthEntries(pth_file) self.pth[pth_file].add(entry) else: self._refuse.add(pth_file)
def add(self, path): head, tail = os.path.split(path) # we normalize the head to resolve parent directory symlinks, but not # the tail, since we only want to uninstall symlinks, not their targets path = os.path.join(normalize_path(head), os.path.normcase(tail)) if not os.path.exists(path): return if self._permitted(path): self.paths.add(path) else: self._refuse.add(path) # __pycache__ files can show up after 'installed-files.txt' is created, # due to imports if os.path.splitext(path)[1] == '.py' and uses_pycache: self.add(cache_from_source(path))
def _main(self, args): # type: (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 may want " "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.1", 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 in_usersite(self) -> bool: if self.installed_location is None or user_site is None: return False return self.installed_location.startswith(normalize_path(user_site))
def in_site_packages(self) -> bool: if self.installed_location is None or site_packages is None: return False return self.installed_location.startswith( normalize_path(site_packages))
def run(self, options, args): options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] if options.python_version: python_versions = [options.python_version] else: python_versions = None cmdoptions.check_dist_restriction(options) options.src_dir = os.path.abspath(options.src_dir) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) with self._build_session(options) as session: finder = self._build_package_finder( options=options, session=session, platform=options.platform, python_versions=python_versions, abi=options.abi, implementation=options.implementation, ) build_delete = (not (options.no_clean or options.build_dir)) if options.cache_dir and not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "by the current user and caching wheels 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 with RequirementTracker() as req_tracker, TempDirectory( options.build_dir, delete=build_delete, kind="download" ) as directory: requirement_set = RequirementSet( require_hashes=options.require_hashes, ) self.populate_requirement_set( requirement_set, args, options, finder, session, self.name, None ) preparer = RequirementPreparer( build_dir=directory.path, src_dir=options.src_dir, download_dir=options.download_dir, wheel_download_dir=None, progress_bar=options.progress_bar, build_isolation=options.build_isolation, req_tracker=req_tracker, ) resolver = Resolver( preparer=preparer, finder=finder, session=session, wheel_cache=None, use_user_site=False, upgrade_strategy="to-satisfy-only", force_reinstall=False, ignore_dependencies=options.ignore_dependencies, ignore_requires_python=False, ignore_installed=True, isolated=options.isolated_mode, ) resolver.resolve(requirement_set) downloaded = ' '.join([ req.name for req in requirement_set.successfully_downloaded ]) if downloaded: logger.info('Successfully downloaded %s', downloaded) # Clean up if not options.no_clean: requirement_set.cleanup_files() return requirement_set
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)
def from_dist(cls, dist): dist_path = normalize_path(dist.location) if not dist_is_local(dist): logger.info( "Not uninstalling %s at %s, outside environment %s", dist.key, dist_path, sys.prefix, ) return cls(dist) if dist_path in { p for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} if p }: logger.info( "Not uninstalling %s at %s, as it is in the standard library.", dist.key, dist_path, ) return cls(dist) paths_to_remove = cls(dist) develop_egg_link = egg_link_path(dist) develop_egg_link_egg_info = '{}.egg-info'.format( pkg_resources.to_filename(dist.project_name)) egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) # Special case for distutils installed package distutils_egg_info = getattr(dist._provider, 'path', None) # Uninstall cases order do matter as in the case of 2 installs of the # same package, pip needs to uninstall the currently detected version if (egg_info_exists and dist.egg_info.endswith('.egg-info') and not dist.egg_info.endswith(develop_egg_link_egg_info)): # if dist.egg_info.endswith(develop_egg_link_egg_info), we # are in fact in the develop_egg_link case paths_to_remove.add(dist.egg_info) if dist.has_metadata('installed-files.txt'): for installed_file in dist.get_metadata( 'installed-files.txt').splitlines(): path = os.path.normpath( os.path.join(dist.egg_info, installed_file)) paths_to_remove.add(path) # FIXME: need a test for this elif block # occurs with --single-version-externally-managed/--record outside # of pip elif dist.has_metadata('top_level.txt'): if dist.has_metadata('namespace_packages.txt'): namespaces = dist.get_metadata('namespace_packages.txt') else: namespaces = [] for top_level_pkg in [ p for p in dist.get_metadata( 'top_level.txt').splitlines() if p and p not in namespaces ]: path = os.path.join(dist.location, top_level_pkg) paths_to_remove.add(path) paths_to_remove.add(path + '.py') paths_to_remove.add(path + '.pyc') paths_to_remove.add(path + '.pyo') elif distutils_egg_info: raise UninstallationError( "Cannot uninstall {!r}. It is a distutils installed project " "and thus we cannot accurately determine which files belong " "to it which would lead to only a partial uninstall.".format( dist.project_name, )) elif dist.location.endswith('.egg'): # package installed by easy_install # We cannot match on dist.egg_name because it can slightly vary # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg paths_to_remove.add(dist.location) easy_install_egg = os.path.split(dist.location)[1] easy_install_pth = os.path.join(os.path.dirname(dist.location), 'easy-install.pth') paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) elif egg_info_exists and dist.egg_info.endswith('.dist-info'): for path in uninstallation_paths(dist): paths_to_remove.add(path) elif develop_egg_link: # develop egg with open(develop_egg_link, 'r') as fh: link_pointer = os.path.normcase(fh.readline().strip()) assert (link_pointer == dist.location), ( 'Egg-link %s does not match installed location of %s ' '(at %s)' % (link_pointer, dist.project_name, dist.location)) paths_to_remove.add(develop_egg_link) easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), 'easy-install.pth') paths_to_remove.add_pth(easy_install_pth, dist.location) else: logger.debug( 'Not sure how to uninstall: %s - Check: %s', dist, dist.location, ) # find distutils scripts= scripts if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): for script in dist.metadata_listdir('scripts'): if dist_in_usersite(dist): bin_dir = bin_user else: bin_dir = bin_py paths_to_remove.add(os.path.join(bin_dir, script)) if WINDOWS: paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') # find console_scripts _scripts_to_remove = [] console_scripts = dist.get_entry_map(group='console_scripts') for name in console_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, False)) # find gui_scripts gui_scripts = dist.get_entry_map(group='gui_scripts') for name in gui_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, True)) for s in _scripts_to_remove: paths_to_remove.add(s) return paths_to_remove
def run(self, options, args): options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] cmdoptions.check_dist_restriction(options) options.src_dir = os.path.abspath(options.src_dir) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) session = self.get_default_session(options) target_python = make_target_python(options) finder = self._build_package_finder( options=options, session=session, target_python=target_python, ) build_delete = (not (options.no_clean or options.build_dir)) if options.cache_dir and not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "by the current user and caching wheels 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 with RequirementTracker() as req_tracker, TempDirectory( options.build_dir, delete=build_delete, kind="download" ) as directory: requirement_set = RequirementSet( require_hashes=options.require_hashes, ) self.populate_requirement_set( requirement_set, args, options, finder, session, None ) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, req_tracker=req_tracker, download_dir=options.download_dir, ) resolver = self.make_resolver( preparer=preparer, finder=finder, session=session, options=options, py_version_info=options.python_version, ) resolver.resolve(requirement_set) downloaded = ' '.join([ req.name for req in requirement_set.successfully_downloaded ]) if downloaded: write_output('Successfully downloaded %s', downloaded) # Clean up if not options.no_clean: requirement_set.cleanup_files() return requirement_set
def is_local(self, path) -> bool: return misc.normalize_path(path).startswith( misc.normalize_path(self.get_paths()["prefix"]))
# Where still-packed archives should be written to. If None, they are ======= # Where still packed archives should be written to. If None, they are >>>>>>> 71358189c5e72ee2ac9883b408a2f540a7f5745e # not saved, and are deleted immediately after unpacking. self.download_dir = download_dir # Where still-packed .whl files should be written to. If None, they are # written to the download_dir parameter. Separate to download_dir to # permit only keeping wheel archives for pip wheel. <<<<<<< HEAD ======= if wheel_download_dir: wheel_download_dir = normalize_path(wheel_download_dir) >>>>>>> 71358189c5e72ee2ac9883b408a2f540a7f5745e self.wheel_download_dir = wheel_download_dir # NOTE # download_dir and wheel_download_dir overlap semantically and may # be combined if we're willing to have non-wheel archives present in # the wheelhouse output by 'pip wheel'. <<<<<<< HEAD # Is build isolation allowed? self.build_isolation = build_isolation # Should hash-checking be required? self.require_hashes = require_hashes
def run(self, options, args): options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] cmdoptions.check_dist_restriction(options) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) session = self.get_default_session(options) target_python = make_target_python(options) finder = self._build_package_finder( options=options, session=session, target_python=target_python, ) build_delete = (not (options.no_clean or options.build_dir)) with get_requirement_tracker() as req_tracker, TempDirectory( options.build_dir, delete=build_delete, kind="download" ) as directory: requirement_set = RequirementSet() self.populate_requirement_set( requirement_set, args, options, finder, session, None ) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, req_tracker=req_tracker, session=session, finder=finder, download_dir=options.download_dir, use_user_site=False, ) resolver = self.make_resolver( preparer=preparer, finder=finder, options=options, py_version_info=options.python_version, ) self.trace_basic_info(finder) resolver.resolve(requirement_set) downloaded = ' '.join([ req.name for req in requirement_set.successfully_downloaded ]) if downloaded: write_output('Successfully downloaded %s', downloaded) # Clean up if not options.no_clean: requirement_set.cleanup_files() return requirement_set
def is_local(self, path) -> bool: """PEP 582 version of ``is_local()`` function.""" return misc.normalize_path(path).startswith( misc.normalize_path(self.packages_path.as_posix()))
def _main(self, args): # type: (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, ) if sys.version_info[:2] == ( 2, 7) and not options.no_python_version_warning: message = ( "pip 21.0 will drop support for Python 2.7 in January 2021. " "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) # 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 installed_location(self) -> Optional[str]: if self._installed_location is None: return None return normalize_path(str(self._installed_location))
def run(self, options, args): options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] if options.python_version: python_versions = [options.python_version] else: python_versions = None dist_restriction_set = any([ options.python_version, options.platform, options.abi, options.implementation, ]) binary_only = FormatControl(set(), {':all:'}) no_sdist_dependencies = ( options.format_control != binary_only and not options.ignore_dependencies ) if dist_restriction_set and no_sdist_dependencies: raise CommandError( "When restricting platform and interpreter constraints using " "--python-version, --platform, --abi, or --implementation, " "either --no-deps must be set, or --only-binary=:all: must be " "set and --no-binary must not be set (or must be set to " ":none:)." ) options.src_dir = os.path.abspath(options.src_dir) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) with self._build_session(options) as session: finder = self._build_package_finder( options=options, session=session, platform=options.platform, python_versions=python_versions, abi=options.abi, implementation=options.implementation, ) build_delete = (not (options.no_clean or options.build_dir)) if options.cache_dir and not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "by the current user and caching wheels 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 with RequirementTracker() as req_tracker, TempDirectory( options.build_dir, delete=build_delete, kind="download" ) as directory: requirement_set = RequirementSet( require_hashes=options.require_hashes, ) self.populate_requirement_set( requirement_set, args, options, finder, session, self.name, None ) preparer = RequirementPreparer( build_dir=directory.path, src_dir=options.src_dir, download_dir=options.download_dir, wheel_download_dir=None, progress_bar=options.progress_bar, build_isolation=options.build_isolation, req_tracker=req_tracker, ) resolver = Resolver( preparer=preparer, finder=finder, session=session, wheel_cache=None, use_user_site=False, upgrade_strategy="to-satisfy-only", force_reinstall=False, ignore_dependencies=options.ignore_dependencies, ignore_requires_python=False, ignore_installed=True, isolated=options.isolated_mode, ) resolver.resolve(requirement_set) downloaded = ' '.join([ req.name for req in requirement_set.successfully_downloaded ]) if downloaded: logger.info('Successfully downloaded %s', downloaded) # Clean up if not options.no_clean: requirement_set.cleanup_files() return requirement_set
def _main(self, args): # type: (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, ) if (sys.version_info[:2] == (2, 7) and not options.no_python_version_warning): message = ( "pip 21.0 will drop support for Python 2.7 in January 2021. " "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="21.0") if (sys.version_info[:2] == (3, 5) and not options.no_python_version_warning): message = ("Python 3.5 reached the end of its life on September " "13th, 2020. Please upgrade your Python as Python 3.5 " "is no longer maintained. pip 21.0 will drop support " "for Python 3.5 in January 2021.") deprecated(message, replacement=None, gone_in="21.0") # 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 if getattr(options, "build_dir", None): deprecated( reason=("The -b/--build/--build-dir/--build-directory " "option is deprecated."), replacement=("use the TMPDIR/TEMP/TMP environment variable, " "possibly combined with --no-clean"), gone_in="20.3", issue=8333, ) if 'resolver' in options.unstable_features: logger.critical( "--unstable-feature=resolver is no longer supported, and " "has been replaced with --use-feature=2020-resolver instead.") sys.exit(ERROR) 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, SubProcessError, 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: BaseDistribution) -> "UninstallPathSet": dist_location = dist.location info_location = dist.info_location if dist_location is None: logger.info( "Not uninstalling %s since it is not installed", dist.canonical_name, ) return cls(dist) normalized_dist_location = normalize_path(dist_location) if not dist.local: logger.info( "Not uninstalling %s at %s, outside environment %s", dist.canonical_name, normalized_dist_location, sys.prefix, ) return cls(dist) if normalized_dist_location in { p for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} if p }: logger.info( "Not uninstalling %s at %s, as it is in the standard library.", dist.canonical_name, normalized_dist_location, ) return cls(dist) paths_to_remove = cls(dist) develop_egg_link = egg_link_path_from_location(dist.raw_name) # Distribution is installed with metadata in a "flat" .egg-info # directory. This means it is not a modern .dist-info installation, an # egg, or legacy editable. setuptools_flat_installation = ( dist.installed_with_setuptools_egg_info and info_location is not None and os.path.exists(info_location) # If dist is editable and the location points to a ``.egg-info``, # we are in fact in the legacy editable case. and not info_location.endswith(f"{dist.setuptools_filename}.egg-info")) # Uninstall cases order do matter as in the case of 2 installs of the # same package, pip needs to uninstall the currently detected version if setuptools_flat_installation: if info_location is not None: paths_to_remove.add(info_location) installed_files = dist.iter_declared_entries() if installed_files is not None: for installed_file in installed_files: paths_to_remove.add( os.path.join(dist_location, installed_file)) # FIXME: need a test for this elif block # occurs with --single-version-externally-managed/--record outside # of pip elif dist.is_file("top_level.txt"): try: namespace_packages = dist.read_text( "namespace_packages.txt") except FileNotFoundError: namespaces = [] else: namespaces = namespace_packages.splitlines(keepends=False) for top_level_pkg in [ p for p in dist.read_text("top_level.txt").splitlines() if p and p not in namespaces ]: path = os.path.join(dist_location, top_level_pkg) paths_to_remove.add(path) paths_to_remove.add(f"{path}.py") paths_to_remove.add(f"{path}.pyc") paths_to_remove.add(f"{path}.pyo") elif dist.installed_by_distutils: raise UninstallationError( "Cannot uninstall {!r}. It is a distutils installed project " "and thus we cannot accurately determine which files belong " "to it which would lead to only a partial uninstall.".format( dist.raw_name, )) elif dist.installed_as_egg: # package installed by easy_install # We cannot match on dist.egg_name because it can slightly vary # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg paths_to_remove.add(dist_location) easy_install_egg = os.path.split(dist_location)[1] easy_install_pth = os.path.join( os.path.dirname(dist_location), "easy-install.pth", ) paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg) elif dist.installed_with_dist_info: for path in uninstallation_paths(dist): paths_to_remove.add(path) elif develop_egg_link: # PEP 660 modern editable is handled in the ``.dist-info`` case # above, so this only covers the setuptools-style editable. with open(develop_egg_link) as fh: link_pointer = os.path.normcase(fh.readline().strip()) normalized_link_pointer = normalize_path(link_pointer) assert os.path.samefile( normalized_link_pointer, normalized_dist_location ), (f"Egg-link {link_pointer} does not match installed location of " f"{dist.raw_name} (at {dist_location})") paths_to_remove.add(develop_egg_link) easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), "easy-install.pth") paths_to_remove.add_pth(easy_install_pth, dist_location) else: logger.debug( "Not sure how to uninstall: %s - Check: %s", dist, dist_location, ) if dist.in_usersite: bin_dir = get_bin_user() else: bin_dir = get_bin_prefix() # find distutils scripts= scripts try: for script in dist.iterdir("scripts"): paths_to_remove.add(os.path.join(bin_dir, script.name)) if WINDOWS: paths_to_remove.add( os.path.join(bin_dir, f"{script.name}.bat")) except (FileNotFoundError, NotADirectoryError): pass # find console_scripts and gui_scripts def iter_scripts_to_remove( dist: BaseDistribution, bin_dir: str, ) -> Generator[str, None, None]: for entry_point in dist.iter_entry_points(): if entry_point.group == "console_scripts": yield from _script_names(bin_dir, entry_point.name, False) elif entry_point.group == "gui_scripts": yield from _script_names(bin_dir, entry_point.name, True) for s in iter_scripts_to_remove(dist, bin_dir): paths_to_remove.add(s) return paths_to_remove
def run(self, options: Values, args: List[str]) -> int: cmdoptions.check_install_build_global(options) session = self.get_default_session(options) finder = self._build_package_finder(options, session) wheel_cache = WheelCache(options.cache_dir, options.format_control) options.wheel_dir = normalize_path(options.wheel_dir) ensure_dir(options.wheel_dir) build_tracker = self.enter_context(get_build_tracker()) directory = TempDirectory( delete=not options.no_clean, kind="wheel", globally_managed=True, ) reqs = self.get_requirements(args, options, finder, session) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, build_tracker=build_tracker, session=session, finder=finder, download_dir=options.wheel_dir, use_user_site=False, verbosity=self.verbosity, ) resolver = self.make_resolver( preparer=preparer, finder=finder, options=options, wheel_cache=wheel_cache, ignore_requires_python=options.ignore_requires_python, use_pep517=options.use_pep517, ) self.trace_basic_info(finder) requirement_set = resolver.resolve(reqs, check_supported_wheels=True) reqs_to_build: List[InstallRequirement] = [] for req in requirement_set.requirements.values(): if req.is_wheel: preparer.save_linked_requirement(req) elif should_build_for_wheel_command(req): reqs_to_build.append(req) # build wheels build_successes, build_failures = build( reqs_to_build, wheel_cache=wheel_cache, verify=(not options.no_verify), build_options=options.build_options or [], global_options=options.global_options or [], ) for req in build_successes: assert req.link and req.link.is_wheel assert req.local_file_path # copy from cache to target directory try: shutil.copy(req.local_file_path, options.wheel_dir) except OSError as e: logger.warning( "Building wheel for %s failed: %s", req.name, e, ) build_failures.append(req) if len(build_failures) != 0: raise CommandError("Failed to build one or more wheels") return SUCCESS
@with_cleanup def run(self, options, args): # type: (Values, List[str]) -> int cmdoptions.check_install_build_global(options) session = self.get_default_session(options) finder = self._build_package_finder(options, session) <<<<<<< HEAD ======= build_delete = (not (options.no_clean or options.build_dir)) >>>>>>> 74c061954d5e927be4caafbd793e96a50563c265 wheel_cache = WheelCache(options.cache_dir, options.format_control) options.wheel_dir = normalize_path(options.wheel_dir) ensure_dir(options.wheel_dir) req_tracker = self.enter_context(get_requirement_tracker()) directory = TempDirectory( <<<<<<< HEAD delete=not options.no_clean, ======= options.build_dir, delete=build_delete, >>>>>>> 74c061954d5e927be4caafbd793e96a50563c265 kind="wheel", globally_managed=True, )
def __init__( self, find_links, # type: List[str] index_urls, # type: List[str] allow_all_prereleases=False, # type: bool trusted_hosts=None, # type: Optional[Iterable[str]] process_dependency_links=False, # type: bool session=None, # type: Optional[PipSession] format_control=None, # type: Optional[FormatControl] platform=None, # type: Optional[str] versions=None, # type: Optional[List[str]] abi=None, # type: Optional[str] implementation=None, # type: Optional[str] prefer_binary=False # type: bool ): # type: (...) -> None """Create a PackageFinder. :param format_control: A FormatControl object or None. Used to control the selection of source packages / binary packages when consulting the index and links. :param platform: A string or None. If None, searches for packages that are supported by the current system. Otherwise, will find packages that can be built on the platform passed in. These packages will only be downloaded for distribution: they will not be built locally. :param versions: A list of strings or None. This is passed directly to pep425tags.py in the get_supported() method. :param abi: A string or None. This is passed directly to pep425tags.py in the get_supported() method. :param implementation: A string or None. This is passed directly to pep425tags.py in the get_supported() method. """ if session is None: raise TypeError( "PackageFinder() missing 1 required keyword argument: " "'session'") # Build find_links. If an argument starts with ~, it may be # a local file relative to a home directory. So try normalizing # it and if it exists, use the normalized version. # This is deliberately conservative - it might be fine just to # blindly normalize anything starting with a ~... self.find_links = [] # type: List[str] for link in find_links: if link.startswith('~'): new_link = normalize_path(link) if os.path.exists(new_link): link = new_link self.find_links.append(link) self.index_urls = index_urls self.dependency_links = [] # type: List[str] # These are boring links that have already been logged somehow: self.logged_links = set() # type: Set[Link] self.format_control = format_control or FormatControl(set(), set()) # Domains that we won't emit warnings for when not using HTTPS self.secure_origins = [ ("*", host, "*") for host in (trusted_hosts if trusted_hosts else []) ] # type: List[SecureOrigin] # Do we want to allow _all_ pre-releases? self.allow_all_prereleases = allow_all_prereleases # Do we process dependency links? self.process_dependency_links = process_dependency_links # The Session we'll use to make requests self.session = session # The valid tags to check potential found wheel candidates against self.valid_tags = get_supported( versions=versions, platform=platform, abi=abi, impl=implementation, ) # Do we prefer old, but valid, binary dist over new source dist self.prefer_binary = prefer_binary # If we don't have TLS enabled, then WARN if anyplace we're looking # relies on TLS. if not HAS_TLS: for link in itertools.chain(self.index_urls, self.find_links): parsed = urllib_parse.urlparse(link) if parsed.scheme == "https": logger.warning( "pip is configured with locations that require " "TLS/SSL, however the ssl module in Python is not " "available.") break
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 <<<<<<< HEAD if getattr(options, "build_dir", None): deprecated( reason=(
def run(self, options, args): # type: (Values, List[Any]) -> None cmdoptions.check_install_build_global(options) session = self.get_default_session(options) finder = self._build_package_finder(options, session) build_delete = (not (options.no_clean or options.build_dir)) wheel_cache = WheelCache(options.cache_dir, options.format_control) options.wheel_dir = normalize_path(options.wheel_dir) ensure_dir(options.wheel_dir) with get_requirement_tracker() as req_tracker, TempDirectory( options.build_dir, delete=build_delete, kind="wheel") as directory: requirement_set = RequirementSet() try: self.populate_requirement_set(requirement_set, args, options, finder, session, wheel_cache) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, req_tracker=req_tracker, session=session, finder=finder, wheel_download_dir=options.wheel_dir, use_user_site=False, ) resolver = self.make_resolver( preparer=preparer, finder=finder, options=options, wheel_cache=wheel_cache, ignore_requires_python=options.ignore_requires_python, use_pep517=options.use_pep517, ) self.trace_basic_info(finder) resolver.resolve(requirement_set) reqs_to_build = [ r for r in requirement_set.requirements.values() if should_build_for_wheel_command(r) ] # build wheels build_successes, build_failures = build( reqs_to_build, wheel_cache=wheel_cache, build_options=options.build_options or [], global_options=options.global_options or [], ) for req in build_successes: assert req.link and req.link.is_wheel assert req.local_file_path # copy from cache to target directory try: shutil.copy(req.local_file_path, options.wheel_dir) except OSError as e: logger.warning( "Building wheel for %s failed: %s", req.name, e, ) build_failures.append(req) if len(build_failures) != 0: raise CommandError("Failed to build one or more wheels") except PreviousBuildDirError: options.no_clean = True raise finally: if not options.no_clean: requirement_set.cleanup_files() wheel_cache.cleanup()
def create( cls, find_links, # type: List[str] index_urls, # type: List[str] allow_all_prereleases=False, # type: bool trusted_hosts=None, # type: Optional[Iterable[str]] session=None, # type: Optional[PipSession] format_control=None, # type: Optional[FormatControl] platform=None, # type: Optional[str] versions=None, # type: Optional[List[str]] abi=None, # type: Optional[str] implementation=None, # type: Optional[str] prefer_binary=False, # type: bool ignore_requires_python=None, # type: Optional[bool] ): # type: (...) -> PackageFinder """Create a PackageFinder. :param trusted_hosts: Domains that we won't emit warnings for when not using HTTPS. :param session: The Session to use to make requests. :param format_control: A FormatControl object or None. Used to control the selection of source packages / binary packages when consulting the index and links. :param platform: A string or None. If None, searches for packages that are supported by the current system. Otherwise, will find packages that can be built on the platform passed in. These packages will only be downloaded for distribution: they will not be built locally. :param versions: A list of strings or None. This is passed directly to pep425tags.py in the get_supported() method. :param abi: A string or None. This is passed directly to pep425tags.py in the get_supported() method. :param implementation: A string or None. This is passed directly to pep425tags.py in the get_supported() method. :param prefer_binary: Whether to prefer an old, but valid, binary dist over a new source dist. :param ignore_requires_python: Whether to ignore incompatible "Requires-Python" values in links. Defaults to False. """ if session is None: raise TypeError( "PackageFinder.create() missing 1 required keyword argument: " "'session'") # Build find_links. If an argument starts with ~, it may be # a local file relative to a home directory. So try normalizing # it and if it exists, use the normalized version. # This is deliberately conservative - it might be fine just to # blindly normalize anything starting with a ~... built_find_links = [] # type: List[str] for link in find_links: if link.startswith('~'): new_link = normalize_path(link) if os.path.exists(new_link): link = new_link built_find_links.append(link) secure_origins = [("*", host, "*") for host in (trusted_hosts if trusted_hosts else []) ] # type: List[SecureOrigin] # The valid tags to check potential found wheel candidates against valid_tags = get_supported( versions=versions, platform=platform, abi=abi, impl=implementation, ) candidate_evaluator = CandidateEvaluator( valid_tags=valid_tags, prefer_binary=prefer_binary, allow_all_prereleases=allow_all_prereleases, ignore_requires_python=ignore_requires_python, ) # If we don't have TLS enabled, then WARN if anyplace we're looking # relies on TLS. if not HAS_TLS: for link in itertools.chain(index_urls, built_find_links): parsed = urllib_parse.urlparse(link) if parsed.scheme == "https": logger.warning( "pip is configured with locations that require " "TLS/SSL, however the ssl module in Python is not " "available.") break return cls( candidate_evaluator=candidate_evaluator, find_links=built_find_links, index_urls=index_urls, secure_origins=secure_origins, session=session, format_control=format_control, )
def run(self, options, args): # type: (Values, List[str]) -> int options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] cmdoptions.check_dist_restriction(options) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) session = self.get_default_session(options) target_python = make_target_python(options) finder = self._build_package_finder( options=options, session=session, target_python=target_python, ) build_delete = (not (options.no_clean or options.build_dir)) req_tracker = self.enter_context(get_requirement_tracker()) directory = TempDirectory( options.build_dir, delete=build_delete, kind="download", globally_managed=True, ) reqs = self.get_requirements(args, options, finder, session) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, req_tracker=req_tracker, session=session, finder=finder, download_dir=options.download_dir, use_user_site=False, ) resolver = self.make_resolver( preparer=preparer, finder=finder, options=options, py_version_info=options.python_version, ) self.trace_basic_info(finder) requirement_set = resolver.resolve( reqs, check_supported_wheels=True ) downloaded = ' '.join([req.name # type: ignore for req in requirement_set.requirements.values() if req.successfully_downloaded]) if downloaded: write_output('Successfully downloaded %s', downloaded) return SUCCESS
def run(self, options, args): options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] if options.python_version: python_versions = [options.python_version] else: python_versions = None dist_restriction_set = any([ options.python_version, options.platform, options.abi, options.implementation, ]) binary_only = FormatControl(set(), {':all:'}) no_sdist_dependencies = (options.format_control != binary_only and not options.ignore_dependencies) if dist_restriction_set and no_sdist_dependencies: raise CommandError( "When restricting platform and interpreter constraints using " "--python-version, --platform, --abi, or --implementation, " "either --no-deps must be set, or --only-binary=:all: must be " "set and --no-binary must not be set (or must be set to " ":none:).") options.src_dir = os.path.abspath(options.src_dir) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) with self._build_session(options) as session: finder = self._build_package_finder( options=options, session=session, platform=options.platform, python_versions=python_versions, abi=options.abi, implementation=options.implementation, ) build_delete = (not (options.no_clean or options.build_dir)) if options.cache_dir and not check_path_owner(options.cache_dir): logger.warning( "The directory '%s' or its parent directory is not owned " "by the current user and caching wheels 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 with TempDirectory(options.build_dir, delete=build_delete, kind="download") as directory: requirement_set = RequirementSet( require_hashes=options.require_hashes, ) self.populate_requirement_set(requirement_set, args, options, finder, session, self.name, None) preparer = RequirementPreparer( build_dir=directory.path, src_dir=options.src_dir, download_dir=options.download_dir, wheel_download_dir=None, progress_bar=options.progress_bar, build_isolation=options.build_isolation, ) resolver = Resolver( preparer=preparer, finder=finder, session=session, wheel_cache=None, use_user_site=False, upgrade_strategy="to-satisfy-only", force_reinstall=False, ignore_dependencies=options.ignore_dependencies, ignore_requires_python=False, ignore_installed=True, isolated=options.isolated_mode, ) resolver.resolve(requirement_set) downloaded = SPACE.join([ req.name for req in requirement_set.successfully_downloaded ]) if downloaded: logger.info('Successfully downloaded %s', downloaded) # Clean up if not options.no_clean: requirement_set.cleanup_files() return requirement_set
def from_dist(cls, dist): dist_path = normalize_path(dist.location) if not dist_is_local(dist): logger.info( "Not uninstalling %s at %s, outside environment %s", dist.key, dist_path, sys.prefix, ) return cls(dist) if dist_path in {p for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} if p}: logger.info( "Not uninstalling %s at %s, as it is in the standard library.", dist.key, dist_path, ) return cls(dist) paths_to_remove = cls(dist) develop_egg_link = egg_link_path(dist) develop_egg_link_egg_info = '{}.egg-info'.format( pkg_resources.to_filename(dist.project_name)) egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) # Special case for distutils installed package distutils_egg_info = getattr(dist._provider, 'path', None) # Uninstall cases order do matter as in the case of 2 installs of the # same package, pip needs to uninstall the currently detected version if (egg_info_exists and dist.egg_info.endswith('.egg-info') and not dist.egg_info.endswith(develop_egg_link_egg_info)): # if dist.egg_info.endswith(develop_egg_link_egg_info), we # are in fact in the develop_egg_link case paths_to_remove.add(dist.egg_info) if dist.has_metadata('installed-files.txt'): for installed_file in dist.get_metadata( 'installed-files.txt').splitlines(): path = os.path.normpath( os.path.join(dist.egg_info, installed_file) ) paths_to_remove.add(path) # FIXME: need a test for this elif block # occurs with --single-version-externally-managed/--record outside # of pip elif dist.has_metadata('top_level.txt'): if dist.has_metadata('namespace_packages.txt'): namespaces = dist.get_metadata('namespace_packages.txt') else: namespaces = [] for top_level_pkg in [ p for p in dist.get_metadata('top_level.txt').splitlines() if p and p not in namespaces]: path = os.path.join(dist.location, top_level_pkg) paths_to_remove.add(path) paths_to_remove.add(path + '.py') paths_to_remove.add(path + '.pyc') paths_to_remove.add(path + '.pyo') elif distutils_egg_info: raise UninstallationError( "Cannot uninstall {!r}. It is a distutils installed project " "and thus we cannot accurately determine which files belong " "to it which would lead to only a partial uninstall.".format( dist.project_name, ) ) elif dist.location.endswith('.egg'): # package installed by easy_install # We cannot match on dist.egg_name because it can slightly vary # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg paths_to_remove.add(dist.location) easy_install_egg = os.path.split(dist.location)[1] easy_install_pth = os.path.join(os.path.dirname(dist.location), 'easy-install.pth') paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) elif egg_info_exists and dist.egg_info.endswith('.dist-info'): for path in uninstallation_paths(dist): paths_to_remove.add(path) elif develop_egg_link: # develop egg with open(develop_egg_link, 'r') as fh: link_pointer = os.path.normcase(fh.readline().strip()) assert (link_pointer == dist.location), ( 'Egg-link %s does not match installed location of %s ' '(at %s)' % (link_pointer, dist.project_name, dist.location) ) paths_to_remove.add(develop_egg_link) easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), 'easy-install.pth') paths_to_remove.add_pth(easy_install_pth, dist.location) else: logger.debug( 'Not sure how to uninstall: %s - Check: %s', dist, dist.location, ) # find distutils scripts= scripts if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): for script in dist.metadata_listdir('scripts'): if dist_in_usersite(dist): bin_dir = bin_user else: bin_dir = bin_py paths_to_remove.add(os.path.join(bin_dir, script)) if WINDOWS: paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') # find console_scripts _scripts_to_remove = [] console_scripts = dist.get_entry_map(group='console_scripts') for name in console_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, False)) # find gui_scripts gui_scripts = dist.get_entry_map(group='gui_scripts') for name in gui_scripts.keys(): _scripts_to_remove.extend(_script_names(dist, name, True)) for s in _scripts_to_remove: paths_to_remove.add(s) return paths_to_remove
def run(self, options: Values, args: List[str]) -> int: options.ignore_installed = True # editable doesn't really make sense for `pip download`, but the bowels # of the RequirementSet code require that property. options.editables = [] cmdoptions.check_dist_restriction(options) options.download_dir = normalize_path(options.download_dir) ensure_dir(options.download_dir) session = self.get_default_session(options) target_python = make_target_python(options) finder = self._build_package_finder( options=options, session=session, target_python=target_python, ignore_requires_python=options.ignore_requires_python, ) build_tracker = self.enter_context(get_build_tracker()) directory = TempDirectory( delete=not options.no_clean, kind="download", globally_managed=True, ) reqs = self.get_requirements(args, options, finder, session) preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, build_tracker=build_tracker, session=session, finder=finder, download_dir=options.download_dir, use_user_site=False, verbosity=self.verbosity, ) resolver = self.make_resolver( preparer=preparer, finder=finder, options=options, ignore_requires_python=options.ignore_requires_python, use_pep517=options.use_pep517, py_version_info=options.python_version, ) self.trace_basic_info(finder) requirement_set = resolver.resolve(reqs, check_supported_wheels=True) downloaded: List[str] = [] for req in requirement_set.requirements.values(): if req.satisfied_by is None: assert req.name is not None preparer.save_linked_requirement(req) downloaded.append(req.name) if downloaded: write_output("Successfully downloaded %s", " ".join(downloaded)) return SUCCESS
def __init__(self, find_links, index_urls, allow_all_prereleases=False, trusted_hosts=None, process_dependency_links=False, session=None, format_control=None, platform=None, versions=None, abi=None, implementation=None): """Create a PackageFinder. :param format_control: A FormatControl object or None. Used to control the selection of source packages / binary packages when consulting the index and links. :param platform: A string or None. If None, searches for packages that are supported by the current system. Otherwise, will find packages that can be built on the platform passed in. These packages will only be downloaded for distribution: they will not be built locally. :param versions: A list of strings or None. This is passed directly to pep425tags.py in the get_supported() method. :param abi: A string or None. This is passed directly to pep425tags.py in the get_supported() method. :param implementation: A string or None. This is passed directly to pep425tags.py in the get_supported() method. """ if session is None: raise TypeError( "PackageFinder() missing 1 required keyword argument: " "'session'" ) # Build find_links. If an argument starts with ~, it may be # a local file relative to a home directory. So try normalizing # it and if it exists, use the normalized version. # This is deliberately conservative - it might be fine just to # blindly normalize anything starting with a ~... self.find_links = [] for link in find_links: if link.startswith('~'): new_link = normalize_path(link) if os.path.exists(new_link): link = new_link self.find_links.append(link) self.index_urls = index_urls self.dependency_links = [] # These are boring links that have already been logged somehow: self.logged_links = set() self.format_control = format_control or FormatControl(set(), set()) # Domains that we won't emit warnings for when not using HTTPS self.secure_origins = [ ("*", host, "*") for host in (trusted_hosts if trusted_hosts else []) ] # Do we want to allow _all_ pre-releases? self.allow_all_prereleases = allow_all_prereleases # Do we process dependency links? self.process_dependency_links = process_dependency_links # The Session we'll use to make requests self.session = session # The valid tags to check potential found wheel candidates against self.valid_tags = get_supported( versions=versions, platform=platform, abi=abi, impl=implementation, ) # If we don't have TLS enabled, then WARN if anyplace we're looking # relies on TLS. if not HAS_TLS: for link in itertools.chain(self.index_urls, self.find_links): parsed = urllib_parse.urlparse(link) if parsed.scheme == "https": logger.warning( "pip is configured with locations that require " "TLS/SSL, however the ssl module in Python is not " "available." ) break
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 "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." ) def intercepts_unhandled_exc( run_func: Callable[..., int] ) -> Callable[..., int]: @functools.wraps(run_func) def exc_logging_wrapper(*args: Any) -> int: try: status = run_func(*args) assert isinstance(status, int) return status except DiagnosticPipError as exc: logger.error("[present-rich] %s", exc) logger.debug("Exception information:", exc_info=True) return ERROR 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 return exc_logging_wrapper try: if not options.debug_mode: run = intercepts_unhandled_exc(self.run) else: run = self.run rich_traceback.install(show_locals=True) return run(options, args) finally: self.handle_pip_version_check(options)