src/platform/dev: cros format everything

Run cros format on all files.  This allows us to enable the check in
pre-upload (done in the follow-on CL).

BUG=none
TEST=none

Change-Id: I7af3847104cc67378851376ce65a61b06faaa715
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/4237542
Tested-by: Jack Rosenthal <jrosenth@chromium.org>
Commit-Queue: Jack Rosenthal <jrosenth@chromium.org>
Reviewed-by: Jesse McGuire <jessemcguire@google.com>
Auto-Submit: Jack Rosenthal <jrosenth@chromium.org>
Reviewed-by: Alex Klein <saklein@chromium.org>
diff --git a/devserver.py b/devserver.py
index 0e2c5c2..4002db3 100755
--- a/devserver.py
+++ b/devserver.py
@@ -26,6 +26,7 @@
 
 import json
 import logging
+from logging import handlers
 import optparse  # pylint: disable=deprecated-module
 import os
 import re
@@ -36,17 +37,13 @@
 import tempfile
 import threading
 import types
-from logging import handlers
 
-from six.moves import http_client
+import autoupdate
 
 # pylint: disable=no-name-in-module, import-error
 import cherrypy
 from cherrypy import _cplogging as cplogging
 from cherrypy.process import plugins
-# pylint: enable=no-name-in-module, import-error
-
-import autoupdate
 import cherrypy_ext
 import health_checker
 
@@ -54,6 +51,8 @@
 # anything from chromite.  Otherwise, really bad things will happen, and
 # you will _not_ understand why.
 import setup_chromite  # pylint: disable=unused-import
+from six.moves import http_client
+
 from chromite.lib import cros_build_lib
 from chromite.lib.xbuddy import android_build
 from chromite.lib.xbuddy import artifact_info
@@ -64,17 +63,23 @@
 from chromite.lib.xbuddy import xbuddy
 
 
+# pylint: enable=no-name-in-module, import-error
+
+
 # Module-local log function.
 def _Log(message, *args):
-  return logging.info(message, *args)
+    return logging.info(message, *args)
+
 
 CACHED_ENTRIES = 12
 
-TELEMETRY_FOLDER = 'telemetry_src'
-TELEMETRY_DEPS = ['dep-telemetry_dep.tar.bz2',
-                  'dep-page_cycler_dep.tar.bz2',
-                  'dep-chrome_test.tar.bz2',
-                  'dep-perf_data_dep.tar.bz2']
+TELEMETRY_FOLDER = "telemetry_src"
+TELEMETRY_DEPS = [
+    "dep-telemetry_dep.tar.bz2",
+    "dep-page_cycler_dep.tar.bz2",
+    "dep-chrome_test.tar.bz2",
+    "dep-perf_data_dep.tar.bz2",
+]
 
 # Sets up global to share between classes.
 updater = None
@@ -85,1230 +90,1357 @@
 #
 # For more, see the documentation in standard python library for
 # logging.handlers.TimedRotatingFileHandler
-_LOG_ROTATION_TIME = 'H'
+_LOG_ROTATION_TIME = "H"
 _LOG_ROTATION_INTERVAL = 12  # hours
 _LOG_ROTATION_BACKUP = 28  # backup counts
 
 # Error msg for deprecated RPC usage.
-DEPRECATED_RPC_ERROR_MSG = ('The %s RPC has been deprecated. Usage of this '
-                            'RPC is discouraged. Please go to '
-                            'go/devserver-deprecation for more information.')
+DEPRECATED_RPC_ERROR_MSG = (
+    "The %s RPC has been deprecated. Usage of this "
+    "RPC is discouraged. Please go to "
+    "go/devserver-deprecation for more information."
+)
 
 
 class DevServerError(Exception):
-  """Exception class used by DevServer."""
+    """Exception class used by DevServer."""
 
 
 class DeprecatedRPCError(DevServerError):
-  """Exception class used when an RPC is deprecated but is still being used."""
+    """Exception class used when an RPC is deprecated but is still being used."""
 
-  def __init__(self, rpc_name):
-    """Constructor for DeprecatedRPCError class.
+    def __init__(self, rpc_name):
+        """Constructor for DeprecatedRPCError class.
 
-    :param rpc_name: (str) name of the RPC that has been deprecated.
-    """
-    super(DeprecatedRPCError, self).__init__(
-        DEPRECATED_RPC_ERROR_MSG % rpc_name)
-    self.rpc_name = rpc_name
+        :param rpc_name: (str) name of the RPC that has been deprecated.
+        """
+        super(DeprecatedRPCError, self).__init__(
+            DEPRECATED_RPC_ERROR_MSG % rpc_name
+        )
+        self.rpc_name = rpc_name
 
 
 class DevServerHTTPError(cherrypy.HTTPError):
-  """Exception class to log the HTTPResponse before routing it to cherrypy."""
-  def __init__(self, status, message):
-    """CherryPy error with logging.
+    """Exception class to log the HTTPResponse before routing it to cherrypy."""
 
-    Args:
-      status: HTTPResponse status.
-      message: Message associated with the response.
-    """
-    cherrypy.HTTPError.__init__(self, status, message)
-    _Log('HTTPError status: %s message: %s', status, message)
+    def __init__(self, status, message):
+        """CherryPy error with logging.
+
+        Args:
+          status: HTTPResponse status.
+          message: Message associated with the response.
+        """
+        cherrypy.HTTPError.__init__(self, status, message)
+        _Log("HTTPError status: %s message: %s", status, message)
 
 
 def _canonicalize_archive_url(archive_url):
-  """Canonicalizes archive_url strings.
+    """Canonicalizes archive_url strings.
 
-  Raises:
-    DevserverError: if archive_url is not set.
-  """
-  if archive_url:
-    if not archive_url.startswith('gs://'):
-      raise DevServerError(
-          "Archive URL isn't from Google Storage (%s) ." % archive_url)
+    Raises:
+      DevserverError: if archive_url is not set.
+    """
+    if archive_url:
+        if not archive_url.startswith("gs://"):
+            raise DevServerError(
+                "Archive URL isn't from Google Storage (%s) ." % archive_url
+            )
 
-    return archive_url.rstrip('/')
-  else:
-    raise DevServerError('Must specify an archive_url in the request')
+        return archive_url.rstrip("/")
+    else:
+        raise DevServerError("Must specify an archive_url in the request")
 
 
 def _canonicalize_local_path(local_path):
-  """Canonicalizes |local_path| strings.
+    """Canonicalizes |local_path| strings.
 
-  Raises:
-    DevserverError: if |local_path| is not set.
-  """
-  # Restrict staging of local content to only files within the static
-  # directory.
-  local_path = os.path.abspath(local_path)
-  if not local_path.startswith(updater.static_dir):
-    raise DevServerError(
-        'Local path %s must be a subdirectory of the static'
-        ' directory: %s' % (local_path, updater.static_dir))
+    Raises:
+      DevserverError: if |local_path| is not set.
+    """
+    # Restrict staging of local content to only files within the static
+    # directory.
+    local_path = os.path.abspath(local_path)
+    if not local_path.startswith(updater.static_dir):
+        raise DevServerError(
+            "Local path %s must be a subdirectory of the static"
+            " directory: %s" % (local_path, updater.static_dir)
+        )
 
-  return local_path.rstrip('/')
+    return local_path.rstrip("/")
 
 
 def _get_artifacts(kwargs):
-  """Returns a tuple of named and file artifacts given the stage rpc kwargs.
+    """Returns a tuple of named and file artifacts given the stage rpc kwargs.
 
-  Raises:
-    DevserverError if no artifacts would be returned.
-  """
-  artifacts = kwargs.get('artifacts')
-  files = kwargs.get('files')
-  if not artifacts and not files:
-    raise DevServerError('No artifacts specified.')
+    Raises:
+      DevserverError if no artifacts would be returned.
+    """
+    artifacts = kwargs.get("artifacts")
+    files = kwargs.get("files")
+    if not artifacts and not files:
+        raise DevServerError("No artifacts specified.")
 
-  # Note we NEED to coerce files to a string as we get raw unicode from
-  # cherrypy and we treat files as strings elsewhere in the code.
-  return (str(artifacts).split(',') if artifacts else [],
-          str(files).split(',') if files else [])
+    # Note we NEED to coerce files to a string as we get raw unicode from
+    # cherrypy and we treat files as strings elsewhere in the code.
+    return (
+        str(artifacts).split(",") if artifacts else [],
+        str(files).split(",") if files else [],
+    )
 
 
 def _is_android_build_request(kwargs):
-  """Check if a devserver call is for Android build, based on the arguments.
+    """Check if a devserver call is for Android build, based on the arguments.
 
-  This method exams the request's arguments (os_type) to determine if the
-  request is for Android build. If os_type is set to `android`, returns True.
-  If os_type is not set or has other values, returns False.
+    This method exams the request's arguments (os_type) to determine if the
+    request is for Android build. If os_type is set to `android`, returns True.
+    If os_type is not set or has other values, returns False.
 
-  Args:
-    kwargs: Keyword arguments for the request.
+    Args:
+      kwargs: Keyword arguments for the request.
 
-  Returns:
-    True if the request is for Android build. False otherwise.
-  """
-  os_type = kwargs.get('os_type', None)
-  return os_type == 'android'
+    Returns:
+      True if the request is for Android build. False otherwise.
+    """
+    os_type = kwargs.get("os_type", None)
+    return os_type == "android"
 
 
 def _get_downloader(kwargs):
-  """Returns the downloader based on passed in arguments.
+    """Returns the downloader based on passed in arguments.
 
-  Args:
-    kwargs: Keyword arguments for the request.
-  """
-  local_path = kwargs.get('local_path')
-  if local_path:
-    local_path = _canonicalize_local_path(local_path)
+    Args:
+      kwargs: Keyword arguments for the request.
+    """
+    local_path = kwargs.get("local_path")
+    if local_path:
+        local_path = _canonicalize_local_path(local_path)
 
-  dl = None
-  if local_path:
-    delete_source = _parse_boolean_arg(kwargs, 'delete_source')
-    dl = downloader.LocalDownloader(updater.static_dir, local_path,
-                                    delete_source=delete_source)
+    dl = None
+    if local_path:
+        delete_source = _parse_boolean_arg(kwargs, "delete_source")
+        dl = downloader.LocalDownloader(
+            updater.static_dir, local_path, delete_source=delete_source
+        )
 
