def test_finder_installs_pre_releases(data): """ Test PackageFinder finds pre-releases if asked to. """ req = InstallRequirement.from_line("bar", None) # using a local index (that has pre & dev releases) finder = PackageFinder( [], [data.index_url("pre")], allow_all_prereleases=True, session=PipSession(), ) link = finder.find_requirement(req, False) assert link.url.endswith("bar-2.0b1.tar.gz"), link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] finder = PackageFinder( links, [], allow_all_prereleases=True, session=PipSession(), ) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-2.0b1.tar.gz" links.reverse() finder = PackageFinder( links, [], allow_all_prereleases=True, session=PipSession(), ) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-2.0b1.tar.gz"
def test_mismatched_versions(caplog: pytest.LogCaptureFixture) -> None: req = InstallRequirement( req=Requirement("simplewheel==2.0"), comes_from=None, ) req.source_dir = "/tmp/somewhere" # make req believe it has been unpacked # Monkeypatch! metadata = email.message.Message() metadata["name"] = "simplewheel" metadata["version"] = "1.0" req._metadata = metadata req.assert_source_matches_version() assert caplog.records[-1].message == ( "Requested simplewheel==2.0, but installing version 1.0")
def test_existing_over_wheel_priority(self, data): """ Test existing install has priority over wheels. `test_link_sorting` also covers this at a lower level """ req = InstallRequirement.from_line('priority', None) latest_version = "1.0" satisfied_by = Mock( location="/path", parsed_version=parse_version(latest_version), version=latest_version, ) req.satisfied_by = satisfied_by finder = PackageFinder( [data.find_links], [], session=PipSession(), ) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True)
def test_finder_priority_page_over_deplink(): """ Test PackageFinder prefers page links over equivalent dependency links """ req = InstallRequirement.from_line('pip==1.5.6', None) finder = PackageFinder( [], ["https://pypi.org/simple/"], process_dependency_links=True, session=PipSession(), ) finder.add_dependency_links([ 'https://files.pythonhosted.org/packages/source/p/pip/pip-1.5.6.tar.gz' ]) all_versions = finder.find_all_candidates(req.name) # Check that the dependency_link is last assert all_versions[-1].location.url.startswith( 'https://files.pythonhosted.org/') link = finder.find_requirement(req, False) assert link.url.startswith( "https://files.pythonhosted.org/packages/3f/08/7347ca4"), link
def format_requirements(fp, packages_groups, grouped_packages, excluded_packages, output_index_url, output_find_links, ext_wheels_lines, loose_packages=set()): fp.write('# This file has been automatically generated, DO NOT EDIT!\n') fp.write('\n') if output_index_url: fp.write('--index-url %s\n' % output_index_url, ) fp.write('\n') for find_links in output_find_links: fp.write('--find-links %s\n' % find_links, ) fp.write('\n') seen = set() for requirements_file, packages in packages_groups: fp.write('# Frozen requirements for "%s"\n' % requirements_file) fp.write('\n') distros = [likely_distro(p) for p in packages] for distro in sorted(distros, key=lambda d: d.key): if (distro.key in seen or distro.key in excluded_packages or '%s:%s' % (requirements_file, distro.key) in excluded_packages): continue seen.add(distro.key) versions = grouped_packages[distro.key] if distro.key not in loose_packages: line = '%s==%s' % (distro.key, versions[-1][0]) else: line = distro.key fp.write('%s\n' % line) for line in ext_wheels_lines[requirements_file]: req = InstallRequirement.from_line(line) if req.name in loose_packages: fp.write('%s\n' % req.name) else: fp.write('%s\n' % line.strip()) fp.write('\n')
def _get_release_info(self, name, version): # type: (str, str) -> dict ireq = InstallRequirement.from_line('{}=={}'.format(name, version)) resolver = Resolver([ireq], self._repository, cache=DependencyCache(self._cache_dir.as_posix())) try: requirements = list(resolver._iter_dependencies(ireq)) except (InstallationError, RequirementParseError): # setup.py egg-info error most likely # So we assume no dependencies requirements = [] requires = [] for dep in requirements: constraint = str(dep.req.specifier) require = dep.name if constraint: require += ' ({})'.format(constraint) requires.append(require) try: hashes = resolver.resolve_hashes([ireq])[ireq] except IndexError: # Sometimes pip-tools fails when getting indices hashes = [] hashes = [h.split(':')[1] for h in hashes] data = { 'name': name, 'version': version, 'summary': '', 'requires_dist': requires, 'digests': hashes } resolver.repository.freshen_build_caches() return data
def test_disabling_pep517_invalid(shared_data, source, msg): """ Test that we fail if we try to disable PEP 517 when it's not acceptable """ src = shared_data.src.joinpath(source) req = InstallRequirement(None, None) req.source_dir = src # make req believe it has been unpacked # Simulate --no-use-pep517 req.use_pep517 = False with pytest.raises(InstallationError) as e: req.load_pyproject_toml() err_msg = e.value.args[0] assert "Disabling PEP 517 processing is invalid" in err_msg assert msg in err_msg
def run(self, options, args): """update install options with caching values""" if options.prune: previously_installed = pip_get_installed() with pipfaster_download_cacher(options.index_url): requirement_set = super(FasterInstallCommand, self).run( options, args, ) required = requirement_set.requirements.values() # With extra_index_urls we don't know where the wheel is from if not options.extra_index_urls: cache_installed_wheels(options.index_url, requirement_set.successfully_downloaded) if not options.ignore_dependencies: # transitive requirements, previously installed, are also required # this has a side-effect of finding any missing / conflicting requirements required = trace_requirements(required) if not options.prune: return requirement_set extraneous = ( reqnames(previously_installed) - reqnames(required) - # the stage1 bootstrap packages reqnames( trace_requirements( [InstallRequirement.from_line('venv-update')])) - # See #186 frozenset(('pkg-resources', ))) if extraneous: extraneous = sorted(extraneous) pip(('uninstall', '--yes') + tuple(extraneous))
def run(self, options, args): with self._build_session(options) as session: reqs_to_uninstall = {} for name in args: req = InstallRequirement.from_line( name, isolated=options.isolated_mode) if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req for filename in options.requirements: for req in parse_requirements(filename, options=options, session=session): if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req if not reqs_to_uninstall: raise InstallationError( "You must give at least one requirement to %(name)s (see " '"pip help %(name)s")' % dict(name=self.name)) for req in reqs_to_uninstall.values(): uninstall_pathset = req.uninstall(auto_confirm=options.yes, verbose=self.verbosity > 0) if uninstall_pathset: uninstall_pathset.commit()
def populate_requirement_set(requirement_set, args, options, finder, session, name, wheel_cache): """ Marshal cmd line args into a requirement set. """ # NOTE: As a side-effect, options.require_hashes and # requirement_set.require_hashes may be updated for filename in options.constraints: for req in parse_requirements( filename, constraint=True, finder=finder, options=options, session=session, wheel_cache=wheel_cache): requirement_set.add_requirement(req) for req in args: requirement_set.add_requirement( InstallRequirement.from_line( req, None, isolated=options.isolated_mode, wheel_cache=wheel_cache ) ) for req in options.editables: requirement_set.add_requirement( InstallRequirement.from_editable( req, isolated=options.isolated_mode, wheel_cache=wheel_cache ) ) for filename in options.requirements: for req in parse_requirements( filename, finder=finder, options=options, session=session, wheel_cache=wheel_cache): requirement_set.add_requirement(req) # If --require-hashes was a line in a requirements file, tell # RequirementSet about it: requirement_set.require_hashes = options.require_hashes if not (args or options.editables or options.requirements): opts = {'name': name} if options.find_links: raise CommandError( 'You must give at least one requirement to %(name)s ' '(maybe you meant "pip %(name)s %(links)s"?)' % dict(opts, links=' '.join(options.find_links))) else: raise CommandError( 'You must give at least one requirement to %(name)s ' '(see "pip help %(name)s")' % opts) # On Windows, any operation modifying pip should be run as: # python -m pip ... # See https://github.com/pypa/pip/issues/1299 for more discussion should_show_use_python_msg = ( WINDOWS and requirement_set.has_requirement('pip') and "pip" in os.path.basename(sys.argv[0]) ) if should_show_use_python_msg: new_command = [ sys.executable, "-m", "pip" ] + sys.argv[1:] raise CommandError( 'To modify pip, please run the following command:\n{}' .format(" ".join(new_command)) )
def create_requirement_from_string(string, comes_from=None): return InstallRequirement(Requirement(string), comes_from)
def freeze(requirement=None, find_links=None, local_only=None, user_only=None, skip_regex=None, isolated=False, wheel_cache=None, exclude_editable=False, skip=()): find_links = find_links or [] skip_match = None if skip_regex: skip_match = re.compile(skip_regex).search dependency_links = [] for dist in pkg_resources.working_set: if dist.has_metadata('dependency_links.txt'): dependency_links.extend( dist.get_metadata_lines('dependency_links.txt')) for link in find_links: if '#egg=' in link: dependency_links.append(link) for link in find_links: yield '-f %s' % link installations = {} for dist in get_installed_distributions(local_only=local_only, skip=(), user_only=user_only): try: req = FrozenRequirement.from_dist(dist, dependency_links) except RequirementParseError: logger.warning("Could not parse requirement: %s", dist.project_name) continue if exclude_editable and req.editable: continue installations[req.name] = req if requirement: # the options that don't get turned into an InstallRequirement # should only be emitted once, even if the same option is in multiple # requirements files, so we need to keep track of what has been emitted # so that we don't emit it again if it's seen again emitted_options = set() # keep track of which files a requirement is in so that we can # give an accurate warning if a requirement appears multiple times. req_files = collections.defaultdict(list) for req_file_path in requirement: with open(req_file_path) as req_file: for line in req_file: if (not line.strip() or line.strip().startswith('#') or (skip_match and skip_match(line)) or line.startswith( ('-r', '--requirement', '-Z', '--always-unzip', '-f', '--find-links', '-i', '--index-url', '--pre', '--trusted-host', '--process-dependency-links', '--extra-index-url'))): line = line.rstrip() if line not in emitted_options: emitted_options.add(line) yield line continue if line.startswith('-e') or line.startswith('--editable'): if line.startswith('-e'): line = line[2:].strip() else: line = line[len('--editable'):].strip().lstrip('=') line_req = InstallRequirement.from_editable( line, isolated=isolated, wheel_cache=wheel_cache, ) else: line_req = InstallRequirement.from_line( COMMENT_RE.sub('', line).strip(), isolated=isolated, wheel_cache=wheel_cache, ) if not line_req.name: logger.info( "Skipping line in requirement File [%s] because " "it's not clear what it would install: %s", req_file_path, line.strip(), ) logger.info( " (add #egg=PackageName to the URL to avoid" " this warning)") elif line_req.name not in installations: # either it's not installed, or it is installed # but has been processed already if not req_files[line_req.name]: logger.warning( "Requirement File [%s] contains %s, but that " "package is not installed", req_file_path, COMMENT_RE.sub('', line).strip(), ) else: req_files[line_req.name].append(req_file_path) else: yield str(installations[line_req.name]).rstrip() del installations[line_req.name] req_files[line_req.name].append(req_file_path) # Warn about requirements that were included multiple times (in a # single requirements File or in different requirements files). for name, files in six.iteritems(req_files): if len(files) > 1: logger.warning("Requirement %s included multiple times [%s]", name, ', '.join(sorted(set(files)))) yield ('## The following requirements were added by ' 'pip freeze:') for installation in sorted(installations.values(), key=lambda x: x.name.lower()): if canonicalize_name(installation.name) not in skip: yield str(installation).rstrip()
def bootstrap(tmpdir=None): # Import pip so we can use it to install pip and maybe setuptools too import pip._internal from pip._internal.commands.install import InstallCommand from pip._internal.req import InstallRequirement # Wrapper to provide default certificate with the lowest priority class CertInstallCommand(InstallCommand): def parse_args(self, args): # If cert isn't specified in config or environment, we provide our # own certificate through defaults. # This allows user to specify custom cert anywhere one likes: # config, environment variable or argv. if not self.parser.get_default_values().cert: self.parser.defaults["cert"] = cert_path # calculated below return super(CertInstallCommand, self).parse_args(args) pip._internal.commands_dict["install"] = CertInstallCommand implicit_pip = True implicit_setuptools = True implicit_wheel = True # Check if the user has requested us not to install setuptools if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"): args = [x for x in sys.argv[1:] if x != "--no-setuptools"] implicit_setuptools = False else: args = sys.argv[1:] # Check if the user has requested us not to install wheel if "--no-wheel" in args or os.environ.get("PIP_NO_WHEEL"): args = [x for x in args if x != "--no-wheel"] implicit_wheel = False # We only want to implicitly install setuptools and wheel if they don't # already exist on the target platform. if implicit_setuptools: try: import setuptools # noqa implicit_setuptools = False except ImportError: pass if implicit_wheel: try: import wheel # noqa implicit_wheel = False except ImportError: pass # We want to support people passing things like 'pip<8' to get-pip.py which # will let them install a specific version. However because of the dreaded # DoubleRequirement error if any of the args look like they might be a # specific for one of our packages, then we'll turn off the implicit # install of them. for arg in args: try: req = InstallRequirement.from_line(arg) except Exception: continue if implicit_pip and req.name == "pip": implicit_pip = False elif implicit_setuptools and req.name == "setuptools": implicit_setuptools = False elif implicit_wheel and req.name == "wheel": implicit_wheel = False # Add any implicit installations to the end of our args if implicit_pip: args += ["pip{pip_version}"] if implicit_setuptools: args += ["setuptools{setuptools_version}"] if implicit_wheel: args += ["wheel{wheel_version}"] # Add our default arguments args = ["install", "--upgrade", "--force-reinstall"] + args delete_tmpdir = False try: # Create a temporary directory to act as a working directory if we were # not given one. if tmpdir is None: tmpdir = tempfile.mkdtemp() delete_tmpdir = True # We need to extract the SSL certificates from requests so that they # can be passed to --cert cert_path = os.path.join(tmpdir, "cacert.pem") with open(cert_path, "wb") as cert: cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem")) # Execute the included pip and use it to install the latest pip and # setuptools from PyPI sys.exit(pip._internal.main(args)) finally: # Remove our temporary directory if delete_tmpdir and tmpdir: shutil.rmtree(tmpdir, ignore_errors=True)
def test_installed_version_not_installed(self): req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') assert req.installed_version is None
def test_finder_detects_latest_find_links(data): """Test PackageFinder detects latest using find-links""" req = InstallRequirement.from_line('simple', None) finder = PackageFinder([data.find_links], [], session=PipSession()) link = finder.find_requirement(req, False) assert link.url.endswith("simple-3.0.tar.gz")
def test_incorrect_case_file_index(data): """Test PackageFinder detects latest using wrong case""" req = InstallRequirement.from_line('dinner', None) finder = PackageFinder([], [data.find_links3], session=PipSession()) link = finder.find_requirement(req, False) assert link.url.endswith("Dinner-2.0.tar.gz")
def combine_install_requirements( ireqs: Iterable[InstallRequirement], ) -> InstallRequirement: """ Return a single install requirement that reflects a combination of all the inputs. """ # We will store the source ireqs in a _source_ireqs attribute; # if any of the inputs have this, then use those sources directly. source_ireqs: List[InstallRequirement] = [] for ireq in ireqs: source_ireqs.extend(getattr(ireq, "_source_ireqs", [ireq])) # Optimization. Don't bother with combination logic. if len(source_ireqs) == 1: return source_ireqs[0] link_attrs = { attr: getattr(source_ireqs[0], attr) for attr in ("link", "original_link") } constraint = source_ireqs[0].constraint extras = set(source_ireqs[0].extras) # deepcopy the accumulator req so as to not modify the inputs req = copy.deepcopy(source_ireqs[0].req) for ireq in source_ireqs[1:]: # NOTE we may be losing some info on dropped reqs here if req is not None and ireq.req is not None: req.specifier &= ireq.req.specifier constraint &= ireq.constraint extras |= ireq.extras if req is not None: req.extras = set(extras) for attr_name, attr_val in link_attrs.items(): link_attrs[attr_name] = attr_val or getattr(ireq, attr_name) # InstallRequirements objects are assumed to come from only one source, and # so they support only a single comes_from entry. This function breaks this # model. As a workaround, we deterministically choose a single source for # the comes_from entry, and add an extra _source_ireqs attribute to keep # track of multiple sources for use within pip-tools. if any(ireq.comes_from is None for ireq in source_ireqs): # None indicates package was directly specified. comes_from = None else: # Populate the comes_from field from one of the sources. # Requirement input order is not stable, so we need to sort: # We choose the shortest entry in order to keep the printed # representation as concise as possible. comes_from = min( (ireq.comes_from for ireq in source_ireqs), key=lambda x: (len(str(x)), str(x)), ) combined_ireq = InstallRequirement( req=req, comes_from=comes_from, editable=source_ireqs[0].editable, link=link_attrs["link"], markers=source_ireqs[0].markers, use_pep517=source_ireqs[0].use_pep517, isolated=source_ireqs[0].isolated, install_options=source_ireqs[0].install_options, global_options=source_ireqs[0].global_options, hash_options=source_ireqs[0].hash_options, constraint=constraint, extras=extras, user_supplied=source_ireqs[0].user_supplied, ) # e.g. If the original_link was None, keep it so. Passing `link` as an # argument to `InstallRequirement` sets it as the original_link: combined_ireq.original_link = link_attrs["original_link"] combined_ireq._source_ireqs = source_ireqs return combined_ireq
def test_str(self): req = InstallRequirement.from_line('simple==0.1') assert str(req) == 'simple==0.1'
def find_requirement(self, req: InstallRequirement, upgrade: bool) -> Optional[InstallationCandidate]: """Try to find a Link matching req Expects req, an InstallRequirement and upgrade, a boolean Returns a InstallationCandidate if found, Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise """ hashes = req.hashes(trust_internet=False) best_candidate_result = self.find_best_candidate( req.name, specifier=req.specifier, hashes=hashes, ) best_candidate = best_candidate_result.best_candidate installed_version: Optional[_BaseVersion] = None if req.satisfied_by is not None: installed_version = req.satisfied_by.version def _format_versions( cand_iter: Iterable[InstallationCandidate]) -> str: # This repeated parse_version and str() conversion is needed to # handle different vendoring sources from pip and pkg_resources. # If we stop using the pkg_resources provided specifier and start # using our own, we can drop the cast to str(). return (", ".join( sorted( {str(c.version) for c in cand_iter}, key=parse_version, )) or "none") if installed_version is None and best_candidate is None: logger.critical( "Could not find a version that satisfies the requirement %s " "(from versions: %s)", req, _format_versions(best_candidate_result.iter_all()), ) raise DistributionNotFound( "No matching distribution found for {}".format(req)) best_installed = False if installed_version and (best_candidate is None or best_candidate.version <= installed_version): best_installed = True if not upgrade and installed_version is not None: if best_installed: logger.debug( "Existing installed version (%s) is most up-to-date and " "satisfies requirement", installed_version, ) else: logger.debug( "Existing installed version (%s) satisfies requirement " "(most up-to-date version is %s)", installed_version, best_candidate.version, ) return None if best_installed: # We have an existing version, and its the best version logger.debug( "Installed version (%s) is most up-to-date (past versions: %s)", installed_version, _format_versions(best_candidate_result.iter_applicable()), ) raise BestVersionAlreadyInstalled logger.debug( "Using version %s (newest of versions: %s)", best_candidate.version, _format_versions(best_candidate_result.iter_applicable()), ) return best_candidate
def test_invalid_wheel_requirement_raises(self): with pytest.raises(InvalidWheelFilename): InstallRequirement.from_line('invalid.whl')
def test_repr(self): req = InstallRequirement.from_line('simple==0.1') assert repr(req) == ( '<InstallRequirement object: simple==0.1 editable=False>')
def test_url_preserved_editable_req(self): """Confirm the url is preserved in a editable requirement""" url = 'git+http://foo.com@ref#egg=foo' req = InstallRequirement.from_editable(url) assert req.link.url == url
def test_wheel_requirement_sets_req_attribute(self): req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl') assert isinstance(req.req, Requirement) assert str(req.req) == 'simple==0.1'
def test_markers_semicolon(self): # check that the markers can contain a semicolon req = InstallRequirement.from_line('semicolon; os_name == "a; b"') assert req.req.name == 'semicolon' assert str(req.req.specifier) == '' assert str(req.markers) == 'os_name == "a; b"'
def test_single_equal_sign(self): with pytest.raises(InstallationError) as e: InstallRequirement.from_line('toto=42') err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "= is not a valid operator. Did you mean == ?" in err_msg
def test_traceback(self): with pytest.raises(InstallationError) as e: InstallRequirement.from_line('toto 42') err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "\nTraceback " in err_msg
def test_url_with_query(self): """InstallRequirement should strip the fragment, but not the query.""" url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' fragment = '#egg=bar' req = InstallRequirement.from_line(url + fragment) assert req.link.url == url + fragment, req.link
def test_unexisting_path(self): with pytest.raises(InstallationError) as e: InstallRequirement.from_line( os.path.join('this', 'path', 'does', 'not', 'exist')) err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "It looks like a path." in err_msg
def test_use_pep517(data, source, expected): """ Test that we choose correctly between PEP 517 and legacy code paths """ src = data.src.join(source) req = InstallRequirement(None, None, source_dir=src) req.load_pyproject_toml() assert req.use_pep517 is expected
def test_exclusive_environment_markers(): """Make sure RequirementSet accepts several excluding env markers""" eq26 = InstallRequirement.from_line( "Django>=1.6.10,<1.7 ; python_version == '2.6'") ne26 = InstallRequirement.from_line( "Django>=1.6.10,<1.8 ; python_version != '2.6'") req_set = RequirementSet('', '', '') req_set.add_requirement(eq26) req_set.add_requirement(ne26) assert req_set.has_requirement('Django')
def test_requirement_file(self): req_file_path = os.path.join(self.tempdir, 'test.txt') with open(req_file_path, 'w') as req_file: req_file.write('pip\nsetuptools') with pytest.raises(InstallationError) as e: InstallRequirement.from_line(req_file_path) err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "It looks like a path. It does exist." in err_msg assert "appears to be a requirements file." in err_msg assert "If that is the case, use the '-r' flag to install" in err_msg
def test_mismatched_versions(caplog, tmpdir): original_source = os.path.join(DATA_DIR, 'src', 'simplewheel-1.0') source_dir = os.path.join(tmpdir, 'simplewheel') shutil.copytree(original_source, source_dir) req = InstallRequirement(req=Requirement('simplewheel==2.0'), comes_from=None, source_dir=source_dir) req.run_egg_info() req.assert_source_matches_version() assert caplog.records[-1].message == ( 'Requested simplewheel==2.0, ' 'but installing version 1.0' )
def test_backend(tmpdir, data): """Check we can call a requirement's backend successfully""" project_dir = make_project(tmpdir, backend="dummy_backend") req = InstallRequirement(None, None, source_dir=project_dir) req.load_pyproject_toml() env = BuildEnvironment() finder = PackageFinder([data.backends], [], session=PipSession()) env.install_requirements(finder, ["dummy_backend"], 'normal', "Installing") conflicting, missing = env.check_requirements(["dummy_backend"]) assert not conflicting and not missing assert hasattr(req.pep517_backend, 'build_wheel') with env: assert req.pep517_backend.build_wheel("dir") == "Backend called"
def test_markers_url(self): # test "URL; markers" syntax url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' line = '%s; python_version >= "3"' % url req = InstallRequirement.from_line(line) assert req.link.url == url, req.url assert str(req.markers) == 'python_version >= "3"' # without space, markers are part of the URL url = 'http://foo.com/?p=bar.git;a=snapshot;h=v0.1;sf=tgz' line = '%s;python_version >= "3"' % url req = InstallRequirement.from_line(line) assert req.link.url == line, req.url assert req.markers is None
def test_extras_for_line_path_requirement(self): line = 'SomeProject[ex1,ex2]' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.from_line(line, comes_from=comes_from) assert len(req.extras) == 2 assert req.extras == set(['ex1', 'ex2'])
def test_no_mpkg(data): """Finder skips zipfiles with "macosx10" in the name.""" finder = PackageFinder([data.find_links], [], session=PipSession()) req = InstallRequirement.from_line("pkgwithmpkg") found = finder.find_requirement(req, False) assert found.url.endswith("pkgwithmpkg-1.0.tar.gz"), found
def test_no_partial_name_match(data): """Finder requires the full project name to match, not just beginning.""" finder = PackageFinder([data.find_links], [], session=PipSession()) req = InstallRequirement.from_line("gmpy") found = finder.find_requirement(req, False) assert found.url.endswith("gmpy-1.15.tar.gz"), found
def test_finder_priority_nonegg_over_eggfragments(): """Test PackageFinder prefers non-egg links over "#egg=" links""" req = InstallRequirement.from_line('bar==1.0', None) links = ['http://foo/bar.py#egg=bar-1.0', 'http://foo/bar-1.0.tar.gz'] finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): all_versions = finder.find_all_candidates(req.name) assert all_versions[0].location.url.endswith('tar.gz') assert all_versions[1].location.url.endswith('#egg=bar-1.0') link = finder.find_requirement(req, False) assert link.url.endswith('tar.gz') links.reverse() finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): all_versions = finder.find_all_candidates(req.name) assert all_versions[0].location.url.endswith('tar.gz') assert all_versions[1].location.url.endswith('#egg=bar-1.0') link = finder.find_requirement(req, False) assert link.url.endswith('tar.gz')
def test_extras_for_editable_url_requirement(self): url = 'git+https://url#egg=SomeProject[ex1,ex2]' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.from_editable(url, comes_from=comes_from) assert len(req.extras) == 2 assert req.extras == set(['ex1', 'ex2'])
def test_disabling_pep517_invalid(data, source, msg): """ Test that we fail if we try to disable PEP 517 when it's not acceptable """ src = data.src.join(source) req = InstallRequirement(None, None, source_dir=src) # Simulate --no-use-pep517 req.use_pep517 = False with pytest.raises(InstallationError) as e: req.load_pyproject_toml() err_msg = e.value.args[0] assert "Disabling PEP 517 processing is invalid" in err_msg assert msg in err_msg
def test_finder_only_installs_stable_releases(data): """ Test PackageFinder only accepts stable versioned releases by default. """ req = InstallRequirement.from_line("bar", None) # using a local index (that has pre & dev releases) finder = PackageFinder([], [data.index_url("pre")], session=PipSession()) link = finder.find_requirement(req, False) assert link.url.endswith("bar-1.0.tar.gz"), link.url # using find-links links = ["https://foo/bar-1.0.tar.gz", "https://foo/bar-2.0b1.tar.gz"] finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-1.0.tar.gz" links.reverse() finder = PackageFinder(links, [], session=PipSession()) with patch.object(finder, "_get_pages", lambda x, y: []): link = finder.find_requirement(req, False) assert link.url == "https://foo/bar-1.0.tar.gz"
def test_extras_for_editable_path_requirement(self): url = '.[ex1,ex2]' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.from_editable(url, comes_from=comes_from) assert len(req.extras) == 2 assert req.extras == set(['ex1', 'ex2'])
def run(self, options, args): with self._build_session(options) as session: reqs_to_uninstall = {} for name in args: req = InstallRequirement.from_line( name, isolated=options.isolated_mode, ) if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req for filename in options.requirements: for req in parse_requirements( filename, options=options, session=session): if req.name: reqs_to_uninstall[canonicalize_name(req.name)] = req if not reqs_to_uninstall: raise InstallationError( 'You must give at least one requirement to %(name)s (see ' '"pip help %(name)s")' % dict(name=self.name) ) protect_pip_from_modification_on_windows( modifying_pip="pip" in reqs_to_uninstall ) for req in reqs_to_uninstall.values(): uninstall_pathset = req.uninstall( auto_confirm=options.yes, verbose=self.verbosity > 0, ) if uninstall_pathset: uninstall_pathset.commit()
def test_tilde(): """Finder can accept a path with ~ in it and will normalize it.""" session = PipSession() with patch('pip._internal.index.os.path.exists', return_value=True): finder = PackageFinder(['~/python-pkgs'], [], session=session) req = InstallRequirement.from_line("gmpy") with pytest.raises(DistributionNotFound): finder.find_requirement(req, False)
def test_get_index_urls_locations(): """Check that the canonical name is on all indexes""" finder = PackageFinder( [], ['file://index1/', 'file://index2'], session=PipSession()) locations = finder._get_index_urls_locations( InstallRequirement.from_line('Complex_Name').name) assert locations == ['file://index1/complex-name/', 'file://index2/complex-name/']
def test_get_dist_trailing_slash(self): # Tests issue fixed by https://github.com/pypa/pip/pull/2530 req = InstallRequirement.from_line('foo') req.egg_info_path = Mock(return_value='/path/to/foo.egg-info/') dist = req.get_dist() assert isinstance(dist, pkg_resources.Distribution) assert dist.project_name == 'foo' assert dist.location == '/path/to'
def test_unsupported_wheel_link_requirement_raises(self): reqset = RequirementSet() req = InstallRequirement.from_line( 'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl', ) assert req.link is not None assert req.link.is_wheel assert req.link.scheme == "https" with pytest.raises(InstallationError): reqset.add_requirement(req)
def test_unsupported_wheel_local_file_requirement_raises(self, data): reqset = RequirementSet() req = InstallRequirement.from_line( data.packages.join('simple.dist-0.1-py1-none-invalid.whl'), ) assert req.link is not None assert req.link.is_wheel assert req.link.scheme == "file" with pytest.raises(InstallationError): reqset.add_requirement(req)
def test_markers_match(self): # match for markers in ( 'python_version >= "1.0"', 'sys_platform == %r' % sys.platform, ): line = 'name; ' + markers req = InstallRequirement.from_line(line, comes_from='') assert str(req.markers) == str(Marker(markers)) assert req.match_markers() # don't match for markers in ( 'python_version >= "5.0"', 'sys_platform != %r' % sys.platform, ): line = 'name; ' + markers req = InstallRequirement.from_line(line, comes_from='') assert str(req.markers) == str(Marker(markers)) assert not req.match_markers()
def test_duplicates_sort_ok(data): """Finder successfully finds one of a set of duplicates in different locations""" finder = PackageFinder( [data.find_links, data.find_links2], [], session=PipSession(), ) req = InstallRequirement.from_line("duplicate") found = finder.find_requirement(req, False) assert found.url.endswith("duplicate-1.0.tar.gz"), found
def test_markers(self): for line in ( # recommended syntax 'mock3; python_version >= "3"', # with more spaces 'mock3 ; python_version >= "3" ', # without spaces 'mock3;python_version >= "3"', ): req = InstallRequirement.from_line(line) assert req.req.name == 'mock3' assert str(req.req.specifier) == '' assert str(req.markers) == 'python_version >= "3"'
def test_wheel_over_sdist_priority(self, data): """ Test wheels have priority over sdists. `test_link_sorting` also covers this at lower level """ req = InstallRequirement.from_line("priority") finder = PackageFinder( [data.find_links], [], session=PipSession(), ) found = finder.find_requirement(req, True) assert found.url.endswith("priority-1.0-py2.py3-none-any.whl"), found
def test_finder_installs_dev_releases(data): """ Test PackageFinder finds dev releases if asked to. """ req = InstallRequirement.from_line("bar", None) # using a local index (that has dev releases) finder = PackageFinder( [], [data.index_url("dev")], allow_all_prereleases=True, session=PipSession(), ) link = finder.find_requirement(req, False) assert link.url.endswith("bar-2.0.dev1.tar.gz"), link.url
def test_finder_detects_latest_already_satisfied_find_links(data): """Test PackageFinder detects latest already satisfied using find-links""" req = InstallRequirement.from_line('simple', None) # the latest simple in local pkgs is 3.0 latest_version = "3.0" satisfied_by = Mock( location="/path", parsed_version=parse_version(latest_version), version=latest_version ) req.satisfied_by = satisfied_by finder = PackageFinder([data.find_links], [], session=PipSession()) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True)
def test_finder_priority_file_over_page(data): """Test PackageFinder prefers file links over equivalent page links""" req = InstallRequirement.from_line('gmpy==1.15', None) finder = PackageFinder( [data.find_links], ["http://pypi.python.org/simple"], session=PipSession(), ) all_versions = finder.find_all_candidates(req.name) # 1 file InstallationCandidate followed by all https ones assert all_versions[0].location.scheme == 'file' assert all(version.location.scheme == 'https' for version in all_versions[1:]), all_versions link = finder.find_requirement(req, False) assert link.url.startswith("file://")
def test_get_dist(self): req = InstallRequirement.from_line('foo') req.egg_info_path = Mock(return_value='/path/to/foo.egg-info') dist = req.get_dist() assert isinstance(dist, pkg_resources.Distribution) assert dist.project_name == 'foo' assert dist.location == '/path/to'
def test_finder_deplink(): """ Test PackageFinder with dependency links only """ req = InstallRequirement.from_line('gmpy==1.15', None) finder = PackageFinder( [], [], process_dependency_links=True, session=PipSession(), ) finder.add_dependency_links( ['https://pypi.python.org/packages/source/g/gmpy/gmpy-1.15.zip']) link = finder.find_requirement(req, False) assert link.url.startswith("https://pypi"), link