Home | History | Annotate | Download | only in buildbot
      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