-  if not _is_android_build_request(kwargs):
-    archive_url = kwargs.get('archive_url')
-    if not archive_url and not local_path:
-      raise DevServerError(
-          'Requires archive_url or local_path to be specified.')
-    if archive_url and local_path:
-      raise DevServerError(
-          'archive_url and local_path can not both be specified.')
-    if not dl:
-      archive_url = _canonicalize_archive_url(archive_url)
-      dl = downloader.GoogleStorageDownloader(
-          updater.static_dir, archive_url,
-          downloader.GoogleStorageDownloader.GetBuildIdFromArchiveURL(
-              archive_url))
-  elif not dl:
-    target = kwargs.get('target', None)
-    branch = kwargs.get('branch', None)
-    build_id = kwargs.get('build_id', None)
-    if not target or not branch or not build_id:
-      raise DevServerError('target, branch, build ID must all be specified for '
-                           'downloading Android build.')
-    dl = downloader.AndroidBuildDownloader(updater.static_dir, branch, build_id,
-                                           target)
+    if not _is_android_build_request(kwargs):
+        archive_url = kwargs.get("archive_url")
+        if not archive_url and not local_path:
+            raise DevServerError(
+                "Requires archive_url or local_path to be specified."
+            )
+        if archive_url and local_path:
+            raise DevServerError(
+                "archive_url and local_path can not both be specified."
+            )
+        if not dl:
+            archive_url = _canonicalize_archive_url(archive_url)
+            dl = downloader.GoogleStorageDownloader(
+                updater.static_dir,
+                archive_url,
+                downloader.GoogleStorageDownloader.GetBuildIdFromArchiveURL(
+                    archive_url
+                ),
+            )
+    elif not dl:
+        target = kwargs.get("target", None)
+        branch = kwargs.get("branch", None)
+        build_id = kwargs.get("build_id", None)
+        if not target or not branch or not build_id:
+            raise DevServerError(
+                "target, branch, build ID must all be specified for "
+                "downloading Android build."
+            )
+        dl = downloader.AndroidBuildDownloader(
+            updater.static_dir, branch, build_id, target
+        )
 
-  return dl
+    return dl
 
 
 def _get_downloader_and_factory(kwargs):
-  """Returns the downloader and artifact factory based on passed in arguments.
+    """Returns the downloader and artifact factory based on passed in arguments.
 
-  Args:
-    kwargs: Keyword arguments for the request.
-  """
-  artifacts, files = _get_artifacts(kwargs)
-  dl = _get_downloader(kwargs)
+    Args:
+      kwargs: Keyword arguments for the request.
+    """
+    artifacts, files = _get_artifacts(kwargs)
+    dl = _get_downloader(kwargs)
 
-  if (isinstance(dl, (downloader.GoogleStorageDownloader,
-                      downloader.LocalDownloader))):
-    factory_class = build_artifact.ChromeOSArtifactFactory
-  elif isinstance(dl, downloader.AndroidBuildDownloader):
-    factory_class = build_artifact.AndroidArtifactFactory
-  else:
-    raise DevServerError(
-        'Unrecognized value for downloader type: %s' % type(dl))
+    if isinstance(
+        dl, (downloader.GoogleStorageDownloader, downloader.LocalDownloader)
+    ):
+        factory_class = build_artifact.ChromeOSArtifactFactory
+    elif isinstance(dl, downloader.AndroidBuildDownloader):
+        factory_class = build_artifact.AndroidArtifactFactory
+    else:
+        raise DevServerError(
+            "Unrecognized value for downloader type: %s" % type(dl)
+        )
 
-  factory = factory_class(dl.GetBuildDir(), artifacts, files, dl.GetBuild())
+    factory = factory_class(dl.GetBuildDir(), artifacts, files, dl.GetBuild())
 
-  return dl, factory
+    return dl, factory
 
 
 def _LeadingWhiteSpaceCount(string):
-  """Count the amount of leading whitespace in a string.
+    """Count the amount of leading whitespace in a string.
 
-  Args:
-    string: The string to count leading whitespace in.
+    Args:
+      string: The string to count leading whitespace in.
 
-  Returns:
-    number of white space chars before characters start.
-  """
-  matched = re.match(r'^\s+', string)
-  if matched:
-    return len(matched.group())
+    Returns:
+      number of white space chars before characters start.
+    """
+    matched = re.match(r"^\s+", string)
+    if matched:
+        return len(matched.group())
 
-  return 0
+    return 0
 
 
 def _PrintDocStringAsHTML(func):
-  """Make a functions docstring somewhat HTML style.
+    """Make a functions docstring somewhat HTML style.
 
-  Args:
-    func: The function to return the docstring from.
+    Args:
+      func: The function to return the docstring from.
 
-  Returns:
-    A string that is somewhat formated for a web browser.
-  """
-  # TODO(scottz): Make this parse Args/Returns in a prettier way.
-  # Arguments could be bolded and indented etc.
-  html_doc = []
-  for line in func.__doc__.splitlines():
-    leading_space = _LeadingWhiteSpaceCount(line)
-    if leading_space > 0:
-      line = '&nbsp;' * leading_space + line
+    Returns:
+      A string that is somewhat formated for a web browser.
+    """
+    # TODO(scottz): Make this parse Args/Returns in a prettier way.
+    # Arguments could be bolded and indented etc.
+    html_doc = []
+    for line in func.__doc__.splitlines():
+        leading_space = _LeadingWhiteSpaceCount(line)
+        if leading_space > 0:
+            line = "&nbsp;" * leading_space + line
 
-    html_doc.append('<BR>%s' % line)
+        html_doc.append("<BR>%s" % line)
 
-  return '\n'.join(html_doc)
+    return "\n".join(html_doc)
 
 
 def _GetUpdateTimestampHandler(static_dir):
-  """Returns a handler to update directory staged.timestamp.
+    """Returns a handler to update directory staged.timestamp.
 
-  This handler resets the stage.timestamp whenever static content is accessed.
+    This handler resets the stage.timestamp whenever static content is accessed.
 
-  Args:
-    static_dir: Directory from which static content is being staged.
+    Args:
+      static_dir: Directory from which static content is being staged.
 
-  Returns:
-    A cherrypy handler to update the timestamp of accessed content.
-  """
-  def UpdateTimestampHandler():
-    if not '404' in cherrypy.response.status:
-      build_match = re.match(devserver_constants.STAGED_BUILD_REGEX,
-                             cherrypy.request.path_info)
-      if build_match:
-        build_dir = os.path.join(static_dir, build_match.group('build'))
-        downloader.Downloader.TouchTimestampForStaged(build_dir)
-  return UpdateTimestampHandler
+    Returns:
+      A cherrypy handler to update the timestamp of accessed content.
+    """
+
+    def UpdateTimestampHandler():
+        if not "404" in cherrypy.response.status:
+            build_match = re.match(
+                devserver_constants.STAGED_BUILD_REGEX,
+                cherrypy.request.path_info,
+            )
+            if build_match:
+                build_dir = os.path.join(static_dir, build_match.group("build"))
+                downloader.Downloader.TouchTimestampForStaged(build_dir)
+
+    return UpdateTimestampHandler
 
 
 def _GetConfig(options):
-  """Returns the configuration for the devserver."""
+    """Returns the configuration for the devserver."""
 
-  socket_host = '::'
-  # Fall back to IPv4 when python is not configured with IPv6.
-  if not socket.has_ipv6:
-    socket_host = '0.0.0.0'
+    socket_host = "::"
+    # Fall back to IPv4 when python is not configured with IPv6.
+    if not socket.has_ipv6:
+        socket_host = "0.0.0.0"
 
-  # Adds the UpdateTimestampHandler to cherrypy's tools. This tools executes
-  # on the on_end_resource hook. This hook is called once processing is
-  # complete and the response is ready to be returned.
-  cherrypy.tools.update_timestamp = cherrypy.Tool(
-      'on_end_resource', _GetUpdateTimestampHandler(options.static_dir))
+    # Adds the UpdateTimestampHandler to cherrypy's tools. This tools executes
+    # on the on_end_resource hook. This hook is called once processing is
+    # complete and the response is ready to be returned.
+    cherrypy.tools.update_timestamp = cherrypy.Tool(
+        "on_end_resource", _GetUpdateTimestampHandler(options.static_dir)
+    )
 
-  base_config = {
-      'global': {
-          'server.log_request_headers': True,
-          'server.protocol_version': 'HTTP/1.1',
-          'server.socket_host': socket_host,
-          'server.socket_port': int(options.port),
-          'response.timeout': 6000,
-          'request.show_tracebacks': True,
-          'server.socket_timeout': 60,
-          'server.thread_pool': 2,
-          'engine.autoreload.on': False,
-      },
-      '/build': {
-          'response.timeout': 100000,
-      },
-      '/update': {
-          # Gets rid of cherrypy parsing post file for args.
-          'request.process_request_body': False,
-          'response.timeout': 10000,
-      },
-      # Sets up the static dir for file hosting.
-      '/static': {
-          'tools.staticdir.dir': options.static_dir,
-          'tools.staticdir.on': True,
-          'response.timeout': 10000,
-          'tools.update_timestamp.on': True,
-      },
-  }
-  if options.production:
-    base_config['global'].update({'server.thread_pool': 150})
+    base_config = {
+        "global": {
+            "server.log_request_headers": True,
+            "server.protocol_version": "HTTP/1.1",
+            "server.socket_host": socket_host,
+            "server.socket_port": int(options.port),
+            "response.timeout": 6000,
+            "request.show_tracebacks": True,
+            "server.socket_timeout": 60,
+            "server.thread_pool": 2,
+            "engine.autoreload.on": False,
+        },
+        "/build": {
+            "response.timeout": 100000,
+        },
+        "/update": {
+            # Gets rid of cherrypy parsing post file for args.
+            "request.process_request_body": False,
+            "response.timeout": 10000,
+        },
+        # Sets up the static dir for file hosting.
+        "/static": {
+            "tools.staticdir.dir": options.static_dir,
+            "tools.staticdir.on": True,
+            "response.timeout": 10000,
+            "tools.update_timestamp.on": True,
+        },
+    }
+    if options.production:
+        base_config["global"].update({"server.thread_pool": 150})
 
-  return base_config
+    return base_config
 
 
 def _GetRecursiveMemberObject(root, member_list):
-  """Returns an object corresponding to a nested member list.
+    """Returns an object corresponding to a nested member list.
 
-  Args:
-    root: the root object to search
-    member_list: list of nested members to search
+    Args:
+      root: the root object to search
+      member_list: list of nested members to search
 
-  Returns:
-    An object corresponding to the member name list; None otherwise.
-  """
-  for member in member_list:
-    next_root = root.__class__.__dict__.get(member)
-    if not next_root:
-      return None
-    root = next_root
-  return root
+    Returns:
+      An object corresponding to the member name list; None otherwise.
+    """
+    for member in member_list:
+        next_root = root.__class__.__dict__.get(member)
+        if not next_root:
+            return None
+        root = next_root
+    return root
 
 
 def _IsExposed(name):
-  """Returns True iff |name| has an `exposed' attribute and it is set."""
-  return hasattr(name, 'exposed') and name.exposed
+    """Returns True iff |name| has an `exposed' attribute and it is set."""
+    return hasattr(name, "exposed") and name.exposed
 
 
 def _GetExposedMethod(nested_member):
-  """Returns a CherryPy-exposed method, if such exists.
+    """Returns a CherryPy-exposed method, if such exists.
 
