diff --git a/.ci/pytorch/create_test_cert.py b/.ci/pytorch/create_test_cert.py index 4e31f97878f..a86facc3b73 100644 --- a/.ci/pytorch/create_test_cert.py +++ b/.ci/pytorch/create_test_cert.py @@ -1,10 +1,10 @@ from datetime import datetime, timedelta from tempfile import mkdtemp -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID -from cryptography.hazmat.primitives import hashes temp_dir = mkdtemp() print(temp_dir) @@ -16,37 +16,43 @@ def genrsa(path): key_size=2048, ) with open(path, "wb") as f: - f.write(key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - )) + f.write( + key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) return key def create_cert(path, C, ST, L, O, key): - subject = issuer = x509.Name([ - x509.NameAttribute(NameOID.COUNTRY_NAME, C), - x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, ST), - x509.NameAttribute(NameOID.LOCALITY_NAME, L), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, O), - ]) - cert = x509.CertificateBuilder().subject_name( - subject - ).issuer_name( - issuer - ).public_key( - key.public_key() - ).serial_number( - x509.random_serial_number() - ).not_valid_before( - datetime.utcnow() - ).not_valid_after( - # Our certificate will be valid for 10 days - datetime.utcnow() + timedelta(days=10) - ).add_extension( - x509.BasicConstraints(ca=True, path_length=None), critical=True, - ).sign(key, hashes.SHA256()) + subject = issuer = x509.Name( + [ + x509.NameAttribute(NameOID.COUNTRY_NAME, C), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, ST), + x509.NameAttribute(NameOID.LOCALITY_NAME, L), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, O), + ] + ) + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.utcnow()) + .not_valid_after( + # Our certificate will be valid for 10 days + datetime.utcnow() + + timedelta(days=10) + ) + .add_extension( + x509.BasicConstraints(ca=True, path_length=None), + critical=True, + ) + .sign(key, hashes.SHA256()) + ) # Write our certificate out to disk. with open(path, "wb") as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) @@ -54,43 +60,65 @@ def create_cert(path, C, ST, L, O, key): def create_req(path, C, ST, L, O, key): - csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ - # Provide various details about who we are. - x509.NameAttribute(NameOID.COUNTRY_NAME, C), - x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, ST), - x509.NameAttribute(NameOID.LOCALITY_NAME, L), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, O), - ])).sign(key, hashes.SHA256()) + csr = ( + x509.CertificateSigningRequestBuilder() + .subject_name( + x509.Name( + [ + # Provide various details about who we are. + x509.NameAttribute(NameOID.COUNTRY_NAME, C), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, ST), + x509.NameAttribute(NameOID.LOCALITY_NAME, L), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, O), + ] + ) + ) + .sign(key, hashes.SHA256()) + ) with open(path, "wb") as f: f.write(csr.public_bytes(serialization.Encoding.PEM)) return csr def sign_certificate_request(path, csr_cert, ca_cert, private_ca_key): - cert = x509.CertificateBuilder().subject_name( - csr_cert.subject - ).issuer_name( - ca_cert.subject - ).public_key( - csr_cert.public_key() - ).serial_number( - x509.random_serial_number() - ).not_valid_before( - datetime.utcnow() - ).not_valid_after( - # Our certificate will be valid for 10 days - datetime.utcnow() + timedelta(days=10) - # Sign our certificate with our private key - ).sign(private_ca_key, hashes.SHA256()) + cert = ( + x509.CertificateBuilder() + .subject_name(csr_cert.subject) + .issuer_name(ca_cert.subject) + .public_key(csr_cert.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.utcnow()) + .not_valid_after( + # Our certificate will be valid for 10 days + datetime.utcnow() + + timedelta(days=10) + # Sign our certificate with our private key + ) + .sign(private_ca_key, hashes.SHA256()) + ) with open(path, "wb") as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) return cert ca_key = genrsa(temp_dir + "/ca.key") -ca_cert = create_cert(temp_dir + "/ca.pem", "US", "New York", "New York", "Gloo Certificate Authority", ca_key) +ca_cert = create_cert( + temp_dir + "/ca.pem", + "US", + "New York", + "New York", + "Gloo Certificate Authority", + ca_key, +) pkey = genrsa(temp_dir + "/pkey.key") -csr = create_req(temp_dir + "/csr.csr", "US", "California", "San Francisco", "Gloo Testing Company", pkey) +csr = create_req( + temp_dir + "/csr.csr", + "US", + "California", + "San Francisco", + "Gloo Testing Company", + pkey, +) cert = sign_certificate_request(temp_dir + "/cert.pem", csr, ca_cert, ca_key) diff --git a/.ci/pytorch/perf_test/compare_with_baseline.py b/.ci/pytorch/perf_test/compare_with_baseline.py index c756df37872..49b77cbba2a 100644 --- a/.ci/pytorch/perf_test/compare_with_baseline.py +++ b/.ci/pytorch/perf_test/compare_with_baseline.py @@ -1,32 +1,41 @@ -import sys +import argparse import json import math -import argparse +import sys parser = argparse.ArgumentParser() -parser.add_argument('--test-name', dest='test_name', action='store', - required=True, help='test name') -parser.add_argument('--sample-stats', dest='sample_stats', action='store', - required=True, help='stats from sample') -parser.add_argument('--update', action='store_true', - help='whether to update baseline using stats from sample') +parser.add_argument( + "--test-name", dest="test_name", action="store", required=True, help="test name" +) +parser.add_argument( + "--sample-stats", + dest="sample_stats", + action="store", + required=True, + help="stats from sample", +) +parser.add_argument( + "--update", + action="store_true", + help="whether to update baseline using stats from sample", +) args = parser.parse_args() test_name = args.test_name -if 'cpu' in test_name: - backend = 'cpu' -elif 'gpu' in test_name: - backend = 'gpu' +if "cpu" in test_name: + backend = "cpu" +elif "gpu" in test_name: + backend = "gpu" -data_file_path = f'../{backend}_runtime.json' +data_file_path = f"../{backend}_runtime.json" with open(data_file_path) as data_file: data = json.load(data_file) if test_name in data: - mean = float(data[test_name]['mean']) - sigma = float(data[test_name]['sigma']) + mean = float(data[test_name]["mean"]) + sigma = float(data[test_name]["sigma"]) else: # Let the test pass if baseline number doesn't exist mean = sys.maxsize @@ -43,37 +52,39 @@ if math.isnan(mean) or math.isnan(sigma): sample_stats_data = json.loads(args.sample_stats) -sample_mean = float(sample_stats_data['mean']) -sample_sigma = float(sample_stats_data['sigma']) +sample_mean = float(sample_stats_data["mean"]) +sample_sigma = float(sample_stats_data["sigma"]) print("sample mean: ", sample_mean) print("sample sigma: ", sample_sigma) if math.isnan(sample_mean): - raise Exception('''Error: sample mean is NaN''') + raise Exception("""Error: sample mean is NaN""") elif math.isnan(sample_sigma): - raise Exception('''Error: sample sigma is NaN''') + raise Exception("""Error: sample sigma is NaN""") z_value = (sample_mean - mean) / sigma print("z-value: ", z_value) if z_value >= 3: - raise Exception(f'''\n + raise Exception( + f"""\n z-value >= 3, there is high chance of perf regression.\n To reproduce this regression, run `cd .ci/pytorch/perf_test/ && bash {test_name}.sh` on your local machine and compare the runtime before/after your code change. -''') +""" + ) else: print("z-value < 3, no perf regression detected.") if args.update: print("We will use these numbers as new baseline.") - new_data_file_path = f'../new_{backend}_runtime.json' + new_data_file_path = f"../new_{backend}_runtime.json" with open(new_data_file_path) as new_data_file: new_data = json.load(new_data_file) new_data[test_name] = {} - new_data[test_name]['mean'] = sample_mean - new_data[test_name]['sigma'] = max(sample_sigma, sample_mean * 0.1) - with open(new_data_file_path, 'w') as new_data_file: + new_data[test_name]["mean"] = sample_mean + new_data[test_name]["sigma"] = max(sample_sigma, sample_mean * 0.1) + with open(new_data_file_path, "w") as new_data_file: json.dump(new_data, new_data_file, indent=4) diff --git a/.ci/pytorch/perf_test/get_stats.py b/.ci/pytorch/perf_test/get_stats.py index 9e6e72a4da8..fbb2876ae3f 100644 --- a/.ci/pytorch/perf_test/get_stats.py +++ b/.ci/pytorch/perf_test/get_stats.py @@ -1,5 +1,6 @@ -import sys import json +import sys + import numpy sample_data_list = sys.argv[1:] @@ -9,8 +10,8 @@ sample_mean = numpy.mean(sample_data_list) sample_sigma = numpy.std(sample_data_list) data = { - 'mean': sample_mean, - 'sigma': sample_sigma, + "mean": sample_mean, + "sigma": sample_sigma, } print(json.dumps(data)) diff --git a/.ci/pytorch/perf_test/update_commit_hash.py b/.ci/pytorch/perf_test/update_commit_hash.py index ee7fa8a9b09..a27b1dec957 100644 --- a/.ci/pytorch/perf_test/update_commit_hash.py +++ b/.ci/pytorch/perf_test/update_commit_hash.py @@ -1,5 +1,5 @@ -import sys import json +import sys data_file_path = sys.argv[1] commit_hash = sys.argv[2] @@ -7,7 +7,7 @@ commit_hash = sys.argv[2] with open(data_file_path) as data_file: data = json.load(data_file) -data['commit'] = commit_hash +data["commit"] = commit_hash -with open(data_file_path, 'w') as data_file: +with open(data_file_path, "w") as data_file: json.dump(data, data_file) diff --git a/.ci/pytorch/print_sccache_log.py b/.ci/pytorch/print_sccache_log.py index e86b5ddfa94..398501b656b 100644 --- a/.ci/pytorch/print_sccache_log.py +++ b/.ci/pytorch/print_sccache_log.py @@ -9,9 +9,9 @@ for line in lines: # Ignore errors from CPU instruction set, symbol existing testing, # or compilation error formatting ignored_keywords = [ - 'src.c', - 'CheckSymbolExists.c', - 'test_compilation_error_formatting', + "src.c", + "CheckSymbolExists.c", + "test_compilation_error_formatting", ] if all(keyword not in line for keyword in ignored_keywords): print(line) diff --git a/.ci/pytorch/win-test-helpers/run_python_nn_smoketests.py b/.ci/pytorch/win-test-helpers/run_python_nn_smoketests.py index 4d264e1da08..07b78b61511 100755 --- a/.ci/pytorch/win-test-helpers/run_python_nn_smoketests.py +++ b/.ci/pytorch/win-test-helpers/run_python_nn_smoketests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -import subprocess import os +import subprocess COMMON_TESTS = [ ( @@ -31,8 +31,7 @@ GPU_TESTS = [ if __name__ == "__main__": - - if 'USE_CUDA' in os.environ and os.environ['USE_CUDA'] == '1': + if "USE_CUDA" in os.environ and os.environ["USE_CUDA"] == "1": TESTS = COMMON_TESTS + GPU_TESTS else: TESTS = COMMON_TESTS @@ -44,8 +43,10 @@ if __name__ == "__main__": try: subprocess.check_call(command_args) except subprocess.CalledProcessError as e: - sdk_root = os.environ.get('WindowsSdkDir', 'C:\\Program Files (x86)\\Windows Kits\\10') - debugger = os.path.join(sdk_root, 'Debuggers', 'x64', 'cdb.exe') + sdk_root = os.environ.get( + "WindowsSdkDir", "C:\\Program Files (x86)\\Windows Kits\\10" + ) + debugger = os.path.join(sdk_root, "Debuggers", "x64", "cdb.exe") if os.path.exists(debugger): command_args = [debugger, "-o", "-c", "~*g; q"] + command_args command_string = " ".join(command_args) diff --git a/.circleci/cimodel/data/binary_build_data.py b/.circleci/cimodel/data/binary_build_data.py index 23191a6f550..2e6f523e02a 100644 --- a/.circleci/cimodel/data/binary_build_data.py +++ b/.circleci/cimodel/data/binary_build_data.py @@ -9,9 +9,10 @@ should be "pruned". from collections import OrderedDict -from cimodel.lib.conf_tree import ConfigNode import cimodel.data.dimensions as dimensions +from cimodel.lib.conf_tree import ConfigNode + LINKING_DIMENSIONS = [ "shared", @@ -26,12 +27,18 @@ DEPS_INCLUSION_DIMENSIONS = [ def get_processor_arch_name(gpu_version): - return "cpu" if not gpu_version else ( - "cu" + gpu_version.strip("cuda") if gpu_version.startswith("cuda") else gpu_version + return ( + "cpu" + if not gpu_version + else ( + "cu" + gpu_version.strip("cuda") + if gpu_version.startswith("cuda") + else gpu_version + ) ) -CONFIG_TREE_DATA = OrderedDict( -) + +CONFIG_TREE_DATA = OrderedDict() # GCC config variants: # @@ -41,8 +48,8 @@ CONFIG_TREE_DATA = OrderedDict( # # Libtorch with new gcc ABI is built with gcc 5.4 on Ubuntu 16.04. LINUX_GCC_CONFIG_VARIANTS = OrderedDict( - manywheel=['devtoolset7'], - conda=['devtoolset7'], + manywheel=["devtoolset7"], + conda=["devtoolset7"], libtorch=[ "devtoolset7", "gcc5.4_cxx11-abi", @@ -63,7 +70,9 @@ class TopLevelNode(ConfigNode): self.props["smoke"] = smoke def get_children(self): - return [OSConfigNode(self, x, c, p) for (x, (c, p)) in self.config_tree_data.items()] + return [ + OSConfigNode(self, x, c, p) for (x, (c, p)) in self.config_tree_data.items() + ] class OSConfigNode(ConfigNode): @@ -85,12 +94,20 @@ class PackageFormatConfigNode(ConfigNode): self.props["python_versions"] = python_versions self.props["package_format"] = package_format - def get_children(self): if self.find_prop("os_name") == "linux": - return [LinuxGccConfigNode(self, v) for v in LINUX_GCC_CONFIG_VARIANTS[self.find_prop("package_format")]] - elif self.find_prop("os_name") == "windows" and self.find_prop("package_format") == "libtorch": - return [WindowsLibtorchConfigNode(self, v) for v in WINDOWS_LIBTORCH_CONFIG_VARIANTS] + return [ + LinuxGccConfigNode(self, v) + for v in LINUX_GCC_CONFIG_VARIANTS[self.find_prop("package_format")] + ] + elif ( + self.find_prop("os_name") == "windows" + and self.find_prop("package_format") == "libtorch" + ): + return [ + WindowsLibtorchConfigNode(self, v) + for v in WINDOWS_LIBTORCH_CONFIG_VARIANTS + ] else: return [ArchConfigNode(self, v) for v in self.find_prop("gpu_versions")] @@ -106,23 +123,29 @@ class LinuxGccConfigNode(ConfigNode): # XXX devtoolset7 on CUDA 9.0 is temporarily disabled # see https://github.com/pytorch/pytorch/issues/20066 - if self.find_prop("gcc_config_variant") == 'devtoolset7': + if self.find_prop("gcc_config_variant") == "devtoolset7": gpu_versions = filter(lambda x: x != "cuda_90", gpu_versions) # XXX disabling conda rocm build since docker images are not there - if self.find_prop("package_format") == 'conda': - gpu_versions = filter(lambda x: x not in dimensions.ROCM_VERSION_LABELS, gpu_versions) + if self.find_prop("package_format") == "conda": + gpu_versions = filter( + lambda x: x not in dimensions.ROCM_VERSION_LABELS, gpu_versions + ) # XXX libtorch rocm build is temporarily disabled - if self.find_prop("package_format") == 'libtorch': - gpu_versions = filter(lambda x: x not in dimensions.ROCM_VERSION_LABELS, gpu_versions) + if self.find_prop("package_format") == "libtorch": + gpu_versions = filter( + lambda x: x not in dimensions.ROCM_VERSION_LABELS, gpu_versions + ) return [ArchConfigNode(self, v) for v in gpu_versions] class WindowsLibtorchConfigNode(ConfigNode): def __init__(self, parent, libtorch_config_variant): - super().__init__(parent, "LIBTORCH_CONFIG_VARIANT=" + str(libtorch_config_variant)) + super().__init__( + parent, "LIBTORCH_CONFIG_VARIANT=" + str(libtorch_config_variant) + ) self.props["libtorch_config_variant"] = libtorch_config_variant @@ -161,11 +184,15 @@ class LinkingVariantConfigNode(ConfigNode): super().__init__(parent, linking_variant) def get_children(self): - return [DependencyInclusionConfigNode(self, v) for v in DEPS_INCLUSION_DIMENSIONS] + return [ + DependencyInclusionConfigNode(self, v) for v in DEPS_INCLUSION_DIMENSIONS + ] class DependencyInclusionConfigNode(ConfigNode): def __init__(self, parent, deps_variant): super().__init__(parent, deps_variant) - self.props["libtorch_variant"] = "-".join([self.parent.get_label(), self.get_label()]) + self.props["libtorch_variant"] = "-".join( + [self.parent.get_label(), self.get_label()] + ) diff --git a/.circleci/cimodel/data/binary_build_definitions.py b/.circleci/cimodel/data/binary_build_definitions.py index 7dccbdc7cbf..7c1ee3694df 100644 --- a/.circleci/cimodel/data/binary_build_definitions.py +++ b/.circleci/cimodel/data/binary_build_definitions.py @@ -1,13 +1,24 @@ from collections import OrderedDict -import cimodel.data.simple.util.branch_filters as branch_filters import cimodel.data.binary_build_data as binary_build_data + +import cimodel.data.simple.util.branch_filters as branch_filters import cimodel.lib.conf_tree as conf_tree import cimodel.lib.miniutils as miniutils -class Conf: - def __init__(self, os, gpu_version, pydistro, parms, smoke, libtorch_variant, gcc_config_variant, libtorch_config_variant): +class Conf: + def __init__( + self, + os, + gpu_version, + pydistro, + parms, + smoke, + libtorch_variant, + gcc_config_variant, + libtorch_config_variant, + ): self.os = os self.gpu_version = gpu_version self.pydistro = pydistro @@ -18,7 +29,11 @@ class Conf: self.libtorch_config_variant = libtorch_config_variant def gen_build_env_parms(self): - elems = [self.pydistro] + self.parms + [binary_build_data.get_processor_arch_name(self.gpu_version)] + elems = ( + [self.pydistro] + + self.parms + + [binary_build_data.get_processor_arch_name(self.gpu_version)] + ) if self.gcc_config_variant is not None: elems.append(str(self.gcc_config_variant)) if self.libtorch_config_variant is not None: @@ -26,7 +41,7 @@ class Conf: return elems def gen_docker_image(self): - if self.gcc_config_variant == 'gcc5.4_cxx11-abi': + if self.gcc_config_variant == "gcc5.4_cxx11-abi": if self.gpu_version is None: return miniutils.quote("pytorch/libtorch-cxx11-builder:cpu") else: @@ -37,30 +52,41 @@ class Conf: if self.gpu_version is None: return miniutils.quote("pytorch/conda-builder:cpu") else: - return miniutils.quote( - f"pytorch/conda-builder:{self.gpu_version}" - ) + return miniutils.quote(f"pytorch/conda-builder:{self.gpu_version}") docker_word_substitution = { "manywheel": "manylinux", "libtorch": "manylinux", } - docker_distro_prefix = miniutils.override(self.pydistro, docker_word_substitution) + docker_distro_prefix = miniutils.override( + self.pydistro, docker_word_substitution + ) # The cpu nightlies are built on the pytorch/manylinux-cuda102 docker image # TODO cuda images should consolidate into tag-base images similar to rocm - alt_docker_suffix = "cuda102" if not self.gpu_version else ( - "rocm:" + self.gpu_version.strip("rocm") if self.gpu_version.startswith("rocm") else self.gpu_version) - docker_distro_suffix = alt_docker_suffix if self.pydistro != "conda" else ( - "cuda" if alt_docker_suffix.startswith("cuda") else "rocm") - return miniutils.quote("pytorch/" + docker_distro_prefix + "-" + docker_distro_suffix) + alt_docker_suffix = ( + "cuda102" + if not self.gpu_version + else ( + "rocm:" + self.gpu_version.strip("rocm") + if self.gpu_version.startswith("rocm") + else self.gpu_version + ) + ) + docker_distro_suffix = ( + alt_docker_suffix + if self.pydistro != "conda" + else ("cuda" if alt_docker_suffix.startswith("cuda") else "rocm") + ) + return miniutils.quote( + "pytorch/" + docker_distro_prefix + "-" + docker_distro_suffix + ) def get_name_prefix(self): return "smoke" if self.smoke else "binary" def gen_build_name(self, build_or_test, nightly): - parts = [self.get_name_prefix(), self.os] + self.gen_build_env_parms() if nightly: @@ -78,7 +104,9 @@ class Conf: def gen_workflow_job(self, phase, upload_phase_dependency=None, nightly=False): job_def = OrderedDict() job_def["name"] = self.gen_build_name(phase, nightly) - job_def["build_environment"] = miniutils.quote(" ".join(self.gen_build_env_parms())) + job_def["build_environment"] = miniutils.quote( + " ".join(self.gen_build_env_parms()) + ) if self.smoke: job_def["requires"] = [ "update_s3_htmls", @@ -116,47 +144,48 @@ class Conf: os_name = miniutils.override(self.os, {"macos": "mac"}) job_name = "_".join([self.get_name_prefix(), os_name, phase]) - return {job_name : job_def} + return {job_name: job_def} def gen_upload_job(self, phase, requires_dependency): """Generate binary_upload job for configuration - Output looks similar to: + Output looks similar to: - - binary_upload: - name: binary_linux_manywheel_3_7m_cu113_devtoolset7_nightly_upload - context: org-member - requires: binary_linux_manywheel_3_7m_cu113_devtoolset7_nightly_test - filters: - branches: - only: - - nightly - tags: - only: /v[0-9]+(\\.[0-9]+)*-rc[0-9]+/ - package_type: manywheel - upload_subfolder: cu113 + - binary_upload: + name: binary_linux_manywheel_3_7m_cu113_devtoolset7_nightly_upload + context: org-member + requires: binary_linux_manywheel_3_7m_cu113_devtoolset7_nightly_test + filters: + branches: + only: + - nightly + tags: + only: /v[0-9]+(\\.[0-9]+)*-rc[0-9]+/ + package_type: manywheel + upload_subfolder: cu113 """ return { - "binary_upload": OrderedDict({ - "name": self.gen_build_name(phase, nightly=True), - "context": "org-member", - "requires": [self.gen_build_name( - requires_dependency, - nightly=True - )], - "filters": branch_filters.gen_filter_dict( - branches_list=["nightly"], - tags_list=[branch_filters.RC_PATTERN], - ), - "package_type": self.pydistro, - "upload_subfolder": binary_build_data.get_processor_arch_name( - self.gpu_version, - ), - }) + "binary_upload": OrderedDict( + { + "name": self.gen_build_name(phase, nightly=True), + "context": "org-member", + "requires": [ + self.gen_build_name(requires_dependency, nightly=True) + ], + "filters": branch_filters.gen_filter_dict( + branches_list=["nightly"], + tags_list=[branch_filters.RC_PATTERN], + ), + "package_type": self.pydistro, + "upload_subfolder": binary_build_data.get_processor_arch_name( + self.gpu_version, + ), + } + ) } -def get_root(smoke, name): +def get_root(smoke, name): return binary_build_data.TopLevelNode( name, binary_build_data.CONFIG_TREE_DATA, @@ -165,7 +194,6 @@ def get_root(smoke, name): def gen_build_env_list(smoke): - root = get_root(smoke, "N/A") config_list = conf_tree.dfs(root) @@ -176,7 +204,8 @@ def gen_build_env_list(smoke): c.find_prop("gpu"), c.find_prop("package_format"), [c.find_prop("pyver")], - c.find_prop("smoke") and not (c.find_prop("os_name") == "macos_arm64"), # don't test arm64 + c.find_prop("smoke") + and not (c.find_prop("os_name") == "macos_arm64"), # don't test arm64 c.find_prop("libtorch_variant"), c.find_prop("gcc_config_variant"), c.find_prop("libtorch_config_variant"), @@ -185,9 +214,11 @@ def gen_build_env_list(smoke): return newlist + def predicate_exclude_macos(config): return config.os == "linux" or config.os == "windows" + def get_nightly_uploads(): configs = gen_build_env_list(False) mylist = [] @@ -197,6 +228,7 @@ def get_nightly_uploads(): return mylist + def get_post_upload_jobs(): return [ { @@ -210,8 +242,8 @@ def get_post_upload_jobs(): }, ] -def get_nightly_tests(): +def get_nightly_tests(): configs = gen_build_env_list(False) filtered_configs = filter(predicate_exclude_macos, configs) diff --git a/.circleci/cimodel/data/dimensions.py b/.circleci/cimodel/data/dimensions.py index 5841b3806b1..403e72981ae 100644 --- a/.circleci/cimodel/data/dimensions.py +++ b/.circleci/cimodel/data/dimensions.py @@ -16,9 +16,4 @@ ROCM_VERSION_LABELS = ["rocm" + v for v in ROCM_VERSIONS] GPU_VERSIONS = [None] + ["cuda" + v for v in CUDA_VERSIONS] + ROCM_VERSION_LABELS -STANDARD_PYTHON_VERSIONS = [ - "3.7", - "3.8", - "3.9", - "3.10" -] +STANDARD_PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] diff --git a/.circleci/cimodel/data/pytorch_build_data.py b/.circleci/cimodel/data/pytorch_build_data.py index ebd6e0a3818..1e350ef134b 100644 --- a/.circleci/cimodel/data/pytorch_build_data.py +++ b/.circleci/cimodel/data/pytorch_build_data.py @@ -1,8 +1,7 @@ from cimodel.lib.conf_tree import ConfigNode -CONFIG_TREE_DATA = [ -] +CONFIG_TREE_DATA = [] def get_major_pyver(dotted_version): @@ -96,6 +95,7 @@ class SlowGradcheckConfigNode(TreeConfigNode): def child_constructor(self): return ExperimentalFeatureConfigNode + class PureTorchConfigNode(TreeConfigNode): def modify_label(self, label): return "PURE_TORCH=" + str(label) @@ -117,6 +117,7 @@ class XlaConfigNode(TreeConfigNode): def child_constructor(self): return ImportantConfigNode + class MPSConfigNode(TreeConfigNode): def modify_label(self, label): return "MPS=" + str(label) @@ -254,8 +255,11 @@ class XenialCompilerConfigNode(TreeConfigNode): # noinspection PyMethodMayBeStatic def child_constructor(self): - - return XenialCompilerVersionConfigNode if self.props["compiler_name"] else PyVerConfigNode + return ( + XenialCompilerVersionConfigNode + if self.props["compiler_name"] + else PyVerConfigNode + ) class BionicCompilerConfigNode(TreeConfigNode): @@ -267,8 +271,11 @@ class BionicCompilerConfigNode(TreeConfigNode): # noinspection PyMethodMayBeStatic def child_constructor(self): - - return BionicCompilerVersionConfigNode if self.props["compiler_name"] else PyVerConfigNode + return ( + BionicCompilerVersionConfigNode + if self.props["compiler_name"] + else PyVerConfigNode + ) class XenialCompilerVersionConfigNode(TreeConfigNode): diff --git a/.circleci/cimodel/data/pytorch_build_definitions.py b/.circleci/cimodel/data/pytorch_build_definitions.py index e6e44bd2b5a..b23334bb737 100644 --- a/.circleci/cimodel/data/pytorch_build_definitions.py +++ b/.circleci/cimodel/data/pytorch_build_definitions.py @@ -111,10 +111,10 @@ class Conf: parameters["resource_class"] = resource_class if phase == "build" and self.rocm_version is not None: parameters["resource_class"] = "xlarge" - if hasattr(self, 'filters'): - parameters['filters'] = self.filters + if hasattr(self, "filters"): + parameters["filters"] = self.filters if self.build_only: - parameters['build_only'] = miniutils.quote(str(int(True))) + parameters["build_only"] = miniutils.quote(str(int(True))) return parameters def gen_workflow_job(self, phase): @@ -122,7 +122,6 @@ class Conf: job_def["name"] = self.gen_build_name(phase) if Conf.is_test_phase(phase): - # TODO When merging the caffe2 and pytorch jobs, it might be convenient for a while to make a # caffe2 test job dependent on a pytorch build job. This way we could quickly dedup the repeated # build of pytorch in the caffe2 build job, and just run the caffe2 tests off of a completed @@ -160,6 +159,7 @@ class HiddenConf: def gen_build_name(self, _): return self.name + class DocPushConf: def __init__(self, name, parent_build=None, branch="master"): self.name = name @@ -173,11 +173,13 @@ class DocPushConf: "branch": self.branch, "requires": [self.parent_build], "context": "org-member", - "filters": gen_filter_dict(branches_list=["nightly"], - tags_list=RC_PATTERN) + "filters": gen_filter_dict( + branches_list=["nightly"], tags_list=RC_PATTERN + ), } } + def gen_docs_configs(xenial_parent_config): configs = [] @@ -185,8 +187,9 @@ def gen_docs_configs(xenial_parent_config): HiddenConf( "pytorch_python_doc_build", parent_build=xenial_parent_config, - filters=gen_filter_dict(branches_list=["master", "main", "nightly"], - tags_list=RC_PATTERN), + filters=gen_filter_dict( + branches_list=["master", "main", "nightly"], tags_list=RC_PATTERN + ), ) ) configs.append( @@ -201,8 +204,9 @@ def gen_docs_configs(xenial_parent_config): HiddenConf( "pytorch_cpp_doc_build", parent_build=xenial_parent_config, - filters=gen_filter_dict(branches_list=["master", "main", "nightly"], - tags_list=RC_PATTERN), + filters=gen_filter_dict( + branches_list=["master", "main", "nightly"], tags_list=RC_PATTERN + ), ) ) configs.append( @@ -226,13 +230,11 @@ def gen_tree(): def instantiate_configs(only_slow_gradcheck): - config_list = [] root = get_root() found_configs = conf_tree.dfs(root) for fc in found_configs: - restrict_phases = None distro_name = fc.find_prop("distro_name") compiler_name = fc.find_prop("compiler_name") @@ -351,8 +353,7 @@ def instantiate_configs(only_slow_gradcheck): and compiler_name == "gcc" and fc.find_prop("compiler_version") == "5.4" ): - c.filters = gen_filter_dict(branches_list=r"/.*/", - tags_list=RC_PATTERN) + c.filters = gen_filter_dict(branches_list=r"/.*/", tags_list=RC_PATTERN) c.dependent_tests = gen_docs_configs(c) config_list.append(c) @@ -361,16 +362,13 @@ def instantiate_configs(only_slow_gradcheck): def get_workflow_jobs(only_slow_gradcheck=False): - config_list = instantiate_configs(only_slow_gradcheck) x = [] for conf_options in config_list: - phases = conf_options.restrict_phases or dimensions.PHASES for phase in phases: - # TODO why does this not have a test? if Conf.is_test_phase(phase) and conf_options.cuda_version == "10": continue diff --git a/.circleci/cimodel/data/simple/docker_definitions.py b/.circleci/cimodel/data/simple/docker_definitions.py index 7299b47e786..bfb1d184e36 100644 --- a/.circleci/cimodel/data/simple/docker_definitions.py +++ b/.circleci/cimodel/data/simple/docker_definitions.py @@ -1,39 +1,39 @@ from collections import OrderedDict -from cimodel.lib.miniutils import quote from cimodel.data.simple.util.branch_filters import gen_filter_dict, RC_PATTERN +from cimodel.lib.miniutils import quote + # NOTE: All hardcoded docker image builds have been migrated to GHA -IMAGE_NAMES = [ -] +IMAGE_NAMES = [] # This entry should be an element from the list above # This should contain the image matching the "slow_gradcheck" entry in # pytorch_build_data.py SLOW_GRADCHECK_IMAGE_NAME = "pytorch-linux-xenial-cuda10.2-cudnn7-py3-gcc7" + def get_workflow_jobs(images=IMAGE_NAMES, only_slow_gradcheck=False): """Generates a list of docker image build definitions""" ret = [] for image_name in images: - if image_name.startswith('docker-'): - image_name = image_name.lstrip('docker-') + if image_name.startswith("docker-"): + image_name = image_name.lstrip("docker-") if only_slow_gradcheck and image_name is not SLOW_GRADCHECK_IMAGE_NAME: continue - parameters = OrderedDict({ - "name": quote(f"docker-{image_name}"), - "image_name": quote(image_name), - }) + parameters = OrderedDict( + { + "name": quote(f"docker-{image_name}"), + "image_name": quote(image_name), + } + ) if image_name == "pytorch-linux-xenial-py3.7-gcc5.4": # pushing documentation on tags requires CircleCI to also # build all the dependencies on tags, including this docker image - parameters['filters'] = gen_filter_dict(branches_list=r"/.*/", - tags_list=RC_PATTERN) - ret.append(OrderedDict( - { - "docker_build_job": parameters - } - )) + parameters["filters"] = gen_filter_dict( + branches_list=r"/.*/", tags_list=RC_PATTERN + ) + ret.append(OrderedDict({"docker_build_job": parameters})) return ret diff --git a/.circleci/cimodel/data/simple/ios_definitions.py b/.circleci/cimodel/data/simple/ios_definitions.py index 42aac5d9012..c8215be61b4 100644 --- a/.circleci/cimodel/data/simple/ios_definitions.py +++ b/.circleci/cimodel/data/simple/ios_definitions.py @@ -1,6 +1,6 @@ -from cimodel.data.simple.util.versions import MultiPartVersion -from cimodel.data.simple.util.branch_filters import gen_filter_dict_exclude import cimodel.lib.miniutils as miniutils +from cimodel.data.simple.util.branch_filters import gen_filter_dict_exclude +from cimodel.data.simple.util.versions import MultiPartVersion XCODE_VERSION = MultiPartVersion([12, 5, 1]) @@ -11,7 +11,9 @@ class ArchVariant: self.custom_build_name = custom_build_name def render(self): - extra_parts = [self.custom_build_name] if len(self.custom_build_name) > 0 else [] + extra_parts = ( + [self.custom_build_name] if len(self.custom_build_name) > 0 else [] + ) return "-".join([self.name] + extra_parts).replace("_", "-") @@ -20,7 +22,9 @@ def get_platform(arch_variant_name): class IOSJob: - def __init__(self, xcode_version, arch_variant, is_org_member_context=True, extra_props=None): + def __init__( + self, xcode_version, arch_variant, is_org_member_context=True, extra_props=None + ): self.xcode_version = xcode_version self.arch_variant = arch_variant self.is_org_member_context = is_org_member_context @@ -29,11 +33,15 @@ class IOSJob: def gen_name_parts(self): version_parts = self.xcode_version.render_dots_or_parts("-") build_variant_suffix = self.arch_variant.render() - return [ - "ios", - ] + version_parts + [ - build_variant_suffix, - ] + return ( + [ + "ios", + ] + + version_parts + + [ + build_variant_suffix, + ] + ) def gen_job_name(self): return "-".join(self.gen_name_parts()) @@ -59,8 +67,12 @@ class IOSJob: WORKFLOW_DATA = [ - IOSJob(XCODE_VERSION, ArchVariant("x86_64"), is_org_member_context=False, extra_props={ - "lite_interpreter": miniutils.quote(str(int(True)))}), + IOSJob( + XCODE_VERSION, + ArchVariant("x86_64"), + is_org_member_context=False, + extra_props={"lite_interpreter": miniutils.quote(str(int(True)))}, + ), # IOSJob(XCODE_VERSION, ArchVariant("arm64"), extra_props={ # "lite_interpreter": miniutils.quote(str(int(True)))}), # IOSJob(XCODE_VERSION, ArchVariant("arm64", "metal"), extra_props={ @@ -69,9 +81,15 @@ WORKFLOW_DATA = [ # IOSJob(XCODE_VERSION, ArchVariant("arm64", "custom-ops"), extra_props={ # "op_list": "mobilenetv2.yaml", # "lite_interpreter": miniutils.quote(str(int(True)))}), - IOSJob(XCODE_VERSION, ArchVariant("x86_64", "coreml"), is_org_member_context=False, extra_props={ - "use_coreml": miniutils.quote(str(int(True))), - "lite_interpreter": miniutils.quote(str(int(True)))}), + IOSJob( + XCODE_VERSION, + ArchVariant("x86_64", "coreml"), + is_org_member_context=False, + extra_props={ + "use_coreml": miniutils.quote(str(int(True))), + "lite_interpreter": miniutils.quote(str(int(True))), + }, + ), # IOSJob(XCODE_VERSION, ArchVariant("arm64", "coreml"), extra_props={ # "use_coreml": miniutils.quote(str(int(True))), # "lite_interpreter": miniutils.quote(str(int(True)))}), diff --git a/.circleci/cimodel/data/simple/mobile_definitions.py b/.circleci/cimodel/data/simple/mobile_definitions.py index 8c5924af386..d9cee4895d3 100644 --- a/.circleci/cimodel/data/simple/mobile_definitions.py +++ b/.circleci/cimodel/data/simple/mobile_definitions.py @@ -2,17 +2,14 @@ PyTorch Mobile PR builds (use linux host toolchain + mobile build options) """ -import cimodel.lib.miniutils as miniutils import cimodel.data.simple.util.branch_filters +import cimodel.lib.miniutils as miniutils class MobileJob: def __init__( - self, - docker_image, - docker_requires, - variant_parts, - is_master_only=False): + self, docker_image, docker_requires, variant_parts, is_master_only=False + ): self.docker_image = docker_image self.docker_requires = docker_requires self.variant_parts = variant_parts @@ -40,13 +37,14 @@ class MobileJob: } if self.is_master_only: - props_dict["filters"] = cimodel.data.simple.util.branch_filters.gen_filter_dict() + props_dict[ + "filters" + ] = cimodel.data.simple.util.branch_filters.gen_filter_dict() return [{"pytorch_linux_build": props_dict}] -WORKFLOW_DATA = [ -] +WORKFLOW_DATA = [] def get_workflow_jobs(): diff --git a/.circleci/cimodel/data/simple/nightly_ios.py b/.circleci/cimodel/data/simple/nightly_ios.py index f75bcb4bfe2..4f00f617332 100644 --- a/.circleci/cimodel/data/simple/nightly_ios.py +++ b/.circleci/cimodel/data/simple/nightly_ios.py @@ -3,11 +3,7 @@ import cimodel.lib.miniutils as miniutils class IOSNightlyJob: - def __init__(self, - variant, - is_full_jit=False, - is_upload=False): - + def __init__(self, variant, is_full_jit=False, is_upload=False): self.variant = variant self.is_full_jit = is_full_jit self.is_upload = is_upload @@ -16,19 +12,24 @@ class IOSNightlyJob: return "upload" if self.is_upload else "build" def get_common_name_pieces(self, sep): - extra_name_suffix = [self.get_phase_name()] if self.is_upload else [] extra_name = ["full_jit"] if self.is_full_jit else [] - common_name_pieces = [ - "ios", - ] + extra_name + [ - ] + ios_definitions.XCODE_VERSION.render_dots_or_parts(sep) + [ - "nightly", - self.variant, - "build", - ] + extra_name_suffix + common_name_pieces = ( + [ + "ios", + ] + + extra_name + + [] + + ios_definitions.XCODE_VERSION.render_dots_or_parts(sep) + + [ + "nightly", + self.variant, + "build", + ] + + extra_name_suffix + ) return common_name_pieces @@ -37,10 +38,14 @@ class IOSNightlyJob: def gen_tree(self): build_configs = BUILD_CONFIGS_FULL_JIT if self.is_full_jit else BUILD_CONFIGS - extra_requires = [x.gen_job_name() for x in build_configs] if self.is_upload else [] + extra_requires = ( + [x.gen_job_name() for x in build_configs] if self.is_upload else [] + ) props_dict = { - "build_environment": "-".join(["libtorch"] + self.get_common_name_pieces(".")), + "build_environment": "-".join( + ["libtorch"] + self.get_common_name_pieces(".") + ), "requires": extra_requires, "context": "org-member", "filters": {"branches": {"only": "nightly"}}, @@ -56,11 +61,13 @@ class IOSNightlyJob: if self.is_full_jit: props_dict["lite_interpreter"] = miniutils.quote(str(int(False))) - template_name = "_".join([ - "binary", - "ios", - self.get_phase_name(), - ]) + template_name = "_".join( + [ + "binary", + "ios", + self.get_phase_name(), + ] + ) return [{template_name: props_dict}] @@ -75,10 +82,14 @@ BUILD_CONFIGS_FULL_JIT = [ IOSNightlyJob("arm64", is_full_jit=True), ] -WORKFLOW_DATA = BUILD_CONFIGS + BUILD_CONFIGS_FULL_JIT + [ - IOSNightlyJob("binary", is_full_jit=False, is_upload=True), - IOSNightlyJob("binary", is_full_jit=True, is_upload=True), -] +WORKFLOW_DATA = ( + BUILD_CONFIGS + + BUILD_CONFIGS_FULL_JIT + + [ + IOSNightlyJob("binary", is_full_jit=False, is_upload=True), + IOSNightlyJob("binary", is_full_jit=True, is_upload=True), + ] +) def get_workflow_jobs(): diff --git a/.circleci/cimodel/data/simple/util/branch_filters.py b/.circleci/cimodel/data/simple/util/branch_filters.py index e87d0045636..2a4d752894e 100644 --- a/.circleci/cimodel/data/simple/util/branch_filters.py +++ b/.circleci/cimodel/data/simple/util/branch_filters.py @@ -15,10 +15,7 @@ RC_PATTERN = r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/" MAC_IOS_EXCLUSION_LIST = ["nightly", "postnightly"] -def gen_filter_dict( - branches_list=NON_PR_BRANCH_LIST, - tags_list=None -): +def gen_filter_dict(branches_list=NON_PR_BRANCH_LIST, tags_list=None): """Generates a filter dictionary for use with CircleCI's job filter""" filter_dict = { "branches": { diff --git a/.circleci/cimodel/data/simple/util/docker_constants.py b/.circleci/cimodel/data/simple/util/docker_constants.py index 985466718ea..9a96b9f9f2a 100644 --- a/.circleci/cimodel/data/simple/util/docker_constants.py +++ b/.circleci/cimodel/data/simple/util/docker_constants.py @@ -1,11 +1,13 @@ AWS_DOCKER_HOST = "308535385114.dkr.ecr.us-east-1.amazonaws.com" + def gen_docker_image(container_type): return ( "/".join([AWS_DOCKER_HOST, "pytorch", container_type]), f"docker-{container_type}", ) + def gen_docker_image_requires(image_name): return [f"docker-{image_name}"] diff --git a/.circleci/cimodel/data/simple/util/versions.py b/.circleci/cimodel/data/simple/util/versions.py index 518feb2e386..bc3a9e3d684 100644 --- a/.circleci/cimodel/data/simple/util/versions.py +++ b/.circleci/cimodel/data/simple/util/versions.py @@ -12,7 +12,9 @@ class MultiPartVersion: with the prefix string. """ if self.parts: - return [self.prefix + str(self.parts[0])] + [str(part) for part in self.parts[1:]] + return [self.prefix + str(self.parts[0])] + [ + str(part) for part in self.parts[1:] + ] else: return [self.prefix] diff --git a/.circleci/cimodel/lib/conf_tree.py b/.circleci/cimodel/lib/conf_tree.py index db2a30f29c2..495b134d99f 100644 --- a/.circleci/cimodel/lib/conf_tree.py +++ b/.circleci/cimodel/lib/conf_tree.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Optional, Dict +from typing import Dict, Optional def X(val): @@ -19,6 +19,7 @@ class Ver: """ Represents a product with a version number """ + name: str version: str = "" @@ -28,7 +29,7 @@ class Ver: @dataclass class ConfigNode: - parent: Optional['ConfigNode'] + parent: Optional["ConfigNode"] node_name: str props: Dict[str, str] = field(default_factory=dict) @@ -40,7 +41,11 @@ class ConfigNode: return [] def get_parents(self): - return (self.parent.get_parents() + [self.parent.get_label()]) if self.parent else [] + return ( + (self.parent.get_parents() + [self.parent.get_label()]) + if self.parent + else [] + ) def get_depth(self): return len(self.get_parents()) @@ -69,13 +74,13 @@ class ConfigNode: def dfs_recurse( - node, - leaf_callback=lambda x: None, - discovery_callback=lambda x, y, z: None, - child_callback=lambda x, y: None, - sibling_index=0, - sibling_count=1): - + node, + leaf_callback=lambda x: None, + discovery_callback=lambda x, y, z: None, + child_callback=lambda x, y: None, + sibling_index=0, + sibling_count=1, +): discovery_callback(node, sibling_index, sibling_count) node_children = node.get_children() @@ -96,7 +101,6 @@ def dfs_recurse( def dfs(toplevel_config_node): - config_list = [] def leaf_callback(node): diff --git a/.circleci/cimodel/lib/miniyaml.py b/.circleci/cimodel/lib/miniyaml.py index 40ffbdbb92e..70199e92309 100644 --- a/.circleci/cimodel/lib/miniyaml.py +++ b/.circleci/cimodel/lib/miniyaml.py @@ -25,7 +25,6 @@ def render(fh, data, depth, is_list_member=False): indentation = " " * INDENTATION_WIDTH * depth if is_dict(data): - tuples = list(data.items()) if type(data) is not OrderedDict: tuples.sort() diff --git a/.circleci/codegen_validation/normalize_yaml_fragment.py b/.circleci/codegen_validation/normalize_yaml_fragment.py index 0778404e8bb..32899cc729c 100755 --- a/.circleci/codegen_validation/normalize_yaml_fragment.py +++ b/.circleci/codegen_validation/normalize_yaml_fragment.py @@ -2,10 +2,11 @@ import os import sys + import yaml # Need to import modules that lie on an upward-relative path -sys.path.append(os.path.join(sys.path[0], '..')) +sys.path.append(os.path.join(sys.path[0], "..")) import cimodel.lib.miniyaml as miniyaml diff --git a/.circleci/ensure-consistency.py b/.circleci/ensure-consistency.py index 972ededb69f..44f5299cbaf 100755 --- a/.circleci/ensure-consistency.py +++ b/.circleci/ensure-consistency.py @@ -21,7 +21,6 @@ Please re-run the "%s" script in the "%s" directory and commit the result. See " def check_consistency(): - _, temp_filename = tempfile.mkstemp("-generated-config.yml") with open(temp_filename, "w") as fh: @@ -30,7 +29,10 @@ def check_consistency(): try: subprocess.check_call(["cmp", temp_filename, CHECKED_IN_FILE]) except subprocess.CalledProcessError: - sys.exit(ERROR_MESSAGE_TEMPLATE % (CHECKED_IN_FILE, REGENERATION_SCRIPT, PARENT_DIR, README_PATH)) + sys.exit( + ERROR_MESSAGE_TEMPLATE + % (CHECKED_IN_FILE, REGENERATION_SCRIPT, PARENT_DIR, README_PATH) + ) finally: os.remove(temp_filename) diff --git a/.circleci/generate_config_yml.py b/.circleci/generate_config_yml.py index d1ef439941d..2d0c93eacd4 100755 --- a/.circleci/generate_config_yml.py +++ b/.circleci/generate_config_yml.py @@ -10,10 +10,11 @@ import shutil import sys from collections import namedtuple +import cimodel.data.simple.anaconda_prune_defintions + import cimodel.data.simple.docker_definitions import cimodel.data.simple.mobile_definitions import cimodel.data.simple.nightly_ios -import cimodel.data.simple.anaconda_prune_defintions import cimodel.lib.miniutils as miniutils import cimodel.lib.miniyaml as miniyaml @@ -82,15 +83,19 @@ def _for_all_items(items, functor) -> None: def filter_master_only_jobs(items): def _is_main_or_master_item(item): - filters = item.get('filters', None) - branches = filters.get('branches', None) if filters is not None else None - branches_only = branches.get('only', None) if branches is not None else None - return ('main' in branches_only or 'master' in branches_only) if branches_only is not None else False + filters = item.get("filters", None) + branches = filters.get("branches", None) if filters is not None else None + branches_only = branches.get("only", None) if branches is not None else None + return ( + ("main" in branches_only or "master" in branches_only) + if branches_only is not None + else False + ) master_deps = set() def _save_requires_if_master(item_type, item): - requires = item.get('requires', None) + requires = item.get("requires", None) item_name = item.get("name", None) if not isinstance(requires, list): return @@ -107,9 +112,9 @@ def filter_master_only_jobs(items): item_name = item_name.strip('"') if item_name is not None else None if not _is_main_or_master_item(item) and item_name not in master_deps: return None - if 'filters' in item: + if "filters" in item: item = item.copy() - item.pop('filters') + item.pop("filters") return {item_type: item} # Scan of dependencies twice to pick up nested required jobs @@ -123,12 +128,12 @@ def generate_required_docker_images(items): required_docker_images = set() def _requires_docker_image(item_type, item): - requires = item.get('requires', None) + requires = item.get("requires", None) if not isinstance(requires, list): return for requirement in requires: - requirement = requirement.replace('"', '') - if requirement.startswith('docker-'): + requirement = requirement.replace('"', "") + if requirement.startswith("docker-"): required_docker_images.add(requirement) _for_all_items(items, _requires_docker_image) @@ -191,5 +196,4 @@ def stitch_sources(output_filehandle): if __name__ == "__main__": - stitch_sources(sys.stdout) diff --git a/.circleci/scripts/trigger_azure_pipeline.py b/.circleci/scripts/trigger_azure_pipeline.py index 9dc9dff2d54..fc15b5810e4 100644 --- a/.circleci/scripts/trigger_azure_pipeline.py +++ b/.circleci/scripts/trigger_azure_pipeline.py @@ -1,12 +1,13 @@ # Documentation: https://docs.microsoft.com/en-us/rest/api/azure/devops/build/?view=azure-devops-rest-6.0 -import re import json import os +import re import sys -import requests import time +import requests + AZURE_PIPELINE_BASE_URL = "https://aiinfra.visualstudio.com/PyTorch/" AZURE_DEVOPS_PAT_BASE64 = os.environ.get("AZURE_DEVOPS_PAT_BASE64_SECRET", "") PIPELINE_ID = "911" @@ -19,54 +20,68 @@ build_base_url = AZURE_PIPELINE_BASE_URL + "_apis/build/builds?api-version=6.0" s = requests.Session() s.headers.update({"Authorization": "Basic " + AZURE_DEVOPS_PAT_BASE64}) + def submit_build(pipeline_id, project_id, source_branch, source_version): print("Submitting build for branch: " + source_branch) print("Commit SHA1: ", source_version) - run_build_raw = s.post(build_base_url, json={ - "definition": {"id": pipeline_id}, - "project": {"id": project_id}, - "sourceBranch": source_branch, - "sourceVersion": source_version - }) + run_build_raw = s.post( + build_base_url, + json={ + "definition": {"id": pipeline_id}, + "project": {"id": project_id}, + "sourceBranch": source_branch, + "sourceVersion": source_version, + }, + ) try: run_build_json = run_build_raw.json() except json.decoder.JSONDecodeError as e: print(e) - print("Failed to parse the response. Check if the Azure DevOps PAT is incorrect or expired.") + print( + "Failed to parse the response. Check if the Azure DevOps PAT is incorrect or expired." + ) sys.exit(-1) - build_id = run_build_json['id'] + build_id = run_build_json["id"] print("Submitted bulid: " + str(build_id)) - print("Bulid URL: " + run_build_json['url']) + print("Bulid URL: " + run_build_json["url"]) return build_id + def get_build(_id): - get_build_url = AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}?api-version=6.0" + get_build_url = ( + AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}?api-version=6.0" + ) get_build_raw = s.get(get_build_url) return get_build_raw.json() + def get_build_logs(_id): - get_build_logs_url = AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}/logs?api-version=6.0" + get_build_logs_url = ( + AZURE_PIPELINE_BASE_URL + f"/_apis/build/builds/{_id}/logs?api-version=6.0" + ) get_build_logs_raw = s.get(get_build_logs_url) return get_build_logs_raw.json() + def get_log_content(url): resp = s.get(url) return resp.text + def wait_for_build(_id): build_detail = get_build(_id) - build_status = build_detail['status'] + build_status = build_detail["status"] - while build_status == 'notStarted': - print('Waiting for run to start: ' + str(_id)) + while build_status == "notStarted": + print("Waiting for run to start: " + str(_id)) sys.stdout.flush() try: build_detail = get_build(_id) - build_status = build_detail['status'] + build_status = build_detail["status"] except Exception as e: print("Error getting build") print(e) @@ -76,7 +91,7 @@ def wait_for_build(_id): print("Bulid started: ", str(_id)) handled_logs = set() - while build_status == 'inProgress': + while build_status == "inProgress": try: print("Waiting for log: " + str(_id)) logs = get_build_logs(_id) @@ -86,38 +101,39 @@ def wait_for_build(_id): time.sleep(30) continue - for log in logs['value']: - log_id = log['id'] + for log in logs["value"]: + log_id = log["id"] if log_id in handled_logs: continue handled_logs.add(log_id) - print('Fetching log: \n' + log['url']) + print("Fetching log: \n" + log["url"]) try: - log_content = get_log_content(log['url']) + log_content = get_log_content(log["url"]) print(log_content) except Exception as e: print("Error getting log content") print(e) sys.stdout.flush() build_detail = get_build(_id) - build_status = build_detail['status'] + build_status = build_detail["status"] time.sleep(30) - build_result = build_detail['result'] + build_result = build_detail["result"] print("Bulid status: " + build_status) print("Bulid result: " + build_result) return build_status, build_result -if __name__ == '__main__': + +if __name__ == "__main__": # Convert the branch name for Azure DevOps - match = re.search(r'pull/(\d+)', TARGET_BRANCH) + match = re.search(r"pull/(\d+)", TARGET_BRANCH) if match is not None: pr_num = match.group(1) - SOURCE_BRANCH = f'refs/pull/{pr_num}/head' + SOURCE_BRANCH = f"refs/pull/{pr_num}/head" else: - SOURCE_BRANCH = f'refs/heads/{TARGET_BRANCH}' + SOURCE_BRANCH = f"refs/heads/{TARGET_BRANCH}" MAX_RETRY = 2 retry = MAX_RETRY @@ -126,7 +142,7 @@ if __name__ == '__main__': build_id = submit_build(PIPELINE_ID, PROJECT_ID, SOURCE_BRANCH, TARGET_COMMIT) build_status, build_result = wait_for_build(build_id) - if build_result != 'succeeded': + if build_result != "succeeded": retry = retry - 1 if retry > 0: print("Retrying... remaining attempt: " + str(retry)) diff --git a/.lintrunner.toml b/.lintrunner.toml index b0ccdd31d5c..39f2c20a5b2 100644 --- a/.lintrunner.toml +++ b/.lintrunner.toml @@ -908,58 +908,12 @@ exclude_patterns = [ 'third_party/**/*.pyi', # These files are all grandfathered in, feel free to remove from this list # as necessary - '.ci/pytorch/create_test_cert.py', - '.ci/pytorch/fake_numpy/numpy.py', - '.ci/pytorch/perf_test/compare_with_baseline.py', - '.ci/pytorch/perf_test/get_stats.py', - '.ci/pytorch/perf_test/update_commit_hash.py', - '.ci/pytorch/print_sccache_log.py', - '.ci/pytorch/win-test-helpers/run_python_nn_smoketests.py', - '.circleci/cimodel/__init__.py', - '.circleci/cimodel/data/__init__.py', - '.circleci/cimodel/data/binary_build_data.py', - '.circleci/cimodel/data/binary_build_definitions.py', - '.circleci/cimodel/data/dimensions.py', - '.circleci/cimodel/data/pytorch_build_data.py', - '.circleci/cimodel/data/pytorch_build_definitions.py', - '.circleci/cimodel/data/simple/__init__.py', - '.circleci/cimodel/data/simple/anaconda_prune_defintions.py', - '.circleci/cimodel/data/simple/docker_definitions.py', - '.circleci/cimodel/data/simple/ios_definitions.py', - '.circleci/cimodel/data/simple/macos_definitions.py', - '.circleci/cimodel/data/simple/mobile_definitions.py', - '.circleci/cimodel/data/simple/nightly_ios.py', - '.circleci/cimodel/data/simple/util/__init__.py', - '.circleci/cimodel/data/simple/util/branch_filters.py', - '.circleci/cimodel/data/simple/util/docker_constants.py', - '.circleci/cimodel/data/simple/util/versions.py', - '.circleci/cimodel/lib/__init__.py', - '.circleci/cimodel/lib/conf_tree.py', - '.circleci/cimodel/lib/miniutils.py', - '.circleci/cimodel/lib/miniyaml.py', - '.circleci/codegen_validation/normalize_yaml_fragment.py', - '.circleci/ensure-consistency.py', - '.circleci/generate_config_yml.py', - '.circleci/scripts/trigger_azure_pipeline.py', - 'android/pytorch_android/generate_test_torchscripts.py', - 'android/test_app/make_assets.py', - 'android/test_app/make_assets_custom.py', 'aten/src/ATen/function_wrapper.py', 'aten/src/ATen/native/quantized/cpu/qnnpack/configure.py', 'aten/src/ATen/native/quantized/cpu/qnnpack/deps/clog/configure.py', 'aten/src/ATen/native/quantized/cpu/qnnpack/generate-wrapper.py', 'aten/src/ATen/native/transformers/cuda/mem_eff_attention/kernels/generate_kernels.py', 'aten/src/ATen/nnapi/codegen.py', - 'binaries/bench_gen/bench_gen.py', - 'docs/caffe2/process.py', - 'docs/cpp/source/conf.py', - 'docs/source/conf.py', - 'docs/source/scripts/build_activation_images.py', - 'docs/source/scripts/build_opsets.py', - 'docs/source/scripts/build_quantization_configs.py', - 'docs/source/scripts/exportdb/generate_example_rst.py', - 'docs/source/scripts/onnx/build_onnx_diagnostics_rules_md.py', - 'docs/source/scripts/onnx/build_onnx_supported_aten_op_csv_table.py', 'functorch/__init__.py', 'functorch/_src/__init__.py', 'functorch/_src/aot_autograd/__init__.py', @@ -1010,25 +964,6 @@ exclude_patterns = [ 'functorch/notebooks/_src/plot_jacobians_and_hessians.py', 'functorch/notebooks/_src/plot_per_sample_gradients.py', 'functorch/op_analysis/gen_data.py', - 'ios/TestApp/benchmark/coreml_backend.py', - 'ios/TestApp/benchmark/trace_model.py', - 'ios/TestApp/custom_build/custom_build.py', - 'modules/detectron/upsample_nearest_op_test.py', - 'mypy_plugins/check_mypy_version.py', - 'scripts/analysis/format_test_csv.py', - 'scripts/diagnose_protobuf.py', - 'scripts/get_python_cmake_flags.py', - 'scripts/jit/log_extract.py', - 'scripts/model_zoo/update-caffe2-models.py', - 'scripts/model_zoo/update-models-from-caffe2.py', - 'scripts/release_notes/apply_categories.py', - 'scripts/release_notes/categorize.py', - 'scripts/release_notes/classifier.py', - 'scripts/release_notes/commitlist.py', - 'scripts/release_notes/common.py', - 'scripts/release_notes/namespace_check.py', - 'scripts/release_notes/test_release_notes.py', - 'setup.py', 'test/_nvfuser/__init__.py', 'test/_nvfuser/test_dynamo.py', 'test/_nvfuser/test_python_frontend.py', diff --git a/android/pytorch_android/generate_test_torchscripts.py b/android/pytorch_android/generate_test_torchscripts.py index 897c430c01f..c3c9518517a 100644 --- a/android/pytorch_android/generate_test_torchscripts.py +++ b/android/pytorch_android/generate_test_torchscripts.py @@ -1,18 +1,21 @@ +from typing import Dict, List, Optional, Tuple + import torch from torch import Tensor -from typing import Dict, List, Tuple, Optional OUTPUT_DIR = "src/androidTest/assets/" + def scriptAndSave(module, fileName): - print('-' * 80) + print("-" * 80) script_module = torch.jit.script(module) print(script_module.graph) outputFileName = OUTPUT_DIR + fileName # note that the lite interpreter model can also be used in full JIT script_module._save_for_lite_interpreter(outputFileName) print("Saved to " + outputFileName) - print('=' * 80) + print("=" * 80) + class Test(torch.jit.ScriptModule): @torch.jit.script_method @@ -73,7 +76,9 @@ class Test(torch.jit.ScriptModule): return res @torch.jit.script_method - def tupleIntSumReturnTuple(self, input: Tuple[int, int, int]) -> Tuple[Tuple[int, int, int], int]: + def tupleIntSumReturnTuple( + self, input: Tuple[int, int, int] + ) -> Tuple[Tuple[int, int, int], int]: sum = 0 for x in input: sum += x @@ -114,7 +119,7 @@ class Test(torch.jit.ScriptModule): @torch.jit.script_method def conv2d(self, x: Tensor, w: Tensor, toChannelsLast: bool) -> Tensor: r = torch.nn.functional.conv2d(x, w) - if (toChannelsLast): + if toChannelsLast: r = r.contiguous(memory_format=torch.channels_last) else: r = r.contiguous() @@ -132,4 +137,5 @@ class Test(torch.jit.ScriptModule): def contiguousChannelsLast3d(self, x: Tensor) -> Tensor: return x.contiguous(memory_format=torch.channels_last_3d) + scriptAndSave(Test(), "test.pt") diff --git a/android/test_app/make_assets.py b/android/test_app/make_assets.py index 8f9c8749199..eaeece4f2f7 100644 --- a/android/test_app/make_assets.py +++ b/android/test_app/make_assets.py @@ -5,12 +5,20 @@ print(torch.version.__version__) resnet18 = torchvision.models.resnet18(pretrained=True) resnet18.eval() -resnet18_traced = torch.jit.trace(resnet18, torch.rand(1, 3, 224, 224)).save("app/src/main/assets/resnet18.pt") +resnet18_traced = torch.jit.trace(resnet18, torch.rand(1, 3, 224, 224)).save( + "app/src/main/assets/resnet18.pt" +) resnet50 = torchvision.models.resnet50(pretrained=True) resnet50.eval() -torch.jit.trace(resnet50, torch.rand(1, 3, 224, 224)).save("app/src/main/assets/resnet50.pt") +torch.jit.trace(resnet50, torch.rand(1, 3, 224, 224)).save( + "app/src/main/assets/resnet50.pt" +) -mobilenet2q = torchvision.models.quantization.mobilenet_v2(pretrained=True, quantize=True) +mobilenet2q = torchvision.models.quantization.mobilenet_v2( + pretrained=True, quantize=True +) mobilenet2q.eval() -torch.jit.trace(mobilenet2q, torch.rand(1, 3, 224, 224)).save("app/src/main/assets/mobilenet2q.pt") +torch.jit.trace(mobilenet2q, torch.rand(1, 3, 224, 224)).save( + "app/src/main/assets/mobilenet2q.pt" +) diff --git a/android/test_app/make_assets_custom.py b/android/test_app/make_assets_custom.py index 8da813ef009..8fe9bc57c4e 100644 --- a/android/test_app/make_assets_custom.py +++ b/android/test_app/make_assets_custom.py @@ -21,5 +21,5 @@ traced_script_module.save("MobileNetV2.pt") # Dump root ops used by the model (for custom build optimization). ops = torch.jit.export_opnames(traced_script_module) -with open('MobileNetV2.yaml', 'w') as output: +with open("MobileNetV2.yaml", "w") as output: yaml.dump(ops, output) diff --git a/binaries/bench_gen/bench_gen.py b/binaries/bench_gen/bench_gen.py index aab941cf1cd..7523e76f8b1 100755 --- a/binaries/bench_gen/bench_gen.py +++ b/binaries/bench_gen/bench_gen.py @@ -3,13 +3,14 @@ import argparse import ast +from caffe2.python import brew, workspace + from caffe2.python.model_helper import ModelHelper from caffe2.python.predictor import mobile_exporter -from caffe2.python import workspace, brew def parse_kwarg(kwarg_str): - key, value = kwarg_str.split('=') + key, value = kwarg_str.split("=") try: value = ast.literal_eval(value) except ValueError: @@ -30,7 +31,7 @@ def main(args): iters = int(args.instances) for i in range(iters): - input_blob_name = input_name + (str(i) if i > 0 and args.chain else '') + input_blob_name = input_name + (str(i) if i > 0 and args.chain else "") output_blob_name = output_name + str(i + 1) add_op = getattr(brew, op_type) add_op(model, input_blob_name, output_blob_name, **kwargs) @@ -39,9 +40,7 @@ def main(args): workspace.RunNetOnce(model.param_init_net) - init_net, predict_net = mobile_exporter.Export( - workspace, model.net, model.params - ) + init_net, predict_net = mobile_exporter.Export(workspace, model.net, model.params) if args.debug: print("init_net:") @@ -51,40 +50,69 @@ def main(args): for op in predict_net.op: print(" ", op.type, op.input, "-->", op.output) - with open(args.predict_net, 'wb') as f: + with open(args.predict_net, "wb") as f: f.write(predict_net.SerializeToString()) - with open(args.init_net, 'wb') as f: + with open(args.init_net, "wb") as f: f.write(init_net.SerializeToString()) if __name__ == "__main__": parser = argparse.ArgumentParser( - description="Utility to generate Caffe2 benchmark models.") + description="Utility to generate Caffe2 benchmark models." + ) parser.add_argument("operator", help="Caffe2 operator to benchmark.") - parser.add_argument("-b", "--blob", - help="Instantiate a blob --blob name=dim1,dim2,dim3", - action='append') + parser.add_argument( + "-b", + "--blob", + help="Instantiate a blob --blob name=dim1,dim2,dim3", + action="append", + ) parser.add_argument("--context", help="Context to run on.", default="CPU") - parser.add_argument("--kwargs", help="kwargs to pass to operator.", - nargs="*", type=parse_kwarg, default=[]) - parser.add_argument("--init-net", "--init_net", help="Output initialization net.", - default="init_net.pb") - parser.add_argument("--predict-net", "--predict_net", help="Output prediction net.", - default="predict_net.pb") - parser.add_argument("--benchmark-name", "--benchmark_name", - help="Name of the benchmark network", - default="benchmark") - parser.add_argument("--input-name", "--input_name", help="Name of the input blob.", - default="data") - parser.add_argument("--output-name", "--output_name", help="Name of the output blob.", - default="output") - parser.add_argument("--instances", - help="Number of instances to run the operator.", - default="1") - parser.add_argument("-d", "--debug", help="Print debug information.", - action='store_true') - parser.add_argument("-c", "--chain", - help="Chain ops together (create data dependencies)", - action='store_true') + parser.add_argument( + "--kwargs", + help="kwargs to pass to operator.", + nargs="*", + type=parse_kwarg, + default=[], + ) + parser.add_argument( + "--init-net", + "--init_net", + help="Output initialization net.", + default="init_net.pb", + ) + parser.add_argument( + "--predict-net", + "--predict_net", + help="Output prediction net.", + default="predict_net.pb", + ) + parser.add_argument( + "--benchmark-name", + "--benchmark_name", + help="Name of the benchmark network", + default="benchmark", + ) + parser.add_argument( + "--input-name", "--input_name", help="Name of the input blob.", default="data" + ) + parser.add_argument( + "--output-name", + "--output_name", + help="Name of the output blob.", + default="output", + ) + parser.add_argument( + "--instances", help="Number of instances to run the operator.", default="1" + ) + parser.add_argument( + "-d", "--debug", help="Print debug information.", action="store_true" + ) + parser.add_argument( + "-c", + "--chain", + help="Chain ops together (create data dependencies)", + action="store_true", + ) args = parser.parse_args() main(args) diff --git a/docs/caffe2/process.py b/docs/caffe2/process.py index 4a59eec388d..ca9ebd97316 100644 --- a/docs/caffe2/process.py +++ b/docs/caffe2/process.py @@ -6,18 +6,20 @@ import os import shutil + # Module caffe2...caffe2.python.control_test def insert(originalfile, first_line, description): with open(originalfile) as f: f1 = f.readline() - if(f1.find(first_line) < 0): + if f1.find(first_line) < 0: docs = first_line + description + f1 - with open('newfile.txt', 'w') as f2: + with open("newfile.txt", "w") as f2: f2.write(docs) f2.write(f.read()) - os.rename('newfile.txt', originalfile) + os.rename("newfile.txt", originalfile) else: - print('already inserted') + print("already inserted") + # move up from /caffe2_root/doxygen os.chdir("..") @@ -28,7 +30,11 @@ os.system("git checkout caffe2/python/.") for root, dirs, files in os.walk("."): for file in files: - if (file.endswith(".py") and not file.endswith("_test.py") and not file.endswith("__.py")): + if ( + file.endswith(".py") + and not file.endswith("_test.py") + and not file.endswith("__.py") + ): filepath = os.path.join(root, file) print("filepath: " + filepath) directory = os.path.dirname(filepath)[2:] diff --git a/docs/cpp/source/conf.py b/docs/cpp/source/conf.py index 2b94cfdb505..917d370b2d9 100644 --- a/docs/cpp/source/conf.py +++ b/docs/cpp/source/conf.py @@ -19,6 +19,7 @@ # enabled on nightlies (and not trunk or on PRs) due to OOM errors in CI. # See https://github.com/pytorch/pytorch/issues/79992. import os + # sys.path.insert(0, os.path.abspath('.')) import textwrap @@ -27,22 +28,17 @@ import textwrap # If your documentation needs a minimal Sphinx version, state it here. # -needs_sphinx = '3.1.2' -run_doxygen = os.environ.get('RUN_DOXYGEN', "false") == "true" +needs_sphinx = "3.1.2" +run_doxygen = os.environ.get("RUN_DOXYGEN", "false") == "true" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.intersphinx', -] + ([ - 'breathe', - 'exhale' -] if run_doxygen else []) + "sphinx.ext.intersphinx", +] + (["breathe", "exhale"] if run_doxygen else []) -intersphinx_mapping = { - 'pytorch': ('https://pytorch.org/docs/main', None) -} +intersphinx_mapping = {"pytorch": ("https://pytorch.org/docs/main", None)} # Setup absolute paths for communicating with breathe / exhale where # items are expected / should be trimmed by. @@ -50,13 +46,13 @@ intersphinx_mapping = { this_file_dir = os.path.abspath(os.path.dirname(__file__)) doxygen_xml_dir = os.path.join( os.path.dirname(this_file_dir), # {repo_root}/docs/cpp - 'build', # {repo_root}/docs/cpp/build - 'xml' # {repo_root}/docs/cpp/build/xml + "build", # {repo_root}/docs/cpp/build + "xml", # {repo_root}/docs/cpp/build/xml ) repo_root = os.path.dirname( # {repo_root} - os.path.dirname( # {repo_root}/docs - os.path.dirname( # {repo_root}/docs/cpp - this_file_dir # {repo_root}/docs/cpp/source + os.path.dirname( # {repo_root}/docs + os.path.dirname( # {repo_root}/docs/cpp + this_file_dir # {repo_root}/docs/cpp/source ) ) ) @@ -98,16 +94,18 @@ exhale_args = { ############################################################################ # Main library page layout example configuration. # ############################################################################ - "afterTitleDescription": textwrap.dedent(''' + "afterTitleDescription": textwrap.dedent( + """ Welcome to the developer reference for the PyTorch C++ API. - '''), + """ + ), } # Tell sphinx what the primary language being documented is. -primary_domain = 'cpp' +primary_domain = "cpp" # Tell sphinx what the pygments highlight language should be. -highlight_language = 'cpp' +highlight_language = "cpp" # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] @@ -116,15 +114,15 @@ highlight_language = 'cpp' # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'PyTorch' -copyright = '2022, PyTorch Contributors' -author = 'PyTorch Contributors' +project = "PyTorch" +copyright = "2022, PyTorch Contributors" +author = "PyTorch Contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -132,10 +130,10 @@ author = 'PyTorch Contributors' # # The short X.Y version. # TODO: change to [:2] at v1.0 -version = 'main' +version = "main" # The full version, including alpha/beta/rc tags. # TODO: verify this works as expected -release = 'main' +release = "main" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -150,7 +148,7 @@ language = None exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -160,30 +158,30 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'pytorch_sphinx_theme' +html_theme = "pytorch_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = { - 'canonical_url': 'https://pytorch.org/docs/stable/', - 'pytorch_project': 'docs', - 'collapse_navigation': False, - 'display_version': True, - 'logo_only': True, + "canonical_url": "https://pytorch.org/docs/stable/", + "pytorch_project": "docs", + "collapse_navigation": False, + "display_version": True, + "logo_only": True, } # NOTE: sharing python docs resources html_logo = os.path.join( - repo_root, 'docs', 'source', '_static', 'img', 'pytorch-logo-dark-unstable.png' + repo_root, "docs", "source", "_static", "img", "pytorch-logo-dark-unstable.png" ) # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # NOTE: sharing python docs resources -html_static_path = [os.path.join(repo_root, 'docs', 'cpp', 'source', '_static')] +html_static_path = [os.path.join(repo_root, "docs", "cpp", "source", "_static")] # Called automatically by Sphinx, making this `conf.py` an "extension". @@ -191,14 +189,15 @@ def setup(app): # NOTE: in Sphinx 1.8+ `html_css_files` is an official configuration value # and can be moved outside of this function (and the setup(app) function # can be deleted). - html_css_files = ['cpp_theme.css'] + html_css_files = ["cpp_theme.css"] # In Sphinx 1.8 it was renamed to `add_css_file`, 1.7 and prior it is # `add_stylesheet` (deprecated in 1.8). - add_css = getattr(app, 'add_css_file', app.add_stylesheet) + add_css = getattr(app, "add_css_file", app.add_stylesheet) for css_file in html_css_files: add_css(css_file) + # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. @@ -211,15 +210,12 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -229,8 +225,13 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'pytorch.tex', 'PyTorch Documentation', - 'Torch Contributors', 'manual'), + ( + master_doc, + "pytorch.tex", + "PyTorch Documentation", + "Torch Contributors", + "manual", + ), ] @@ -238,10 +239,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'PyTorch', 'PyTorch Documentation', - [author], 1) -] +man_pages = [(master_doc, "PyTorch", "PyTorch Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -250,7 +248,13 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PyTorch', 'PyTorch Documentation', - author, 'PyTorch', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "PyTorch", + "PyTorch Documentation", + author, + "PyTorch", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/docs/source/conf.py b/docs/source/conf.py index 8fec5f16f98..6039a5b8265 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,10 +16,11 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # import os -from os import path -import re + # import sys import pkgutil +import re +from os import path # source code directory, relative to this file, for sphinx-autobuild # sys.path.insert(0, os.path.abspath('../..')) @@ -30,9 +31,10 @@ try: import torchvision # noqa: F401 except ImportError: import warnings + warnings.warn('unable to load "torchvision" package') -RELEASE = os.environ.get('RELEASE', False) +RELEASE = os.environ.get("RELEASE", False) import pytorch_sphinx_theme @@ -40,25 +42,25 @@ import pytorch_sphinx_theme # If your documentation needs a minimal Sphinx version, state it here. # -needs_sphinx = '3.1.2' +needs_sphinx = "3.1.2" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'sphinxcontrib.katex', - 'sphinx.ext.autosectionlabel', - 'sphinx_copybutton', - 'sphinx_panels', - 'myst_parser', + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinxcontrib.katex", + "sphinx.ext.autosectionlabel", + "sphinx_copybutton", + "sphinx_panels", + "myst_parser", ] # build the templated autosummary files @@ -82,7 +84,7 @@ katex_prerender = True napoleon_use_ivar = True # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # TODO: document these and remove them from here. @@ -343,15 +345,15 @@ coverage_ignore_classes = [ # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'PyTorch' -copyright = '2023, PyTorch Contributors' -author = 'PyTorch Contributors' +project = "PyTorch" +copyright = "2023, PyTorch Contributors" +author = "PyTorch Contributors" torch_version = str(torch.__version__) # The version info for the project you're documenting, acts as replacement for @@ -360,10 +362,10 @@ torch_version = str(torch.__version__) # # The short X.Y version. # TODO: change to [:2] at v1.0 -version = 'main (' + torch_version + ' )' +version = "main (" + torch_version + " )" # The full version, including alpha/beta/rc tags. # TODO: verify this works as expected -release = 'main' +release = "main" # Customized html_title here. # Default is " ".join(project, release, "documentation") if not set @@ -371,7 +373,7 @@ if RELEASE: # Turn 1.11.0aHASH into 1.11 # Note: the release candidates should no longer have the aHASH suffix, but in any # case we wish to leave only major.minor, even for rc builds. - version = '.'.join(torch_version.split('.')[:2]) + version = ".".join(torch_version.split(".")[:2]) html_title = " ".join((project, version, "documentation")) release = version @@ -388,7 +390,7 @@ language = "en" exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True @@ -397,10 +399,10 @@ todo_include_todos = True autodoc_inherit_docstrings = False # Show type hints in the description -autodoc_typehints = 'description' +autodoc_typehints = "description" # Add parameter types if the parameter is documented in the docstring -autodoc_typehints_description_target = 'documented_params' +autodoc_typehints_description_target = "documented_params" # Type aliases for common types # Sphinx type aliases only works with Postponed Evaluation of Annotations @@ -442,7 +444,7 @@ autodoc_docstring_signature = True # # -html_theme = 'pytorch_sphinx_theme' +html_theme = "pytorch_sphinx_theme" html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme @@ -450,30 +452,31 @@ html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()] # documentation. html_theme_options = { - 'pytorch_project': 'docs', - 'canonical_url': 'https://pytorch.org/docs/stable/', - 'collapse_navigation': False, - 'display_version': True, - 'logo_only': True, - 'analytics_id': 'GTM-T8XT4PS', + "pytorch_project": "docs", + "canonical_url": "https://pytorch.org/docs/stable/", + "collapse_navigation": False, + "display_version": True, + "logo_only": True, + "analytics_id": "GTM-T8XT4PS", } -html_logo = '_static/img/pytorch-logo-dark-unstable.png' +html_logo = "_static/img/pytorch-logo-dark-unstable.png" if RELEASE: - html_logo = '_static/img/pytorch-logo-dark.svg' + html_logo = "_static/img/pytorch-logo-dark.svg" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] html_css_files = [ - 'css/jit.css', + "css/jit.css", ] from sphinx.ext.coverage import CoverageBuilder + def coverage_post_process(app, exception): if exception is not None: return @@ -483,14 +486,16 @@ def coverage_post_process(app, exception): return if not torch.distributed.is_available(): - raise RuntimeError("The coverage tool cannot run with a version " - "of PyTorch that was built with USE_DISTRIBUTED=0 " - "as this module's API changes.") + raise RuntimeError( + "The coverage tool cannot run with a version " + "of PyTorch that was built with USE_DISTRIBUTED=0 " + "as this module's API changes." + ) # These are all the modules that have "automodule" in an rst file # These modules are the ones for which coverage is checked # Here, we make sure that no module is missing from that list - modules = app.env.domaindata['py']['modules'] + modules = app.env.domaindata["py"]["modules"] # We go through all the torch submodules and make sure they are # properly tested @@ -507,8 +512,9 @@ def coverage_post_process(app, exception): if "torch" not in modules: missing.add("torch") - for _, modname, ispkg in pkgutil.walk_packages(path=torch.__path__, - prefix=torch.__name__ + '.'): + for _, modname, ispkg in pkgutil.walk_packages( + path=torch.__path__, prefix=torch.__name__ + "." + ): if ispkg and is_not_internal(modname): if modname not in modules: missing.add(modname) @@ -517,16 +523,18 @@ def coverage_post_process(app, exception): if missing: mods = ", ".join(missing) - output.append(f"\nYou added the following module(s) to the PyTorch namespace '{mods}' " - "but they have no corresponding entry in a doc .rst file. You should " - "either make sure that the .rst file that contains the module's documentation " - "properly contains either '.. automodule:: mod_name' (if you do not want " - "the paragraph added by the automodule, you can simply use '.. py:module:: mod_name') " - " or make the module private (by appending an '_' at the beginning of its name).") + output.append( + f"\nYou added the following module(s) to the PyTorch namespace '{mods}' " + "but they have no corresponding entry in a doc .rst file. You should " + "either make sure that the .rst file that contains the module's documentation " + "properly contains either '.. automodule:: mod_name' (if you do not want " + "the paragraph added by the automodule, you can simply use '.. py:module:: mod_name') " + " or make the module private (by appending an '_' at the beginning of its name)." + ) # The output file is hard-coded by the coverage tool # Our CI is setup to fail if any line is added to this file - output_file = path.join(app.outdir, 'python.txt') + output_file = path.join(app.outdir, "python.txt") if output: with open(output_file, "a") as f: @@ -561,21 +569,21 @@ def process_docstring(app, what_, name, obj, options, lines): https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html """ import re + remove_directives = [ # Remove all xdoctest directives - re.compile(r'\s*>>>\s*#\s*x?doctest:\s*.*'), - re.compile(r'\s*>>>\s*#\s*x?doc:\s*.*'), + re.compile(r"\s*>>>\s*#\s*x?doctest:\s*.*"), + re.compile(r"\s*>>>\s*#\s*x?doc:\s*.*"), ] filtered_lines = [ - line for line in lines - if not any(pat.match(line) for pat in remove_directives) + line for line in lines if not any(pat.match(line) for pat in remove_directives) ] # Modify the lines inplace lines[:] = filtered_lines # make sure there is a blank line at the end if lines and lines[-1].strip(): - lines.append('') + lines.append("") # Called automatically by Sphinx, making this `conf.py` an "extension". @@ -584,17 +592,18 @@ def setup(app): # and can be moved outside of this function (and the setup(app) function # can be deleted). html_css_files = [ - 'https://cdn.jsdelivr.net/npm/katex@0.10.0-beta/dist/katex.min.css' + "https://cdn.jsdelivr.net/npm/katex@0.10.0-beta/dist/katex.min.css" ] # In Sphinx 1.8 it was renamed to `add_css_file`, 1.7 and prior it is # `add_stylesheet` (deprecated in 1.8). - add_css = getattr(app, 'add_css_file', app.add_stylesheet) + add_css = getattr(app, "add_css_file", app.add_stylesheet) for css_file in html_css_files: add_css(css_file) app.connect("build-finished", coverage_post_process) - app.connect('autodoc-process-docstring', process_docstring) + app.connect("autodoc-process-docstring", process_docstring) + # From PyTorch 1.5, we now use autogenerated files to document classes and # functions. This breaks older references since @@ -608,13 +617,14 @@ def setup(app): from sphinx.writers import html, html5 + def replace(Klass): old_call = Klass.visit_reference def visit_reference(self, node): - if 'refuri' in node and 'generated' in node.get('refuri'): - ref = node.get('refuri') - ref_anchor = ref.split('#') + if "refuri" in node and "generated" in node.get("refuri"): + ref = node.get("refuri") + ref_anchor = ref.split("#") if len(ref_anchor) > 1: # Only add the id if the node href and the text match, # i.e. the href is "torch.flip#torch.flip" and the content is @@ -622,18 +632,20 @@ def replace(Klass): # to autogenerated content anchor = ref_anchor[1] txt = node.parent.astext() - if txt == anchor or txt == anchor.split('.')[-1]: + if txt == anchor or txt == anchor.split(".")[-1]: self.body.append(f'
') return old_call(self, node) + Klass.visit_reference = visit_reference + replace(html.HTMLTranslator) replace(html5.HTML5Translator) # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'PyTorchdoc' +htmlhelp_basename = "PyTorchdoc" # -- Options for LaTeX output --------------------------------------------- @@ -642,15 +654,12 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -660,8 +669,13 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'pytorch.tex', 'PyTorch Documentation', - 'Torch Contributors', 'manual'), + ( + master_doc, + "pytorch.tex", + "PyTorch Documentation", + "Torch Contributors", + "manual", + ), ] @@ -669,10 +683,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'PyTorch', 'PyTorch Documentation', - [author], 1) -] +man_pages = [(master_doc, "PyTorch", "PyTorch Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -681,36 +692,43 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PyTorch', 'PyTorch Documentation', - author, 'PyTorch', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "PyTorch", + "PyTorch Documentation", + author, + "PyTorch", + "One line description of project.", + "Miscellaneous", + ), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - 'numpy': ('https://numpy.org/doc/stable', None), + "python": ("https://docs.python.org/3", None), + "numpy": ("https://numpy.org/doc/stable", None), } +import sphinx.ext.doctest + # -- A patch that prevents Sphinx from cross-referencing ivar tags ------- # See http://stackoverflow.com/a/41184353/3343043 from docutils import nodes -from sphinx.util.docfields import TypedField from sphinx import addnodes -import sphinx.ext.doctest +from sphinx.util.docfields import TypedField # Without this, doctest adds any example with a `>>>` as a test -doctest_test_doctest_blocks = '' +doctest_test_doctest_blocks = "" doctest_default_flags = sphinx.ext.doctest.doctest.ELLIPSIS -doctest_global_setup = ''' +doctest_global_setup = """ import torch try: import torchvision except ImportError: torchvision = None -''' +""" def patched_make_field(self, types, domain, items, **kw): @@ -720,43 +738,51 @@ def patched_make_field(self, types, domain, items, **kw): # type: (List, unicode, Tuple) -> nodes.field def handle_item(fieldarg, content): par = nodes.paragraph() - par += addnodes.literal_strong('', fieldarg) # Patch: this line added + par += addnodes.literal_strong("", fieldarg) # Patch: this line added # par.extend(self.make_xrefs(self.rolename, domain, fieldarg, # addnodes.literal_strong)) if fieldarg in types: - par += nodes.Text(' (') + par += nodes.Text(" (") # NOTE: using .pop() here to prevent a single type node to be # inserted twice into the doctree, which leads to # inconsistencies later when references are resolved fieldtype = types.pop(fieldarg) if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): typename = fieldtype[0].astext() - builtin_types = ['int', 'long', 'float', 'bool', 'type'] + builtin_types = ["int", "long", "float", "bool", "type"] for builtin_type in builtin_types: - pattern = fr'(?>> |\.\.\. ' +copybutton_prompt_text = r">>> |\.\.\. " copybutton_prompt_is_regexp = True diff --git a/docs/source/scripts/build_activation_images.py b/docs/source/scripts/build_activation_images.py index 94f0416bc1b..cce571531d1 100644 --- a/docs/source/scripts/build_activation_images.py +++ b/docs/source/scripts/build_activation_images.py @@ -6,8 +6,9 @@ online tutorials. from pathlib import Path -import torch import matplotlib + +import torch from matplotlib import pyplot as plt matplotlib.use("Agg") diff --git a/docs/source/scripts/build_opsets.py b/docs/source/scripts/build_opsets.py index 2ab913fe85a..65b35be7d93 100644 --- a/docs/source/scripts/build_opsets.py +++ b/docs/source/scripts/build_opsets.py @@ -1,10 +1,10 @@ import os +from collections import OrderedDict from pathlib import Path import torch -from collections import OrderedDict -from torchgen.gen import parse_native_yaml import torch._prims as prims +from torchgen.gen import parse_native_yaml ROOT = Path(__file__).absolute().parent.parent.parent.parent NATIVE_FUNCTION_YAML_PATH = ROOT / Path("aten/src/ATen/native/native_functions.yaml") @@ -38,7 +38,6 @@ def get_aten(): def get_prims(): op_schema_pairs = [] for op_name in prims.__all__: - op_overload = getattr(prims, op_name, None) if not isinstance(op_overload, torch._ops.OpOverload): @@ -53,6 +52,7 @@ def get_prims(): return op_schema_pairs + def main(): aten_ops_list = get_aten() prims_ops_list = get_prims() @@ -70,5 +70,5 @@ def main(): f.write(f'"``{name}``","{schema}"\n') -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/docs/source/scripts/build_quantization_configs.py b/docs/source/scripts/build_quantization_configs.py index 6ab4fd433ef..bf405662040 100644 --- a/docs/source/scripts/build_quantization_configs.py +++ b/docs/source/scripts/build_quantization_configs.py @@ -3,33 +3,35 @@ This script will generate default values of quantization configs. These are for use in the documentation. """ +import os.path + import torch from torch.ao.quantization.backend_config import get_native_backend_config_dict from torch.ao.quantization.backend_config.utils import ( entry_to_pretty_str, remove_boolean_dispatch_from_name, ) -import os.path # Create a directory for the images, if it doesn't exist QUANTIZATION_BACKEND_CONFIG_IMAGE_PATH = os.path.join( - os.path.realpath(os.path.join(__file__, "..")), - "quantization_backend_configs" + os.path.realpath(os.path.join(__file__, "..")), "quantization_backend_configs" ) if not os.path.exists(QUANTIZATION_BACKEND_CONFIG_IMAGE_PATH): os.mkdir(QUANTIZATION_BACKEND_CONFIG_IMAGE_PATH) -output_path = os.path.join(QUANTIZATION_BACKEND_CONFIG_IMAGE_PATH, "default_backend_config.txt") +output_path = os.path.join( + QUANTIZATION_BACKEND_CONFIG_IMAGE_PATH, "default_backend_config.txt" +) with open(output_path, "w") as f: native_backend_config_dict = get_native_backend_config_dict() - configs = native_backend_config_dict['configs'] + configs = native_backend_config_dict["configs"] def _sort_key_func(entry): - pattern = entry['pattern'] + pattern = entry["pattern"] while isinstance(pattern, tuple): pattern = pattern[-1] @@ -49,8 +51,8 @@ with open(output_path, "w") as f: # to be next to each other, so convert to all lower case # and remove the underscores, and compare the last part # of the string - pattern_str_normalized = pattern.lower().replace('_', '') - key = pattern_str_normalized.split('.')[-1] + pattern_str_normalized = pattern.lower().replace("_", "") + key = pattern_str_normalized.split(".")[-1] return key configs.sort(key=_sort_key_func) diff --git a/docs/source/scripts/exportdb/generate_example_rst.py b/docs/source/scripts/exportdb/generate_example_rst.py index 96b6aae37f5..88697f629d2 100644 --- a/docs/source/scripts/exportdb/generate_example_rst.py +++ b/docs/source/scripts/exportdb/generate_example_rst.py @@ -159,13 +159,14 @@ def generate_rst(): tag_to_modules = {} support_level_to_modules = {} for example_case in example_cases.values(): - doc_contents = generate_example_rst(example_case) for tag in example_case.tags: tag_to_modules.setdefault(tag, []).append(doc_contents) - support_level_to_modules.setdefault(example_case.support_level, []).append(doc_contents) + support_level_to_modules.setdefault(example_case.support_level, []).append( + doc_contents + ) generate_tag_rst(tag_to_modules) generate_index_rst(example_cases, tag_to_modules, support_level_to_modules) diff --git a/docs/source/scripts/onnx/build_onnx_supported_aten_op_csv_table.py b/docs/source/scripts/onnx/build_onnx_supported_aten_op_csv_table.py index e4b4eb11fdf..88e055a8cfe 100644 --- a/docs/source/scripts/onnx/build_onnx_supported_aten_op_csv_table.py +++ b/docs/source/scripts/onnx/build_onnx_supported_aten_op_csv_table.py @@ -5,6 +5,7 @@ docs/source/onnx_supported_aten_list.rst. """ import os + from torch.onnx import _onnx_supported_ops # Constants diff --git a/ios/TestApp/benchmark/coreml_backend.py b/ios/TestApp/benchmark/coreml_backend.py index 347ac5ca1b8..140080b2e83 100644 --- a/ios/TestApp/benchmark/coreml_backend.py +++ b/ios/TestApp/benchmark/coreml_backend.py @@ -1,11 +1,8 @@ import torch import torchvision -from torch.backends._coreml.preprocess import ( - CompileSpec, - TensorSpec, - CoreMLComputeUnit, -) +from torch.backends._coreml.preprocess import CompileSpec, CoreMLComputeUnit, TensorSpec + def mobilenetv2_spec(): return { diff --git a/ios/TestApp/benchmark/trace_model.py b/ios/TestApp/benchmark/trace_model.py index ef82d92fed6..39158f04498 100644 --- a/ios/TestApp/benchmark/trace_model.py +++ b/ios/TestApp/benchmark/trace_model.py @@ -7,5 +7,7 @@ model.eval() example = torch.rand(1, 3, 224, 224) traced_script_module = torch.jit.trace(model, example) optimized_scripted_module = optimize_for_mobile(traced_script_module) -torch.jit.save(optimized_scripted_module, '../models/model.pt') -exported_optimized_scripted_module = optimized_scripted_module._save_for_lite_interpreter("../models/model_lite.ptl") +torch.jit.save(optimized_scripted_module, "../models/model.pt") +exported_optimized_scripted_module = ( + optimized_scripted_module._save_for_lite_interpreter("../models/model_lite.ptl") +) diff --git a/ios/TestApp/custom_build/custom_build.py b/ios/TestApp/custom_build/custom_build.py index f6980de5938..d009252c297 100644 --- a/ios/TestApp/custom_build/custom_build.py +++ b/ios/TestApp/custom_build/custom_build.py @@ -7,5 +7,5 @@ model.eval() example = torch.rand(1, 3, 224, 224) traced_script_module = torch.jit.trace(model, example) ops = torch.jit.export_opnames(traced_script_module) -with open('mobilenetv2.yaml', 'w') as output: +with open("mobilenetv2.yaml", "w") as output: yaml.dump(ops, output) diff --git a/modules/detectron/upsample_nearest_op_test.py b/modules/detectron/upsample_nearest_op_test.py index 96209cb80cb..276d50474d1 100644 --- a/modules/detectron/upsample_nearest_op_test.py +++ b/modules/detectron/upsample_nearest_op_test.py @@ -1,4 +1,3 @@ - import unittest import caffe2.python.hypothesis_test_util as hu @@ -17,7 +16,7 @@ class TestUpsampleNearestOp(hu.HypothesisTestCase): H=st.integers(10, 300), W=st.integers(10, 300), scale=st.integers(1, 3), - **hu.gcs + **hu.gcs, ) @settings(deadline=None, max_examples=20) def test_upsample_nearest_op(self, N, H, W, scale, gc, dc): diff --git a/mypy_plugins/check_mypy_version.py b/mypy_plugins/check_mypy_version.py index 7ef19ef22b0..4fa72e85577 100644 --- a/mypy_plugins/check_mypy_version.py +++ b/mypy_plugins/check_mypy_version.py @@ -7,18 +7,21 @@ from mypy.plugin import Plugin def get_correct_mypy_version(): # there's probably a more elegant way to do this - match, = re.finditer( - r'mypy==(\d+(?:\.\d+)*)', - (Path(__file__).parent.parent / '.ci' / 'docker' / 'requirements-ci.txt').read_text(), + (match,) = re.finditer( + r"mypy==(\d+(?:\.\d+)*)", + ( + Path(__file__).parent.parent / ".ci" / "docker" / "requirements-ci.txt" + ).read_text(), ) - version, = match.groups() + (version,) = match.groups() return version def plugin(version: str): correct_version = get_correct_mypy_version() if version != correct_version: - print(f'''\ + print( + f"""\ You are using mypy version {version}, which is not supported in the PyTorch repo. Please switch to mypy version {correct_version}. @@ -29,5 +32,7 @@ For example, if you installed mypy via pip, run this: Or if you installed mypy via conda, run this: conda install -c conda-forge mypy={correct_version} -''', file=sys.stderr) +""", + file=sys.stderr, + ) return Plugin diff --git a/scripts/analysis/format_test_csv.py b/scripts/analysis/format_test_csv.py index f91419342d2..da796f01fdd 100644 --- a/scripts/analysis/format_test_csv.py +++ b/scripts/analysis/format_test_csv.py @@ -15,13 +15,14 @@ onto your local file system is to send it to GitHub Gist: See also scripts/analysis/run_test_csv.sh """ +import argparse import csv import subprocess import sys -import argparse -parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) +parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter +) parser.add_argument("--log-url", type=str, default="", help="URL of raw logs") parser.add_argument("file", help="pytest CSV file to format") args = parser.parse_args() diff --git a/scripts/diagnose_protobuf.py b/scripts/diagnose_protobuf.py index bc643b39792..30bdbb2c2fe 100644 --- a/scripts/diagnose_protobuf.py +++ b/scripts/diagnose_protobuf.py @@ -15,44 +15,45 @@ Usage: import os import re -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen # Get python protobuf version. try: import google.protobuf + python_version = google.protobuf.__version__ python_protobuf_installed = True except ImportError: print("DEBUG: cannot find python protobuf install.") python_protobuf_installed = False -if os.name == 'nt': - protoc_name = 'protoc.exe' +if os.name == "nt": + protoc_name = "protoc.exe" else: - protoc_name = 'protoc' + protoc_name = "protoc" try: - p = Popen([protoc_name, '--version'], stdout=PIPE, stderr=PIPE) + p = Popen([protoc_name, "--version"], stdout=PIPE, stderr=PIPE) out, err = p.communicate() except: - print('DEBUG: did not find protoc binary.') - print('DEBUG: out: ' + out) - print('DEBUG: err: ' + err) + print("DEBUG: did not find protoc binary.") + print("DEBUG: out: " + out) + print("DEBUG: err: " + err) native_protobuf_installed = False else: if p.returncode: - print('DEBUG: protoc returned a non-zero return code.') - print('DEBUG: out: ' + out) - print('DEBUG: err: ' + err) + print("DEBUG: protoc returned a non-zero return code.") + print("DEBUG: out: " + out) + print("DEBUG: err: " + err) native_protobuf_installed = False else: - tmp = re.search(r'\d\.\d\.\d', out) + tmp = re.search(r"\d\.\d\.\d", out) if tmp: native_version = tmp.group(0) native_protobuf_installed = True else: - print('DEBUG: cannot parse protoc version string.') - print('DEBUG: out: ' + out) + print("DEBUG: cannot parse protoc version string.") + print("DEBUG: out: " + out) native_protobuf_installed = False PYTHON_PROTOBUF_NOT_INSTALLED = """ @@ -74,7 +75,9 @@ VERSION_MISMATCH = """ Your python protobuf is of version {py_ver} but your native protoc version is of version {native_ver}. This will cause the installation to produce incompatible protobuf files. This is bad in general - consider installing the same version. -""".format(py_ver=python_version, native_ver=native_version) +""".format( + py_ver=python_version, native_ver=native_version +) # Now, give actual recommendations if not python_protobuf_installed: @@ -87,4 +90,4 @@ if python_protobuf_installed and native_protobuf_installed: if python_version != native_version: print(VERSION_MISMATCH) else: - print('All looks good.') + print("All looks good.") diff --git a/scripts/get_python_cmake_flags.py b/scripts/get_python_cmake_flags.py index 05562510d05..a1aad03888f 100644 --- a/scripts/get_python_cmake_flags.py +++ b/scripts/get_python_cmake_flags.py @@ -13,14 +13,12 @@ # - - -import sysconfig import sys +import sysconfig flags = [ - f'-DPYTHON_EXECUTABLE:FILEPATH={sys.executable}', + f"-DPYTHON_EXECUTABLE:FILEPATH={sys.executable}", f"-DPYTHON_INCLUDE_DIR={sysconfig.get_path('include')}", ] -print(' '.join(flags), end='') +print(" ".join(flags), end="") diff --git a/scripts/jit/log_extract.py b/scripts/jit/log_extract.py index 61e3172fe0b..14c45049a68 100644 --- a/scripts/jit/log_extract.py +++ b/scripts/jit/log_extract.py @@ -1,10 +1,17 @@ import argparse import functools import traceback -from torch.utils.jit.log_extract import extract_ir, load_graph_and_inputs, run_baseline_no_fusion, run_nnc, run_nvfuser -from typing import List, Tuple, Callable, Optional +from typing import Callable, List, Optional, Tuple -''' +from torch.utils.jit.log_extract import ( + extract_ir, + load_graph_and_inputs, + run_baseline_no_fusion, + run_nnc, + run_nvfuser, +) + +""" Usage: 1. Run your script and pipe into a log file PYTORCH_JIT_LOG_LEVEL=">>graph_fuser" python3 my_test.py &> log.txt @@ -15,10 +22,14 @@ You can also extract the list of extracted IR: log_extract.py log.txt --output Passing in --graphs 0 2 will only run graphs 0 and 2 -''' +""" -def test_runners(graphs: List[str], runners: List[Tuple[str, Callable]], graph_set: Optional[List[int]]): +def test_runners( + graphs: List[str], + runners: List[Tuple[str, Callable]], + graph_set: Optional[List[int]], +): for i, ir in enumerate(graphs): _, inputs = load_graph_and_inputs(ir) if graph_set and i not in graph_set: @@ -33,7 +44,9 @@ def test_runners(graphs: List[str], runners: List[Tuple[str, Callable]], graph_s result = runner_fn(ir, inputs) if prev_result: improvement = (prev_result / result - 1) * 100 - print(f"{runner_name} : {result:.6f} ms improvement over {prev_runner_name}: improvement: {improvement:.2f}%") + print( + f"{runner_name} : {result:.6f} ms improvement over {prev_runner_name}: improvement: {improvement:.2f}%" + ) else: print(f"{runner_name} : {result:.6f} ms") prev_result = result @@ -47,32 +60,66 @@ def run(): description="Extracts torchscript IR from log files and, optionally, benchmarks it or outputs the IR" ) parser.add_argument("filename", help="Filename of log file") - parser.add_argument("--nvfuser", dest="nvfuser", action="store_true", help="benchmark nvfuser") - parser.add_argument("--no-nvfuser", dest="nvfuser", action="store_false", help="DON'T benchmark nvfuser") + parser.add_argument( + "--nvfuser", dest="nvfuser", action="store_true", help="benchmark nvfuser" + ) + parser.add_argument( + "--no-nvfuser", + dest="nvfuser", + action="store_false", + help="DON'T benchmark nvfuser", + ) parser.set_defaults(nvfuser=False) - parser.add_argument("--nnc-static", dest="nnc_static", action="store_true", help="benchmark nnc static") - parser.add_argument("--no-nnc-static", dest="nnc_static", action="store_false", help="DON'T benchmark nnc static") + parser.add_argument( + "--nnc-static", + dest="nnc_static", + action="store_true", + help="benchmark nnc static", + ) + parser.add_argument( + "--no-nnc-static", + dest="nnc_static", + action="store_false", + help="DON'T benchmark nnc static", + ) parser.set_defaults(nnc_static=False) - parser.add_argument("--nnc-dynamic", dest="nnc_dynamic", action="store_true", help="nnc with dynamic shapes") + parser.add_argument( + "--nnc-dynamic", + dest="nnc_dynamic", + action="store_true", + help="nnc with dynamic shapes", + ) parser.add_argument( "--no-nnc-dynamic", dest="nnc_dynamic", action="store_false", - help="DONT't benchmark nnc with dynamic shapes") + help="DONT't benchmark nnc with dynamic shapes", + ) parser.set_defaults(nnc_dynamic=False) - - parser.add_argument("--baseline", dest="baseline", action="store_true", help="benchmark baseline") - parser.add_argument("--no-baseline", dest="baseline", action="store_false", help="DON'T benchmark baseline") + parser.add_argument( + "--baseline", dest="baseline", action="store_true", help="benchmark baseline" + ) + parser.add_argument( + "--no-baseline", + dest="baseline", + action="store_false", + help="DON'T benchmark baseline", + ) parser.set_defaults(baseline=False) - parser.add_argument("--output", dest="output", action="store_true", help="Output graph IR") - parser.add_argument("--no-output", dest="output", action="store_false", help="DON'T output graph IR") + parser.add_argument( + "--output", dest="output", action="store_true", help="Output graph IR" + ) + parser.add_argument( + "--no-output", dest="output", action="store_false", help="DON'T output graph IR" + ) parser.set_defaults(output=False) - parser.add_argument('--graphs', nargs="+", type=int, help="Run only specified graph indices") - + parser.add_argument( + "--graphs", nargs="+", type=int, help="Run only specified graph indices" + ) args = parser.parse_args() graphs = extract_ir(args.filename) @@ -97,8 +144,9 @@ def run(): for i, ir in enumerate(graphs): if graph_set and i not in graph_set: continue - quoted.append("\"\"\"" + ir + "\"\"\"") + quoted.append('"""' + ir + '"""') print("[" + ", ".join(quoted) + "]") + if __name__ == "__main__": run() diff --git a/scripts/model_zoo/update-caffe2-models.py b/scripts/model_zoo/update-caffe2-models.py index 9f48920e6a8..aad5ec58e5d 100755 --- a/scripts/model_zoo/update-caffe2-models.py +++ b/scripts/model_zoo/update-caffe2-models.py @@ -8,7 +8,12 @@ import tempfile from urllib.request import urlretrieve -from caffe2.python.models.download import downloadFromURLToFile, getURLFromName, deleteDirectory +from caffe2.python.models.download import ( + deleteDirectory, + downloadFromURLToFile, + getURLFromName, +) + class SomeClass: # largely copied from @@ -17,13 +22,12 @@ class SomeClass: model_dir = self._caffe2_model_dir(model) assert not os.path.exists(model_dir) os.makedirs(model_dir) - for f in ['predict_net.pb', 'init_net.pb', 'value_info.json']: + for f in ["predict_net.pb", "init_net.pb", "value_info.json"]: url = getURLFromName(model, f) dest = os.path.join(model_dir, f) try: try: - downloadFromURLToFile(url, dest, - show_progress=False) + downloadFromURLToFile(url, dest, show_progress=False) except TypeError: # show_progress not supported prior to # Caffe2 78c014e752a374d905ecfb465d44fa16e02a28f1 @@ -36,13 +40,13 @@ class SomeClass: exit(1) def _caffe2_model_dir(self, model): - caffe2_home = os.path.expanduser('~/.caffe2') - models_dir = os.path.join(caffe2_home, 'models') + caffe2_home = os.path.expanduser("~/.caffe2") + models_dir = os.path.join(caffe2_home, "models") return os.path.join(models_dir, model) def _onnx_model_dir(self, model): - onnx_home = os.path.expanduser('~/.onnx') - models_dir = os.path.join(onnx_home, 'models') + onnx_home = os.path.expanduser("~/.onnx") + models_dir = os.path.join(onnx_home, "models") model_dir = os.path.join(models_dir, model) return model_dir, os.path.dirname(model_dir) @@ -53,45 +57,44 @@ class SomeClass: if os.path.exists(model_dir): return os.makedirs(model_dir) - url = f'https://s3.amazonaws.com/download.onnx/models/{model}.tar.gz' + url = f"https://s3.amazonaws.com/download.onnx/models/{model}.tar.gz" # On Windows, NamedTemporaryFile cannot be opened for a # second time download_file = tempfile.NamedTemporaryFile(delete=False) try: download_file.close() - print(f'Start downloading model {model} from {url}') + print(f"Start downloading model {model} from {url}") urlretrieve(url, download_file.name) - print('Done') + print("Done") with tarfile.open(download_file.name) as t: t.extractall(models_dir) except Exception as e: - print(f'Failed to prepare data for model {model}: {e}') + print(f"Failed to prepare data for model {model}: {e}") raise finally: os.remove(download_file.name) -models = [ - 'bvlc_alexnet', - 'densenet121', - 'inception_v1', - 'inception_v2', - 'resnet50', +models = [ + "bvlc_alexnet", + "densenet121", + "inception_v1", + "inception_v2", + "resnet50", # TODO currently onnx can't translate squeezenet :( # 'squeezenet', - - 'vgg16', - + "vgg16", # TODO currently vgg19 doesn't work in the CI environment, # possibly due to OOM # 'vgg19' ] + def download_models(): sc = SomeClass() for model in models: - print('update-caffe2-models.py: downloading', model) + print("update-caffe2-models.py: downloading", model) caffe2_model_dir = sc._caffe2_model_dir(model) onnx_model_dir, onnx_models_dir = sc._onnx_model_dir(model) if not os.path.exists(caffe2_model_dir): @@ -99,61 +102,74 @@ def download_models(): if not os.path.exists(onnx_model_dir): sc._prepare_model_data(model) + def generate_models(): sc = SomeClass() for model in models: - print('update-caffe2-models.py: generating', model) + print("update-caffe2-models.py: generating", model) caffe2_model_dir = sc._caffe2_model_dir(model) onnx_model_dir, onnx_models_dir = sc._onnx_model_dir(model) - subprocess.check_call(['echo', model]) - with open(os.path.join(caffe2_model_dir, 'value_info.json'), 'r') as f: + subprocess.check_call(["echo", model]) + with open(os.path.join(caffe2_model_dir, "value_info.json"), "r") as f: value_info = f.read() - subprocess.check_call([ - 'convert-caffe2-to-onnx', - '--caffe2-net-name', model, - '--caffe2-init-net', os.path.join(caffe2_model_dir, 'init_net.pb'), - '--value-info', value_info, - '-o', os.path.join(onnx_model_dir, 'model.pb'), - os.path.join(caffe2_model_dir, 'predict_net.pb') - ]) - subprocess.check_call([ - 'tar', - '-czf', - model + '.tar.gz', - model - ], cwd=onnx_models_dir) + subprocess.check_call( + [ + "convert-caffe2-to-onnx", + "--caffe2-net-name", + model, + "--caffe2-init-net", + os.path.join(caffe2_model_dir, "init_net.pb"), + "--value-info", + value_info, + "-o", + os.path.join(onnx_model_dir, "model.pb"), + os.path.join(caffe2_model_dir, "predict_net.pb"), + ] + ) + subprocess.check_call( + ["tar", "-czf", model + ".tar.gz", model], cwd=onnx_models_dir + ) + def upload_models(): sc = SomeClass() for model in models: - print('update-caffe2-models.py: uploading', model) + print("update-caffe2-models.py: uploading", model) onnx_model_dir, onnx_models_dir = sc._onnx_model_dir(model) - subprocess.check_call([ - 'aws', - 's3', - 'cp', - model + '.tar.gz', - f"s3://download.onnx/models/{model}.tar.gz", - '--acl', 'public-read' - ], cwd=onnx_models_dir) + subprocess.check_call( + [ + "aws", + "s3", + "cp", + model + ".tar.gz", + f"s3://download.onnx/models/{model}.tar.gz", + "--acl", + "public-read", + ], + cwd=onnx_models_dir, + ) + def cleanup(): sc = SomeClass() for model in models: onnx_model_dir, onnx_models_dir = sc._onnx_model_dir(model) - os.remove(os.path.join(os.path.dirname(onnx_model_dir), model + '.tar.gz')) + os.remove(os.path.join(os.path.dirname(onnx_model_dir), model + ".tar.gz")) -if __name__ == '__main__': + +if __name__ == "__main__": try: - subprocess.check_call(['aws', 'sts', 'get-caller-identity']) + subprocess.check_call(["aws", "sts", "get-caller-identity"]) except: - print('update-caffe2-models.py: please run `aws configure` manually to set up credentials') + print( + "update-caffe2-models.py: please run `aws configure` manually to set up credentials" + ) sys.exit(1) - if sys.argv[1] == 'download': + if sys.argv[1] == "download": download_models() - if sys.argv[1] == 'generate': + if sys.argv[1] == "generate": generate_models() - elif sys.argv[1] == 'upload': + elif sys.argv[1] == "upload": upload_models() - elif sys.argv[1] == 'cleanup': + elif sys.argv[1] == "cleanup": cleanup() diff --git a/scripts/model_zoo/update-models-from-caffe2.py b/scripts/model_zoo/update-models-from-caffe2.py index 537fc03a60a..a75683abed6 100644 --- a/scripts/model_zoo/update-models-from-caffe2.py +++ b/scripts/model_zoo/update-models-from-caffe2.py @@ -1,26 +1,29 @@ #! /usr/bin/env python3 -import onnx.backend - import argparse -import caffe2.python.workspace as c2_workspace import glob import json -import numpy as np -import onnx -import caffe2.python.onnx.frontend -import caffe2.python.onnx.backend import os import shutil import tarfile import tempfile -import boto3 - from urllib.request import urlretrieve -from caffe2.python.models.download import downloadFromURLToFile, getURLFromName, deleteDirectory +import boto3 +import caffe2.python.onnx.backend +import caffe2.python.onnx.frontend +import caffe2.python.workspace as c2_workspace +import numpy as np +import onnx +import onnx.backend from caffe2.proto import caffe2_pb2 + +from caffe2.python.models.download import ( + deleteDirectory, + downloadFromURLToFile, + getURLFromName, +) from onnx import numpy_helper @@ -46,25 +49,31 @@ Examples: def upload_onnx_model(model_name, zoo_dir, backup=False, only_local=False): if only_local: - print('No uploading in local only mode.') + print("No uploading in local only mode.") return model_dir = os.path.join(zoo_dir, model_name) - suffix = '-backup' if backup else '' + suffix = "-backup" if backup else "" if backup: - print(f'Backing up the previous version of ONNX model {model_name}...') - rel_file_name = f'{model_name}{suffix}.tar.gz' + print(f"Backing up the previous version of ONNX model {model_name}...") + rel_file_name = f"{model_name}{suffix}.tar.gz" abs_file_name = os.path.join(zoo_dir, rel_file_name) - print(f'Compressing {model_name} model to {abs_file_name}') - with tarfile.open(abs_file_name, 'w:gz') as f: + print(f"Compressing {model_name} model to {abs_file_name}") + with tarfile.open(abs_file_name, "w:gz") as f: f.add(model_dir, arcname=model_name) file_size = os.stat(abs_file_name).st_size - print(f'Uploading {abs_file_name} ({float(file_size) / 1024 / 1024} MB) to s3 cloud...') - client = boto3.client('s3', 'us-east-1') + print( + f"Uploading {abs_file_name} ({float(file_size) / 1024 / 1024} MB) to s3 cloud..." + ) + client = boto3.client("s3", "us-east-1") transfer = boto3.s3.transfer.S3Transfer(client) - transfer.upload_file(abs_file_name, 'download.onnx', f'models/latest/{rel_file_name}', - extra_args={'ACL': 'public-read'}) + transfer.upload_file( + abs_file_name, + "download.onnx", + f"models/latest/{rel_file_name}", + extra_args={"ACL": "public-read"}, + ) - print(f'Successfully uploaded {rel_file_name} to s3!') + print(f"Successfully uploaded {rel_file_name} to s3!") def download_onnx_model(model_name, zoo_dir, use_cache=True, only_local=False): @@ -75,19 +84,22 @@ def download_onnx_model(model_name, zoo_dir, use_cache=True, only_local=False): return else: shutil.rmtree(model_dir) - url = f'https://s3.amazonaws.com/download.onnx/models/latest/{model_name}.tar.gz' + url = f"https://s3.amazonaws.com/download.onnx/models/latest/{model_name}.tar.gz" download_file = tempfile.NamedTemporaryFile(delete=False) try: download_file.close() - print('Downloading ONNX model {} from {} and save in {} ...\n'.format( - model_name, url, download_file.name)) + print( + "Downloading ONNX model {} from {} and save in {} ...\n".format( + model_name, url, download_file.name + ) + ) urlretrieve(url, download_file.name) with tarfile.open(download_file.name) as t: - print(f'Extracting ONNX model {model_name} to {zoo_dir} ...\n') + print(f"Extracting ONNX model {model_name} to {zoo_dir} ...\n") t.extractall(zoo_dir) except Exception as e: - print(f'Failed to download/backup data for ONNX model {model_name}: {e}') + print(f"Failed to download/backup data for ONNX model {model_name}: {e}") if not os.path.exists(model_dir): os.makedirs(model_dir) finally: @@ -106,13 +118,12 @@ def download_caffe2_model(model_name, zoo_dir, use_cache=True): shutil.rmtree(model_dir) os.makedirs(model_dir) - for f in ['predict_net.pb', 'init_net.pb', 'value_info.json']: + for f in ["predict_net.pb", "init_net.pb", "value_info.json"]: url = getURLFromName(model_name, f) dest = os.path.join(model_dir, f) try: try: - downloadFromURLToFile(url, dest, - show_progress=False) + downloadFromURLToFile(url, dest, show_progress=False) except TypeError: # show_progress not supported prior to # Caffe2 78c014e752a374d905ecfb465d44fa16e02a28f1 @@ -129,20 +140,22 @@ def caffe2_to_onnx(caffe2_model_name, caffe2_model_dir): caffe2_init_proto = caffe2_pb2.NetDef() caffe2_predict_proto = caffe2_pb2.NetDef() - with open(os.path.join(caffe2_model_dir, 'init_net.pb'), 'rb') as f: + with open(os.path.join(caffe2_model_dir, "init_net.pb"), "rb") as f: caffe2_init_proto.ParseFromString(f.read()) - caffe2_init_proto.name = f'{caffe2_model_name}_init' - with open(os.path.join(caffe2_model_dir, 'predict_net.pb'), 'rb') as f: + caffe2_init_proto.name = f"{caffe2_model_name}_init" + with open(os.path.join(caffe2_model_dir, "predict_net.pb"), "rb") as f: caffe2_predict_proto.ParseFromString(f.read()) caffe2_predict_proto.name = caffe2_model_name - with open(os.path.join(caffe2_model_dir, 'value_info.json'), 'rb') as f: + with open(os.path.join(caffe2_model_dir, "value_info.json"), "rb") as f: value_info = json.loads(f.read()) - print(f'Converting Caffe2 model {caffe2_model_name} in {caffe2_model_dir} to ONNX format') + print( + f"Converting Caffe2 model {caffe2_model_name} in {caffe2_model_dir} to ONNX format" + ) onnx_model = caffe2.python.onnx.frontend.caffe2_net_to_onnx_model( init_net=caffe2_init_proto, predict_net=caffe2_predict_proto, - value_info=value_info + value_info=value_info, ) return onnx_model, caffe2_init_proto, caffe2_predict_proto @@ -163,7 +176,10 @@ def tensortype_to_ndarray(tensor_type): def generate_test_input_data(onnx_model, scale): - real_inputs_names = list({input.name for input in onnx_model.graph.input} - {init.name for init in onnx_model.graph.initializer}) + real_inputs_names = list( + {input.name for input in onnx_model.graph.input} + - {init.name for init in onnx_model.graph.initializer} + ) real_inputs = [] for name in real_inputs_names: for input in onnx_model.graph.input: @@ -180,7 +196,7 @@ def generate_test_input_data(onnx_model, scale): def generate_test_output_data(caffe2_init_net, caffe2_predict_net, inputs): p = c2_workspace.Predictor(caffe2_init_net, caffe2_predict_net) - inputs_map = {input[0]:input[1] for input in inputs} + inputs_map = {input[0]: input[1] for input in inputs} output = p.run(inputs_map) c2_workspace.ResetWorkspace() @@ -200,37 +216,53 @@ def onnx_verify(onnx_model, inputs, ref_outputs): model_mapping = { - 'bvlc_alexnet': 'bvlc_alexnet', - 'bvlc_googlenet': 'bvlc_googlenet', - 'bvlc_reference_caffenet': 'bvlc_reference_caffenet', - 'bvlc_reference_rcnn_ilsvrc13': 'bvlc_reference_rcnn_ilsvrc13', - 'densenet121': 'densenet121', + "bvlc_alexnet": "bvlc_alexnet", + "bvlc_googlenet": "bvlc_googlenet", + "bvlc_reference_caffenet": "bvlc_reference_caffenet", + "bvlc_reference_rcnn_ilsvrc13": "bvlc_reference_rcnn_ilsvrc13", + "densenet121": "densenet121", #'finetune_flickr_style': 'finetune_flickr_style', - 'inception_v1': 'inception_v1', - 'inception_v2': 'inception_v2', - 'resnet50': 'resnet50', - 'shufflenet': 'shufflenet', - 'squeezenet': 'squeezenet_old', + "inception_v1": "inception_v1", + "inception_v2": "inception_v2", + "resnet50": "resnet50", + "shufflenet": "shufflenet", + "squeezenet": "squeezenet_old", #'vgg16': 'vgg16', - 'vgg19': 'vgg19', - 'zfnet512': 'zfnet512', + "vgg19": "vgg19", + "zfnet512": "zfnet512", } - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Update the ONNX models.') - parser.add_argument('-v', action="store_true", default=False, help="verbose") - parser.add_argument("--local-dir", type=str, default=os.path.expanduser('~'), - help="local dir to store Caffe2 and ONNX models") - parser.add_argument("--no-cache", action="store_true", default=False, - help="whether use local ONNX models") - parser.add_argument('--clean-test-data', action="store_true", default=False, - help="remove the old test data") - parser.add_argument('--add-test-data', type=int, default=0, - help="add new test data") - parser.add_argument('--only-local', action="store_true", default=False, - help="no upload including backup") +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Update the ONNX models.") + parser.add_argument("-v", action="store_true", default=False, help="verbose") + parser.add_argument( + "--local-dir", + type=str, + default=os.path.expanduser("~"), + help="local dir to store Caffe2 and ONNX models", + ) + parser.add_argument( + "--no-cache", + action="store_true", + default=False, + help="whether use local ONNX models", + ) + parser.add_argument( + "--clean-test-data", + action="store_true", + default=False, + help="remove the old test data", + ) + parser.add_argument( + "--add-test-data", type=int, default=0, help="add new test data" + ) + parser.add_argument( + "--only-local", + action="store_true", + default=False, + help="no upload including backup", + ) args = parser.parse_args() delete_test_data = args.clean_test_data @@ -245,87 +277,99 @@ if __name__ == '__main__': for onnx_model_name in model_mapping: c2_model_name = model_mapping[onnx_model_name] - print(f'####### Processing ONNX model {onnx_model_name} ({c2_model_name} in Caffe2) #######') + print( + f"####### Processing ONNX model {onnx_model_name} ({c2_model_name} in Caffe2) #######" + ) download_caffe2_model(c2_model_name, caffe2_zoo_dir, use_cache=use_cache) - download_onnx_model(onnx_model_name, onnx_zoo_dir, use_cache=use_cache, only_local=only_local) + download_onnx_model( + onnx_model_name, onnx_zoo_dir, use_cache=use_cache, only_local=only_local + ) onnx_model_dir = os.path.join(onnx_zoo_dir, onnx_model_name) if delete_test_data: - print('Deleting all the existing test data...') + print("Deleting all the existing test data...") # NB: For now, we don't delete the npz files. - #for f in glob.glob(os.path.join(onnx_model_dir, '*.npz')): + # for f in glob.glob(os.path.join(onnx_model_dir, '*.npz')): # os.remove(f) - for f in glob.glob(os.path.join(onnx_model_dir, 'test_data_set*')): + for f in glob.glob(os.path.join(onnx_model_dir, "test_data_set*")): shutil.rmtree(f) - onnx_model, c2_init_net, c2_predict_net = caffe2_to_onnx(c2_model_name, os.path.join(caffe2_zoo_dir, c2_model_name)) + onnx_model, c2_init_net, c2_predict_net = caffe2_to_onnx( + c2_model_name, os.path.join(caffe2_zoo_dir, c2_model_name) + ) - print(f'Deleteing old ONNX {onnx_model_name} model...') - for f in glob.glob(os.path.join(onnx_model_dir, 'model*'.format(onnx_model_name))): + print(f"Deleteing old ONNX {onnx_model_name} model...") + for f in glob.glob( + os.path.join(onnx_model_dir, "model*".format(onnx_model_name)) + ): os.remove(f) - print(f'Serializing generated ONNX {onnx_model_name} model ...') - with open(os.path.join(onnx_model_dir, 'model.onnx'), 'wb') as file: + print(f"Serializing generated ONNX {onnx_model_name} model ...") + with open(os.path.join(onnx_model_dir, "model.onnx"), "wb") as file: file.write(onnx_model.SerializeToString()) - print(f'Verifying model {onnx_model_name} with ONNX model checker...') + print(f"Verifying model {onnx_model_name} with ONNX model checker...") onnx.checker.check_model(onnx_model) total_existing_data_set = 0 - print(f'Verifying model {onnx_model_name} with existing test data...') - for f in glob.glob(os.path.join(onnx_model_dir, '*.npz')): - test_data = np.load(f, encoding='bytes') - inputs = list(test_data['inputs']) - ref_outputs = list(test_data['outputs']) + print(f"Verifying model {onnx_model_name} with existing test data...") + for f in glob.glob(os.path.join(onnx_model_dir, "*.npz")): + test_data = np.load(f, encoding="bytes") + inputs = list(test_data["inputs"]) + ref_outputs = list(test_data["outputs"]) onnx_verify(onnx_model, inputs, ref_outputs) total_existing_data_set += 1 - for f in glob.glob(os.path.join(onnx_model_dir, 'test_data_set*')): + for f in glob.glob(os.path.join(onnx_model_dir, "test_data_set*")): inputs = [] - inputs_num = len(glob.glob(os.path.join(f, 'input_*.pb'))) + inputs_num = len(glob.glob(os.path.join(f, "input_*.pb"))) for i in range(inputs_num): tensor = onnx.TensorProto() - with open(os.path.join(f, f'input_{i}.pb'), 'rb') as pf: + with open(os.path.join(f, f"input_{i}.pb"), "rb") as pf: tensor.ParseFromString(pf.read()) inputs.append(numpy_helper.to_array(tensor)) ref_outputs = [] - ref_outputs_num = len(glob.glob(os.path.join(f, 'output_*.pb'))) + ref_outputs_num = len(glob.glob(os.path.join(f, "output_*.pb"))) for i in range(ref_outputs_num): tensor = onnx.TensorProto() - with open(os.path.join(f, f'output_{i}.pb'), 'rb') as pf: + with open(os.path.join(f, f"output_{i}.pb"), "rb") as pf: tensor.ParseFromString(pf.read()) ref_outputs.append(numpy_helper.to_array(tensor)) onnx_verify(onnx_model, inputs, ref_outputs) total_existing_data_set += 1 starting_index = 0 - while os.path.exists(os.path.join(onnx_model_dir, f'test_data_set_{starting_index}')): + while os.path.exists( + os.path.join(onnx_model_dir, f"test_data_set_{starting_index}") + ): starting_index += 1 if total_existing_data_set == 0 and add_test_data == 0: add_test_data = 3 total_existing_data_set = 3 - print(f'Generating {add_test_data} sets of new test data...') + print(f"Generating {add_test_data} sets of new test data...") for i in range(starting_index, add_test_data + starting_index): - data_dir = os.path.join(onnx_model_dir, f'test_data_set_{i}') + data_dir = os.path.join(onnx_model_dir, f"test_data_set_{i}") os.makedirs(data_dir) inputs = generate_test_input_data(onnx_model, 255) ref_outputs = generate_test_output_data(c2_init_net, c2_predict_net, inputs) onnx_verify(onnx_model, inputs, ref_outputs) for index, input in enumerate(inputs): tensor = numpy_helper.from_array(input[1]) - with open(os.path.join(data_dir, f'input_{index}.pb'), 'wb') as file: + with open(os.path.join(data_dir, f"input_{index}.pb"), "wb") as file: file.write(tensor.SerializeToString()) for index, output in enumerate(ref_outputs): tensor = numpy_helper.from_array(output) - with open(os.path.join(data_dir, f'output_{index}.pb'), 'wb') as file: + with open(os.path.join(data_dir, f"output_{index}.pb"), "wb") as file: file.write(tensor.SerializeToString()) del onnx_model del c2_init_net del c2_predict_net - upload_onnx_model(onnx_model_name, onnx_zoo_dir, backup=False, only_local=only_local) + upload_onnx_model( + onnx_model_name, onnx_zoo_dir, backup=False, only_local=only_local + ) - print('\n\n') + print("\n\n") diff --git a/scripts/release_notes/apply_categories.py b/scripts/release_notes/apply_categories.py index b656f83c1e1..5fd2928c89c 100644 --- a/scripts/release_notes/apply_categories.py +++ b/scripts/release_notes/apply_categories.py @@ -2,9 +2,10 @@ # base commitlist . Useful if you are refactoring any code # but want to keep the previous data on categories -import commitlist import csv +import commitlist + category_csv = "results/category_data.csv" commitlist_csv = "results/commitlist.csv" diff --git a/scripts/release_notes/categorize.py b/scripts/release_notes/categorize.py index e7be5a5315a..0187637e7b8 100644 --- a/scripts/release_notes/categorize.py +++ b/scripts/release_notes/categorize.py @@ -1,27 +1,44 @@ import argparse import os import textwrap -from common import topics, get_commit_data_cache -from commitlist import CommitList - -# Imports for working with classi -from classifier import CommitClassifier, CategoryConfig, XLMR_BASE, get_author_map, get_file_map, CommitClassifierInputs -import common -import torch from pathlib import Path +import common +import torch + +# Imports for working with classi +from classifier import ( + CategoryConfig, + CommitClassifier, + CommitClassifierInputs, + get_author_map, + get_file_map, + XLMR_BASE, +) +from commitlist import CommitList +from common import get_commit_data_cache, topics + + class Categorizer: - def __init__(self, path, category='Uncategorized', use_classifier:bool = False): + def __init__(self, path, category="Uncategorized", use_classifier: bool = False): self.cache = get_commit_data_cache() self.commits = CommitList.from_existing(path) if use_classifier: print("Using a classifier to aid with categorization.") device = "cuda" if torch.cuda.is_available() else "cpu" classifier_config = CategoryConfig(common.categories) - author_map = get_author_map(Path("results/classifier"), regen_data=False, assert_stored=True) - file_map = get_file_map(Path("results/classifier"), regen_data=False, assert_stored=True) - self.classifier = CommitClassifier(XLMR_BASE, author_map, file_map, classifier_config).to(device) - self.classifier.load_state_dict(torch.load(Path("results/classifier/commit_classifier.pt"))) + author_map = get_author_map( + Path("results/classifier"), regen_data=False, assert_stored=True + ) + file_map = get_file_map( + Path("results/classifier"), regen_data=False, assert_stored=True + ) + self.classifier = CommitClassifier( + XLMR_BASE, author_map, file_map, classifier_config + ).to(device) + self.classifier.load_state_dict( + torch.load(Path("results/classifier/commit_classifier.pt")) + ) self.classifier.eval() else: self.classifier = None @@ -37,7 +54,9 @@ class Categorizer: while i < len(commits): cur_commit = commits[i] next_commit = commits[i + 1] if i + 1 < len(commits) else None - jump_to = self.handle_commit(cur_commit, already_done + i + 1, total_commits, commits) + jump_to = self.handle_commit( + cur_commit, already_done + i + 1, total_commits, commits + ) # Increment counter if jump_to is not None: @@ -51,15 +70,17 @@ class Categorizer: return self.cache.get(commit.commit_hash) def potential_reverts_of(self, commit, commits): - submodule_update_str = ['Update TensorPipe submodule', - 'Updating submodules', - 'Automated submodule update'] + submodule_update_str = [ + "Update TensorPipe submodule", + "Updating submodules", + "Automated submodule update", + ] if any(a in commit.title for a in submodule_update_str): return [] features = self.features(commit) - if 'Reverted' in features.labels: - reasons = {'GithubBot': "Reverted"} + if "Reverted" in features.labels: + reasons = {"GithubBot": "Reverted"} else: reasons = {} @@ -67,15 +88,20 @@ class Categorizer: # -8 to remove the (#35011) cleaned_title = commit.title[:-10] # NB: the index + 2 is sketch - reasons.update({(index + 2 + delta): cand for delta, cand in enumerate(commits[index + 1:]) - if cleaned_title in cand.title and - commit.commit_hash != cand.commit_hash}) + reasons.update( + { + (index + 2 + delta): cand + for delta, cand in enumerate(commits[index + 1 :]) + if cleaned_title in cand.title + and commit.commit_hash != cand.commit_hash + } + ) return reasons def handle_commit(self, commit, i, total, commits): potential_reverts = self.potential_reverts_of(commit, commits) if potential_reverts: - potential_reverts = f'!!!POTENTIAL REVERTS!!!: {potential_reverts}' + potential_reverts = f"!!!POTENTIAL REVERTS!!!: {potential_reverts}" else: potential_reverts = "" @@ -83,22 +109,27 @@ class Categorizer: if self.classifier is not None: # Some commits don't have authors: author = features.author if features.author else "Unknown" - files = ' '.join(features.files_changed) - classifier_input = CommitClassifierInputs(title=[features.title], files=[files], author=[author]) - classifier_category = self.classifier.get_most_likely_category_name(classifier_input)[0] + files = " ".join(features.files_changed) + classifier_input = CommitClassifierInputs( + title=[features.title], files=[files], author=[author] + ) + classifier_category = self.classifier.get_most_likely_category_name( + classifier_input + )[0] else: classifier_category = commit.category breaking_alarm = "" - if 'module: bc-breaking' in features.labels: + if "module: bc-breaking" in features.labels: breaking_alarm += "\n!!!!!! BC BREAKING !!!!!!" - if 'module: deprecation' in features.labels: + if "module: deprecation" in features.labels: breaking_alarm += "\n!!!!!! DEPRECATION !!!!!!" - os.system('clear') - view = textwrap.dedent(f'''\ + os.system("clear") + view = textwrap.dedent( + f"""\ [{i}/{total}] ================================================================================ {features.title} @@ -115,38 +146,37 @@ Current category: {commit.category} Select from: {', '.join(common.categories)} - ''') + """ + ) print(view) cat_choice = None while cat_choice is None: print("Enter category: ") - value = input(f'{classifier_category} ').strip() + value = input(f"{classifier_category} ").strip() if len(value) == 0: # The user just pressed enter and likes the default value cat_choice = classifier_category continue - choices = [cat for cat in common.categories - if cat.startswith(value)] + choices = [cat for cat in common.categories if cat.startswith(value)] if len(choices) != 1: - print(f'Possible matches: {choices}, try again') + print(f"Possible matches: {choices}, try again") continue cat_choice = choices[0] - print(f'\nSelected: {cat_choice}') - print(f'\nCurrent topic: {commit.topic}') - print(f'''Select from: {', '.join(topics)}''') + print(f"\nSelected: {cat_choice}") + print(f"\nCurrent topic: {commit.topic}") + print(f"""Select from: {', '.join(topics)}""") topic_choice = None while topic_choice is None: - value = input('topic> ').strip() + value = input("topic> ").strip() if len(value) == 0: topic_choice = commit.topic continue - choices = [cat for cat in topics - if cat.startswith(value)] + choices = [cat for cat in topics if cat.startswith(value)] if len(choices) != 1: - print(f'Possible matches: {choices}, try again') + print(f"Possible matches: {choices}, try again") continue topic_choice = choices[0] - print(f'\nSelected: {topic_choice}') + print(f"\nSelected: {topic_choice}") self.update_commit(commit, cat_choice, topic_choice) return None @@ -157,18 +187,30 @@ Select from: {', '.join(common.categories)} commit.topic = topic self.commits.write_result() + def main(): - parser = argparse.ArgumentParser(description='Tool to help categorize commits') - parser.add_argument('--category', type=str, default='Uncategorized', - help='Which category to filter by. "Uncategorized", None, or a category name') - parser.add_argument('--file', help='The location of the commits CSV', - default='results/commitlist.csv') - parser.add_argument('--use_classifier', action='store_true', help="Whether or not to use a classifier to aid in categorization.") + parser = argparse.ArgumentParser(description="Tool to help categorize commits") + parser.add_argument( + "--category", + type=str, + default="Uncategorized", + help='Which category to filter by. "Uncategorized", None, or a category name', + ) + parser.add_argument( + "--file", + help="The location of the commits CSV", + default="results/commitlist.csv", + ) + parser.add_argument( + "--use_classifier", + action="store_true", + help="Whether or not to use a classifier to aid in categorization.", + ) args = parser.parse_args() categorizer = Categorizer(args.file, args.category, args.use_classifier) categorizer.categorize() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/release_notes/classifier.py b/scripts/release_notes/classifier.py index 1a3a55626bb..b906c3849fd 100644 --- a/scripts/release_notes/classifier.py +++ b/scripts/release_notes/classifier.py @@ -1,20 +1,20 @@ import argparse -from pathlib import Path -import torch -import torchtext -from torchtext.functional import to_tensor -import torch.nn as nn -import torch.nn.functional as F -from typing import List, Dict -import pandas as pd -from dataclasses import dataclass import math import pickle import random -from tqdm import tqdm +from dataclasses import dataclass from itertools import chain +from pathlib import Path +from typing import Dict, List import common +import pandas as pd +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchtext +from torchtext.functional import to_tensor +from tqdm import tqdm XLMR_BASE = torchtext.models.XLMR_BASE_ENCODER @@ -24,6 +24,7 @@ device = "cuda" if torch.cuda.is_available() else "cpu" HAS_IMBLEARN = False try: import imblearn + HAS_IMBLEARN = True except ImportError: HAS_IMBLEARN = False @@ -37,12 +38,14 @@ UNKNOWN_TOKEN = "