from __future__ import unicode_literals import re import sys import platform from furl import furl import urllib.parse from operator import itemgetter from html.parser import HTMLParser from typing import Text, Optional, Dict import attr import requests import six from .requirements import ( SimpleSubstitution, FatalSpecsResolutionError, SimpleVersion, MarkerRequirement, compare_version_rules, ) from ...definitions import ENV_PACKAGE_PYTORCH_RESOLVE from ...external.requirements_parser.requirement import Requirement OS_TO_WHEEL_NAME = {"linux": "linux_x86_64", "windows": "win_amd64"} def os_to_wheel_name(x): return OS_TO_WHEEL_NAME[x] def fix_version(version): def replace(nums, prerelease): if prerelease: return "{}-{}".format(nums, prerelease) return nums return re.sub( r"(\d+(?:\.\d+){,2})(?:\.(.*))?", lambda match: replace(*match.groups()), version, ) class LinksHTMLParser(HTMLParser): def __init__(self): super(LinksHTMLParser, self).__init__() self.links = [] def handle_data(self, data): if data and data.strip(): self.links += [data] @attr.s class PytorchWheel(object): os_name = attr.ib(type=str, converter=os_to_wheel_name) cuda_version = attr.ib(converter=lambda x: "cu{}".format(x) if x else "cpu") python = attr.ib(type=str, converter=lambda x: str(x).replace(".", "")) torch_version = attr.ib(type=str, converter=fix_version) url_template_prefix = "http://download.pytorch.org/whl/" url_template = "{0.cuda_version}/torch-{0.torch_version}" \ "-cp{0.python}-cp{0.python}m{0.unicode}-{0.os_name}.whl" def __attrs_post_init__(self): self.unicode = "u" if self.python.startswith("2") else "" def make_url(self): # type: () -> Text return (self.url_template_prefix + self.url_template).format(self) class PytorchResolutionError(FatalSpecsResolutionError): pass class SimplePytorchRequirement(SimpleSubstitution): name = "torch" packages = ("torch", "torchvision", "torchaudio") page_lookup_template = 'https://download.pytorch.org/whl/cu{}/torch_stable.html' nightly_page_lookup_template = 'https://download.pytorch.org/whl/nightly/cu{}/torch_nightly.html' torch_page_lookup = { 0: 'https://download.pytorch.org/whl/cpu/torch_stable.html', 80: 'https://download.pytorch.org/whl/cu80/torch_stable.html', 90: 'https://download.pytorch.org/whl/cu90/torch_stable.html', 92: 'https://download.pytorch.org/whl/cu92/torch_stable.html', 100: 'https://download.pytorch.org/whl/cu100/torch_stable.html', 101: 'https://download.pytorch.org/whl/cu101/torch_stable.html', 102: 'https://download.pytorch.org/whl/cu102/torch_stable.html', 110: 'https://download.pytorch.org/whl/cu110/torch_stable.html', } def __init__(self, *args, **kwargs): super(SimplePytorchRequirement, self).__init__(*args, **kwargs) self._matched = False def match(self, req): # match both any of out packages return req.name in self.packages def replace(self, req): """ Replace a requirement :raises: ValueError if version is pre-release """ # Get rid of +cpu +cu?? etc. try: req.specs[0] = (req.specs[0][0], req.specs[0][1].split('+')[0]) except: pass self._matched = True return Text(req) def matching_done(self, reqs, package_manager): # type: (Sequence[MarkerRequirement], object) -> () if not self._matched: return # TODO: add conda channel support from .pip_api.system import SystemPip if package_manager and isinstance(package_manager, SystemPip): extra_url, _ = self.get_torch_page(self.cuda_version) package_manager.add_extra_install_flags(('-f', extra_url)) @classmethod def get_torch_page(cls, cuda_version, nightly=False): # noinspection PyBroadException try: cuda = int(cuda_version) except Exception: cuda = 0 if nightly: for c in range(cuda, max(-1, cuda-15), -1): # then try the nightly builds, it might be there... torch_url = cls.nightly_page_lookup_template.format(c) # noinspection PyBroadException try: if requests.get(torch_url, timeout=10).ok: print('Torch nightly CUDA {} download page found'.format(c)) cls.torch_page_lookup[c] = torch_url return cls.torch_page_lookup[c], c except Exception: pass return # first check if key is valid if cuda in cls.torch_page_lookup: return cls.torch_page_lookup[cuda], cuda # then try a new cuda version page for c in range(cuda, max(-1, cuda-15), -1): torch_url = cls.page_lookup_template.format(c) # noinspection PyBroadException try: if requests.get(torch_url, timeout=10).ok: print('Torch CUDA {} download page found'.format(c)) cls.torch_page_lookup[c] = torch_url return cls.torch_page_lookup[c], c except Exception: pass keys = sorted(cls.torch_page_lookup.keys(), reverse=True) for k in keys: if k <= cuda: return cls.torch_page_lookup[k], k # return default - zero return cls.torch_page_lookup[0], 0 class PytorchRequirement(SimpleSubstitution): name = "torch" packages = ("torch", "torchvision", "torchaudio", "torchcsprng", "torchtext") extra_index_url_template = 'https://download.pytorch.org/whl/cu{}/' nightly_extra_index_url_template = 'https://download.pytorch.org/whl/nightly/cu{}/' torch_index_url_lookup = {} resolver_types = ("pip", "direct", "none") def __init__(self, *args, **kwargs): os_name = kwargs.pop("os_override", None) super(PytorchRequirement, self).__init__(*args, **kwargs) self.log = self._session.get_logger(__name__) self.package_manager = self.config["agent.package_manager.type"].lower() self.os = os_name or self.get_platform() self.cuda = None self.python_version_string = None self.python_major_minor_str = None self.python = None self._fix_setuptools = None self.exceptions = [] self._original_req = [] # allow override pytorch lookup pages if self.config.get("agent.package_manager.extra_index_url_template", None): self.extra_index_url_template = \ self.config.get("agent.package_manager.extra_index_url_template", None) if self.config.get("agent.package_manager.nightly_extra_index_url_template", None): self.nightly_extra_index_url_template = \ self.config.get("agent.package_manager.nightly_extra_index_url_template", None) # allow override pytorch lookup pages if self.config.get("agent.package_manager.torch_page", None): SimplePytorchRequirement.page_lookup_template = \ self.config.get("agent.package_manager.torch_page", None) if self.config.get("agent.package_manager.torch_nightly_page", None): SimplePytorchRequirement.nightly_page_lookup_template = \ self.config.get("agent.package_manager.torch_nightly_page", None) if self.config.get("agent.package_manager.torch_url_template_prefix", None): PytorchWheel.url_template_prefix = \ self.config.get("agent.package_manager.torch_url_template_prefix", None) if self.config.get("agent.package_manager.torch_url_template", None): PytorchWheel.url_template = \ self.config.get("agent.package_manager.torch_url_template", None) self.resolve_algorithm = str( ENV_PACKAGE_PYTORCH_RESOLVE.get() or self.config.get("agent.package_manager.pytorch_resolve", "pip")).lower() if self.resolve_algorithm not in self.resolver_types: print("WARNING: agent.package_manager.pytorch_resolve=={} not in {} reverting to '{}'".format( self.resolve_algorithm, self.resolver_types, self.resolver_types[0])) self.resolve_algorithm = self.resolver_types[0] def _init_python_ver_cuda_ver(self): if self.cuda is None: self.cuda = "cuda{}".format(self.cuda_version).lower() if self.python_version_string is None: self.python_version_string = str(self.config["agent.default_python"]) if self.python_major_minor_str is None: self.python_major_minor_str = '.'.join(self.python_version_string.split('.')[:2]) if '.' not in self.python_major_minor_str: raise PytorchResolutionError( "invalid python version {!r} defined in configuration file, key 'agent.default_python': " "must have both major and minor parts of the version (for example: '3.7')".format( self.python_version_string ) ) if self.python is None: self.python = "python{}".format(self.python_major_minor_str) if not self.exceptions: self.exceptions = [ PytorchResolutionError(message) for message in ( None, 'cuda version "{}" is not supported'.format(self.cuda), 'python version "{}" is not supported'.format( self.python_version_string ), ) ] @property def is_conda(self): return self.package_manager == "conda" @property def is_pip(self): return not self.is_conda def validate_python_version(self): """ Make sure python version has both major and minor versions as required for choosing pytorch wheel """ self._init_python_ver_cuda_ver() if self.is_pip and not self.python_major_minor_str: raise PytorchResolutionError( "invalid python version {!r} defined in configuration file, key 'agent.default_python': " "must have both major and minor parts of the version (for example: '3.7')".format( self.python_version_string ) ) def match(self, req): if self.resolve_algorithm == "none": # skipping resolver return False return req.name in self.packages @staticmethod def get_platform(): if sys.platform == "linux": return "linux" if sys.platform == "win32" or sys.platform == "cygwin": return "windows" if sys.platform == "darwin": return "macos" raise RuntimeError("unrecognized OS") @staticmethod def get_arch(): return str(platform.machine()).lower() def _get_link_from_torch_page(self, req, torch_url): links_parser = LinksHTMLParser() links_parser.feed(requests.get(torch_url, timeout=10).text) platform_wheel = "win" if self.get_platform() == "windows" else self.get_platform() arch_wheel = self.get_arch() py_ver = self.python_major_minor_str.replace('.', '') url = None last_v = None closest_v = None # search for our package for l in links_parser.links: parts = l.split('/')[-1].split('-') if len(parts) < 5: continue if parts[0] != req.name: continue # version (ignore +cpu +cu92 etc. + is %2B in the file link) # version ignore .postX suffix (treat as regular version) # noinspection PyBroadException try: v = str(parts[1].split('%')[0].split('+')[0]) except Exception: continue if len(parts) < 3 or not parts[2].endswith(py_ver): continue if len(parts) < 5 or platform_wheel not in parts[4].lower(): continue if len(parts) < 5 or arch_wheel not in parts[4].lower(): continue # yes this is for linux python 2.7 support, this is the only python 2.7 we support... if py_ver and py_ver[0] == '2' and len(parts) > 3 and not parts[3].endswith('u'): continue # check if this an actual match if not req.compare_version(v) or \ (last_v and SimpleVersion.compare_versions(last_v, '>', v, ignore_sub_versions=False)): continue # update the closest matched version (from above) if not closest_v: closest_v = v elif SimpleVersion.compare_versions( version_a=closest_v, op='>=', version_b=v, num_parts=3) and \ SimpleVersion.compare_versions( version_a=v, op='>=', version_b=req.specs[0][1], num_parts=3): closest_v = v url = '/'.join(torch_url.split('/')[:-1] + l.split('/')) last_v = v # if we found an exact match, use it # noinspection PyBroadException try: if req.specs[0][0] == '==' and \ SimpleVersion.compare_versions(req.specs[0][1], '==', v, ignore_sub_versions=False): break except Exception: pass return url, last_v or closest_v def get_url_for_platform(self, req): # check if package is already installed with system packages self.validate_python_version() # noinspection PyBroadException try: if self.config.get("agent.package_manager.system_site_packages", None): from pip._internal.commands.show import search_packages_info installed_torch = list(search_packages_info([req.name])) # notice the comparison order, the first part will make sure we have a valid installed package installed_torch_version = \ (getattr(installed_torch[0], 'version', None) or installed_torch[0]['version']) if installed_torch else None if installed_torch and installed_torch_version and \ req.compare_version(installed_torch_version): print('PyTorch: requested "{}" version {}, using pre-installed version {}'.format( req.name, req.specs[0] if req.specs else 'unspecified', installed_torch_version)) # package already installed, do nothing req.specs = [('==', str(installed_torch_version))] return '{} {} {}'.format(req.name, req.specs[0][0], req.specs[0][1]), True except Exception: pass # make sure we have a specific version to retrieve if not req.specs: req.specs = [('>', '0')] # noinspection PyBroadException try: req.specs[0] = (req.specs[0][0], req.specs[0][1].split('+')[0]) except Exception: pass op, version = req.specs[0] # assert op == "==" torch_url, torch_url_key = SimplePytorchRequirement.get_torch_page(self.cuda_version) url, closest_matched_version = self._get_link_from_torch_page(req, torch_url) if not url and self.config.get("agent.package_manager.torch_nightly", None): torch_url, torch_url_key = SimplePytorchRequirement.get_torch_page(self.cuda_version, nightly=True) url, closest_matched_version = self._get_link_from_torch_page(req, torch_url) # try one more time, with a lower cuda version (never fallback to CPU): while not url and torch_url_key > 0: previous_cuda_key = torch_url_key print('Warning, could not locate PyTorch {} matching CUDA version {}, best candidate {}\n'.format( req, previous_cuda_key, closest_matched_version)) url, closest_matched_version = self._get_link_from_torch_page(req, torch_url) if url: break torch_url, torch_url_key = SimplePytorchRequirement.get_torch_page(int(torch_url_key)-1) # never fallback to CPU if torch_url_key < 1: print( 'Error! Could not locate PyTorch version {} matching CUDA version {}'.format( req, previous_cuda_key)) raise ValueError( 'Could not locate PyTorch version {} matching CUDA version {}'.format(req, self.cuda_version)) else: print('Trying PyTorch CUDA version {} support'.format(torch_url_key)) # fix broken pytorch setuptools incompatibility if req.name == "torch" and closest_matched_version and \ SimpleVersion.compare_versions(closest_matched_version, "<", "1.11.0"): self._fix_setuptools = "setuptools < 59" if not url: url = PytorchWheel( torch_version=fix_version(version), python=self.python_major_minor_str.replace('.', ''), os_name=self.os, cuda_version=self.cuda_version, ).make_url() if url: # normalize url (sometimes we will get ../ which we should not... url = '/'.join(url.split('/')[:3]) + urllib.parse.quote(str(furl(url).path.normalize())) # print found print('Found PyTorch version {} matching CUDA version {}'.format(req, torch_url_key)) self.log.debug("checking url: %s", url) return url, requests.head(url, timeout=10).ok @staticmethod def match_version(req, options): versioned_options = sorted( ((fix_version(key), value) for key, value in options.items()), key=itemgetter(0), reverse=True, ) req.specs = [(op, fix_version(version)) for op, version in req.specs] try: return next( replacement for version, replacement in versioned_options if req.compare_version(version) ) except StopIteration: raise PytorchResolutionError( 'Could not find wheel for "{}", ' "Available versions: {}".format(req, list(options)) ) def replace_conda(self, req): spec = "".join(req.specs[0]) if req.specs else "" if not self.cuda_version: return "pytorch-cpu{spec}\ntorchvision-cpu".format(spec=spec) return "pytorch{spec}\ntorchvision\ncuda{self.cuda_version}".format( self=self, spec=spec ) def _table_lookup(self, req): """ Look for pytorch wheel matching `req` in table :param req: python requirement """ def check(base_, key_, exception_): result = base_.get(key_) if not result: if key_.startswith('cuda'): print('Could not locate, {}'.format(exception_)) ver = sorted([float(a.replace('cuda', '').replace('none', '0')) for a in base_.keys()], reverse=True)[0] key_ = 'cuda'+str(int(ver)) result = base_.get(key_) print('Reverting to \"{}\"'.format(key_)) if not result: raise exception_ return result raise exception_ if isinstance(result, Exception): raise result return result if self.is_conda: return self.replace_conda(req) base = self.MAP for key, exception in zip((self.os, self.cuda, self.python), self.exceptions): base = check(base, key, exception) return self.match_version(req, base).replace(" ", "\n") def replace(self, req): # we first try to resolve things ourselves because pytorch pip is not always picking the correct # versions from their pip repository resolve_algorithm = self.resolve_algorithm if resolve_algorithm == "none": # skipping resolver return None elif resolve_algorithm == "direct": # noinspection PyBroadException try: new_req = self._replace(req) if new_req: self._original_req.append((req, new_req)) return new_req except Exception: print("Warning: Failed resolving using `pytorch_resolve=direct` reverting to `pytorch_resolve=pip`") elif resolve_algorithm not in self.resolver_types: print("Warning: `agent.package_manager.pytorch_resolve={}` " "unrecognized, default to `pip`".format(resolve_algorithm)) # check if package is already installed with system packages self.validate_python_version() # try to check if we can just use the new index URL, if we do not we will revert to old method try: extra_index_url = self.get_torch_index_url(self.cuda_version) if extra_index_url: # check if the torch version cannot be above 1.11 , we need to fix setup tools try: if req.name == "torch" and not compare_version_rules(req.specs, [(">=", "1.11.0")]): self._fix_setuptools = "setuptools < 59" except Exception: # noqa pass # now we just need to add the correct extra index url for the cuda version self.set_add_install_extra_index(extra_index_url[0]) if req.specs and len(req.specs) == 1 and req.specs[0][0] == "==": # remove any +cu extension and let pip resolve that # and add .* if we have 3 parts version to deal with nvidia container 'a' version # i.e. "1.13.0" -> "1.13.0.*" so it should match preinstalled "1.13.0a0+936e930" spec_3_parts = req.format_specs(num_parts=3) spec_max3_parts = req.format_specs(max_num_parts=3) if spec_3_parts == spec_max3_parts and not spec_max3_parts.endswith("*"): line = "{} {}.*".format(req.name, spec_max3_parts) else: line = "{} {}".format(req.name, spec_max3_parts) if req.marker: line += " ; {}".format(req.marker) else: # return the original line line = req.line print("PyTorch: Adding index `{}` and installing `{}`".format(extra_index_url[0], line)) return line except Exception: # noqa pass try: new_req = self._replace(req) if new_req: self._original_req.append((req, new_req)) return new_req except Exception as e: message = "Exception when trying to resolve python wheel" self.log.debug(message, exc_info=True) raise PytorchResolutionError("{}: {}".format(message, e)) def _replace(self, req): self.validate_python_version() try: result, ok = self.get_url_for_platform(req) self.log.debug('Replacing requirement "%s" with %r', req, result) return result except: pass # try: # result = self._table_lookup(req) # except Exception as e: # exc = e # else: # self.log.debug('Replacing requirement "%s" with %r', req, result) # return result # self.log.debug( # "Could not find Pytorch wheel in table, trying manually constructing URL" # ) result = ok = None # try: # result, ok = self.get_url_for_platform(req) # except Exception: # pass if not ok: if result: self.log.debug("URL not found: {}".format(result)) exc = PytorchResolutionError( "Could not find pytorch wheel URL for: {} with cuda {} support".format(req, self.cuda_version) ) # cancel exception chaining six.raise_from(exc, None) self.log.debug('Replacing requirement "%s" with %r', req, result) return result def replace_back(self, list_of_requirements): # type: (Dict) -> Dict """ :param list_of_requirements: {'pip': ['a==1.0', ]} :return: {'pip': ['a==1.0', ]} """ def build_specific_version_req(a_line, a_name, a_new_req): try: r = Requirement.parse(a_line) wheel_parts = r.uri.split("/")[-1].split('-') version = str(wheel_parts[1].split('%')[0].split('+')[0]) new_r = Requirement.parse("{} == {} # {}".format(a_name, version, str(a_new_req))) if new_r.line: # great it worked! return new_r.line except: # noqa pass return None if not self._original_req: return list_of_requirements try: for k, lines in list_of_requirements.items(): # k is either pip/conda if k not in ('pip', 'conda'): continue for i, line in enumerate(lines): if not line or line.lstrip().startswith('#'): continue parts = [p for p in re.split(r'\s|=|\.|<|>|~|!|@|#', line) if p] if not parts: continue for req, new_req in self._original_req: if req.req.name == parts[0]: # support for pip >= 20.1 if '@' in line: # skip if we have nothing to add if str(req).strip() != str(new_req).strip(): # if this is local file and use the version detection if req.local_file: lines[i] = '{}'.format(str(new_req)) else: # try to rebuild requirements with specific version: new_line = build_specific_version_req(line, req.req.name, new_req) if new_line: lines[i] = new_line else: lines[i] = '{} # {}'.format(str(req), str(new_req)) else: new_line = build_specific_version_req(line, req.req.name, new_req) if new_line: lines[i] = new_line else: lines[i] = '{} # {}'.format(line, str(new_req)) break except: pass return list_of_requirements def post_scan_add_req(self): # type: () -> Optional[MarkerRequirement] """ Allows the RequirementSubstitution to add an extra line/requirements after the initial requirements scan is completed. Called only once per requirements.txt object """ if self._fix_setuptools: return MarkerRequirement(Requirement.parse(self._fix_setuptools)) return None def get_torch_index_url(self, cuda_version, nightly=False): # noinspection PyBroadException try: cuda = int(cuda_version) except Exception: cuda = 0 if nightly: for c in range(cuda, max(-1, cuda-15), -1): # then try the nightly builds, it might be there... torch_url = self.nightly_extra_index_url_template.format(c) # noinspection PyBroadException try: if requests.get(torch_url, timeout=10).ok: print('Torch nightly CUDA {} index page found'.format(c)) self.torch_index_url_lookup[c] = torch_url return self.torch_index_url_lookup[c], c except Exception: pass return # first check if key is valid if cuda in self.torch_index_url_lookup: return self.torch_index_url_lookup[cuda], cuda # then try a new cuda version page for c in range(cuda, max(-1, cuda-15), -1): torch_url = self.extra_index_url_template.format(c) # noinspection PyBroadException try: if requests.get(torch_url, timeout=10).ok: print('Torch CUDA {} index page found, adding `{}`'.format(c, torch_url)) self.torch_index_url_lookup[c] = torch_url return self.torch_index_url_lookup[c], c except Exception: pass keys = sorted(self.torch_index_url_lookup.keys(), reverse=True) for k in keys: if k <= cuda: return self.torch_index_url_lookup[k], k # return default - zero return self.torch_index_url_lookup[0], 0 MAP = { "windows": { "cuda100": { "python3.7": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp37-cp37m-win_amd64.whl" }, "python3.6": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp36-cp36m-win_amd64.whl" }, "python3.5": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp35-cp35m-win_amd64.whl" }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, "cuda92": { "python3.7": { "0.4.1", "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp37-cp37m-win_amd64.whl", }, "python3.6": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp36-cp36m-win_amd64.whl" }, "python3.5": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp35-cp35m-win_amd64.whl" }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, "cuda91": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cu91/torch-0.4.0-cp36-cp36m-win_amd64.whl" }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cu91/torch-0.4.0-cp35-cp35m-win_amd64.whl" }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, "cuda90": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cu90/torch-0.4.0-cp36-cp36m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cu90/torch-1.0.0-cp36-cp36m-win_amd64.whl", }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cu90/torch-0.4.0-cp35-cp35m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cu90/torch-1.0.0-cp35-cp35m-win_amd64.whl", }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, "cuda80": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cu80/torch-0.4.0-cp36-cp36m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cu80/torch-1.0.0-cp36-cp36m-win_amd64.whl", }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cu80/torch-0.4.0-cp35-cp35m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cu80/torch-1.0.0-cp35-cp35m-win_amd64.whl", }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, "cudanone": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cpu/torch-0.4.0-cp36-cp36m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cpu/torch-1.0.0-cp36-cp36m-win_amd64.whl", }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cpu/torch-0.4.0-cp35-cp35m-win_amd64.whl", "1.0.0": "http://download.pytorch.org/whl/cpu/torch-1.0.0-cp35-cp35m-win_amd64.whl", }, "python2.7": PytorchResolutionError( "PyTorch does not support Python 2.7 on Windows" ), }, }, "macos": { "cuda100": PytorchResolutionError( "MacOS Binaries dont support CUDA, install from source if CUDA is needed" ), "cuda92": PytorchResolutionError( "MacOS Binaries dont support CUDA, install from source if CUDA is needed" ), "cuda91": PytorchResolutionError( "MacOS Binaries dont support CUDA, install from source if CUDA is needed" ), "cuda90": PytorchResolutionError( "MacOS Binaries dont support CUDA, install from source if CUDA is needed" ), "cuda80": PytorchResolutionError( "MacOS Binaries dont support CUDA, install from source if CUDA is needed" ), "cudanone": { "python3.6": {"0.4.0": "torch"}, "python3.5": {"0.4.0": "torch"}, "python2.7": {"0.4.0": "torch"}, }, }, "linux": { "cuda100": { "python3.7": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp37-cp37m-linux_x86_64.whl", "1.0.1": "http://download.pytorch.org/whl/cu100/torch-1.0.1-cp37-cp37m-linux_x86_64.whl", "1.1.0": "http://download.pytorch.org/whl/cu100/torch-1.1.0-cp37-cp37m-linux_x86_64.whl", "1.2.0": "http://download.pytorch.org/whl/cu100/torch-1.2.0-cp37-cp37m-manylinux1_x86_64.whl", }, "python3.6": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp36-cp36m-linux_x86_64.whl", "1.0.1": "http://download.pytorch.org/whl/cu100/torch-1.0.1-cp36-cp36m-linux_x86_64.whl", "1.1.0": "http://download.pytorch.org/whl/cu100/torch-1.1.0-cp36-cp36m-linux_x86_64.whl", "1.2.0": "http://download.pytorch.org/whl/cu100/torch-1.2.0-cp36-cp36m-manylinux1_x86_64.whl", }, "python3.5": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp35-cp35m-linux_x86_64.whl", "1.0.1": "http://download.pytorch.org/whl/cu100/torch-1.0.1-cp35-cp35m-linux_x86_64.whl", "1.1.0": "http://download.pytorch.org/whl/cu100/torch-1.1.0-cp35-cp35m-linux_x86_64.whl", "1.2.0": "http://download.pytorch.org/whl/cu100/torch-1.2.0-cp35-cp35m-manylinux1_x86_64.whl", }, "python2.7": { "1.0.0": "http://download.pytorch.org/whl/cu100/torch-1.0.0-cp27-cp27mu-linux_x86_64.whl", "1.0.1": "http://download.pytorch.org/whl/cu100/torch-1.0.1-cp27-cp27mu-linux_x86_64.whl", "1.1.0": "http://download.pytorch.org/whl/cu100/torch-1.1.0-cp27-cp27mu-linux_x86_64.whl", "1.2.0": "http://download.pytorch.org/whl/cu100/torch-1.2.0-cp27-cp27mu-manylinux1_x86_64.whl", }, }, "cuda92": { "python3.7": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1.post2-cp37-cp37m-linux_x86_64.whl", "1.2.0": "https://download.pytorch.org/whl/cu92/torch-1.2.0%2Bcu92-cp37-cp37m-manylinux1_x86_64.whl" }, "python3.6": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp36-cp36m-linux_x86_64.whl", "1.2.0": "https://download.pytorch.org/whl/cu92/torch-1.2.0%2Bcu92-cp36-cp36m-manylinux1_x86_64.whl" }, "python3.5": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp35-cp35m-linux_x86_64.whl", "1.2.0": "https://download.pytorch.org/whl/cu92/torch-1.2.0%2Bcu92-cp35-cp35m-manylinux1_x86_64.whl" }, "python2.7": { "0.4.1": "http://download.pytorch.org/whl/cu92/torch-0.4.1-cp27-cp27mu-linux_x86_64.whl", "1.2.0": "https://download.pytorch.org/whl/cu92/torch-1.2.0%2Bcu92-cp27-cp27mu-manylinux1_x86_64.whl" }, }, "cuda91": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cu91/torch-0.4.0-cp36-cp36m-linux_x86_64.whl" }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cu91/torch-0.4.0-cp35-cp35m-linux_x86_64.whl" }, "python2.7": { "0.4.0": "http://download.pytorch.org/whl/cu91/torch-0.4.0-cp27-cp27mu-linux_x86_64.whl" }, }, "cuda90": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cu90/torch-0.4.0-cp36-cp36m-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cu90/torch-1.0.0-cp36-cp36m-linux_x86_64.whl", }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cu90/torch-0.4.0-cp35-cp35m-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cu90/torch-1.0.0-cp35-cp35m-linux_x86_64.whl", }, "python2.7": { "0.4.0": "http://download.pytorch.org/whl/cu90/torch-0.4.0-cp27-cp27mu-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cu90/torch-1.0.0-cp27-cp27mu-linux_x86_64.whl", }, }, "cuda80": { "python3.6": { "0.4.1": "http://download.pytorch.org/whl/cu80/torch-0.4.1-cp36-cp36m-linux_x86_64.whl", "0.3.1": "torch==0.3.1", "0.3.0.post4": "torch==0.3.0.post4", "0.1.2.post1": "torch==0.1.2.post1", "0.1.2": "torch==0.1.2", "1.0.0": "http://download.pytorch.org/whl/cu80/torch-1.0.0-cp36-cp36m-linux_x86_64.whl", }, "python3.5": { "0.4.1": "http://download.pytorch.org/whl/cu80/torch-0.4.1-cp35-cp35m-linux_x86_64.whl", "0.3.1": "torch==0.3.1", "0.3.0.post4": "torch==0.3.0.post4", "0.1.2.post1": "torch==0.1.2.post1", "0.1.2": "torch==0.1.2", "1.0.0": "http://download.pytorch.org/whl/cu80/torch-1.0.0-cp35-cp35m-linux_x86_64.whl", }, "python2.7": { "0.4.1": "http://download.pytorch.org/whl/cu80/torch-0.4.1-cp27-cp27mu-linux_x86_64.whl", "0.3.1": "torch==0.3.1", "0.3.0.post4": "torch==0.3.0.post4", "0.1.2.post1": "torch==0.1.2.post1", "0.1.2": "torch==0.1.2", "1.0.0": "http://download.pytorch.org/whl/cu80/torch-1.0.0-cp27-cp27mu-linux_x86_64.whl", }, }, "cudanone": { "python3.6": { "0.4.0": "http://download.pytorch.org/whl/cpu/torch-0.4.0-cp36-cp36m-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cpu/torch-1.0.0-cp36-cp36m-linux_x86_64.whl", }, "python3.5": { "0.4.0": "http://download.pytorch.org/whl/cpu/torch-0.4.0-cp35-cp35m-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cpu/torch-1.0.0-cp35-cp35m-linux_x86_64.whl", }, "python2.7": { "0.4.0": "http://download.pytorch.org/whl/cpu/torch-0.4.0-cp27-cp27mu-linux_x86_64.whl", "1.0.0": "http://download.pytorch.org/whl/cpu/torch-1.0.0-cp27-cp27mu-linux_x86_64.whl", }, }, }, }