-  Args:
-    nested_member: a slash-joined path to the nested member
+    Args:
+      nested_member: a slash-joined path to the nested member
 
-  Returns:
-    A function object corresponding to the path defined by |nested_member| from
-    the app root object registered, if the function is exposed; None otherwise.
-  """
-  for app in cherrypy.tree.apps.values():
-    # Use the 'index' function doc as the doc of the app.
-    if nested_member == app.script_name.lstrip('/'):
-      nested_member = 'index'
+    Returns:
+      A function object corresponding to the path defined by |nested_member| from
+      the app root object registered, if the function is exposed; None otherwise.
+    """
+    for app in cherrypy.tree.apps.values():
+        # Use the 'index' function doc as the doc of the app.
+        if nested_member == app.script_name.lstrip("/"):
+            nested_member = "index"
 
-    method = _GetRecursiveMemberObject(app.root, nested_member.split('/'))
-    if method and isinstance(method, types.FunctionType) and _IsExposed(method):
-      return method
+        method = _GetRecursiveMemberObject(app.root, nested_member.split("/"))
+        if (
+            method
+            and isinstance(method, types.FunctionType)
+            and _IsExposed(method)
+        ):
+            return method
 
 
 def _FindExposedMethods(root, prefix, unlisted=None):
-  """Finds exposed CherryPy methods.
+    """Finds exposed CherryPy methods.
 
-  Args:
-    root: the root object for searching
-    prefix: slash-joined chain of members leading to current object
-    unlisted: URLs to be excluded regardless of their exposed status
+    Args:
+      root: the root object for searching
+      prefix: slash-joined chain of members leading to current object
+      unlisted: URLs to be excluded regardless of their exposed status
 
-  Returns:
-    List of exposed URLs that are not unlisted.
-  """
-  method_list = []
-  for member in root.__class__.__dict__.keys():
-    prefixed_member = prefix + '/' + member if prefix else member
-    if unlisted and prefixed_member in unlisted:
-      continue
-    member_obj = root.__class__.__dict__[member]
-    if _IsExposed(member_obj):
-      if isinstance(member_obj, types.FunctionType):
-        # Regard the app name as exposed "method" name if it exposed 'index'
-        # function.
-        if prefix and member == 'index':
-          method_list.append(prefix)
-        else:
-          method_list.append(prefixed_member)
-      else:
-        method_list += _FindExposedMethods(
-            member_obj, prefixed_member, unlisted)
-  return method_list
+    Returns:
+      List of exposed URLs that are not unlisted.
+    """
+    method_list = []
+    for member in root.__class__.__dict__.keys():
+        prefixed_member = prefix + "/" + member if prefix else member
+        if unlisted and prefixed_member in unlisted:
+            continue
+        member_obj = root.__class__.__dict__[member]
+        if _IsExposed(member_obj):
+            if isinstance(member_obj, types.FunctionType):
+                # Regard the app name as exposed "method" name if it exposed 'index'
+                # function.
+                if prefix and member == "index":
+                    method_list.append(prefix)
+                else:
+                    method_list.append(prefixed_member)
+            else:
+                method_list += _FindExposedMethods(
+                    member_obj, prefixed_member, unlisted
+                )
+    return method_list
 
 
 def _parse_boolean_arg(kwargs, key):
-  """Parse boolean arg from kwargs.
+    """Parse boolean arg from kwargs.
 
-  Args:
-    kwargs: the parameters to be checked.
-    key: the key to be parsed.
+    Args:
+      kwargs: the parameters to be checked.
+      key: the key to be parsed.
 
-  Returns:
-    The boolean value of kwargs[key], or False if key doesn't exist in kwargs.
+    Returns:
+      The boolean value of kwargs[key], or False if key doesn't exist in kwargs.
 
-  Raises:
-    DevServerHTTPError if kwargs[key] is not a boolean variable.
-  """
-  if key in kwargs:
-    if kwargs[key] == 'True':
-      return True
-    elif kwargs[key] == 'False':
-      return False
+    Raises:
+      DevServerHTTPError if kwargs[key] is not a boolean variable.
+    """
+    if key in kwargs:
+        if kwargs[key] == "True":
+            return True
+        elif kwargs[key] == "False":
+            return False
+        else:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR,
+                "The value for key %s is not boolean." % key,
+            )
     else:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               'The value for key %s is not boolean.' % key)
-  else:
-    return False
+        return False
 
 
 def _parse_string_arg(kwargs, key):
-  """Parse string arg from kwargs.
+    """Parse string arg from kwargs.
 
-  Args:
-    kwargs: the parameters to be checked.
-    key: the key to be parsed.
+    Args:
+      kwargs: the parameters to be checked.
+      key: the key to be parsed.
 
-  Returns:
-    The string value of kwargs[key], or None if key doesn't exist in kwargs.
-  """
-  if key in kwargs:
-    return kwargs[key]
-  else:
-    return None
+    Returns:
+      The string value of kwargs[key], or None if key doesn't exist in kwargs.
+    """
+    if key in kwargs:
+        return kwargs[key]
+    else:
+        return None
 
 
 def _build_uri_from_build_name(build_name):
-  """Get build url from a given build name.
+    """Get build url from a given build name.
 
-  Args:
-    build_name: the build name to be parsed, whose format is
-        'board/release_version'.
+    Args:
+      build_name: the build name to be parsed, whose format is
+          'board/release_version'.
 
-  Returns:
-    The release_archive_url on Google Storage for this build name.
-  """
-  # TODO(ahassani): This function doesn't seem to be used anywhere since its
-  # previous use of lib.paygen.gspath was broken and it doesn't seem to be
-  # causing any runtime issues. So deprecate this in the future.
-  tokens = build_name.split('/')
-  return 'gs://chromeos-releases/stable-channel/%s/%s' % (tokens[0], tokens[1])
+    Returns:
+      The release_archive_url on Google Storage for this build name.
+    """
+    # TODO(ahassani): This function doesn't seem to be used anywhere since its
+    # previous use of lib.paygen.gspath was broken and it doesn't seem to be
+    # causing any runtime issues. So deprecate this in the future.
+    tokens = build_name.split("/")
+    return "gs://chromeos-releases/stable-channel/%s/%s" % (
+        tokens[0],
+        tokens[1],
+    )
 
 
 def is_deprecated_server():
-  """Gets whether the devserver has deprecated RPCs."""
-  return cherrypy.config.get('infra_removal', False)
+    """Gets whether the devserver has deprecated RPCs."""
+    return cherrypy.config.get("infra_removal", False)
 
 
 class DevServerRoot(object):
-  """The Root Class for the Dev Server.
+    """The Root Class for the Dev Server.
 
-  CherryPy works as follows:
-    For each method in this class, cherrpy interprets root/path
-    as a call to an instance of DevServerRoot->method_name.  For example,
-    a call to http://myhost/build will call build.  CherryPy automatically
-    parses http args and places them as keyword arguments in each method.
-    For paths http://myhost/update/dir1/dir2, you can use *args so that
-    cherrypy uses the update method and puts the extra paths in args.
-  """
-  # Method names that should not be listed on the index page.
-  _UNLISTED_METHODS = ['index', 'doc']
-
-  # Number of threads that devserver is staging images.
-  _staging_thread_count = 0
-  # Lock used to lock increasing/decreasing count.
-  _staging_thread_count_lock = threading.Lock()
-
-  def __init__(self, _xbuddy):
-    self._builder = None
-    self._telemetry_lock_dict = common_util.LockDict()
-    self._xbuddy = _xbuddy
-
-  @property
-  def staging_thread_count(self):
-    """Get the staging thread count."""
-    return self._staging_thread_count
-
-  @cherrypy.expose
-  def build(self, board, pkg, **kwargs):
-    """Builds the package specified."""
-    if is_deprecated_server():
-      raise DeprecatedRPCError('build')
-
-    import builder
-    if self._builder is None:
-      self._builder = builder.Builder()
-    return self._builder.Build(board, pkg, kwargs)
-
-  @cherrypy.expose
-  def is_staged(self, **kwargs):
-    """Check if artifacts have been downloaded.
-
-    Examples:
-      To check if autotest and test_suites are staged:
-        http://devserver_url:<port>/is_staged?archive_url=gs://your_url/path&
-            artifacts=autotest,test_suites
-
-    Args:
-      async: True to return without waiting for download to complete.
-      artifacts: Comma separated list of named artifacts to download.
-        These are defined in artifact_info and have their implementation
-        in build_artifact.py.
-      files: Comma separated list of file artifacts to stage. These
-        will be available as is in the corresponding static directory with no
-        custom post-processing.
-
-    Returns:
-      True of all artifacts are staged.
+    CherryPy works as follows:
+      For each method in this class, cherrpy interprets root/path
+      as a call to an instance of DevServerRoot->method_name.  For example,
+      a call to http://myhost/build will call build.  CherryPy automatically
+      parses http args and places them as keyword arguments in each method.
+      For paths http://myhost/update/dir1/dir2, you can use *args so that
+      cherrypy uses the update method and puts the extra paths in args.
     """
-    dl, factory = _get_downloader_and_factory(kwargs)
-    response = str(dl.IsStaged(factory))
-    _Log('Responding to is_staged %s request with %r', kwargs, response)
-    return response
 
-  @cherrypy.expose
-  def list_image_dir(self, **kwargs):
-    """Take an archive url and list the contents in its staged directory.
+    # Method names that should not be listed on the index page.
+    _UNLISTED_METHODS = ["index", "doc"]
 
-    Examples:
-      To list the contents of where this devserver should have staged
-      gs://image-archive/<board>-release/<build> call:
-      http://devserver_url:<port>/list_image_dir?archive_url=<gs://..>
+    # Number of threads that devserver is staging images.
+    _staging_thread_count = 0
+    # Lock used to lock increasing/decreasing count.
+    _staging_thread_count_lock = threading.Lock()
 
-    Args:
-      archive_url: Google Storage URL for the build.
+    def __init__(self, _xbuddy):
+        self._builder = None
+        self._telemetry_lock_dict = common_util.LockDict()
+        self._xbuddy = _xbuddy
 
-    Returns:
-      A string with information about the contents of the image directory.
-    """
-    dl = _get_downloader(kwargs)
-    try:
-      image_dir_contents = dl.ListBuildDir()
-    except build_artifact.ArtifactDownloadError as e:
-      return 'Cannot list the contents of staged artifacts. %s' % e
-    if not image_dir_contents:
-      return '%s has not been staged on this devserver.' % dl.DescribeSource()
-    return image_dir_contents
+    @property
+    def staging_thread_count(self):
+        """Get the staging thread count."""
+        return self._staging_thread_count
 
-  @cherrypy.expose
-  def stage(self, **kwargs):
-    """Downloads and caches build artifacts.
+    @cherrypy.expose
+    def build(self, board, pkg, **kwargs):
+        """Builds the package specified."""
+        if is_deprecated_server():
+            raise DeprecatedRPCError("build")
 
