• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import logging
6import urllib
7
8from google.appengine.api import urlfetch
9
10from common.buildbot import build
11from common.buildbot import network
12
13
14class 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