1 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import logging 6 import urllib 7 8 from google.appengine.api import urlfetch 9 10 from common.buildbot import build 11 from common.buildbot import network 12 13 14 class Builds(object): 15 16 def __init__(self, master_name, builder_name, url): 17 self._master_name = master_name 18 self._builder_name = builder_name 19 self._url = url 20 21 def __getitem__(self, key): 22 """Fetches a Build object containing build details. 23 24 Args: 25 key: A nonnegative build number. 26 27 Returns: 28 A Build object. 29 30 Raises: 31 TypeError: key is not an int. 32 ValueError: key is negative. 33 """ 34 # We can't take slices because we don't have a defined length. 35 if not isinstance(key, int): 36 raise TypeError('build numbers must be integers, not %s' % 37 type(key).__name__) 38 39 return self.Fetch((key,)) 40 41 def Fetch(self, build_numbers): 42 """Downloads and returns build details. 43 44 If a build has corrupt data, it is not included in the result. If you 45 strictly need all the builds requested, be sure to check the result length. 46 47 Args: 48 build_numbers: An iterable of build numbers to download. 49 50 Yields: 51 Build objects, in the order requested. Some may be missing. 52 53 Raises: 54 ValueError: A build number is invalid. 55 """ 56 if not build_numbers: 57 return 58 59 for build_number in build_numbers: 60 if build_number < 0: 61 raise ValueError('Invalid build number: %d' % build_number) 62 63 build_query = urllib.urlencode( 64 [('select', build_number) for build_number in build_numbers]) 65 url = 'json/builders/%s/builds/?%s' % ( 66 urllib.quote(self._builder_name), build_query) 67 url = network.BuildUrl(self._master_name, url) 68 try: 69 builds = network.FetchData(url).values() 70 except (ValueError, urlfetch.ResponseTooLargeError): 71 # The JSON decode failed, or the data was too large. 72 # Try downloading the builds individually instead. 73 builds = [] 74 for build_number in build_numbers: 75 url = 'json/builders/%s/builds/%d' % ( 76 urllib.quote(self._builder_name), build_number) 77 url = network.BuildUrl(self._master_name, url) 78 try: 79 builds.append(network.FetchData(url)) 80 except (ValueError, urlfetch.ResponseTooLargeError): 81 logging.warning('Unable to fetch %s build %d', 82 self._master_name, build_number) 83 continue 84 85 for build_data in builds: 86 if 'error' in build_data: 87 continue 88 yield build.Build(build_data, self._url) 89