-    Downloads and caches build artifacts, possibly from a Google Storage URL,
-    or from Android's build server. Returns once these have been downloaded
-    on the devserver. A call to this will attempt to cache non-specified
-    artifacts in the background for the given from the given URL following
-    the principle of spatial locality. Spatial locality of different
-    artifacts is explicitly defined in the build_artifact module.
+        import builder
 
-    These artifacts will then be available from the static/ sub-directory of
-    the devserver.
+        if self._builder is None:
+            self._builder = builder.Builder()
+        return self._builder.Build(board, pkg, kwargs)
 
-    Examples:
-      To download the autotest and test suites tarballs:
-        http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
-            artifacts=autotest,test_suites
-      To download the full update payload:
-        http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
-            artifacts=full_payload
-      To download just a file called blah.bin:
-        http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
-            files=blah.bin
+    @cherrypy.expose
+    def is_staged(self, **kwargs):
+        """Check if artifacts have been downloaded.
 
-      For both these examples, one could find these artifacts at:
-        http://devserver_url:<port>/static/<relative_path>*
+        Examples:
+          To check if autotest and test_suites are staged:
+            http://devserver_url:<port>/is_staged?archive_url=gs://your_url/path&
+                artifacts=autotest,test_suites
 
-      Note for this example, relative path is the archive_url stripped of its
-      basename i.e. path/ in the examples above. Specific example:
+        Args:
+          async: True to return without waiting for download to complete.
+          artifacts: Comma separated list of named artifacts to download.
+            These are defined in artifact_info and have their implementation
+            in build_artifact.py.
+          files: Comma separated list of file artifacts to stage. These
+            will be available as is in the corresponding static directory with no
+            custom post-processing.
 
-      gs://chromeos-image-archive/x86-mario-release/R26-3920.0.0
+        Returns:
+          True of all artifacts are staged.
+        """
+        dl, factory = _get_downloader_and_factory(kwargs)
+        response = str(dl.IsStaged(factory))
+        _Log("Responding to is_staged %s request with %r", kwargs, response)
+        return response
 
-      Will get staged to:
+    @cherrypy.expose
+    def list_image_dir(self, **kwargs):
+        """Take an archive url and list the contents in its staged directory.
 
-      http://devserver_url:<port>/static/x86-mario-release/R26-3920.0.0
+        Examples:
+          To list the contents of where this devserver should have staged
+          gs://image-archive/<board>-release/<build> call:
+          http://devserver_url:<port>/list_image_dir?archive_url=<gs://..>
 
-    Args:
-      archive_url: Google Storage URL for the build.
-      local_path: Local path for the build.
-      delete_source: Only meaningful with local_path. bool to indicate if the
-          source files should be deleted. This is especially useful when staging
-          a file locally in resource constrained environments as it allows us to
-          move the relevant files locally instead of copying them.
-      async: True to return without waiting for download to complete.
-      artifacts: Comma separated list of named artifacts to download.
-        These are defined in artifact_info and have their implementation
-        in build_artifact.py.
-      files: Comma separated list of files to stage. These
-        will be available as is in the corresponding static directory with no
-        custom post-processing.
-      clean: True to remove any previously staged artifacts first.
-    """
-    dl, factory = _get_downloader_and_factory(kwargs)
+        Args:
+          archive_url: Google Storage URL for the build.
 
-    with DevServerRoot._staging_thread_count_lock:
-      DevServerRoot._staging_thread_count += 1
-    try:
-      boolean_string = kwargs.get('clean')
-      clean = xbuddy.XBuddy.ParseBoolean(boolean_string)
-      if clean and os.path.exists(dl.GetBuildDir()):
-        _Log('Removing %s' % dl.GetBuildDir())
-        shutil.rmtree(dl.GetBuildDir())
-      dl.Download(factory)
-    finally:
-      with DevServerRoot._staging_thread_count_lock:
-        DevServerRoot._staging_thread_count -= 1
-    return 'Success'
-
-  @cherrypy.expose
-  def locate_file(self, **kwargs):
-    """Get the path to the given file name.
-
-    This method looks up the given file name inside specified build artifacts.
-    One use case is to help caller to locate an apk file inside a build
-    artifact. The location of the apk file could be different based on the
-    branch and target.
-
-    Args:
-      file_name: Name of the file to look for.
-      artifacts: A list of artifact names to search for the file.
-
-    Returns:
-      Path to the file with the given name. It's relative to the folder for the
-      build, e.g., DATA/priv-app/sl4a/sl4a.apk
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('locate_file')
-
-    dl, _ = _get_downloader_and_factory(kwargs)
-    try:
-      file_name = kwargs['file_name']
-      artifacts = kwargs['artifacts']
-    except KeyError:
-      raise DevServerError(
-          '`file_name` and `artifacts` are required to search '
-          'for a file in build artifacts.')
-    build_path = dl.GetBuildDir()
-    for artifact in artifacts:
-      # Get the unzipped folder of the artifact. If it's not defined in
-      # ARTIFACT_UNZIP_FOLDER_MAP, assume the files are unzipped to the build
-      # directory directly.
-      folder = artifact_info.ARTIFACT_UNZIP_FOLDER_MAP.get(artifact, '')
-      artifact_path = os.path.join(build_path, folder)
-      for root, _, filenames in os.walk(artifact_path):
-        if file_name in set([f for f in filenames]):
-          return os.path.relpath(os.path.join(root, file_name), build_path)
-    raise DevServerError(
-        'File `%s` can not be found in artifacts: %s' % (file_name, artifacts))
-
-  @cherrypy.expose
-  def setup_telemetry(self, **kwargs):
-    """Extracts and sets up telemetry
-
-    This method goes through the telemetry deps packages, and stages them on
-    the devserver to be used by the drones and the telemetry tests.
-
-    Args:
-      archive_url: Google Storage URL for the build.
-
-    Returns:
-      Path to the source folder for the telemetry codebase once it is staged.
-    """
-    dl = _get_downloader(kwargs)
-
-    build_path = dl.GetBuildDir()
-    deps_path = os.path.join(build_path, 'autotest/packages')
-    telemetry_path = os.path.join(build_path, TELEMETRY_FOLDER)
-    src_folder = os.path.join(telemetry_path, 'src')
-
-    with self._telemetry_lock_dict.lock(telemetry_path):
-      if os.path.exists(src_folder):
-        # Telemetry is already fully stage return
-        return src_folder
-
-      common_util.MkDirP(telemetry_path)
-
-      # Copy over the required deps tar balls to the telemetry directory.
-      for dep in TELEMETRY_DEPS:
-        dep_path = os.path.join(deps_path, dep)
-        if not os.path.exists(dep_path):
-          # This dep does not exist (could be new), do not extract it.
-          continue
+        Returns:
+          A string with information about the contents of the image directory.
+        """
+        dl = _get_downloader(kwargs)
         try:
-          cros_build_lib.ExtractTarball(dep_path, telemetry_path)
-        except cros_build_lib.TarballError as e:
-          shutil.rmtree(telemetry_path)
-          raise DevServerError(str(e))
+            image_dir_contents = dl.ListBuildDir()
+        except build_artifact.ArtifactDownloadError as e:
+            return "Cannot list the contents of staged artifacts. %s" % e
+        if not image_dir_contents:
+            return (
+                "%s has not been staged on this devserver."
+                % dl.DescribeSource()
+            )
+        return image_dir_contents
 
-      # By default all the tarballs extract to test_src but some parts of
-      # the telemetry code specifically hardcoded to exist inside of 'src'.
-      test_src = os.path.join(telemetry_path, 'test_src')
-      try:
-        shutil.move(test_src, src_folder)
-      except shutil.Error:
-        # This can occur if src_folder already exists. Remove and retry move.
-        shutil.rmtree(src_folder)
+    @cherrypy.expose
+    def stage(self, **kwargs):
+        """Downloads and caches build artifacts.
+
+        Downloads and caches build artifacts, possibly from a Google Storage URL,
+        or from Android's build server. Returns once these have been downloaded
+        on the devserver. A call to this will attempt to cache non-specified
+        artifacts in the background for the given from the given URL following
+        the principle of spatial locality. Spatial locality of different
+        artifacts is explicitly defined in the build_artifact module.
+
+        These artifacts will then be available from the static/ sub-directory of
+        the devserver.
+
+        Examples:
+          To download the autotest and test suites tarballs:
+            http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
+                artifacts=autotest,test_suites
+          To download the full update payload:
+            http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
+                artifacts=full_payload
+          To download just a file called blah.bin:
+            http://devserver_url:<port>/stage?archive_url=gs://your_url/path&
+                files=blah.bin
+
+          For both these examples, one could find these artifacts at:
+            http://devserver_url:<port>/static/<relative_path>*
+
+          Note for this example, relative path is the archive_url stripped of its
+          basename i.e. path/ in the examples above. Specific example:
+
+          gs://chromeos-image-archive/x86-mario-release/R26-3920.0.0
+
+          Will get staged to:
+
+          http://devserver_url:<port>/static/x86-mario-release/R26-3920.0.0
+
+        Args:
+          archive_url: Google Storage URL for the build.
+          local_path: Local path for the build.
+          delete_source: Only meaningful with local_path. bool to indicate if the
+              source files should be deleted. This is especially useful when staging
+              a file locally in resource constrained environments as it allows us to
+              move the relevant files locally instead of copying them.
+          async: True to return without waiting for download to complete.
+          artifacts: Comma separated list of named artifacts to download.
+            These are defined in artifact_info and have their implementation
+            in build_artifact.py.
+          files: Comma separated list of files to stage. These
+            will be available as is in the corresponding static directory with no
+            custom post-processing.
+          clean: True to remove any previously staged artifacts first.
+        """
+        dl, factory = _get_downloader_and_factory(kwargs)
+
+        with DevServerRoot._staging_thread_count_lock:
+            DevServerRoot._staging_thread_count += 1
+        try:
+            boolean_string = kwargs.get("clean")
+            clean = xbuddy.XBuddy.ParseBoolean(boolean_string)
+            if clean and os.path.exists(dl.GetBuildDir()):
+                _Log("Removing %s" % dl.GetBuildDir())
+                shutil.rmtree(dl.GetBuildDir())
+            dl.Download(factory)
+        finally:
+            with DevServerRoot._staging_thread_count_lock:
+                DevServerRoot._staging_thread_count -= 1
+        return "Success"
+
+    @cherrypy.expose
+    def locate_file(self, **kwargs):
+        """Get the path to the given file name.
+
+        This method looks up the given file name inside specified build artifacts.
+        One use case is to help caller to locate an apk file inside a build
+        artifact. The location of the apk file could be different based on the
+        branch and target.
+
+        Args:
+          file_name: Name of the file to look for.
+          artifacts: A list of artifact names to search for the file.
+
+        Returns:
+          Path to the file with the given name. It's relative to the folder for the
+          build, e.g., DATA/priv-app/sl4a/sl4a.apk
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("locate_file")
+
+        dl, _ = _get_downloader_and_factory(kwargs)
+        try:
+            file_name = kwargs["file_name"]
+            artifacts = kwargs["artifacts"]
+        except KeyError:
+            raise DevServerError(
+                "`file_name` and `artifacts` are required to search "
+                "for a file in build artifacts."
+            )
+        build_path = dl.GetBuildDir()
+        for artifact in artifacts:
+            # Get the unzipped folder of the artifact. If it's not defined in
+            # ARTIFACT_UNZIP_FOLDER_MAP, assume the files are unzipped to the build
+            # directory directly.
+            folder = artifact_info.ARTIFACT_UNZIP_FOLDER_MAP.get(artifact, "")
+            artifact_path = os.path.join(build_path, folder)
+            for root, _, filenames in os.walk(artifact_path):
+                if file_name in set([f for f in filenames]):
+                    return os.path.relpath(
+                        os.path.join(root, file_name), build_path
+                    )
         raise DevServerError(
-            'Failure in telemetry setup for build %s. Appears that the '
-            'test_src to src move failed.' % dl.GetBuild())
+            "File `%s` can not be found in artifacts: %s"
+            % (file_name, artifacts)
+        )
 
-      return src_folder
+    @cherrypy.expose
+    def setup_telemetry(self, **kwargs):
+        """Extracts and sets up telemetry
 
