def find_wheel_url(project: str, version: str, content: str): project = project.replace("-", "_") tags = set(sys_tags()) links = LinkFinder.extract_links(content) not_matched = [] found = False for link in links: link = posixpath.basename(unquote_plus(link)) if not link.endswith(".whl"): continue wproject, wversion, wtags = link[:-4].split("-", 2) if wproject.lower() != project.lower(): continue # Add to list so we can print it at the end if nothing matches not_matched.append(link) if wversion != version: continue for wtag in parse_tag(wtags): if wtag in tags: print("Found matching wheel", link) return True if not found: print("Did not find matching wheels in:") for link in not_matched: print("-", link) return False
def test_multi_platform(self): expected = { tags.Tag("cp37", "cp37m", platform) for platform in ( "macosx_10_6_intel", "macosx_10_9_intel", "macosx_10_9_x86_64", "macosx_10_10_intel", "macosx_10_10_x86_64", ) } given = tags.parse_tag( "cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64." "macosx_10_10_intel.macosx_10_10_x86_64") assert given == expected
def is_compatible(package): """ Check whether the given python package is a wheel compatible with the current platform and python interpreter. Compatibility is based on https://www.python.org/dev/peps/pep-0425/ """ try: w = parse_wheel_filename(package) for systag in tags.sys_tags(): for tag in w.tag_triples(): if systag in tags.parse_tag(tag): return True except InvalidFilenameError: return False
def test_parse_tag_multi_platform(): expected = { tags.Tag("cp37", "cp37m", platform) for platform in ( "macosx_10_6_intel", "macosx_10_9_intel", "macosx_10_9_x86_64", "macosx_10_10_intel", "macosx_10_10_x86_64", ) } given = tags.parse_tag( "cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64." "macosx_10_10_intel.macosx_10_10_x86_64" ) assert given == expected
def test_multi_interpreter(self, example_tag): expected = {example_tag, tags.Tag("py2", "none", "any")} given = tags.parse_tag("py2.py3-none-any") assert given == expected
def test_simple(self, example_tag): parsed_tags = tags.parse_tag(str(example_tag)) assert parsed_tags == {example_tag}
def archives(self): if self._archives: return self._archives AutoInstall._verify_index() path = 'simple/{}/'.format(self.pypi_name) response = AutoInstall._request('https://{}/{}'.format( AutoInstall.index, path)) try: if response.code != 200: raise ValueError('The package {} was not found on {}'.format( self.pypi_name, AutoInstall.index)) page = minidom.parseString(response.read()) cached_tags = None for element in reversed(page.getElementsByTagName("a")): if not len(element.childNodes): continue if element.childNodes[0].nodeType != minidom.Node.TEXT_NODE: continue attributes = {} for index in range(element.attributes.length): attributes[element.attributes.item( index).name] = element.attributes.item(index).value if not attributes.get('href', None): continue if self.wheel: match = re.search(r'.+-([^-]+-[^-]+-[^-]+).whl', element.childNodes[0].data) if not match: continue from packaging import tags if not cached_tags: cached_tags = set(AutoInstall.tags()) if all([ tag not in cached_tags for tag in tags.parse_tag(match.group(1)) ]): continue extension = 'whl' else: if element.childNodes[0].data.endswith( ('.tar.gz', '.tar.bz2')): extension = 'tar.gz' elif element.childNodes[0].data.endswith('.zip'): extension = 'zip' else: continue requires = attributes.get('data-requires-python') if requires and not AutoInstall.version.matches(requires): continue version_candidate = re.search(r'\d+\.\d+(\.\d+)?', element.childNodes[0].data) if not version_candidate: continue version = Version(*version_candidate.group().split('.')) if self.version and version not in self.version: continue link = attributes['href'].split('#')[0] if '://' not in link: depth = 0 while link.startswith('../'): depth += 1 link = link[3:] link = 'https://{}/{}{}'.format( AutoInstall.index, '/'.join(path.split('/')[depth:]), link) self._archives.append( self.Archive( name=self.pypi_name, link=link, version=version, extension=extension, )) self._archives = sorted(self._archives, key=lambda archive: archive.version) return self._archives finally: response.close()
def test_parse_tag_multi_interpreter(example_tag): expected = {example_tag, tags.Tag("py2", "none", "any")} given = tags.parse_tag("py2.py3-none-any") assert given == expected
def test_parse_tag_simple(example_tag): parsed_tags = tags.parse_tag(str(example_tag)) assert parsed_tags == {example_tag}
def archives(self): if self._archives: return self._archives AutoInstall._verify_index() path = 'simple/{}/'.format(self.pypi_name) response = AutoInstall._request('https://{}/{}'.format( AutoInstall.index, path)) try: if response.code != 200: raise ValueError('The package {} was not found on {}'.format( self.pypi_name, AutoInstall.index)) packages = SimplyPypiIndexPageParser.parse( response.read().decode("UTF-8")) cached_tags = None for package in reversed(packages): if self.wheel: match = re.search(r'.+-([^-]+-[^-]+-[^-]+).whl', package['name']) if not match: continue from packaging import tags if not cached_tags: cached_tags = set(AutoInstall.tags()) if all([ tag not in cached_tags for tag in tags.parse_tag(match.group(1)) ]): continue extension = 'whl' else: if package['name'].endswith(('.tar.gz', '.tar.bz2')): extension = 'tar.gz' elif package['name'].endswith('.zip'): extension = 'zip' else: continue requires = package.get('data-requires-python') if requires and not AutoInstall.version.matches(requires): continue version_candidate = re.search(r'\d+\.\d+(\.\d+)?', package["name"]) if not version_candidate: continue version = Version(*version_candidate.group().split('.')) if self.version and version not in self.version: continue link = package['href'].split('#')[0] if '://' not in link: depth = 0 while link.startswith('../'): depth += 1 link = link[3:] link = 'https://{}/{}{}'.format( AutoInstall.index, '/'.join(path.split('/')[depth:]), link) self._archives.append( self.Archive( name=self.pypi_name, link=link, version=version, extension=extension, )) self._archives = sorted(self._archives, key=lambda archive: archive.version) return self._archives finally: response.close()