def _check_dist_requires_python( dist, # type: pkg_resources.Distribution version_info, # type: Tuple[int, ...] ignore_requires_python=False, # type: bool ): # type: (...) -> None """ Check whether the given Python version is compatible with a distribution's "Requires-Python" value. :param version_info: The Python version to use to check, as a 3-tuple of ints (major-minor-micro). :param ignore_requires_python: Whether to ignore the "Requires-Python" value if the given Python version isn't compatible. :raises UnsupportedPythonVersion: When the given Python version isn't compatible. """ requires_python = get_requires_python(dist) try: is_compatible = check_requires_python( requires_python, version_info=version_info, ) except specifiers.InvalidSpecifier as exc: logger.warning( "Package %r has an invalid Requires-Python: %s", dist.project_name, exc, ) return if is_compatible: return version = '.'.join(map(str, version_info)) if ignore_requires_python: logger.debug( 'Ignoring failed Requires-Python check for package %r: ' '%s not in %r', dist.project_name, version, requires_python, ) return raise UnsupportedPythonVersion( 'Package {!r} requires a different Python: {} not in {!r}'.format( dist.project_name, version, requires_python, ))
def _check_dist_requires_python( dist: BaseDistribution, version_info: Tuple[int, int, int], ignore_requires_python: bool = False, ) -> None: """ Check whether the given Python version is compatible with a distribution's "Requires-Python" value. :param version_info: A 3-tuple of ints representing the Python major-minor-micro version to check. :param ignore_requires_python: Whether to ignore the "Requires-Python" value if the given Python version isn't compatible. :raises UnsupportedPythonVersion: When the given Python version isn't compatible. """ # This idiosyncratically converts the SpecifierSet to str and let # check_requires_python then parse it again into SpecifierSet. But this # is the legacy resolver so I'm just not going to bother refactoring. try: requires_python = str(dist.requires_python) except FileNotFoundError as e: raise NoneMetadataError(dist, str(e)) try: is_compatible = check_requires_python( requires_python, version_info=version_info, ) except specifiers.InvalidSpecifier as exc: logger.warning("Package %r has an invalid Requires-Python: %s", dist.raw_name, exc) return if is_compatible: return version = ".".join(map(str, version_info)) if ignore_requires_python: logger.debug( "Ignoring failed Requires-Python check for package %r: %s not in %r", dist.raw_name, version, requires_python, ) return raise UnsupportedPythonVersion( "Package {!r} requires a different Python: {} not in {!r}".format( dist.raw_name, version, requires_python))
def _check_link_requires_python( link, # type: Link version_info, # type: Tuple[int, int, int] ignore_requires_python=False, # type: bool ): # type: (...) -> bool """ Return whether the given Python version is compatible with a link's "Requires-Python" value. :param version_info: A 3-tuple of ints representing the Python major-minor-micro version to check. :param ignore_requires_python: Whether to ignore the "Requires-Python" value if the given Python version isn't compatible. """ try: is_compatible = check_requires_python( link.requires_python, version_info=version_info, ) except specifiers.InvalidSpecifier: logger.debug( "Ignoring invalid Requires-Python (%r) for link: %s", link.requires_python, link, ) else: if not is_compatible: version = '.'.join(map(str, version_info)) if not ignore_requires_python: logger.debug( 'Link requires a different Python (%s not in: %r): %s', version, link.requires_python, link, ) return False logger.debug( 'Ignoring failed Requires-Python check (%s not in: %r) ' 'for link: %s', version, link.requires_python, link, ) return True
def _check_dist_requires_python( dist: Distribution, version_info: Tuple[int, int, int], ignore_requires_python: bool = False, ) -> None: """ Check whether the given Python version is compatible with a distribution's "Requires-Python" value. :param version_info: A 3-tuple of ints representing the Python major-minor-micro version to check. :param ignore_requires_python: Whether to ignore the "Requires-Python" value if the given Python version isn't compatible. :raises UnsupportedPythonVersion: When the given Python version isn't compatible. """ requires_python = get_requires_python(dist) try: is_compatible = check_requires_python( requires_python, version_info=version_info ) except specifiers.InvalidSpecifier as exc: logger.warning( "Package %r has an invalid Requires-Python: %s", dist.project_name, exc ) return if is_compatible: return version = ".".join(map(str, version_info)) if ignore_requires_python: logger.debug( "Ignoring failed Requires-Python check for package %r: %s not in %r", dist.project_name, version, requires_python, ) return raise UnsupportedPythonVersion( "Package {!r} requires a different Python: {} not in {!r}".format( dist.project_name, version, requires_python ) )
def _link_package_versions(self, link, search): """Return an InstallationCandidate or None""" version = None if link.egg_fragment: egg_info = link.egg_fragment ext = link.ext else: egg_info, ext = link.splitext() if not ext: self._log_skipped_link(link, 'not a file') return if ext not in SUPPORTED_EXTENSIONS: self._log_skipped_link( link, 'unsupported archive format: %s' % ext, ) return if "binary" not in search.formats and ext == wheel_ext: self._log_skipped_link( link, 'No binaries permitted for %s' % search.supplied, ) return if "macosx10" in link.path and ext == '.zip': self._log_skipped_link(link, 'macosx10 one') return if ext == wheel_ext: try: wheel = Wheel(link.filename) except InvalidWheelFilename: self._log_skipped_link(link, 'invalid wheel filename') return if canonicalize_name(wheel.name) != search.canonical: self._log_skipped_link( link, 'wrong project name (not %s)' % search.supplied) return if not wheel.supported(self.valid_tags): self._log_skipped_link( link, 'it is not compatible with this Python') return version = wheel.version # This should be up by the search.ok_binary check, but see issue 2700. if "source" not in search.formats and ext != wheel_ext: self._log_skipped_link( link, 'No sources permitted for %s' % search.supplied, ) return if not version: version = egg_info_matches(egg_info, search.supplied, link) if version is None: self._log_skipped_link( link, 'wrong project name (not %s)' % search.supplied) return match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: self._log_skipped_link(link, 'Python version is incorrect') return try: support_this_python = check_requires_python(link.requires_python) except specifiers.InvalidSpecifier: logger.debug("Package %s has an invalid Requires-Python entry: %s", link.filename, link.requires_python) support_this_python = True if not support_this_python: logger.debug( "The package %s is incompatible with the python" "version in use. Acceptable python versions are:%s", link, link.requires_python) return logger.debug('Found link %s, version: %s', link, version) return InstallationCandidate(search.supplied, version, link)
def evaluate_link(self, link, search): # type: (Link, Search) -> Optional[InstallationCandidate] """ Determine whether a link is a candidate for installation. Returns an InstallationCandidate if so, otherwise None. """ version = None if link.egg_fragment: egg_info = link.egg_fragment ext = link.ext else: egg_info, ext = link.splitext() if not ext: self._log_skipped_link(link, 'not a file') return None if ext not in SUPPORTED_EXTENSIONS: self._log_skipped_link( link, 'unsupported archive format: %s' % ext, ) return None if "binary" not in search.formats and ext == WHEEL_EXTENSION: self._log_skipped_link( link, 'No binaries permitted for %s' % search.supplied, ) return None if "macosx10" in link.path and ext == '.zip': self._log_skipped_link(link, 'macosx10 one') return None if ext == WHEEL_EXTENSION: try: wheel = Wheel(link.filename) except InvalidWheelFilename: self._log_skipped_link(link, 'invalid wheel filename') return None if canonicalize_name(wheel.name) != search.canonical: self._log_skipped_link( link, 'wrong project name (not %s)' % search.supplied) return None if not self._is_wheel_supported(wheel): self._log_skipped_link( link, 'it is not compatible with this Python') return None version = wheel.version # This should be up by the search.ok_binary check, but see issue 2700. if "source" not in search.formats and ext != WHEEL_EXTENSION: self._log_skipped_link( link, 'No sources permitted for %s' % search.supplied, ) return None if not version: version = _egg_info_matches(egg_info, search.canonical) if not version: self._log_skipped_link( link, 'Missing project version for %s' % search.supplied) return None match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: self._log_skipped_link(link, 'Python version is incorrect') return None try: support_this_python = check_requires_python( link.requires_python, version_info=sys.version_info[:3], ) except specifiers.InvalidSpecifier: logger.debug("Package %s has an invalid Requires-Python entry: %s", link.filename, link.requires_python) support_this_python = True if not support_this_python: logger.debug( "The package %s is incompatible with the python " "version in use. Acceptable python versions are: %s", link, link.requires_python) return None logger.debug('Found link %s, version: %s', link, version) return InstallationCandidate(search.supplied, version, link)
def _link_package_versions(self, link, search): """Return an InstallationCandidate or None""" version = None if link.egg_fragment: egg_info = link.egg_fragment ext = link.ext else: egg_info, ext = link.splitext() if not ext: self._log_skipped_link(link, 'not a file') return if ext not in SUPPORTED_EXTENSIONS: self._log_skipped_link( link, 'unsupported archive format: %s' % ext, ) return if "binary" not in search.formats and ext == wheel_ext: self._log_skipped_link( link, 'No binaries permitted for %s' % search.supplied, ) return if "macosx10" in link.path and ext == '.zip': self._log_skipped_link(link, 'macosx10 one') return if ext == wheel_ext: try: wheel = Wheel(link.filename) except InvalidWheelFilename: self._log_skipped_link(link, 'invalid wheel filename') return if canonicalize_name(wheel.name) != search.canonical: self._log_skipped_link( link, 'wrong project name (not %s)' % search.supplied) return if not wheel.supported(self.valid_tags): self._log_skipped_link( link, 'it is not compatible with this Python') return version = wheel.version # This should be up by the search.ok_binary check, but see issue 2700. if "source" not in search.formats and ext != wheel_ext: self._log_skipped_link( link, 'No sources permitted for %s' % search.supplied, ) return if not version: version = egg_info_matches(egg_info, search.supplied, link) if version is None: self._log_skipped_link( link, 'wrong project name (not %s)' % search.supplied) return match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: self._log_skipped_link( link, 'Python version is incorrect') return try: support_this_python = check_requires_python(link.requires_python) except specifiers.InvalidSpecifier: logger.debug("Package %s has an invalid Requires-Python entry: %s", link.filename, link.requires_python) support_this_python = True if not support_this_python: logger.debug("The package %s is incompatible with the python" "version in use. Acceptable python versions are:%s", link, link.requires_python) return logger.debug('Found link %s, version: %s', link, version) return InstallationCandidate(search.supplied, version, link)
""" Check whether the given Python version is compatible with a distribution's "Requires-Python" value. :param version_info: A 3-tuple of ints representing the Python major-minor-micro version to check. :param ignore_requires_python: Whether to ignore the "Requires-Python" value if the given Python version isn't compatible. :raises UnsupportedPythonVersion: When the given Python version isn't compatible. """ requires_python = get_requires_python(dist) try: is_compatible = check_requires_python( requires_python, version_info=version_info, ) except specifiers.InvalidSpecifier as exc: logger.warning( "Package %r has an invalid Requires-Python: %s", dist.project_name, exc, ) return if is_compatible: return version = '.'.join(map(str, version_info)) if ignore_requires_python: logger.debug( 'Ignoring failed Requires-Python check for package %r: '
def test_check_requires_python__invalid(): """ Test an invalid Requires-Python value. """ with pytest.raises(specifiers.InvalidSpecifier): check_requires_python('invalid', (3, 6, 5))
def test_check_requires_python(version_info, requires_python, expected): actual = check_requires_python(requires_python, version_info) assert actual == expected
def _evaluate_link(self, link, search): # type: (Link, Search) -> Tuple[bool, Optional[str]] """ Determine whether a link is a candidate for installation. :return: A tuple (is_candidate, result), where `result` is (1) a version string if `is_candidate` is True, and (2) if `is_candidate` is False, an optional string to log the reason the link fails to qualify. """ version = None if link.egg_fragment: egg_info = link.egg_fragment ext = link.ext else: egg_info, ext = link.splitext() if not ext: return (False, 'not a file') if ext not in SUPPORTED_EXTENSIONS: return (False, 'unsupported archive format: %s' % ext) if "binary" not in search.formats and ext == WHEEL_EXTENSION: reason = 'No binaries permitted for %s' % search.supplied return (False, reason) if "macosx10" in link.path and ext == '.zip': return (False, 'macosx10 one') if ext == WHEEL_EXTENSION: try: wheel = Wheel(link.filename) except InvalidWheelFilename: return (False, 'invalid wheel filename') if canonicalize_name(wheel.name) != search.canonical: reason = 'wrong project name (not %s)' % search.supplied return (False, reason) if not self._is_wheel_supported(wheel): return (False, 'it is not compatible with this Python') version = wheel.version # This should be up by the search.ok_binary check, but see issue 2700. if "source" not in search.formats and ext != WHEEL_EXTENSION: return (False, 'No sources permitted for %s' % search.supplied) if not version: version = _egg_info_matches(egg_info, search.canonical) if not version: return (False, 'Missing project version for %s' % search.supplied) match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: return (False, 'Python version is incorrect') try: support_this_python = check_requires_python( link.requires_python, version_info=sys.version_info[:3], ) except specifiers.InvalidSpecifier: logger.debug("Package %s has an invalid Requires-Python entry: %s", link.filename, link.requires_python) support_this_python = True if not support_this_python: logger.debug("The package %s is incompatible with the python " "version in use. Acceptable python versions are: %s", link, link.requires_python) # Return None for the reason text to suppress calling # _log_skipped_link(). return (False, None) logger.debug('Found link %s, version: %s', link, version) return (True, version)
def _evaluate_link(self, link, search): # type: (Link, Search) -> Tuple[bool, Optional[str]] """ Determine whether a link is a candidate for installation. :return: A tuple (is_candidate, result), where `result` is (1) a version string if `is_candidate` is True, and (2) if `is_candidate` is False, an optional string to log the reason the link fails to qualify. """ version = None if link.egg_fragment: egg_info = link.egg_fragment ext = link.ext else: egg_info, ext = link.splitext() if not ext: return (False, 'not a file') if ext not in SUPPORTED_EXTENSIONS: return (False, 'unsupported archive format: %s' % ext) if "binary" not in search.formats and ext == WHEEL_EXTENSION: reason = 'No binaries permitted for %s' % search.supplied return (False, reason) if "macosx10" in link.path and ext == '.zip': return (False, 'macosx10 one') if ext == WHEEL_EXTENSION: try: wheel = Wheel(link.filename) except InvalidWheelFilename: return (False, 'invalid wheel filename') if canonicalize_name(wheel.name) != search.canonical: reason = 'wrong project name (not %s)' % search.supplied return (False, reason) if not self._is_wheel_supported(wheel): return (False, 'it is not compatible with this Python') version = wheel.version # This should be up by the search.ok_binary check, but see issue 2700. if "source" not in search.formats and ext != WHEEL_EXTENSION: return (False, 'No sources permitted for %s' % search.supplied) if not version: version = _egg_info_matches(egg_info, search.canonical) if not version: return (False, 'Missing project version for %s' % search.supplied) match = self._py_version_re.search(version) if match: version = version[:match.start()] py_version = match.group(1) if py_version != sys.version[:3]: return (False, 'Python version is incorrect') try: support_this_python = check_requires_python( link.requires_python, version_info=sys.version_info[:3], ) except specifiers.InvalidSpecifier: logger.debug("Package %s has an invalid Requires-Python entry: %s", link.filename, link.requires_python) support_this_python = True if not support_this_python: logger.debug( "The package %s is incompatible with the python " "version in use. Acceptable python versions are: %s", link, link.requires_python) # Return None for the reason text to suppress calling # _log_skipped_link(). return (False, None) logger.debug('Found link %s, version: %s', link, version) return (True, version)
def test_check_requires_python(version_info: Tuple[int, int, int], requires_python: Optional[str], expected: bool) -> None: actual = check_requires_python(requires_python, version_info) assert actual == expected