-  @cherrypy.expose
-  def symbolicate_dump(self, minidump, **kwargs):
-    """Symbolicates a minidump using pre-downloaded symbols, returns it.
+        This method goes through the telemetry deps packages, and stages them on
+        the devserver to be used by the drones and the telemetry tests.
 
-    Callers will need to POST to this URL with a body of MIME-type
-    "multipart/form-data".
-    The body should include a single argument, 'minidump', containing the
-    binary-formatted minidump to symbolicate.
+        Args:
+          archive_url: Google Storage URL for the build.
 
-    Args:
-      archive_url: Google Storage URL for the build.
-      minidump: The binary minidump file to symbolicate.
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('symbolicate_dump')
+        Returns:
+          Path to the source folder for the telemetry codebase once it is staged.
+        """
+        dl = _get_downloader(kwargs)
 
-    # Ensure the symbols have been staged.
-    # Try debug.tar.xz first, then debug.tgz
-    for artifact in (artifact_info.SYMBOLS_ONLY, artifact_info.SYMBOLS):
-      kwargs['artifacts'] = artifact
-      dl = _get_downloader(kwargs)
+        build_path = dl.GetBuildDir()
+        deps_path = os.path.join(build_path, "autotest/packages")
+        telemetry_path = os.path.join(build_path, TELEMETRY_FOLDER)
+        src_folder = os.path.join(telemetry_path, "src")
 
-      try:
-        if self.stage(**kwargs) == 'Success':
-          break
-      except build_artifact.ArtifactDownloadError:
-        continue
-    else:
-      raise DevServerError(
-          'Failed to stage symbols for %s' % dl.DescribeSource())
+        with self._telemetry_lock_dict.lock(telemetry_path):
+            if os.path.exists(src_folder):
+                # Telemetry is already fully stage return
+                return src_folder
 
-    to_return = ''
-    with tempfile.NamedTemporaryFile() as local:
-      while True:
-        data = minidump.file.read(8192)
-        if not data:
-          break
-        local.write(data)
+            common_util.MkDirP(telemetry_path)
 
-      local.flush()
+            # Copy over the required deps tar balls to the telemetry directory.
+            for dep in TELEMETRY_DEPS:
+                dep_path = os.path.join(deps_path, dep)
+                if not os.path.exists(dep_path):
+                    # This dep does not exist (could be new), do not extract it.
+                    continue
+                try:
+                    cros_build_lib.ExtractTarball(dep_path, telemetry_path)
+                except cros_build_lib.TarballError as e:
+                    shutil.rmtree(telemetry_path)
+                    raise DevServerError(str(e))
 
-      symbols_directory = os.path.join(dl.GetBuildDir(), 'debug', 'breakpad')
+            # By default all the tarballs extract to test_src but some parts of
+            # the telemetry code specifically hardcoded to exist inside of 'src'.
+            test_src = os.path.join(telemetry_path, "test_src")
+            try:
+                shutil.move(test_src, src_folder)
+            except shutil.Error:
+                # This can occur if src_folder already exists. Remove and retry move.
+                shutil.rmtree(src_folder)
+                raise DevServerError(
+                    "Failure in telemetry setup for build %s. Appears that the "
+                    "test_src to src move failed." % dl.GetBuild()
+                )
 
-      # The location of minidump_stackwalk is defined in chromeos-admin.
-      stackwalk = subprocess.Popen(
-          ['/usr/local/bin/minidump_stackwalk', local.name, symbols_directory],
-          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            return src_folder
 
-      to_return, error_text = stackwalk.communicate()
-      if stackwalk.returncode != 0:
-        raise DevServerError(
-            "Can't generate stack trace: %s (rc=%d)" % (error_text,
-                                                        stackwalk.returncode))
+    @cherrypy.expose
+    def symbolicate_dump(self, minidump, **kwargs):
+        """Symbolicates a minidump using pre-downloaded symbols, returns it.
 
-    return to_return
+        Callers will need to POST to this URL with a body of MIME-type
+        "multipart/form-data".
+        The body should include a single argument, 'minidump', containing the
+        binary-formatted minidump to symbolicate.
 
-  @cherrypy.expose
-  def latestbuild(self, **kwargs):
-    """Return a string representing the latest build for a given target.
+        Args:
+          archive_url: Google Storage URL for the build.
+          minidump: The binary minidump file to symbolicate.
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("symbolicate_dump")
 
-    Args:
-      target: The build target, typically a combination of the board and the
-          type of build e.g. x86-mario-release.
-      milestone: The milestone to filter builds on. E.g. R16. Optional, if not
-          provided the latest RXX build will be returned.
+        # Ensure the symbols have been staged.
+        # Try debug.tar.xz first, then debug.tgz
+        for artifact in (artifact_info.SYMBOLS_ONLY, artifact_info.SYMBOLS):
+            kwargs["artifacts"] = artifact
+            dl = _get_downloader(kwargs)
 
-    Returns:
-      A string representation of the latest build if one exists, i.e.
-          R19-1993.0.0-a1-b1480.
-      An empty string if no latest could be found.
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('latestbuild')
+            try:
+                if self.stage(**kwargs) == "Success":
+                    break
+            except build_artifact.ArtifactDownloadError:
+                continue
+        else:
+            raise DevServerError(
+                "Failed to stage symbols for %s" % dl.DescribeSource()
+            )
 
-    if not kwargs:
-      return _PrintDocStringAsHTML(self.latestbuild)
+        to_return = ""
+        with tempfile.NamedTemporaryFile() as local:
+            while True:
+                data = minidump.file.read(8192)
+                if not data:
+                    break
+                local.write(data)
 
-    if 'target' not in kwargs:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               'Error: target= is required!')
+            local.flush()
 
-    if _is_android_build_request(kwargs):
-      branch = kwargs.get('branch', None)
-      target = kwargs.get('target', None)
-      if not target or not branch:
-        raise DevServerError('Both target and branch must be specified to query'
-                             ' for the latest Android build.')
-      return android_build.BuildAccessor.GetLatestBuildID(target, branch)
+            symbols_directory = os.path.join(
+                dl.GetBuildDir(), "debug", "breakpad"
+            )
 
-    try:
-      return common_util.GetLatestBuildVersion(
-          updater.static_dir, kwargs['target'],
-          milestone=kwargs.get('milestone'))
-    except common_util.CommonUtilError as errmsg:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               str(errmsg))
+            # The location of minidump_stackwalk is defined in chromeos-admin.
+            stackwalk = subprocess.Popen(
+                [
+                    "/usr/local/bin/minidump_stackwalk",
+                    local.name,
+                    symbols_directory,
+                ],
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+            )
 
-  @cherrypy.expose
-  def list_suite_controls(self, **kwargs):
-    """Return a list of contents of all known control files.
+            to_return, error_text = stackwalk.communicate()
+            if stackwalk.returncode != 0:
+                raise DevServerError(
+                    "Can't generate stack trace: %s (rc=%d)"
+                    % (error_text, stackwalk.returncode)
+                )
 
-    Example URL:
-      To List all control files' content:
-      http://dev-server/list_suite_controls?suite_name=bvt&
-      build=daisy_spring-release/R29-4279.0.0
+        return to_return
 
-    Args:
-      build: The build i.e. x86-alex-release/R18-1514.0.0-a1-b1450.
-      suite_name: List the control files belonging to that suite.
+    @cherrypy.expose
+    def latestbuild(self, **kwargs):
+        """Return a string representing the latest build for a given target.
 
-    Returns:
-      A dictionary of all control files's path to its content for given suite.
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('list_suite_controls')
+        Args:
+          target: The build target, typically a combination of the board and the
+              type of build e.g. x86-mario-release.
+          milestone: The milestone to filter builds on. E.g. R16. Optional, if not
+              provided the latest RXX build will be returned.
 
-    if not kwargs:
-      return _PrintDocStringAsHTML(self.controlfiles)
+        Returns:
+          A string representation of the latest build if one exists, i.e.
+              R19-1993.0.0-a1-b1480.
+          An empty string if no latest could be found.
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("latestbuild")
 
-    if 'build' not in kwargs:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               'Error: build= is required!')
+        if not kwargs:
+            return _PrintDocStringAsHTML(self.latestbuild)
 
-    if 'suite_name' not in kwargs:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               'Error: suite_name= is required!')
+        if "target" not in kwargs:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR, "Error: target= is required!"
+            )
 
-    control_file_list = [
-        line.rstrip() for line in common_util.GetControlFileListForSuite(
-            updater.static_dir, kwargs['build'],
-            kwargs['suite_name']).splitlines()]
+        if _is_android_build_request(kwargs):
+            branch = kwargs.get("branch", None)
+            target = kwargs.get("target", None)
+            if not target or not branch:
+                raise DevServerError(
+                    "Both target and branch must be specified to query"
+                    " for the latest Android build."
+                )
+            return android_build.BuildAccessor.GetLatestBuildID(target, branch)
 
-    control_file_content_dict = {}
-    for control_path in control_file_list:
-      control_file_content_dict[control_path] = (common_util.GetControlFile(
-          updater.static_dir, kwargs['build'], control_path))
+        try:
+            return common_util.GetLatestBuildVersion(
+                updater.static_dir,
+                kwargs["target"],
+                milestone=kwargs.get("milestone"),
+            )
+        except common_util.CommonUtilError as errmsg:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR, str(errmsg)
+            )
 
