| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import urllib |
| |
| from google.appengine.api import urlfetch |
| |
| from common.buildbot import build |
| from common.buildbot import network |
| |
| |
| class Builds(object): |
| |
| def __init__(self, master_name, builder_name, url): |
| self._master_name = master_name |
| self._builder_name = builder_name |
| self._url = url |
| |
| def __getitem__(self, key): |
| """Fetches a Build object containing build details. |
| |
| Args: |
| key: A nonnegative build number. |
| |
| Returns: |
| A Build object. |
| |
| Raises: |
| TypeError: key is not an int. |
| ValueError: key is negative. |
| """ |
| # We can't take slices because we don't have a defined length. |
| if not isinstance(key, int): |
| raise TypeError('build numbers must be integers, not %s' % |
| type(key).__name__) |
| |
| return self.Fetch((key,)) |
| |
| def Fetch(self, build_numbers): |
| """Downloads and returns build details. |
| |
| If a build has corrupt data, it is not included in the result. If you |
| strictly need all the builds requested, be sure to check the result length. |
| |
| Args: |
| build_numbers: An iterable of build numbers to download. |
| |
| Yields: |
| Build objects, in the order requested. Some may be missing. |
| |
| Raises: |
| ValueError: A build number is invalid. |
| """ |
| if not build_numbers: |
| return |
| |
| for build_number in build_numbers: |
| if build_number < 0: |
| raise ValueError('Invalid build number: %d' % build_number) |
| |
| build_query = urllib.urlencode( |
| [('select', build_number) for build_number in build_numbers]) |
| url = 'json/builders/%s/builds/?%s' % ( |
| urllib.quote(self._builder_name), build_query) |
| url = network.BuildUrl(self._master_name, url) |
| try: |
| builds = network.FetchData(url).values() |
| except (ValueError, urlfetch.ResponseTooLargeError): |
| # The JSON decode failed, or the data was too large. |
| # Try downloading the builds individually instead. |
| builds = [] |
| for build_number in build_numbers: |
| url = 'json/builders/%s/builds/%d' % ( |
| urllib.quote(self._builder_name), build_number) |
| url = network.BuildUrl(self._master_name, url) |
| try: |
| builds.append(network.FetchData(url)) |
| except (ValueError, urlfetch.ResponseTooLargeError): |
| logging.warning('Unable to fetch %s build %d', |
| self._master_name, build_number) |
| continue |
| |
| for build_data in builds: |
| if 'error' in build_data: |
| continue |
| yield build.Build(build_data, self._url) |