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 = InstallRequirement.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 = InstallRequirement.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 install_req_from_editable( editable_req: str, comes_from: Optional[Union[InstallRequirement, str]] = None, use_pep517: Optional[bool] = None, isolated: bool = False, options: Optional[Dict[str, Any]] = None, constraint: bool = False, user_supplied: bool = False, permit_editable_wheels: bool = False, ) -> InstallRequirement: parts = parse_req_from_editable(editable_req) return InstallRequirement( parts.requirement, comes_from=comes_from, user_supplied=user_supplied, editable=True, permit_editable_wheels=permit_editable_wheels, link=parts.link, constraint=constraint, use_pep517=use_pep517, isolated=isolated, install_options=options.get("install_options", []) if options else [], global_options=options.get("global_options", []) if options else [], hash_options=options.get("hashes", {}) if options else {}, extras=parts.extras, )
def prepare_linked_requirement( self, req: InstallRequirement, parallel_builds: bool = False) -> BaseDistribution: """Prepare a requirement to be obtained from req.link.""" assert req.link link = req.link self._log_preparing_link(req) with indent_log(): # Check if the relevant file is already available # in the download directory file_path = None if self.download_dir is not None and link.is_wheel: hashes = self._get_linked_req_hashes(req) file_path = _check_download_dir(req.link, self.download_dir, hashes) if file_path is not None: # The file is already available, so mark it as downloaded self._downloaded[req.link.url] = file_path else: # The file is not available, attempt to fetch only metadata wheel_dist = self._fetch_metadata_using_lazy_wheel(link) if wheel_dist is not None: req.needs_more_preparation = True return wheel_dist # None of the optimizations worked, fully prepare the requirement return self._prepare_linked_requirement(req, parallel_builds)
def install_req_from_editable( editable_req, # type: str comes_from=None, # type: Optional[str] use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] wheel_cache=None, # type: Optional[WheelCache] constraint=False # type: bool ): # type: (...) -> InstallRequirement parts = parse_req_from_editable(editable_req) source_dir = parts.link.file_path if parts.link.scheme == 'file' else None return InstallRequirement( parts.requirement, comes_from, source_dir=source_dir, editable=True, link=parts.link, constraint=constraint, use_pep517=use_pep517, isolated=isolated, options=options if options else {}, wheel_cache=wheel_cache, extras=parts.extras, )
def make_test_install_req(base_name=None): """ Return an InstallRequirement object for testing purposes. """ if base_name is None: base_name = 'pendulum-2.0.4' req = Requirement('pendulum') link_url = ( 'https://files.pythonhosted.org/packages/aa/{base_name}.tar.gz' '#sha256=cf535d36c063575d4752af36df928882b2e0e31541b4482c97d637527' '85f9fcb' ).format(base_name=base_name) link = Link( url=link_url, comes_from='https://pypi.org/simple/pendulum/', requires_python='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', ) req = InstallRequirement( req=req, comes_from=None, constraint=False, editable=False, link=link, source_dir='/tmp/pip-install-9py5m2z1/pendulum', ) return req
def test_should_use_ephemeral_cache__disallow_binaries_and_vcs_checkout( disallow_binaries, expected, ): """ Test that disallowing binaries (e.g. from passing --global-option) causes should_use_ephemeral_cache() to return None for VCS checkouts. """ req = Requirement('pendulum') link = Link(url='git+https://git.example.com/pendulum.git') req = InstallRequirement( req=req, comes_from=None, constraint=False, editable=False, link=link, source_dir='/tmp/pip-install-9py5m2z1/pendulum', ) assert not req.is_wheel assert req.link.is_vcs check_binary_allowed = Mock(return_value=not disallow_binaries) # The cache_available value doesn't matter for this test. ephem_cache = wheel.should_use_ephemeral_cache( req, should_unpack=True, cache_available=True, check_binary_allowed=check_binary_allowed, ) assert ephem_cache is expected
def test_yield_line_requirement_with_spaces_in_specifier(self): line = 'SomeProject >= 2' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.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_yield_editable_requirement(self): url = 'git+https://url#egg=SomeProject' line = '-e %s' % url filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.from_editable(url, comes_from=comes_from) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
def install_req_from_editable( editable_req, # type: str comes_from=None, # type: Optional[str] use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] wheel_cache=None, # type: Optional[WheelCache] constraint=False # type: bool ): # type: (...) -> InstallRequirement name, url, extras_override = parse_editable(editable_req) if url.startswith('file:'): source_dir = url_to_path(url) else: source_dir = None if name is not None: try: req = Requirement(name) except InvalidRequirement: raise InstallationError("Invalid requirement: '%s'" % name) else: req = None return InstallRequirement( req, comes_from, source_dir=source_dir, editable=True, link=Link(url), constraint=constraint, use_pep517=use_pep517, isolated=isolated, options=options if options else {}, wheel_cache=wheel_cache, extras=extras_override or (), )
def install_req_from_req_string( req_string, # type: str comes_from=None, # type: Optional[InstallRequirement] isolated=False, # type: bool wheel_cache=None, # type: Optional[WheelCache] use_pep517=None # type: Optional[bool] ): # type: (...) -> InstallRequirement try: req = Requirement(req_string) except InvalidRequirement: raise InstallationError("Invalid requirement: '%s'" % req) domains_not_allowed = [ PyPI.file_storage_domain, TestPyPI.file_storage_domain, ] if req.url and comes_from.link.netloc in domains_not_allowed: # Explicitly disallow pypi packages that depend on external urls raise InstallationError( "Packages installed from PyPI cannot depend on packages " "which are not also hosted on PyPI.\n" "%s depends on %s " % (comes_from.name, req) ) return InstallRequirement( req, comes_from, isolated=isolated, wheel_cache=wheel_cache, use_pep517=use_pep517 )
def test_should_use_ephemeral_cache__disallow_binaries_and_vcs_checkout( disallow_binaries, expected, ): """ Test that disallowing binaries (e.g. from passing --global-option) causes should_use_ephemeral_cache() to return None for VCS checkouts. """ req = Requirement('pendulum') # Passing a VCS url causes link.is_artifact to return False. link = Link(url='git+https://git.example.com/pendulum.git') req = InstallRequirement( req=req, comes_from=None, constraint=False, editable=False, link=link, source_dir='/tmp/pip-install-9py5m2z1/pendulum', ) assert not req.is_wheel assert not req.link.is_artifact format_control = FormatControl() if disallow_binaries: format_control.disallow_binaries() # The cache_available value doesn't matter for this test. ephem_cache = wheel.should_use_ephemeral_cache( req, format_control=format_control, autobuilding=True, cache_available=True, ) assert ephem_cache is expected
def install_req_from_req_string( req_string, # type: str comes_from=None, # type: Optional[InstallRequirement] isolated=False, # type: bool use_pep517=None, # type: Optional[bool] user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement try: req = Requirement(req_string) except InvalidRequirement: raise InstallationError(f"Invalid requirement: '{req_string}'") domains_not_allowed = [ PyPI.file_storage_domain, TestPyPI.file_storage_domain, ] if (req.url and comes_from and comes_from.link and comes_from.link.netloc in domains_not_allowed): # Explicitly disallow pypi packages that depend on external urls raise InstallationError( "Packages installed from PyPI cannot depend on packages " "which are not also hosted on PyPI.\n" "{} depends on {} ".format(comes_from.name, req)) return InstallRequirement( req, comes_from, isolated=isolated, use_pep517=use_pep517, user_supplied=user_supplied, )
def install_req_from_editable( editable_req, # type: str comes_from=None, # type: Optional[Union[InstallRequirement, str]] use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] constraint=False, # type: bool user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement parts = parse_req_from_editable(editable_req) return InstallRequirement( parts.requirement, comes_from=comes_from, user_supplied=user_supplied, editable=True, link=parts.link, constraint=constraint, use_pep517=use_pep517, isolated=isolated, install_options=options.get("install_options", []) if options else [], global_options=options.get("global_options", []) if options else [], hash_options=options.get("hashes", {}) if options else {}, extras=parts.extras, )
def install_req_from_line( name, # type: str comes_from=None, # type: Optional[Union[str, InstallRequirement]] use_pep517=None, # type: Optional[bool] isolated=False, # type: bool options=None, # type: Optional[Dict[str, Any]] constraint=False, # type: bool line_source=None, # type: Optional[str] user_supplied=False, # type: bool ): # type: (...) -> InstallRequirement """Creates an InstallRequirement from a name, which might be a requirement, directory containing 'setup.py', filename, or URL. :param line_source: An optional string describing where the line is from, for logging purposes in case of an error. """ parts = parse_req_from_line(name, line_source) return InstallRequirement( parts.requirement, comes_from, link=parts.link, markers=parts.markers, use_pep517=use_pep517, isolated=isolated, install_options=options.get("install_options", []) if options else [], global_options=options.get("global_options", []) if options else [], hash_options=options.get("hashes", {}) if options else {}, constraint=constraint, extras=parts.extras, user_supplied=user_supplied, )
def test_install_req_from_string_with_comes_from_without_link( self) -> None: """ Test to make sure that install_req_from_string succeeds when called with URL (PEP 508) and comes_from does not have a link. """ # Test with a PEP 508 url install string: wheel_url = ("https://download.pytorch.org/whl/cu90/" "torch-1.0.0-cp36-cp36m-win_amd64.whl") install_str = "torch@ " + wheel_url # Dummy numpy "comes_from" requirement without link: comes_from = InstallRequirement(Requirement("numpy>=1.15.0"), comes_from=None) # Attempt install from install string comes: install_req = install_req_from_req_string(install_str, comes_from=comes_from) assert isinstance(install_req, InstallRequirement) assert isinstance(install_req.comes_from, InstallRequirement) assert install_req.comes_from.link is None assert install_req.link is not None assert install_req.link.url == wheel_url assert install_req.req is not None assert install_req.req.url == wheel_url assert install_req.is_wheel
def install_req_from_editable( editable_req, comes_from=None, isolated=False, options=None, wheel_cache=None, constraint=False ): name, url, extras_override = parse_editable(editable_req) if url.startswith('file:'): source_dir = url_to_path(url) else: source_dir = None if name is not None: try: req = Requirement(name) except InvalidRequirement: raise InstallationError("Invalid requirement: '%s'" % name) else: req = None return InstallRequirement( req, comes_from, source_dir=source_dir, editable=True, link=Link(url), constraint=constraint, isolated=isolated, options=options if options else {}, wheel_cache=wheel_cache, extras=extras_override or (), )
def _make_requirement_from_install_req( self, ireq: InstallRequirement, requested_extras: Iterable[str]) -> Optional[Requirement]: if not ireq.match_markers(requested_extras): logger.info( "Ignoring %s: markers '%s' don't match your environment", ireq.name, ireq.markers, ) return None if not ireq.link: return SpecifierRequirement(ireq) self._fail_if_link_is_unsupported_wheel(ireq.link) cand = self._make_candidate_from_link( ireq.link, extras=frozenset(ireq.extras), template=ireq, name=canonicalize_name(ireq.name) if ireq.name else None, version=None, ) if cand is None: # There's no way we can satisfy a URL requirement if the underlying # candidate fails to build. An unnamed URL must be user-supplied, so # we fail eagerly. If the URL is named, an unsatisfiable requirement # can make the resolver do the right thing, either backtrack (and # maybe find some other requirement that's buildable) or raise a # ResolutionImpossible eventually. if not ireq.name: raise self._build_failures[ireq.link] return UnsatisfiableRequirement(canonicalize_name(ireq.name)) return self.make_requirement_from_candidate(cand)
def test_tmp_build_directory(self): # when req is None, we can produce a temporary directory # Make sure we're handling it correctly with real path. requirement = InstallRequirement(None, None) tmp_dir = tempfile.mkdtemp('-build', 'pip-') tmp_build_dir = requirement.build_location(tmp_dir) assert ( os.path.dirname(tmp_build_dir) == os.path.realpath(os.path.dirname(tmp_dir)) ) # are we on a system where /tmp is a symlink if os.path.realpath(tmp_dir) != os.path.abspath(tmp_dir): assert os.path.dirname(tmp_build_dir) != os.path.dirname(tmp_dir) else: assert os.path.dirname(tmp_build_dir) == os.path.dirname(tmp_dir) os.rmdir(tmp_dir) assert not os.path.exists(tmp_dir)
def _get_dist_for(self, req: InstallRequirement) -> Distribution: """Takes a InstallRequirement and returns a single AbstractDist \ representing a prepared variant of the same. """ if req.editable: return self.preparer.prepare_editable_requirement(req) # satisfied_by is only evaluated by calling _check_skip_installed, # so it must be None here. assert req.satisfied_by is None skip_reason = self._check_skip_installed(req) if req.satisfied_by: return self.preparer.prepare_installed_requirement(req, skip_reason) # We eagerly populate the link, since that's our "legacy" behavior. self._populate_link(req) dist = self.preparer.prepare_linked_requirement(req) # NOTE # The following portion is for determining if a certain package is # going to be re-installed/upgraded or not and reporting to the user. # This should probably get cleaned up in a future refactor. # req.req is only avail after unpack for URL # pkgs repeat check_if_exists to uninstall-on-upgrade # (#14) if not self.ignore_installed: req.check_if_exists(self.use_user_site) if req.satisfied_by: should_modify = ( self.upgrade_strategy != "to-satisfy-only" or self.force_reinstall or self.ignore_installed or req.link.scheme == "file" ) if should_modify: self._set_req_to_reinstall(req) else: logger.info( "Requirement already satisfied (use --upgrade to upgrade): %s", req, ) return dist
def prepare_editable_requirement( self, req: InstallRequirement, ) -> BaseDistribution: """Prepare an editable requirement.""" assert req.editable, "cannot prepare a non-editable req as editable" logger.info("Obtaining %s", req) with indent_log(): if self.require_hashes: raise InstallationError( "The editable requirement {} cannot be installed when " "requiring hashes, because there is no single file to " "hash.".format(req)) req.ensure_has_source_dir(self.src_dir) req.update_editable() assert req.source_dir req.download_info = direct_url_for_editable( req.unpacked_source_directory) dist = _get_prepared_distribution( req, self.build_tracker, self.finder, self.build_isolation, self.check_build_deps, ) req.check_if_exists(self.use_user_site) return dist
def test_yield_line_constraint(self): line = 'SomeProject' filename = 'filename' comes_from = '-c %s (line %s)' % (filename, 1) req = InstallRequirement.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 __and__(self, other: InstallRequirement) -> "Constraint": if not isinstance(other, InstallRequirement): return NotImplemented specifier = self.specifier & other.specifier hashes = self.hashes & other.hashes(trust_internet=False) links = self.links if other.link: links = links.union([other.link]) return Constraint(specifier, hashes, links)
def test_yield_editable_constraint(self): url = 'git+https://url#egg=SomeProject' line = '-e %s' % url filename = 'filename' comes_from = '-c %s (line %s)' % (filename, 1) req = InstallRequirement.from_editable( url, 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 test_tmp_build_directory(self) -> None: # when req is None, we can produce a temporary directory # Make sure we're handling it correctly with real path. requirement = InstallRequirement(None, None) tmp_dir = tempfile.mkdtemp("-build", "pip-") tmp_build_dir = requirement.ensure_build_location( tmp_dir, autodelete=False, parallel_builds=False, ) assert os.path.dirname(tmp_build_dir) == os.path.realpath( os.path.dirname(tmp_dir)) # are we on a system where /tmp is a symlink if os.path.realpath(tmp_dir) != os.path.abspath(tmp_dir): assert os.path.dirname(tmp_build_dir) != os.path.dirname(tmp_dir) else: assert os.path.dirname(tmp_build_dir) == os.path.dirname(tmp_dir) os.rmdir(tmp_dir) assert not os.path.exists(tmp_dir)
def test_rejection_for_location_requirement_options(): install_options = [] bad_named_req_options = ["--home=/wow"] bad_named_req = InstallRequirement(Requirement("hello"), "requirements.txt", install_options=bad_named_req_options) bad_unnamed_req_options = ["--install-lib=/lib"] bad_unnamed_req = InstallRequirement( None, "requirements2.txt", install_options=bad_unnamed_req_options) with pytest.raises(CommandError) as e: reject_location_related_install_options( [bad_named_req, bad_unnamed_req], install_options) assert ( "['--install-lib'] from <InstallRequirement> (from requirements2.txt)" in str(e.value)) assert "['--home'] from hello (from requirements.txt)" in str(e.value)
def add_req(subreq, extras_requested): sub_install_req = InstallRequirement.from_req( str(subreq), req_to_install, isolated=self.isolated, wheel_cache=self.wheel_cache, ) more_reqs.extend( requirement_set.add_requirement( sub_install_req, req_to_install.name, extras_requested=extras_requested))
def test_nested_constraints_file(self, monkeypatch): line = '-c another_file' req = InstallRequirement.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 prepare_editable_requirement( self, req: InstallRequirement, ) -> Distribution: """Prepare an editable requirement """ assert req.editable, "cannot prepare a non-editable req as editable" logger.info('Obtaining %s', req) with indent_log(): if self.require_hashes: raise InstallationError( 'The editable requirement {} cannot be installed when ' 'requiring hashes, because there is no single file to ' 'hash.'.format(req) ) req.ensure_has_source_dir(self.src_dir) req.update_editable() dist = _get_prepared_distribution( req, self.req_tracker, self.finder, self.build_isolation, ) req.check_if_exists(self.use_user_site) return dist
def add_req(subreq, extras_requested): sub_install_req = InstallRequirement.from_req( str(subreq), req_to_install, isolated=self.isolated, wheel_cache=self.wheel_cache, ) more_reqs.extend( requirement_set.add_requirement( sub_install_req, req_to_install.name, extras_requested=extras_requested ) )
def test_deprecation_notice_for_requirement_options(recwarn): install_options = [] bad_named_req_options = ["--home=/wow"] bad_named_req = InstallRequirement(Requirement("hello"), "requirements.txt", install_options=bad_named_req_options) bad_unnamed_req_options = ["--install-lib=/lib"] bad_unnamed_req = InstallRequirement( None, "requirements2.txt", install_options=bad_unnamed_req_options) warn_deprecated_install_options([bad_named_req, bad_unnamed_req], install_options) assert len(recwarn) == 1 message = recwarn[0].message.args[0] assert ( "['--install-lib'] from <InstallRequirement> (from requirements2.txt)" in message) assert "['--home'] from hello (from requirements.txt)" in message
def _should_build( req: InstallRequirement, need_wheel: bool, check_binary_allowed: BinaryAllowedPredicate, ) -> bool: """Return whether an InstallRequirement should be built into a wheel.""" if req.constraint: # never build requirements that are merely constraints return False if req.is_wheel: if need_wheel: logger.info( "Skipping %s, due to already being wheel.", req.name, ) return False if need_wheel: # i.e. pip wheel, not pip install return True # From this point, this concerns the pip install command only # (need_wheel=False). if not req.source_dir: return False if req.editable: # we only build PEP 660 editable requirements return req.supports_pyproject_editable() if req.use_pep517: return True if not check_binary_allowed(req): logger.info( "Skipping wheel build for %s, due to binaries being disabled for it.", req.name, ) return False if not is_wheel_installed(): # we don't build legacy requirements if wheel is not installed logger.info( "Using legacy 'setup.py install' for %s, " "since package 'wheel' is not installed.", req.name, ) return False return True
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 = InstallRequirement.from_line( str(install_dir).replace(os.sep, os.altsep or os.sep) ) assert requirement.link is not None
def _install_build_reqs(self, reqs, prefix): # Local import to avoid circular import (wheel <-> req_install) from pip._internal.req.req_install import InstallRequirement from pip._internal.index import FormatControl # Ignore the --no-binary option when installing the build system, so # we don't recurse trying to build a self-hosting build system. finder = copy.copy(self.finder) finder.format_control = FormatControl(set(), set()) urls = [finder.find_requirement(InstallRequirement.from_line(r), upgrade=False).url for r in reqs] args = [sys.executable, '-m', 'pip', 'install', '--ignore-installed', '--prefix', prefix] + list(urls) with open_spinner("Installing build dependencies") as spinner: call_subprocess(args, show_stdout=False, spinner=spinner)
def _install_build_reqs(finder, prefix, build_requirements): # NOTE: What follows is not a very good thing. # Eventually, this should move into the BuildEnvironment class and # that should handle all the isolation and sub-process invocation. finder = copy(finder) finder.format_control = FormatControl(set(), set([":all:"])) urls = [ finder.find_requirement( InstallRequirement.from_line(r), upgrade=False).url for r in build_requirements ] args = [ sys.executable, '-m', 'pip', 'install', '--ignore-installed', '--no-user', '--prefix', prefix, ] + list(urls) with open_spinner("Installing build dependencies") as spinner: call_subprocess(args, show_stdout=False, spinner=spinner)
def add_req(subreq, extras_requested): sub_install_req = InstallRequirement.from_req( str(subreq), req_to_install, isolated=self.isolated, wheel_cache=self.wheel_cache, ) parent_req_name = req_to_install.name to_scan_again, add_to_parent = requirement_set.add_requirement( sub_install_req, parent_req_name=parent_req_name, extras_requested=extras_requested, ) if parent_req_name and add_to_parent: self._discovered_dependencies[parent_req_name].append( add_to_parent ) more_reqs.extend(to_scan_again)
def test_yield_line_requirement(self): line = 'SomeProject' filename = 'filename' comes_from = '-r %s (line %s)' % (filename, 1) req = InstallRequirement.from_line(line, comes_from=comes_from) assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
def process_line(line, filename, line_number, finder=None, comes_from=None, options=None, session=None, wheel_cache=None, constraint=False): """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: # `finder.format_control` will be updated during parsing defaults.format_control = finder.format_control args_str, options_str = break_args_options(line) if sys.version_info < (2, 7, 3): # Prior to 2.7.3, shlex cannot deal with unicode entries options_str = options_str.encode('utf8') opts, _ = parser.parse_args(shlex.split(options_str), defaults) # 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 InstallRequirement.from_line( args_str, line_comes_from, constraint=constraint, 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 InstallRequirement.from_editable( opts.editables[0], comes_from=line_comes_from, 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? parser = parse_requirements( req_path, finder, comes_from, options, session, constraint=nested_constraint, wheel_cache=wheel_cache ) for req in parser: 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.process_dependency_links: finder.process_dependency_links = True if opts.trusted_hosts: finder.secure_origins.extend( ("*", host, "*") for host in opts.trusted_hosts)
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)) )