-    return json.dumps(control_file_content_dict)
+    @cherrypy.expose
+    def list_suite_controls(self, **kwargs):
+        """Return a list of contents of all known control files.
 
-  @cherrypy.expose
-  def controlfiles(self, **kwargs):
-    """Return a control file or a list of all known control files.
+        Example URL:
+          To List all control files' content:
+          http://dev-server/list_suite_controls?suite_name=bvt&
+          build=daisy_spring-release/R29-4279.0.0
 
-    Example URL:
-      To List all control files:
-      http://dev-server/controlfiles?suite_name=&build=daisy_spring-release/R29-4279.0.0
-      To List all control files for, say, the bvt suite:
-      http://dev-server/controlfiles?suite_name=bvt&build=daisy_spring-release/R29-4279.0.0
-      To return the contents of a path:
-      http://dev-server/controlfiles?board=x86-alex-release&build=R18-1514.0.0&control_path=client/sleeptest/control
+        Args:
+          build: The build i.e. x86-alex-release/R18-1514.0.0-a1-b1450.
+          suite_name: List the control files belonging to that suite.
 
-    Args:
-      build: The build i.e. x86-alex-release/R18-1514.0.0-a1-b1450.
-      control_path: If you want the contents of a control file set this
-        to the path. E.g. client/site_tests/sleeptest/control
-        Optional, if not provided return a list of control files is returned.
-      suite_name: If control_path is not specified but a suite_name is
-        specified, list the control files belonging to that suite instead of
-        all control files. The empty string for suite_name will list all control
-        files for the build.
+        Returns:
+          A dictionary of all control files's path to its content for given suite.
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("list_suite_controls")
 
-    Returns:
-      Contents of a control file if control_path is provided.
-      A list of control files if no control_path is provided.
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('controlfiles')
+        if not kwargs:
+            return _PrintDocStringAsHTML(self.controlfiles)
 
