1*da0073e9SAndroid Build Coastguard Workerimport json 2*da0073e9SAndroid Build Coastguard Workerimport locale 3*da0073e9SAndroid Build Coastguard Workerimport os 4*da0073e9SAndroid Build Coastguard Workerimport re 5*da0073e9SAndroid Build Coastguard Workerimport subprocess 6*da0073e9SAndroid Build Coastguard Workerfrom collections import namedtuple 7*da0073e9SAndroid Build Coastguard Workerfrom dataclasses import dataclass 8*da0073e9SAndroid Build Coastguard Workerfrom pathlib import Path 9*da0073e9SAndroid Build Coastguard Worker 10*da0073e9SAndroid Build Coastguard Workerimport requests 11*da0073e9SAndroid Build Coastguard Worker 12*da0073e9SAndroid Build Coastguard Worker 13*da0073e9SAndroid Build Coastguard Worker@dataclass 14*da0073e9SAndroid Build Coastguard Workerclass CategoryGroup: 15*da0073e9SAndroid Build Coastguard Worker name: str 16*da0073e9SAndroid Build Coastguard Worker categories: list 17*da0073e9SAndroid Build Coastguard Worker 18*da0073e9SAndroid Build Coastguard Worker 19*da0073e9SAndroid Build Coastguard Workerfrontend_categories = [ 20*da0073e9SAndroid Build Coastguard Worker "meta", 21*da0073e9SAndroid Build Coastguard Worker "nn", 22*da0073e9SAndroid Build Coastguard Worker "linalg", 23*da0073e9SAndroid Build Coastguard Worker "cpp", 24*da0073e9SAndroid Build Coastguard Worker "python", 25*da0073e9SAndroid Build Coastguard Worker "complex", 26*da0073e9SAndroid Build Coastguard Worker "vmap", 27*da0073e9SAndroid Build Coastguard Worker "autograd", 28*da0073e9SAndroid Build Coastguard Worker "build", 29*da0073e9SAndroid Build Coastguard Worker "memory_format", 30*da0073e9SAndroid Build Coastguard Worker "foreach", 31*da0073e9SAndroid Build Coastguard Worker "dataloader", 32*da0073e9SAndroid Build Coastguard Worker "sparse", 33*da0073e9SAndroid Build Coastguard Worker "nested tensor", 34*da0073e9SAndroid Build Coastguard Worker "optimizer", 35*da0073e9SAndroid Build Coastguard Worker] 36*da0073e9SAndroid Build Coastguard Worker 37*da0073e9SAndroid Build Coastguard Workerpytorch_2_categories = [ 38*da0073e9SAndroid Build Coastguard Worker "dynamo", 39*da0073e9SAndroid Build Coastguard Worker "inductor", 40*da0073e9SAndroid Build Coastguard Worker] 41*da0073e9SAndroid Build Coastguard Worker 42*da0073e9SAndroid Build Coastguard Worker# These will all get mapped to quantization 43*da0073e9SAndroid Build Coastguard Workerquantization = CategoryGroup( 44*da0073e9SAndroid Build Coastguard Worker name="quantization", 45*da0073e9SAndroid Build Coastguard Worker categories=[ 46*da0073e9SAndroid Build Coastguard Worker "quantization", 47*da0073e9SAndroid Build Coastguard Worker "AO frontend", 48*da0073e9SAndroid Build Coastguard Worker "AO Pruning", 49*da0073e9SAndroid Build Coastguard Worker ], 50*da0073e9SAndroid Build Coastguard Worker) 51*da0073e9SAndroid Build Coastguard Worker 52*da0073e9SAndroid Build Coastguard Worker# Distributed has a number of release note labels we want to map to one 53*da0073e9SAndroid Build Coastguard Workerdistributed = CategoryGroup( 54*da0073e9SAndroid Build Coastguard Worker name="distributed", 55*da0073e9SAndroid Build Coastguard Worker categories=[ 56*da0073e9SAndroid Build Coastguard Worker "distributed", 57*da0073e9SAndroid Build Coastguard Worker "distributed (c10d)", 58*da0073e9SAndroid Build Coastguard Worker "distributed (composable)", 59*da0073e9SAndroid Build Coastguard Worker "distributed (ddp)", 60*da0073e9SAndroid Build Coastguard Worker "distributed (fsdp)", 61*da0073e9SAndroid Build Coastguard Worker "distributed (rpc)", 62*da0073e9SAndroid Build Coastguard Worker "distributed (sharded)", 63*da0073e9SAndroid Build Coastguard Worker ], 64*da0073e9SAndroid Build Coastguard Worker) 65*da0073e9SAndroid Build Coastguard Worker 66*da0073e9SAndroid Build Coastguard Workercategories = ( 67*da0073e9SAndroid Build Coastguard Worker [ 68*da0073e9SAndroid Build Coastguard Worker "Uncategorized", 69*da0073e9SAndroid Build Coastguard Worker "lazy", 70*da0073e9SAndroid Build Coastguard Worker "hub", 71*da0073e9SAndroid Build Coastguard Worker "mobile", 72*da0073e9SAndroid Build Coastguard Worker "jit", 73*da0073e9SAndroid Build Coastguard Worker "visualization", 74*da0073e9SAndroid Build Coastguard Worker "onnx", 75*da0073e9SAndroid Build Coastguard Worker "caffe2", 76*da0073e9SAndroid Build Coastguard Worker "amd", 77*da0073e9SAndroid Build Coastguard Worker "rocm", 78*da0073e9SAndroid Build Coastguard Worker "cuda", 79*da0073e9SAndroid Build Coastguard Worker "cpu", 80*da0073e9SAndroid Build Coastguard Worker "cudnn", 81*da0073e9SAndroid Build Coastguard Worker "xla", 82*da0073e9SAndroid Build Coastguard Worker "benchmark", 83*da0073e9SAndroid Build Coastguard Worker "profiler", 84*da0073e9SAndroid Build Coastguard Worker "performance_as_product", 85*da0073e9SAndroid Build Coastguard Worker "package", 86*da0073e9SAndroid Build Coastguard Worker "dispatcher", 87*da0073e9SAndroid Build Coastguard Worker "releng", 88*da0073e9SAndroid Build Coastguard Worker "fx", 89*da0073e9SAndroid Build Coastguard Worker "code_coverage", 90*da0073e9SAndroid Build Coastguard Worker "vulkan", 91*da0073e9SAndroid Build Coastguard Worker "skip", 92*da0073e9SAndroid Build Coastguard Worker "composability", 93*da0073e9SAndroid Build Coastguard Worker # 2.0 release 94*da0073e9SAndroid Build Coastguard Worker "mps", 95*da0073e9SAndroid Build Coastguard Worker "intel", 96*da0073e9SAndroid Build Coastguard Worker "functorch", 97*da0073e9SAndroid Build Coastguard Worker "gnn", 98*da0073e9SAndroid Build Coastguard Worker "distributions", 99*da0073e9SAndroid Build Coastguard Worker "serialization", 100*da0073e9SAndroid Build Coastguard Worker ] 101*da0073e9SAndroid Build Coastguard Worker + [f"{category}_frontend" for category in frontend_categories] 102*da0073e9SAndroid Build Coastguard Worker + pytorch_2_categories 103*da0073e9SAndroid Build Coastguard Worker + [quantization.name] 104*da0073e9SAndroid Build Coastguard Worker + [distributed.name] 105*da0073e9SAndroid Build Coastguard Worker) 106*da0073e9SAndroid Build Coastguard Worker 107*da0073e9SAndroid Build Coastguard Worker 108*da0073e9SAndroid Build Coastguard Workertopics = [ 109*da0073e9SAndroid Build Coastguard Worker "bc breaking", 110*da0073e9SAndroid Build Coastguard Worker "deprecation", 111*da0073e9SAndroid Build Coastguard Worker "new features", 112*da0073e9SAndroid Build Coastguard Worker "improvements", 113*da0073e9SAndroid Build Coastguard Worker "bug fixes", 114*da0073e9SAndroid Build Coastguard Worker "performance", 115*da0073e9SAndroid Build Coastguard Worker "docs", 116*da0073e9SAndroid Build Coastguard Worker "devs", 117*da0073e9SAndroid Build Coastguard Worker "Untopiced", 118*da0073e9SAndroid Build Coastguard Worker "not user facing", 119*da0073e9SAndroid Build Coastguard Worker "security", 120*da0073e9SAndroid Build Coastguard Worker] 121*da0073e9SAndroid Build Coastguard Worker 122*da0073e9SAndroid Build Coastguard Worker 123*da0073e9SAndroid Build Coastguard WorkerFeatures = namedtuple( 124*da0073e9SAndroid Build Coastguard Worker "Features", 125*da0073e9SAndroid Build Coastguard Worker ["title", "body", "pr_number", "files_changed", "labels", "author", "accepters"], 126*da0073e9SAndroid Build Coastguard Worker) 127*da0073e9SAndroid Build Coastguard Worker 128*da0073e9SAndroid Build Coastguard Worker 129*da0073e9SAndroid Build Coastguard Workerdef dict_to_features(dct): 130*da0073e9SAndroid Build Coastguard Worker return Features( 131*da0073e9SAndroid Build Coastguard Worker title=dct["title"], 132*da0073e9SAndroid Build Coastguard Worker body=dct["body"], 133*da0073e9SAndroid Build Coastguard Worker pr_number=dct["pr_number"], 134*da0073e9SAndroid Build Coastguard Worker files_changed=dct["files_changed"], 135*da0073e9SAndroid Build Coastguard Worker labels=dct["labels"], 136*da0073e9SAndroid Build Coastguard Worker author=dct["author"], 137*da0073e9SAndroid Build Coastguard Worker accepters=tuple(dct["accepters"]), 138*da0073e9SAndroid Build Coastguard Worker ) 139*da0073e9SAndroid Build Coastguard Worker 140*da0073e9SAndroid Build Coastguard Worker 141*da0073e9SAndroid Build Coastguard Workerdef features_to_dict(features): 142*da0073e9SAndroid Build Coastguard Worker return dict(features._asdict()) 143*da0073e9SAndroid Build Coastguard Worker 144*da0073e9SAndroid Build Coastguard Worker 145*da0073e9SAndroid Build Coastguard Workerdef run(command): 146*da0073e9SAndroid Build Coastguard Worker """Returns (return-code, stdout, stderr)""" 147*da0073e9SAndroid Build Coastguard Worker p = subprocess.Popen( 148*da0073e9SAndroid Build Coastguard Worker command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True 149*da0073e9SAndroid Build Coastguard Worker ) 150*da0073e9SAndroid Build Coastguard Worker output, err = p.communicate() 151*da0073e9SAndroid Build Coastguard Worker rc = p.returncode 152*da0073e9SAndroid Build Coastguard Worker enc = locale.getpreferredencoding() 153*da0073e9SAndroid Build Coastguard Worker output = output.decode(enc) 154*da0073e9SAndroid Build Coastguard Worker err = err.decode(enc) 155*da0073e9SAndroid Build Coastguard Worker return rc, output.strip(), err.strip() 156*da0073e9SAndroid Build Coastguard Worker 157*da0073e9SAndroid Build Coastguard Worker 158*da0073e9SAndroid Build Coastguard Workerdef commit_body(commit_hash): 159*da0073e9SAndroid Build Coastguard Worker cmd = f"git log -n 1 --pretty=format:%b {commit_hash}" 160*da0073e9SAndroid Build Coastguard Worker ret, out, err = run(cmd) 161*da0073e9SAndroid Build Coastguard Worker return out if ret == 0 else None 162*da0073e9SAndroid Build Coastguard Worker 163*da0073e9SAndroid Build Coastguard Worker 164*da0073e9SAndroid Build Coastguard Workerdef commit_title(commit_hash): 165*da0073e9SAndroid Build Coastguard Worker cmd = f"git log -n 1 --pretty=format:%s {commit_hash}" 166*da0073e9SAndroid Build Coastguard Worker ret, out, err = run(cmd) 167*da0073e9SAndroid Build Coastguard Worker return out if ret == 0 else None 168*da0073e9SAndroid Build Coastguard Worker 169*da0073e9SAndroid Build Coastguard Worker 170*da0073e9SAndroid Build Coastguard Workerdef commit_files_changed(commit_hash): 171*da0073e9SAndroid Build Coastguard Worker cmd = f"git diff-tree --no-commit-id --name-only -r {commit_hash}" 172*da0073e9SAndroid Build Coastguard Worker ret, out, err = run(cmd) 173*da0073e9SAndroid Build Coastguard Worker return out.split("\n") if ret == 0 else None 174*da0073e9SAndroid Build Coastguard Worker 175*da0073e9SAndroid Build Coastguard Worker 176*da0073e9SAndroid Build Coastguard Workerdef parse_pr_number(body, commit_hash, title): 177*da0073e9SAndroid Build Coastguard Worker regex = r"Pull Request resolved: https://github.com/pytorch/pytorch/pull/([0-9]+)" 178*da0073e9SAndroid Build Coastguard Worker matches = re.findall(regex, body) 179*da0073e9SAndroid Build Coastguard Worker if len(matches) == 0: 180*da0073e9SAndroid Build Coastguard Worker if "revert" not in title.lower() and "updating submodules" not in title.lower(): 181*da0073e9SAndroid Build Coastguard Worker print(f"[{commit_hash}: {title}] Could not parse PR number, ignoring PR") 182*da0073e9SAndroid Build Coastguard Worker return None 183*da0073e9SAndroid Build Coastguard Worker if len(matches) > 1: 184*da0073e9SAndroid Build Coastguard Worker print(f"[{commit_hash}: {title}] Got two PR numbers, using the first one") 185*da0073e9SAndroid Build Coastguard Worker return matches[0] 186*da0073e9SAndroid Build Coastguard Worker return matches[0] 187*da0073e9SAndroid Build Coastguard Worker 188*da0073e9SAndroid Build Coastguard Worker 189*da0073e9SAndroid Build Coastguard Workerdef get_ghstack_token(): 190*da0073e9SAndroid Build Coastguard Worker pattern = "github_oauth = (.*)" 191*da0073e9SAndroid Build Coastguard Worker with open(Path("~/.ghstackrc").expanduser(), "r+") as f: 192*da0073e9SAndroid Build Coastguard Worker config = f.read() 193*da0073e9SAndroid Build Coastguard Worker matches = re.findall(pattern, config) 194*da0073e9SAndroid Build Coastguard Worker if len(matches) == 0: 195*da0073e9SAndroid Build Coastguard Worker raise RuntimeError("Can't find a github oauth token") 196*da0073e9SAndroid Build Coastguard Worker return matches[0] 197*da0073e9SAndroid Build Coastguard Worker 198*da0073e9SAndroid Build Coastguard Worker 199*da0073e9SAndroid Build Coastguard Workerdef get_token(): 200*da0073e9SAndroid Build Coastguard Worker env_token = os.environ.get("GITHUB_TOKEN") 201*da0073e9SAndroid Build Coastguard Worker if env_token is not None: 202*da0073e9SAndroid Build Coastguard Worker print("using GITHUB_TOKEN from environment variable") 203*da0073e9SAndroid Build Coastguard Worker return env_token 204*da0073e9SAndroid Build Coastguard Worker else: 205*da0073e9SAndroid Build Coastguard Worker return get_ghstack_token() 206*da0073e9SAndroid Build Coastguard Worker 207*da0073e9SAndroid Build Coastguard Worker 208*da0073e9SAndroid Build Coastguard Workertoken = get_token() 209*da0073e9SAndroid Build Coastguard Worker 210*da0073e9SAndroid Build Coastguard Workerheaders = {"Authorization": f"token {token}"} 211*da0073e9SAndroid Build Coastguard Worker 212*da0073e9SAndroid Build Coastguard Worker 213*da0073e9SAndroid Build Coastguard Workerdef run_query(query): 214*da0073e9SAndroid Build Coastguard Worker request = requests.post( 215*da0073e9SAndroid Build Coastguard Worker "https://api.github.com/graphql", json={"query": query}, headers=headers 216*da0073e9SAndroid Build Coastguard Worker ) 217*da0073e9SAndroid Build Coastguard Worker if request.status_code == 200: 218*da0073e9SAndroid Build Coastguard Worker return request.json() 219*da0073e9SAndroid Build Coastguard Worker else: 220*da0073e9SAndroid Build Coastguard Worker raise Exception( # noqa: TRY002 221*da0073e9SAndroid Build Coastguard Worker f"Query failed to run by returning code of {request.status_code}. {request.json()}" 222*da0073e9SAndroid Build Coastguard Worker ) 223*da0073e9SAndroid Build Coastguard Worker 224*da0073e9SAndroid Build Coastguard Worker 225*da0073e9SAndroid Build Coastguard Worker_ERRORS = [] 226*da0073e9SAndroid Build Coastguard Worker_MAX_ERROR_LEN = 20 227*da0073e9SAndroid Build Coastguard Worker 228*da0073e9SAndroid Build Coastguard Worker 229*da0073e9SAndroid Build Coastguard Workerdef github_data(pr_number): 230*da0073e9SAndroid Build Coastguard Worker query = ( 231*da0073e9SAndroid Build Coastguard Worker """ 232*da0073e9SAndroid Build Coastguard Worker { 233*da0073e9SAndroid Build Coastguard Worker repository(owner: "pytorch", name: "pytorch") { 234*da0073e9SAndroid Build Coastguard Worker pullRequest(number: %s ) { 235*da0073e9SAndroid Build Coastguard Worker author { 236*da0073e9SAndroid Build Coastguard Worker login 237*da0073e9SAndroid Build Coastguard Worker } 238*da0073e9SAndroid Build Coastguard Worker reviews(last: 5, states: APPROVED) { 239*da0073e9SAndroid Build Coastguard Worker nodes { 240*da0073e9SAndroid Build Coastguard Worker author { 241*da0073e9SAndroid Build Coastguard Worker login 242*da0073e9SAndroid Build Coastguard Worker } 243*da0073e9SAndroid Build Coastguard Worker } 244*da0073e9SAndroid Build Coastguard Worker } 245*da0073e9SAndroid Build Coastguard Worker labels(first: 10) { 246*da0073e9SAndroid Build Coastguard Worker edges { 247*da0073e9SAndroid Build Coastguard Worker node { 248*da0073e9SAndroid Build Coastguard Worker name 249*da0073e9SAndroid Build Coastguard Worker } 250*da0073e9SAndroid Build Coastguard Worker } 251*da0073e9SAndroid Build Coastguard Worker } 252*da0073e9SAndroid Build Coastguard Worker } 253*da0073e9SAndroid Build Coastguard Worker } 254*da0073e9SAndroid Build Coastguard Worker } 255*da0073e9SAndroid Build Coastguard Worker """ # noqa: UP031 256*da0073e9SAndroid Build Coastguard Worker % pr_number 257*da0073e9SAndroid Build Coastguard Worker ) 258*da0073e9SAndroid Build Coastguard Worker query = run_query(query) 259*da0073e9SAndroid Build Coastguard Worker if query.get("errors"): 260*da0073e9SAndroid Build Coastguard Worker global _ERRORS 261*da0073e9SAndroid Build Coastguard Worker _ERRORS.append(query.get("errors")) 262*da0073e9SAndroid Build Coastguard Worker if len(_ERRORS) < _MAX_ERROR_LEN: 263*da0073e9SAndroid Build Coastguard Worker return [], "None", () 264*da0073e9SAndroid Build Coastguard Worker else: 265*da0073e9SAndroid Build Coastguard Worker raise Exception( # noqa: TRY002 266*da0073e9SAndroid Build Coastguard Worker f"Got {_MAX_ERROR_LEN} errors: {_ERRORS}, please check if" 267*da0073e9SAndroid Build Coastguard Worker " there is something wrong" 268*da0073e9SAndroid Build Coastguard Worker ) 269*da0073e9SAndroid Build Coastguard Worker edges = query["data"]["repository"]["pullRequest"]["labels"]["edges"] 270*da0073e9SAndroid Build Coastguard Worker labels = [edge["node"]["name"] for edge in edges] 271*da0073e9SAndroid Build Coastguard Worker author = query["data"]["repository"]["pullRequest"]["author"]["login"] 272*da0073e9SAndroid Build Coastguard Worker nodes = query["data"]["repository"]["pullRequest"]["reviews"]["nodes"] 273*da0073e9SAndroid Build Coastguard Worker 274*da0073e9SAndroid Build Coastguard Worker # using set to dedup multiple accepts from same accepter 275*da0073e9SAndroid Build Coastguard Worker accepters = {node["author"]["login"] for node in nodes} 276*da0073e9SAndroid Build Coastguard Worker accepters = tuple(sorted(accepters)) 277*da0073e9SAndroid Build Coastguard Worker 278*da0073e9SAndroid Build Coastguard Worker return labels, author, accepters 279*da0073e9SAndroid Build Coastguard Worker 280*da0073e9SAndroid Build Coastguard Worker 281*da0073e9SAndroid Build Coastguard Workerdef get_features(commit_hash): 282*da0073e9SAndroid Build Coastguard Worker title, body, files_changed = ( 283*da0073e9SAndroid Build Coastguard Worker commit_title(commit_hash), 284*da0073e9SAndroid Build Coastguard Worker commit_body(commit_hash), 285*da0073e9SAndroid Build Coastguard Worker commit_files_changed(commit_hash), 286*da0073e9SAndroid Build Coastguard Worker ) 287*da0073e9SAndroid Build Coastguard Worker pr_number = parse_pr_number(body, commit_hash, title) 288*da0073e9SAndroid Build Coastguard Worker labels = [] 289*da0073e9SAndroid Build Coastguard Worker author = "" 290*da0073e9SAndroid Build Coastguard Worker accepters = () 291*da0073e9SAndroid Build Coastguard Worker if pr_number is not None: 292*da0073e9SAndroid Build Coastguard Worker labels, author, accepters = github_data(pr_number) 293*da0073e9SAndroid Build Coastguard Worker result = Features(title, body, pr_number, files_changed, labels, author, accepters) 294*da0073e9SAndroid Build Coastguard Worker return result 295*da0073e9SAndroid Build Coastguard Worker 296*da0073e9SAndroid Build Coastguard Worker 297*da0073e9SAndroid Build Coastguard Worker_commit_data_cache = None 298*da0073e9SAndroid Build Coastguard Worker 299*da0073e9SAndroid Build Coastguard Worker 300*da0073e9SAndroid Build Coastguard Workerdef get_commit_data_cache(path="results/data.json"): 301*da0073e9SAndroid Build Coastguard Worker global _commit_data_cache 302*da0073e9SAndroid Build Coastguard Worker if _commit_data_cache is None: 303*da0073e9SAndroid Build Coastguard Worker _commit_data_cache = _CommitDataCache(path) 304*da0073e9SAndroid Build Coastguard Worker return _commit_data_cache 305*da0073e9SAndroid Build Coastguard Worker 306*da0073e9SAndroid Build Coastguard Worker 307*da0073e9SAndroid Build Coastguard Workerclass _CommitDataCache: 308*da0073e9SAndroid Build Coastguard Worker def __init__(self, path): 309*da0073e9SAndroid Build Coastguard Worker self.path = path 310*da0073e9SAndroid Build Coastguard Worker self.data = {} 311*da0073e9SAndroid Build Coastguard Worker if os.path.exists(path): 312*da0073e9SAndroid Build Coastguard Worker self.data = self.read_from_disk() 313*da0073e9SAndroid Build Coastguard Worker else: 314*da0073e9SAndroid Build Coastguard Worker os.makedirs(Path(path).parent, exist_ok=True) 315*da0073e9SAndroid Build Coastguard Worker 316*da0073e9SAndroid Build Coastguard Worker def get(self, commit): 317*da0073e9SAndroid Build Coastguard Worker if commit not in self.data.keys(): 318*da0073e9SAndroid Build Coastguard Worker # Fetch and cache the data 319*da0073e9SAndroid Build Coastguard Worker self.data[commit] = get_features(commit) 320*da0073e9SAndroid Build Coastguard Worker self.write_to_disk() 321*da0073e9SAndroid Build Coastguard Worker return self.data[commit] 322*da0073e9SAndroid Build Coastguard Worker 323*da0073e9SAndroid Build Coastguard Worker def read_from_disk(self): 324*da0073e9SAndroid Build Coastguard Worker with open(self.path) as f: 325*da0073e9SAndroid Build Coastguard Worker data = json.load(f) 326*da0073e9SAndroid Build Coastguard Worker data = {commit: dict_to_features(dct) for commit, dct in data.items()} 327*da0073e9SAndroid Build Coastguard Worker return data 328*da0073e9SAndroid Build Coastguard Worker 329*da0073e9SAndroid Build Coastguard Worker def write_to_disk(self): 330*da0073e9SAndroid Build Coastguard Worker data = {commit: features._asdict() for commit, features in self.data.items()} 331*da0073e9SAndroid Build Coastguard Worker with open(self.path, "w") as f: 332*da0073e9SAndroid Build Coastguard Worker json.dump(data, f) 333