def test_unexisting_path(self): with pytest.raises(InstallationError) as e: install_req_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_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: install_req_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_exclusive_environment_markers(): """Make sure RequirementSet accepts several excluding env markers""" eq26 = install_req_from_line( "Django>=1.6.10,<1.7 ; python_version == '2.6'") eq26.is_direct = True ne26 = install_req_from_line( "Django>=1.6.10,<1.8 ; python_version != '2.6'") ne26.is_direct = True req_set = RequirementSet() req_set.add_requirement(eq26) req_set.add_requirement(ne26) assert req_set.has_requirement('Django')
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 = install_req_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 = install_req_from_line(line) assert req.link.url == line, req.url assert req.markers is None
def test_no_mpkg(data): """Finder skips zipfiles with "macosx10" in the name.""" finder = PackageFinder([data.find_links], [], session=PipSession()) req = install_req_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 = install_req_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 = install_req_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_finder_only_installs_stable_releases(data): """ Test PackageFinder only accepts stable versioned releases by default. """ req = install_req_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_yield_line_requirement_with_spaces_in_specifier(self): line = 'SomeProject >= 2' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert repr(list(process_line(line, filename, 1))[0]) == repr(req) assert str(req.req.specifier) == '>=2'
def test_get_dist(self, path): req = install_req_from_line('foo') req._egg_info_path = path dist = req.get_dist() assert isinstance(dist, pkg_resources.Distribution) assert dist.project_name == 'foo' assert dist.location == '/path/to'.replace('/', os.path.sep)
def test_extras_for_line_url_requirement(self): line = 'git+https://url#egg=SomeProject[ex1,ex2]' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert len(req.extras) == 2 assert req.extras == {'ex1', 'ex2'}
def run(self, options, args): with self._build_session(options) as session: reqs_to_uninstall = {} for name in args: req = install_req_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 = install_req_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( install_req_from_line('Complex_Name').name) assert locations == ['file://index1/complex-name/', 'file://index2/complex-name/']
def test_yield_line_constraint(self): line = 'SomeProject' filename = 'filename' comes_from = '-c %s (line %s)' % (filename, 1) req = install_req_from_line( line, comes_from=comes_from, constraint=True) found_req = list(process_line(line, filename, 1, constraint=True))[0] assert repr(found_req) == repr(req) assert found_req.constraint is True
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_to_add in parse_requirements( filename, constraint=True, finder=finder, options=options, session=session, wheel_cache=wheel_cache): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in args: req_to_add = install_req_from_line( req, None, isolated=options.isolated_mode, wheel_cache=wheel_cache ) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, isolated=options.isolated_mode, wheel_cache=wheel_cache ) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for filename in options.requirements: for req_to_add in parse_requirements( filename, finder=finder, options=options, session=session, wheel_cache=wheel_cache): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # 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)
def test_unsupported_wheel_link_requirement_raises(self): reqset = RequirementSet() req = install_req_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 = install_req_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 = install_req_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 = install_req_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 = install_req_from_line("duplicate") found = finder.find_requirement(req, False) assert found.url.endswith("duplicate-1.0.tar.gz"), found
def test_nested_constraints_file(self, monkeypatch): line = '-c another_file' req = install_req_from_line('SomeProject') import pip._internal.req.req_file def stub_parse_requirements(req_url, finder, comes_from, options, session, wheel_cache, constraint): return [(req, constraint)] parse_requirements_stub = stub(call=stub_parse_requirements) monkeypatch.setattr(pip._internal.req.req_file, 'parse_requirements', parse_requirements_stub.call) assert list(process_line(line, 'filename', 1)) == [(req, True)]
def test_wheel_over_sdist_priority(self, data): """ Test wheels have priority over sdists. `test_link_sorting` also covers this at lower level """ req = install_req_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_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 = install_req_from_line(line) assert req.req.name == 'mock3' assert str(req.req.specifier) == '' assert str(req.markers) == 'python_version >= "3"'
def test_forward_slash_results_in_a_link(self, tmpdir): install_dir = tmpdir / "foo" / "bar" # Just create a file for letting the logic work setup_py_path = install_dir / "setup.py" os.makedirs(str(install_dir)) with open(setup_py_path, 'w') as f: f.write('') requirement = install_req_from_line( str(install_dir).replace(os.sep, os.altsep or os.sep) ) assert requirement.link is not None
def test_finder_installs_dev_releases(data): """ Test PackageFinder finds dev releases if asked to. """ req = install_req_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 = install_req_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 = install_req_from_line('gmpy==1.15', None) finder = PackageFinder( [data.find_links], ["http://pypi.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_finder_deplink(): """ Test PackageFinder with dependency links only """ req = install_req_from_line('gmpy==1.15', None) finder = PackageFinder( [], [], process_dependency_links=True, session=PipSession(), ) finder.add_dependency_links( ['https://files.pythonhosted.org/packages/source/g/gmpy/gmpy-1.15.zip'] ) link = finder.find_requirement(req, False) assert link.url.startswith("https://files.pythonhosted.org/"), link
def test_finder_installs_pre_releases_with_version_spec(): """ Test PackageFinder only accepts stable versioned releases by default. """ req = install_req_from_line("bar>=0.0.dev0", None) 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-2.0b1.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-2.0b1.tar.gz"
def test_finder_detects_latest_already_satisfied_pypi_links(): """Test PackageFinder detects latest already satisfied using pypi links""" req = install_req_from_line('initools', None) # the latest initools on PyPI is 0.3.1 latest_version = "0.3.1" satisfied_by = Mock( location="/path", parsed_version=parse_version(latest_version), version=latest_version, ) req.satisfied_by = satisfied_by finder = PackageFinder( [], ["http://pypi.org/simple/"], session=PipSession(), ) with pytest.raises(BestVersionAlreadyInstalled): finder.find_requirement(req, True)
def bootstrap(tmpdir=None): # Import pip so we can use it to install pip and maybe setuptools too from pip._internal.cli.main import main as pip_entry_point from pip._internal.commands.install import InstallCommand from pip._internal.req.constructors import install_req_from_line # Wrapper to provide default certificate with the lowest priority # Due to pip._internal.commands.commands_dict structure, a monkeypatch # seems the simplest workaround. install_parse_args = InstallCommand.parse_args def cert_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 install_parse_args(self, args) InstallCommand.parse_args = cert_parse_args 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 = install_req_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_entry_point(args)) finally: # Remove our temporary directory if delete_tmpdir and tmpdir: shutil.rmtree(tmpdir, ignore_errors=True)
def test_url_preserved_line_req(self): """Confirm the url is preserved in a non-editable requirement""" url = 'git+http://foo.com@ref#egg=foo' req = install_req_from_line(url) assert req.link.url == url
def test_finder_detects_latest_find_links(data): """Test PackageFinder detects latest using find-links""" req = install_req_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 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 = install_req_from_editable( line, isolated=isolated, wheel_cache=wheel_cache, ) else: line_req = install_req_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 freeze( requirement: Optional[List[str]] = None, local_only: bool = False, user_only: bool = False, paths: Optional[List[str]] = None, isolated: bool = False, exclude_editable: bool = False, skip: Container[str] = (), ) -> Generator[str, None, None]: installations: Dict[str, FrozenRequirement] = {} dists = get_environment(paths).iter_installed_distributions( local_only=local_only, skip=(), user_only=user_only, ) for dist in dists: req = FrozenRequirement.from_dist(dist) if exclude_editable and req.editable: continue installations[req.canonical_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[str] = 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: Dict[str, List[str]] = 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 line.startswith( ( "-r", "--requirement", "-f", "--find-links", "-i", "--index-url", "--pre", "--trusted-host", "--process-dependency-links", "--extra-index-url", "--use-feature", ) ) ): 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 = install_req_from_editable( line, isolated=isolated, ) else: line_req = install_req_from_line( COMMENT_RE.sub("", line).strip(), isolated=isolated, ) 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)" ) else: line_req_canonical_name = canonicalize_name(line_req.name) if line_req_canonical_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 " "package %r is not installed", req_file_path, COMMENT_RE.sub("", line).strip(), line_req.name, ) else: req_files[line_req.name].append(req_file_path) else: yield str(installations[line_req_canonical_name]).rstrip() del installations[line_req_canonical_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 req_files.items(): 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 installation.canonical_name not in skip: yield str(installation).rstrip()
def test_repr(self): req = install_req_from_line('simple==0.1') assert repr(req) == ( '<InstallRequirement object: simple==0.1 editable=False>')
def test_yield_pep440_line_requirement(self): line = 'SomeProject @ https://url/SomeProject-py2-py3-none-any.whl' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
def test_single_equal_sign(self): with pytest.raises(InstallationError) as e: install_req_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_yield_pep440_line_requirement(self, line_processor): line = 'SomeProject @ https://url/SomeProject-py2-py3-none-any.whl' filename = 'filename' comes_from = '-r {} (line {})'.format(filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert repr(line_processor(line, filename, 1)[0]) == repr(req)
def populate_requirement_set( self, requirement_set, # type: RequirementSet args, # type: List[str] options, # type: Values finder, # type: PackageFinder session, # type: PipSession wheel_cache, # type: Optional[WheelCache] ): # type: (...) -> None """ Marshal cmd line args into a requirement set. """ for filename in options.constraints: for req_to_add in parse_requirements(filename, constraint=True, finder=finder, options=options, session=session, wheel_cache=wheel_cache): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in args: req_to_add = install_req_from_line(req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: for req_to_add in parse_requirements( filename, finder=finder, options=options, session=session, wheel_cache=wheel_cache, use_pep517=options.use_pep517): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # If any requirement has hash options, enable hash checking. requirements = (requirement_set.unnamed_requirements + list(requirement_set.requirements.values())) if any(req.has_hash_options for req in requirements): options.require_hashes = True if not (args or options.editables or options.requirements): opts = {'name': self.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)
def test_yield_line_requirement(self): line = 'SomeProject' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
def populate_requirement_set( requirement_set, # type: RequirementSet args, # type: List[str] options, # type: Values finder, # type: PackageFinder session, # type: PipSession name, # type: str wheel_cache # type: Optional[WheelCache] ): # type: (...) -> None """ 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_to_add in parse_requirements(filename, constraint=True, finder=finder, options=options, session=session, wheel_cache=wheel_cache): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in args: req_to_add = install_req_from_line(req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for filename in options.requirements: for req_to_add in parse_requirements( filename, finder=finder, options=options, session=session, wheel_cache=wheel_cache, use_pep517=options.use_pep517): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # 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)
def cli( ctx, verbose, quiet, dry_run, pre, rebuild, find_links, index_url, extra_index_url, cert, client_cert, trusted_host, header, index, emit_trusted_host, annotate, upgrade, upgrade_packages, output_file, allow_unsafe, generate_hashes, src_files, max_rounds, build_isolation, emit_find_links, cache_dir, ): """Compiles requirements.txt from requirements.in specs.""" log.verbosity = verbose - quiet if len(src_files) == 0: if os.path.exists(DEFAULT_REQUIREMENTS_FILE): src_files = (DEFAULT_REQUIREMENTS_FILE, ) elif os.path.exists("setup.py"): src_files = ("setup.py", ) else: raise click.BadParameter(("If you do not specify an input file, " "the default is {} or setup.py" ).format(DEFAULT_REQUIREMENTS_FILE)) if not output_file: # An output file must be provided for stdin if src_files == ("-", ): raise click.BadParameter( "--output-file is required if input is from stdin") # Use default requirements output file if there is a setup.py the source file elif src_files == ("setup.py", ): file_name = DEFAULT_REQUIREMENTS_OUTPUT_FILE # An output file must be provided if there are multiple source files elif len(src_files) > 1: raise click.BadParameter( "--output-file is required if two or more input files are given." ) # Otherwise derive the output file from the source file else: base_name = src_files[0].rsplit(".", 1)[0] file_name = base_name + ".txt" output_file = click.open_file(file_name, "w+b", atomic=True, lazy=True) # Close the file at the end of the context execution ctx.call_on_close(safecall(output_file.close_intelligently)) ### # Setup ### pip_args = [] if find_links: for link in find_links: pip_args.extend(["-f", link]) if index_url: pip_args.extend(["-i", index_url]) if extra_index_url: for extra_index in extra_index_url: pip_args.extend(["--extra-index-url", extra_index]) if cert: pip_args.extend(["--cert", cert]) if client_cert: pip_args.extend(["--client-cert", client_cert]) if pre: pip_args.extend(["--pre"]) if trusted_host: for host in trusted_host: pip_args.extend(["--trusted-host", host]) repository = PyPIRepository(pip_args, build_isolation=build_isolation, cache_dir=cache_dir) # Parse all constraints coming from --upgrade-package/-P upgrade_reqs_gen = (install_req_from_line(pkg) for pkg in upgrade_packages) upgrade_install_reqs = { key_from_ireq(install_req): install_req for install_req in upgrade_reqs_gen } existing_pins_to_upgrade = set() # Proxy with a LocalRequirementsRepository if --upgrade is not specified # (= default invocation) if not upgrade and os.path.exists(output_file.name): # Use a temporary repository to ensure outdated(removed) options from # existing requirements.txt wouldn't get into the current repository. tmp_repository = PyPIRepository(pip_args, build_isolation=build_isolation, cache_dir=cache_dir) ireqs = parse_requirements( output_file.name, finder=tmp_repository.finder, session=tmp_repository.session, options=tmp_repository.options, ) # Exclude packages from --upgrade-package/-P from the existing # constraints, and separately gather pins to be upgraded existing_pins = {} for ireq in filter(is_pinned_requirement, ireqs): key = key_from_ireq(ireq) if key in upgrade_install_reqs: existing_pins_to_upgrade.add(key) else: existing_pins[key] = ireq repository = LocalRequirementsRepository(existing_pins, repository) ### # Parsing/collecting initial requirements ### constraints = [] for src_file in src_files: is_setup_file = os.path.basename(src_file) == "setup.py" if is_setup_file or src_file == "-": # pip requires filenames and not files. Since we want to support # piping from stdin, we need to briefly save the input from stdin # to a temporary file and have pip read that. also used for # reading requirements from install_requires in setup.py. tmpfile = tempfile.NamedTemporaryFile(mode="wt", delete=False) if is_setup_file: from distutils.core import run_setup dist = run_setup(src_file) tmpfile.write("\n".join(dist.install_requires)) comes_from = "{name} ({filename})".format(name=dist.get_name(), filename=src_file) else: tmpfile.write(sys.stdin.read()) comes_from = "-r -" tmpfile.flush() reqs = list( parse_requirements( tmpfile.name, finder=repository.finder, session=repository.session, options=repository.options, )) for req in reqs: req.comes_from = comes_from constraints.extend(reqs) else: constraints.extend( parse_requirements( src_file, finder=repository.finder, session=repository.session, options=repository.options, )) primary_packages = { key_from_ireq(ireq) for ireq in constraints if not ireq.constraint } allowed_upgrades = primary_packages | existing_pins_to_upgrade constraints.extend(ireq for key, ireq in upgrade_install_reqs.items() if key in allowed_upgrades) # Filter out pip environment markers which do not match (PEP496) constraints = [ req for req in constraints if req.markers is None or req.markers.evaluate() ] log.debug("Using indexes:") for index_url in dedup(repository.finder.index_urls): log.debug(" {}".format(index_url)) if repository.finder.find_links: log.debug("") log.debug("Configuration:") for find_link in dedup(repository.finder.find_links): log.debug(" -f {}".format(find_link)) try: resolver = Resolver( constraints, repository, prereleases=repository.finder.allow_all_prereleases or pre, cache=DependencyCache(cache_dir), clear_caches=rebuild, allow_unsafe=allow_unsafe, ) results = resolver.resolve(max_rounds=max_rounds) if generate_hashes: hashes = resolver.resolve_hashes(results) else: hashes = None except PipToolsError as e: log.error(str(e)) sys.exit(2) log.debug("") ## # Output ## writer = OutputWriter( src_files, output_file, click_ctx=ctx, dry_run=dry_run, emit_header=header, emit_index=index, emit_trusted_host=emit_trusted_host, annotate=annotate, generate_hashes=generate_hashes, default_index_url=repository.DEFAULT_INDEX_URL, index_urls=repository.finder.index_urls, trusted_hosts=repository.finder.trusted_hosts, format_control=repository.finder.format_control, allow_unsafe=allow_unsafe, find_links=repository.finder.find_links, emit_find_links=emit_find_links, ) writer.write( results=results, unsafe_requirements=resolver.unsafe_constraints, markers={ key_from_ireq(ireq): ireq.markers for ireq in constraints if ireq.markers }, hashes=hashes, ) if dry_run: log.info("Dry-run, so nothing updated.")
def test_unidentifiable_name(self): test_name = '-' with pytest.raises(InstallationError) as e: install_req_from_line(test_name) err_msg = e.value.args[0] assert f"Invalid requirement: '{test_name}'" == err_msg
def test_wheel_requirement_sets_req_attribute(self): req = install_req_from_line('simple-0.1-py2.py3-none-any.whl') assert isinstance(req.req, Requirement) assert str(req.req) == 'simple==0.1'
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 = install_req_from_line(url + fragment) assert req.link.url == url + fragment, req.link
def test_yield_line_requirement(self, line_processor): line = 'SomeProject' filename = 'filename' comes_from = '-r {} (line {})'.format(filename, 1) req = install_req_from_line(line, comes_from=comes_from) assert repr(line_processor(line, filename, 1)[0]) == repr(req)
def get_requirements( self, args, # type: List[str] options, # type: Values finder, # type: PackageFinder session, # type: PipSession ): # type: (...) -> List[InstallRequirement] """ Parse command-line arguments into the corresponding requirements. """ requirements = [] # type: List[InstallRequirement] for filename in options.constraints: for parsed_req in parse_requirements( filename, constraint=True, finder=finder, options=options, session=session, ): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, user_supplied=False ) requirements.append(req_to_add) for req in args: req_to_add = install_req_from_line( req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, user_supplied=True, ) requirements.append(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, user_supplied=True, isolated=options.isolated_mode, use_pep517=options.use_pep517, ) requirements.append(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: for parsed_req in parse_requirements( filename, finder=finder, options=options, session=session ): req_to_add = install_req_from_parsed_requirement( parsed_req, isolated=options.isolated_mode, use_pep517=options.use_pep517, user_supplied=True, ) requirements.append(req_to_add) # If any requirement has hash options, enable hash checking. if any(req.has_hash_options for req in requirements): options.require_hashes = True if not (args or options.editables or options.requirements): opts = {"name": self.name} if options.find_links: raise CommandError( "You must give at least one requirement to {name} " '(maybe you meant "pip {name} {links}"?)'.format( **dict(opts, links=" ".join(options.find_links)) ) ) else: raise CommandError( "You must give at least one requirement to {name} " '(see "pip help {name}")'.format(**opts) ) return requirements
def test_invalid_wheel_requirement_raises(self): with pytest.raises(InvalidWheelFilename): install_req_from_line('invalid.whl')
def test_traceback(self): with pytest.raises(InstallationError) as e: install_req_from_line('toto 42') err_msg = e.value.args[0] assert "Invalid requirement" in err_msg assert "\nTraceback " in err_msg
def test_str(self): req = install_req_from_line('simple==0.1') assert str(req) == 'simple==0.1'
def handle_line( line, # type: ParsedLine finder=None, # type: Optional[PackageFinder] options=None, # type: Optional[optparse.Values] session=None, # type: Optional[PipSession] wheel_cache=None, # type: Optional[WheelCache] use_pep517=None, # type: Optional[bool] ): # type: (...) -> Optional[InstallRequirement] """Handle a single parsed requirements line; This can result in creating/yielding requirements, or updating the finder. For lines that contain requirements, the only options that have an effect are from SUPPORTED_OPTIONS_REQ, and they are scoped to the requirement. Other options from SUPPORTED_OPTIONS may be present, but are ignored. For lines that do not contain requirements, the only options that have an effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may be present, but are ignored. These lines may contain multiple options (although our docs imply only one is supported), and all our parsed and affect the finder. """ # preserve for the nested code path line_comes_from = '%s %s (line %s)' % ( '-c' if line.constraint else '-r', line.filename, line.lineno, ) # return a line requirement if line.args: isolated = options.isolated_mode if options else False if options: cmdoptions.check_install_build_global(options, line.opts) # get the options that apply to requirements req_options = {} for dest in SUPPORTED_OPTIONS_REQ_DEST: if dest in line.opts.__dict__ and line.opts.__dict__[dest]: req_options[dest] = line.opts.__dict__[dest] line_source = 'line {} of {}'.format(line.lineno, line.filename) return install_req_from_line( line.args, comes_from=line_comes_from, use_pep517=use_pep517, isolated=isolated, options=req_options, wheel_cache=wheel_cache, constraint=line.constraint, line_source=line_source, ) # return an editable requirement elif line.opts.editables: isolated = options.isolated_mode if options else False return install_req_from_editable(line.opts.editables[0], comes_from=line_comes_from, use_pep517=use_pep517, constraint=line.constraint, isolated=isolated, wheel_cache=wheel_cache) # percolate hash-checking option upward elif line.opts.require_hashes: options.require_hashes = line.opts.require_hashes # set finder options elif finder: find_links = finder.find_links index_urls = finder.index_urls if line.opts.index_url: index_urls = [line.opts.index_url] if line.opts.no_index is True: index_urls = [] if line.opts.extra_index_urls: index_urls.extend(line.opts.extra_index_urls) if line.opts.find_links: # FIXME: it would be nice to keep track of the source # of the find_links: support a find-links local path # relative to a requirements file. value = line.opts.find_links[0] req_dir = os.path.dirname(os.path.abspath(line.filename)) relative_to_reqs_file = os.path.join(req_dir, value) if os.path.exists(relative_to_reqs_file): value = relative_to_reqs_file find_links.append(value) search_scope = SearchScope( find_links=find_links, index_urls=index_urls, ) finder.search_scope = search_scope if line.opts.pre: finder.set_allow_all_prereleases() if session: for host in line.opts.trusted_hosts or []: source = 'line {} of {}'.format(line.lineno, line.filename) session.add_trusted_host(host, source=source) return None
def test_installed_version_not_installed(self): req = install_req_from_line('simple-0.1-py2.py3-none-any.whl') assert req.installed_version is None
def test_markers_semicolon(self): # check that the markers can contain a semicolon req = install_req_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 process_line( line, # type: Text filename, # type: str line_number, # type: int finder=None, # type: Optional[PackageFinder] comes_from=None, # type: Optional[str] options=None, # type: Optional[optparse.Values] session=None, # type: Optional[PipSession] wheel_cache=None, # type: Optional[WheelCache] use_pep517=None, # type: Optional[bool] constraint=False # type: bool ): # type: (...) -> Iterator[InstallRequirement] """Process a single requirements line; This can result in creating/yielding requirements, or updating the finder. For lines that contain requirements, the only options that have an effect are from SUPPORTED_OPTIONS_REQ, and they are scoped to the requirement. Other options from SUPPORTED_OPTIONS may be present, but are ignored. For lines that do not contain requirements, the only options that have an effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may be present, but are ignored. These lines may contain multiple options (although our docs imply only one is supported), and all our parsed and affect the finder. :param constraint: If True, parsing a constraints file. :param options: OptionParser options that we may update """ parser = build_parser(line) defaults = parser.get_default_values() defaults.index_url = None if finder: defaults.format_control = finder.format_control args_str, options_str = break_args_options(line) # Prior to 2.7.3, shlex cannot deal with unicode entries if sys.version_info < (2, 7, 3): # https://github.com/python/mypy/issues/1174 options_str = options_str.encode('utf8') # type: ignore # https://github.com/python/mypy/issues/1174 opts, _ = parser.parse_args(shlex.split(options_str), defaults) # type: ignore # preserve for the nested code path line_comes_from = '%s %s (line %s)' % ( '-c' if constraint else '-r', filename, line_number, ) # yield a line requirement if args_str: isolated = options.isolated_mode if options else False if options: cmdoptions.check_install_build_global(options, opts) # get the options that apply to requirements req_options = {} for dest in SUPPORTED_OPTIONS_REQ_DEST: if dest in opts.__dict__ and opts.__dict__[dest]: req_options[dest] = opts.__dict__[dest] yield install_req_from_line(args_str, line_comes_from, constraint=constraint, use_pep517=use_pep517, isolated=isolated, options=req_options, wheel_cache=wheel_cache) # yield an editable requirement elif opts.editables: isolated = options.isolated_mode if options else False yield install_req_from_editable(opts.editables[0], comes_from=line_comes_from, use_pep517=use_pep517, constraint=constraint, isolated=isolated, wheel_cache=wheel_cache) # parse a nested requirements file elif opts.requirements or opts.constraints: if opts.requirements: req_path = opts.requirements[0] nested_constraint = False else: req_path = opts.constraints[0] nested_constraint = True # original file is over http if SCHEME_RE.search(filename): # do a url join so relative paths work req_path = urllib_parse.urljoin(filename, req_path) # original file and nested file are paths elif not SCHEME_RE.search(req_path): # do a join so relative paths work req_path = os.path.join(os.path.dirname(filename), req_path) # TODO: Why not use `comes_from='-r {} (line {})'` here as well? parsed_reqs = parse_requirements(req_path, finder, comes_from, options, session, constraint=nested_constraint, wheel_cache=wheel_cache) for req in parsed_reqs: yield req # percolate hash-checking option upward elif opts.require_hashes: options.require_hashes = opts.require_hashes # set finder options elif finder: if opts.index_url: finder.index_urls = [opts.index_url] if opts.no_index is True: finder.index_urls = [] if opts.extra_index_urls: finder.index_urls.extend(opts.extra_index_urls) if opts.find_links: # FIXME: it would be nice to keep track of the source # of the find_links: support a find-links local path # relative to a requirements file. value = opts.find_links[0] req_dir = os.path.dirname(os.path.abspath(filename)) relative_to_reqs_file = os.path.join(req_dir, value) if os.path.exists(relative_to_reqs_file): value = relative_to_reqs_file finder.find_links.append(value) if opts.pre: finder.allow_all_prereleases = True if opts.trusted_hosts: finder.secure_origins.extend( ("*", host, "*") for host in opts.trusted_hosts)
def get_requirements( self, args, # type: List[str] options, # type: Values finder, # type: PackageFinder session, # type: PipSession wheel_cache, # type: Optional[WheelCache] check_supported_wheels=True, # type: bool ): # type: (...) -> List[InstallRequirement] """ Parse command-line arguments into the corresponding requirements. """ requirement_set = RequirementSet( check_supported_wheels=check_supported_wheels) for filename in options.constraints: for req_to_add in parse_requirements(filename, constraint=True, finder=finder, options=options, session=session, wheel_cache=wheel_cache): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in args: req_to_add = install_req_from_line(req, None, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) for req in options.editables: req_to_add = install_req_from_editable( req, isolated=options.isolated_mode, use_pep517=options.use_pep517, wheel_cache=wheel_cache) req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # NOTE: options.require_hashes may be set if --require-hashes is True for filename in options.requirements: for req_to_add in parse_requirements( filename, finder=finder, options=options, session=session, wheel_cache=wheel_cache, use_pep517=options.use_pep517): req_to_add.is_direct = True requirement_set.add_requirement(req_to_add) # If any requirement has hash options, enable hash checking. requirements = requirement_set.all_requirements if any(req.has_hash_options for req in requirements): options.require_hashes = True if not (args or options.editables or options.requirements): opts = {'name': self.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) return requirements
def test_incorrect_case_file_index(data): """Test PackageFinder detects latest using wrong case""" req = install_req_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 test_incorrect_case_file_index(data): """Test PackageFinder detects latest using wrong case""" req = install_req_from_line('dinner', None) finder = make_test_finder(index_urls=[data.find_links3]) found = finder.find_requirement(req, False) assert found.link.url.endswith("Dinner-2.0.tar.gz")
def freeze( requirement=None, # type: Optional[List[str]] find_links=None, # type: Optional[List[str]] local_only=None, # type: Optional[bool] user_only=None, # type: Optional[bool] paths=None, # type: Optional[List[str]] isolated=False, # type: bool wheel_cache=None, # type: Optional[WheelCache] exclude_editable=False, # type: bool skip=(), # type: Container[str] ): # type: (...) -> Iterator[str] find_links = find_links or [] for link in find_links: yield "-f {}".format(link) installations = {} # type: Dict[str, FrozenRequirement] for dist in get_installed_distributions( local_only=local_only, skip=(), user_only=user_only, paths=paths ): try: req = FrozenRequirement.from_dist(dist) except RequirementParseError as exc: # We include dist rather than dist.project_name because the # dist string includes more information, like the version and # location. We also include the exception message to aid # troubleshooting. logger.warning( "Could not generate requirement for distribution %r: %s", dist, exc ) continue if exclude_editable and req.editable: continue installations[req.canonical_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() # type: Set[str] # 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) # type: Dict[str, List[str]] 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 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 = install_req_from_editable( line, isolated=isolated, ) else: line_req = install_req_from_line( COMMENT_RE.sub("", line).strip(), isolated=isolated, ) 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)" ) else: line_req_canonical_name = canonicalize_name(line_req.name) if line_req_canonical_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 " "package %r is not installed", req_file_path, COMMENT_RE.sub("", line).strip(), line_req.name, ) else: req_files[line_req.name].append(req_file_path) else: yield str(installations[line_req_canonical_name]).rstrip() del installations[line_req_canonical_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 installation.canonical_name not in skip: yield str(installation).rstrip()
def test_finder_detects_latest_find_links(data): """Test PackageFinder detects latest using find-links""" req = install_req_from_line('simple', None) finder = make_test_finder(find_links=[data.find_links]) found = finder.find_requirement(req, False) assert found.link.url.endswith("simple-3.0.tar.gz")