-    if not kwargs:
-      return _PrintDocStringAsHTML(self.controlfiles)
+        if "build" not in kwargs:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR, "Error: build= is required!"
+            )
+
+        if "suite_name" not in kwargs:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR,
+                "Error: suite_name= is required!",
+            )
+
+        control_file_list = [
+            line.rstrip()
+            for line in common_util.GetControlFileListForSuite(
+                updater.static_dir, kwargs["build"], kwargs["suite_name"]
+            ).splitlines()
+        ]
+
+        control_file_content_dict = {}
+        for control_path in control_file_list:
+            control_file_content_dict[
+                control_path
+            ] = common_util.GetControlFile(
+                updater.static_dir, kwargs["build"], control_path
+            )
+
+        return json.dumps(control_file_content_dict)
+
+    @cherrypy.expose
+    def controlfiles(self, **kwargs):
+        """Return a control file or a list of all known control files.
+
+        Example URL:
+          To List all control files:
+          http://dev-server/controlfiles?suite_name=&build=daisy_spring-release/R29-4279.0.0
+          To List all control files for, say, the bvt suite:
+          http://dev-server/controlfiles?suite_name=bvt&build=daisy_spring-release/R29-4279.0.0
+          To return the contents of a path:
+          http://dev-server/controlfiles?board=x86-alex-release&build=R18-1514.0.0&control_path=client/sleeptest/control
+
+        Args:
+          build: The build i.e. x86-alex-release/R18-1514.0.0-a1-b1450.
+          control_path: If you want the contents of a control file set this
+            to the path. E.g. client/site_tests/sleeptest/control
+            Optional, if not provided return a list of control files is returned.
+          suite_name: If control_path is not specified but a suite_name is
+            specified, list the control files belonging to that suite instead of
+            all control files. The empty string for suite_name will list all control
+            files for the build.
+
+        Returns:
+          Contents of a control file if control_path is provided.
+          A list of control files if no control_path is provided.
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("controlfiles")
+
+        if not kwargs:
+            return _PrintDocStringAsHTML(self.controlfiles)
 
-    if 'build' not in kwargs:
-      raise DevServerHTTPError(http_client.INTERNAL_SERVER_ERROR,
-                               'Error: build= is required!')
+        if "build" not in kwargs:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR, "Error: build= is required!"
+            )
 
-    if 'control_path' not in kwargs:
-      if 'suite_name' in kwargs and kwargs['suite_name']:
-        return common_util.GetControlFileListForSuite(
-            updater.static_dir, kwargs['build'], kwargs['suite_name'])
-      else:
-        return common_util.GetControlFileList(
-            updater.static_dir, kwargs['build'])
-    else:
-      return common_util.GetControlFile(
-          updater.static_dir, kwargs['build'], kwargs['control_path'])
+        if "control_path" not in kwargs:
+            if "suite_name" in kwargs and kwargs["suite_name"]:
+                return common_util.GetControlFileListForSuite(
+                    updater.static_dir, kwargs["build"], kwargs["suite_name"]
+                )
+            else:
+                return common_util.GetControlFileList(
+                    updater.static_dir, kwargs["build"]
+                )
+        else:
+            return common_util.GetControlFile(
+                updater.static_dir, kwargs["build"], kwargs["control_path"]
+            )
 
-  @cherrypy.expose
-  def xbuddy_translate(self, *args, **kwargs):
-    """Translates an xBuddy path to a real path to artifact if it exists.
+    @cherrypy.expose
+    def xbuddy_translate(self, *args, **kwargs):
+        """Translates an xBuddy path to a real path to artifact if it exists.
 
-    Args:
-      args: An xbuddy path in the form of {local|remote}/build_id/artifact.
-        Local searches the devserver's static directory. Remote searches a
-        Google Storage image archive.
+        Args:
+          args: An xbuddy path in the form of {local|remote}/build_id/artifact.
+            Local searches the devserver's static directory. Remote searches a
+            Google Storage image archive.
 
-    Kwargs:
-      image_dir: Google Storage image archive to search in if requesting a
-        remote artifact. If none uses the default bucket.
+        Kwargs:
+          image_dir: Google Storage image archive to search in if requesting a
+            remote artifact. If none uses the default bucket.
 
-    Returns:
-      String in the format of build_id/artifact as stored on the local server
-      or in Google Storage.
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('xbuddy_translate')
+        Returns:
+          String in the format of build_id/artifact as stored on the local server
+          or in Google Storage.
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("xbuddy_translate")
 
-    build_id, filename = self._xbuddy.Translate(
-        args, image_dir=kwargs.get('image_dir'))
-    response = os.path.join(build_id, filename)
-    _Log('Path translation requested, returning: %s', response)
-    return response
+        build_id, filename = self._xbuddy.Translate(
+            args, image_dir=kwargs.get("image_dir")
+        )
+        response = os.path.join(build_id, filename)
+        _Log("Path translation requested, returning: %s", response)
+        return response
 
-  @cherrypy.expose
-  def xbuddy(self, *args, **kwargs):
-    """The full xBuddy call, returns resource specified by path_parts.
+    @cherrypy.expose
+    def xbuddy(self, *args, **kwargs):
+        """The full xBuddy call, returns resource specified by path_parts.
 
-    Args:
-      path_parts: the path following xbuddy/ in the call url is split into the
-        components of the path. The path can be understood as
-        "{local|remote}/build_id/artifact" where build_id is composed of
-        "board/version."
+        Args:
+          path_parts: the path following xbuddy/ in the call url is split into the
+            components of the path. The path can be understood as
+            "{local|remote}/build_id/artifact" where build_id is composed of
+            "board/version."
 
-        The first path element is optional, and can be "remote" or "local"
-          If local (the default), devserver will not attempt to access Google
-          Storage, and will only search the static directory for the files.
-          If remote, devserver will try to obtain the artifact off GS if it's
-          not found locally.
-        The board is the familiar board name, optionally suffixed.
-        The version can be the google storage version number, and may also be
-          any of a number of xBuddy defined version aliases that will be
-          translated into the latest built image that fits the description.
-          Defaults to latest.
-        The artifact is one of a number of image or artifact aliases used by
-          xbuddy, defined in xbuddy:ALIASES. Defaults to test.
+            The first path element is optional, and can be "remote" or "local"
+              If local (the default), devserver will not attempt to access Google
+              Storage, and will only search the static directory for the files.
+              If remote, devserver will try to obtain the artifact off GS if it's
+              not found locally.
+            The board is the familiar board name, optionally suffixed.
+            The version can be the google storage version number, and may also be
+              any of a number of xBuddy defined version aliases that will be
+              translated into the latest built image that fits the description.
+              Defaults to latest.
+            The artifact is one of a number of image or artifact aliases used by
+              xbuddy, defined in xbuddy:ALIASES. Defaults to test.
 
-    Kwargs:
-      return_dir: {true|false}
-                  if set to true, returns the url to the update.gz
-      relative_path: {true|false}
-                     if set to true, returns the relative path to the payload
-                     directory from static_dir.
-    Example URL:
-      http://host:port/xbuddy/x86-generic/R26-4000.0.0/test
-      or
-      http://host:port/xbuddy/x86-generic/R26-4000.0.0/test?return_dir=true
+        Kwargs:
+          return_dir: {true|false}
+                      if set to true, returns the url to the update.gz
+          relative_path: {true|false}
+                         if set to true, returns the relative path to the payload
+                         directory from static_dir.
+        Example URL:
+          http://host:port/xbuddy/x86-generic/R26-4000.0.0/test
+          or
+          http://host:port/xbuddy/x86-generic/R26-4000.0.0/test?return_dir=true
 
-    Returns:
-      If |return_dir|, return a uri to the folder where the artifact is. E.g.,
-        http://host:port/static/x86-generic-release/R26-4000.0.0/
-      If |relative_path| is true, return a relative path the folder where the
-      payloads are. E.g.,
-        archive/x86-generic-release/R26-4000.0.0
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('xbuddy')
+        Returns:
+          If |return_dir|, return a uri to the folder where the artifact is. E.g.,
+            http://host:port/static/x86-generic-release/R26-4000.0.0/
+          If |relative_path| is true, return a relative path the folder where the
+          payloads are. E.g.,
+            archive/x86-generic-release/R26-4000.0.0
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("xbuddy")
 
-    boolean_string = kwargs.get('return_dir')
-    return_dir = xbuddy.XBuddy.ParseBoolean(boolean_string)
-    boolean_string = kwargs.get('relative_path')
-    relative_path = xbuddy.XBuddy.ParseBoolean(boolean_string)
+        boolean_string = kwargs.get("return_dir")
+        return_dir = xbuddy.XBuddy.ParseBoolean(boolean_string)
+        boolean_string = kwargs.get("relative_path")
+        relative_path = xbuddy.XBuddy.ParseBoolean(boolean_string)
 
-    if return_dir and relative_path:
-      raise DevServerHTTPError(
-          http_client.INTERNAL_SERVER_ERROR,
-          'Cannot specify both return_dir and relative_path')
+        if return_dir and relative_path:
+            raise DevServerHTTPError(
+                http_client.INTERNAL_SERVER_ERROR,
+                "Cannot specify both return_dir and relative_path",
+            )
 
-    build_id, file_name = self._xbuddy.Get(args)
+        build_id, file_name = self._xbuddy.Get(args)
 
-    response = None
-    if return_dir:
-      response = os.path.join(cherrypy.request.base, 'static', build_id)
-      _Log('Directory requested, returning: %s', response)
-    elif relative_path:
-      response = build_id
-      _Log('Relative path requested, returning: %s', response)
-    else:
-      # Redirect to download the payload if no kwargs are set.
-      build_id = '/' + os.path.join('static', build_id, file_name)
-      _Log('Payload requested, returning: %s', build_id)
-      raise cherrypy.HTTPRedirect(build_id, 302)
+        response = None
+        if return_dir:
+            response = os.path.join(cherrypy.request.base, "static", build_id)
+            _Log("Directory requested, returning: %s", response)
+        elif relative_path:
+            response = build_id
+            _Log("Relative path requested, returning: %s", response)
+        else:
+            # Redirect to download the payload if no kwargs are set.
+            build_id = "/" + os.path.join("static", build_id, file_name)
+            _Log("Payload requested, returning: %s", build_id)
+            raise cherrypy.HTTPRedirect(build_id, 302)
 
-    return response
+        return response
 
-  @cherrypy.expose
-  def xbuddy_capacity(self):
-    """Returns the number of images cached by xBuddy."""
-    if is_deprecated_server():
-      raise DeprecatedRPCError('xbuddy_capacity')
+    @cherrypy.expose
+    def xbuddy_capacity(self):
+        """Returns the number of images cached by xBuddy."""
+        if is_deprecated_server():
+            raise DeprecatedRPCError("xbuddy_capacity")
 
-    return self._xbuddy.Capacity()
+        return self._xbuddy.Capacity()
 
-  @cherrypy.expose
-  def index(self):
-    """Presents a welcome message and documentation links."""
-    if is_deprecated_server():
-      raise DeprecatedRPCError('index')
+    @cherrypy.expose
+    def index(self):
+        """Presents a welcome message and documentation links."""
+        if is_deprecated_server():
+            raise DeprecatedRPCError("index")
 
-    html_template = (
-        'Welcome to the Dev Server!<br>\n'
-        '<br>\n'
-        'Here are the available methods, click for documentation:<br>\n'
-        '<br>\n'
-        '%s')
+        html_template = (
+            "Welcome to the Dev Server!<br>\n"
+            "<br>\n"
+            "Here are the available methods, click for documentation:<br>\n"
+            "<br>\n"
+            "%s"
+        )
 
-    exposed_methods = []
-    for app in cherrypy.tree.apps.values():
-      exposed_methods += _FindExposedMethods(
-          app.root, app.script_name.lstrip('/'),
-          unlisted=self._UNLISTED_METHODS)
+        exposed_methods = []
+        for app in cherrypy.tree.apps.values():
+            exposed_methods += _FindExposedMethods(
+                app.root,
+                app.script_name.lstrip("/"),
+                unlisted=self._UNLISTED_METHODS,
+            )
 
-    return html_template % '<br>\n'.join(
-        ['<a href=doc/%s>%s</a>' % (name, name)
-         for name in sorted(exposed_methods)])
+        return html_template % "<br>\n".join(
+            [
+                "<a href=doc/%s>%s</a>" % (name, name)
+                for name in sorted(exposed_methods)
+            ]
+        )
 
-  @cherrypy.expose
-  def doc(self, *args):
-    """Shows the documentation for available methods / URLs.
+    @cherrypy.expose
+    def doc(self, *args):
+        """Shows the documentation for available methods / URLs.
 
-    Examples:
-      http://myhost/doc/update
-    """
-    if is_deprecated_server():
-      raise DeprecatedRPCError('doc')
+        Examples:
+          http://myhost/doc/update
+        """
+        if is_deprecated_server():
+            raise DeprecatedRPCError("doc")
 
-    name = '/'.join(args)
-    method = _GetExposedMethod(name)
-    if not method:
-      raise DevServerError("No exposed method named `%s'" % name)
-    if not method.__doc__:
-      raise DevServerError("No documentation for exposed method `%s'" % name)
-    return '<pre>\n%s</pre>' % method.__doc__
+        name = "/".join(args)
+        method = _GetExposedMethod(name)
+        if not method:
+            raise DevServerError("No exposed method named `%s'" % name)
+        if not method.__doc__:
+            raise DevServerError(
+                "No documentation for exposed method `%s'" % name
+            )
+        return "<pre>\n%s</pre>" % method.__doc__
 
-  @cherrypy.expose
-  def update(self, *args, **kwargs):
-    """Handles an update check from a Chrome OS client.
+    @cherrypy.expose
+    def update(self, *args, **kwargs):
+        """Handles an update check from a Chrome OS client.
 
-    The HTTP request should contain the standard Omaha-style XML blob. The URL
-    line may contain an additional intermediate path to the update payload.
+        The HTTP request should contain the standard Omaha-style XML blob. The URL
+        line may contain an additional intermediate path to the update payload.
 
-    This request can be handled in one of 4 ways, depending on the devsever
-    settings and intermediate path.
+        This request can be handled in one of 4 ways, depending on the devsever
+        settings and intermediate path.
 
-    1. No intermediate path. DEPRECATED
+        1. No intermediate path. DEPRECATED
 
-    2. Path explicitly invokes XBuddy
-    If there is a path given, it can explicitly invoke xbuddy by prefixing it
-    with 'xbuddy'. This path is then used to acquire an image binary for the
-    devserver to generate an update payload from. Devserver then serves this
-    payload.
+        2. Path explicitly invokes XBuddy
+        If there is a path given, it can explicitly invoke xbuddy by prefixing it
+        with 'xbuddy'. This path is then used to acquire an image binary for the
+        devserver to generate an update payload from. Devserver then serves this
+        payload.
 
-    3. Path is left for the devserver to interpret.
-    If the path given doesn't explicitly invoke xbuddy, devserver will attempt
-    to generate a payload from the test image in that directory and serve it.
+        3. Path is left for the devserver to interpret.
+        If the path given doesn't explicitly invoke xbuddy, devserver will attempt
+        to generate a payload from the test image in that directory and serve it.
 
-    Examples:
-      2. Explicitly invoke xbuddy
-      update_engine_client --omaha_url=
-      http://myhost/update/xbuddy/remote/board/version/dev
-      This would go to GS to download the dev image for the board, from which
-      the devserver would generate a payload to serve.
+        Examples:
+          2. Explicitly invoke xbuddy
+          update_engine_client --omaha_url=
+          http://myhost/update/xbuddy/remote/board/version/dev
+          This would go to GS to download the dev image for the board, from which
+          the devserver would generate a payload to serve.
 
-      3. Give a path for devserver to interpret
-      update_engine_client --omaha_url=http://myhost/update/some/random/path
-      This would attempt, in order to:
-        a) Generate an update from a test image binary if found in
-           static_dir/some/random/path.
-        b) Serve an update payload found in static_dir/some/random/path.
-        c) Hope that some/random/path takes the form "board/version" and
-           and attempt to download an update payload for that board/version
-           from GS.
-    """
-    label = '/'.join(args)
-    body_length = int(cherrypy.request.headers.get('Content-Length', 0))
-    data = cherrypy.request.rfile.read(body_length)
+          3. Give a path for devserver to interpret
+          update_engine_client --omaha_url=http://myhost/update/some/random/path
+          This would attempt, in order to:
+            a) Generate an update from a test image binary if found in
+               static_dir/some/random/path.
+            b) Serve an update payload found in static_dir/some/random/path.
+            c) Hope that some/random/path takes the form "board/version" and
+               and attempt to download an update payload for that board/version
+               from GS.
+        """
+        label = "/".join(args)
+        body_length = int(cherrypy.request.headers.get("Content-Length", 0))
+        data = cherrypy.request.rfile.read(body_length)
 
-    return updater.HandleUpdatePing(data, label, **kwargs)
+        return updater.HandleUpdatePing(data, label, **kwargs)
 
 
 def _CleanCache(cache_dir, wipe):
-  """Wipes any excess cached items in the cache_dir.
+    """Wipes any excess cached items in the cache_dir.
 
-  Args:
-    cache_dir: the directory we are wiping from.
-    wipe: If True, wipe all the contents -- not just the excess.
-  """
-  if wipe:
-    # Clear the cache and exit on error.
-    cmd = 'rm -rf %s/*' % cache_dir
-    if os.system(cmd) != 0:
-      _Log('Failed to clear the cache with %s' % cmd)
-      sys.exit(1)
-  else:
-    # Clear all but the last N cached updates
-    cmd = ('cd %s; ls -tr | head --lines=-%d | xargs rm -rf' %
-           (cache_dir, CACHED_ENTRIES))
-    if os.system(cmd) != 0:
-      _Log('Failed to clean up old delta cache files with %s' % cmd)
-      sys.exit(1)
+    Args:
+      cache_dir: the directory we are wiping from.
+      wipe: If True, wipe all the contents -- not just the excess.
+    """
+    if wipe:
+        # Clear the cache and exit on error.
+        cmd = "rm -rf %s/*" % cache_dir
+        if os.system(cmd) != 0:
+            _Log("Failed to clear the cache with %s" % cmd)
+            sys.exit(1)
+    else:
+        # Clear all but the last N cached updates
+        cmd = "cd %s; ls -tr | head --lines=-%d | xargs rm -rf" % (
+            cache_dir,
+            CACHED_ENTRIES,
+        )
+        if os.system(cmd) != 0:
+            _Log("Failed to clean up old delta cache files with %s" % cmd)
+            sys.exit(1)
 
 
 def _AddTestingOptions(parser):
-  group = optparse.OptionGroup(
-      parser, 'Advanced Testing Options', 'These are used by test scripts and '
-      'developers writing integration tests utilizing the devserver. They are '
-      'not intended to be really used outside the scope of someone '
-      'knowledgable about the test.')
-  group.add_option('--exit',
-                   action='store_true',
-                   help='do not start the server (yet clear cache)')
-  parser.add_option_group(group)
+    group = optparse.OptionGroup(
+        parser,
+        "Advanced Testing Options",
+        "These are used by test scripts and "
+        "developers writing integration tests utilizing the devserver. They are "
+        "not intended to be really used outside the scope of someone "
+        "knowledgable about the test.",
+    )
+    group.add_option(
+        "--exit",
+        action="store_true",
+        help="do not start the server (yet clear cache)",
+    )
+    parser.add_option_group(group)
 
 
 def _AddProductionOptions(parser):
-  group = optparse.OptionGroup(
-      parser, 'Advanced Server Options', 'These options can be used to changed '
-      'for advanced server behavior.')
-  group.add_option('--clear_cache',
-                   action='store_true', default=False,
-                   help='At startup, removes all cached entries from the'
-                   "devserver's cache.")
-  group.add_option('--logfile',
-                   metavar='PATH',
-                   help='log output to this file instead of stdout')
-  group.add_option('--pidfile',
-                   metavar='PATH',
-                   help='path to output a pid file for the server.')
-  group.add_option('--portfile',
-                   metavar='PATH',
-                   help='path to output the port number being served on.')
-  group.add_option('--production',
-                   action='store_true', default=False,
-                   help='have the devserver use production values when '
-                   'starting up. This includes using more threads and '
-                   'performing less logging.')
-  parser.add_option_group(group)
+    group = optparse.OptionGroup(
+        parser,
+        "Advanced Server Options",
+        "These options can be used to changed " "for advanced server behavior.",
+    )
+    group.add_option(
+        "--clear_cache",
+        action="store_true",
+        default=False,
+        help="At startup, removes all cached entries from the"
+        "devserver's cache.",
+    )
+    group.add_option(
+        "--logfile",
+        metavar="PATH",
+        help="log output to this file instead of stdout",
+    )
+    group.add_option(
+        "--pidfile",
+        metavar="PATH",
+        help="path to output a pid file for the server.",
+    )
+    group.add_option(
+        "--portfile",
+        metavar="PATH",
+        help="path to output the port number being served on.",
+    )
+    group.add_option(
+        "--production",
+        action="store_true",
+        default=False,
+        help="have the devserver use production values when "
+        "starting up. This includes using more threads and "
+        "performing less logging.",
+    )
+    parser.add_option_group(group)
 
 
 def MakeLogHandler(logfile):
-  """Create a LogHandler instance used to log all messages."""
-  hdlr_cls = handlers.TimedRotatingFileHandler
-  hdlr = hdlr_cls(logfile, when=_LOG_ROTATION_TIME,
-                  interval=_LOG_ROTATION_INTERVAL,
-                  backupCount=_LOG_ROTATION_BACKUP)
-  hdlr.setFormatter(cplogging.logfmt)
-  return hdlr
+    """Create a LogHandler instance used to log all messages."""
+    hdlr_cls = handlers.TimedRotatingFileHandler
+    hdlr = hdlr_cls(
+        logfile,
+        when=_LOG_ROTATION_TIME,
+        interval=_LOG_ROTATION_INTERVAL,
+        backupCount=_LOG_ROTATION_BACKUP,
+    )
+    hdlr.setFormatter(cplogging.logfmt)
+    return hdlr
 
 
 def main():
-  usage = '\n\n'.join(['usage: %prog [options]', __doc__])
-  parser = optparse.OptionParser(usage=usage)
+    usage = "\n\n".join(["usage: %prog [options]", __doc__])
+    parser = optparse.OptionParser(usage=usage)
 
-  # get directory that the devserver is run from
-  devserver_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
-  default_static_dir = '%s/static' % devserver_dir
-  parser.add_option('--static_dir',
-                    metavar='PATH',
-                    default=default_static_dir,
-                    help='writable static directory')
-  parser.add_option('--port',
-                    default=8080, type='int',
-                    help=('port for the dev server to use; if zero, binds to '
-                          'an arbitrary available port (default: 8080)'))
-  parser.add_option('-t', '--test_image',
-                    action='store_true',
-                    help='Deprecated.')
-  parser.add_option('-x', '--xbuddy_manage_builds',
-                    action='store_true',
-                    default=False,
-                    help='If set, allow xbuddy to manage images in'
-                    'build/images.')
-  parser.add_option('-a', '--android_build_credential',
-                    default=None,
-                    help='Path to a json file which contains the credential '
-                    'needed to access Android builds.')
-  parser.add_option('--infra_removal',
-                    action='store_true', default=False,
-                    help='If option is present, some RPCs will be disabled to '
-                         'help with infra removal efforts. See '
-                         'go/devserver-deprecation')
-  _AddProductionOptions(parser)
-  _AddTestingOptions(parser)
-  (options, _) = parser.parse_args()
+    # get directory that the devserver is run from
+    devserver_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+    default_static_dir = "%s/static" % devserver_dir
+    parser.add_option(
+        "--static_dir",
+        metavar="PATH",
+        default=default_static_dir,
+        help="writable static directory",
+    )
+    parser.add_option(
+        "--port",
+        default=8080,
+        type="int",
+        help=(
+            "port for the dev server to use; if zero, binds to "
+            "an arbitrary available port (default: 8080)"
+        ),
+    )
+    parser.add_option(
+        "-t", "--test_image", action="store_true", help="Deprecated."
+    )
+    parser.add_option(
+        "-x",
+        "--xbuddy_manage_builds",
+        action="store_true",
+        default=False,
+        help="If set, allow xbuddy to manage images in" "build/images.",
+    )
+    parser.add_option(
+        "-a",
+        "--android_build_credential",
+        default=None,
+        help="Path to a json file which contains the credential "
+        "needed to access Android builds.",
+    )
+    parser.add_option(
+        "--infra_removal",
+        action="store_true",
+        default=False,
+        help="If option is present, some RPCs will be disabled to "
+        "help with infra removal efforts. See "
+        "go/devserver-deprecation",
+    )
+    _AddProductionOptions(parser)
+    _AddTestingOptions(parser)
+    (options, _) = parser.parse_args()
 
-  # Handle options that must be set globally in cherrypy.  Do this
-  # work up front, because calls to _Log() below depend on this
-  # initialization.
-  if options.production:
-    cherrypy.config.update({'environment': 'production'})
-  cherrypy.config.update({'infra_removal': options.infra_removal})
-  if not options.logfile:
-    cherrypy.config.update({'log.screen': True})
-  else:
-    cherrypy.config.update({'log.error_file': '',
-                            'log.access_file': ''})
-    hdlr = MakeLogHandler(options.logfile)
-    # Pylint can't seem to process these two calls properly
-    # pylint: disable=E1101
-    cherrypy.log.access_log.addHandler(hdlr)
-    cherrypy.log.error_log.addHandler(hdlr)
-    # pylint: enable=E1101
+    # Handle options that must be set globally in cherrypy.  Do this
+    # work up front, because calls to _Log() below depend on this
+    # initialization.
+    if options.production:
+        cherrypy.config.update({"environment": "production"})
+    cherrypy.config.update({"infra_removal": options.infra_removal})
+    if not options.logfile:
+        cherrypy.config.update({"log.screen": True})
+    else:
+        cherrypy.config.update({"log.error_file": "", "log.access_file": ""})
+        hdlr = MakeLogHandler(options.logfile)
+        # Pylint can't seem to process these two calls properly
+        # pylint: disable=E1101
+        cherrypy.log.access_log.addHandler(hdlr)
+        cherrypy.log.error_log.addHandler(hdlr)
+        # pylint: enable=E1101
 
-  # set static_dir, from which everything will be served
-  options.static_dir = os.path.realpath(options.static_dir)
+    # set static_dir, from which everything will be served
+    options.static_dir = os.path.realpath(options.static_dir)
 
-  cache_dir = os.path.join(options.static_dir, 'cache')
-  # If our devserver is only supposed to serve payloads, we shouldn't be
-  # mucking with the cache at all. If the devserver hadn't previously
-  # generated a cache and is expected, the caller is using it wrong.
-  if os.path.exists(cache_dir):
-    _CleanCache(cache_dir, options.clear_cache)
-  else:
-    os.makedirs(cache_dir)
+    cache_dir = os.path.join(options.static_dir, "cache")
+    # If our devserver is only supposed to serve payloads, we shouldn't be
+    # mucking with the cache at all. If the devserver hadn't previously
+    # generated a cache and is expected, the caller is using it wrong.
+    if os.path.exists(cache_dir):
+        _CleanCache(cache_dir, options.clear_cache)
+    else:
+        os.makedirs(cache_dir)
 
-  pkgroot_dir = os.path.join(options.static_dir, 'pkgroot')
-  common_util.SymlinkFile('/build', pkgroot_dir)
+    pkgroot_dir = os.path.join(options.static_dir, "pkgroot")
+    common_util.SymlinkFile("/build", pkgroot_dir)
 
-  _Log('Using cache directory %s' % cache_dir)
-  _Log('Serving from %s' % options.static_dir)
+    _Log("Using cache directory %s" % cache_dir)
+    _Log("Serving from %s" % options.static_dir)
 
-  _xbuddy = xbuddy.XBuddy(manage_builds=options.xbuddy_manage_builds,
-                          static_dir=options.static_dir)
-  if options.clear_cache and options.xbuddy_manage_builds:
-    _xbuddy.CleanCache()
+    _xbuddy = xbuddy.XBuddy(
+        manage_builds=options.xbuddy_manage_builds,
+        static_dir=options.static_dir,
+    )
+    if options.clear_cache and options.xbuddy_manage_builds:
+        _xbuddy.CleanCache()
 
-  # We allow global use here to share with cherrypy classes.
-  # pylint: disable=W0603
-  global updater
-  updater = autoupdate.Autoupdate(_xbuddy, static_dir=options.static_dir)
+    # We allow global use here to share with cherrypy classes.
+    # pylint: disable=W0603
+    global updater
+    updater = autoupdate.Autoupdate(_xbuddy, static_dir=options.static_dir)
 
-  if options.exit:
-    return
+    if options.exit:
+        return
 
-  dev_server = DevServerRoot(_xbuddy)
-  health_checker_app = health_checker.Root(dev_server, options.static_dir)
+    dev_server = DevServerRoot(_xbuddy)
+    health_checker_app = health_checker.Root(dev_server, options.static_dir)
 
-  if options.pidfile:
-    plugins.PIDFile(cherrypy.engine, options.pidfile).subscribe()
+    if options.pidfile:
+        plugins.PIDFile(cherrypy.engine, options.pidfile).subscribe()
 
-  if options.portfile:
-    cherrypy_ext.PortFile(cherrypy.engine, options.portfile).subscribe()
+    if options.portfile:
+        cherrypy_ext.PortFile(cherrypy.engine, options.portfile).subscribe()
 
-  if (options.android_build_credential and
-      os.path.exists(options.android_build_credential)):
-    try:
-      with open(options.android_build_credential) as f:
-        android_build.BuildAccessor.credential_info = json.load(f)
-    except ValueError as e:
-      _Log('Failed to load the android build credential: %s. Error: %s.' %
-           (options.android_build_credential, e))
+    if options.android_build_credential and os.path.exists(
+        options.android_build_credential
+    ):
+        try:
+            with open(options.android_build_credential) as f:
+                android_build.BuildAccessor.credential_info = json.load(f)
+        except ValueError as e:
+            _Log(
+                "Failed to load the android build credential: %s. Error: %s."
+                % (options.android_build_credential, e)
+            )
 
-  cherrypy.tree.mount(health_checker_app, '/check_health',
-                      config=health_checker.get_config())
-  cherrypy.quickstart(dev_server, config=_GetConfig(options))
+    cherrypy.tree.mount(
+        health_checker_app, "/check_health", config=health_checker.get_config()
+    )
+    cherrypy.quickstart(dev_server, config=_GetConfig(options))
 
 
-if __name__ == '__main__':
-  main()
+if __name__ == "__main__":
+    main()