def DoDownloadApp(rpcserver, out_dir, app_id, server, app_version):
  """Downloads the files associated with a particular app version.

  Args:
    rpcserver: The RPC server to use to download.
    out_dir: The directory the files should be downloaded to.
    app_id: The app ID of the app whose files we want to download.
    server: The server we want to download from.  Can be:
      - None: We'll download from the default server.
      - <server>: We'll download from the specified server.
    app_version: The version number we want to download.  Can be:
      - None: We'll download the latest default version.
      - <major>: We'll download the latest minor version.
      - <major>/<minor>: We'll download that exact version.
  """

  StatusUpdate('Fetching file list...')

  url_args = {'app_id': app_id}
  if server:
    url_args['server'] = server
  if app_version is not None:
    url_args['version_match'] = app_version

  result = rpcserver.Send('/api/files/list', **url_args)

  StatusUpdate('Fetching files...')

  lines = result.splitlines()

  if len(lines) < 1:
    logging.error('Invalid response from server: empty')
    return

  full_version = lines[0]
  file_lines = lines[1:]

  current_file_number = 0
  num_files = len(file_lines)

  num_errors = 0

  for line in file_lines:
    parts = line.split('|', 2)
    if len(parts) != 3:
      logging.error('Invalid response from server: expecting '
                    '"<id>|<size>|<path>", found: "%s"\n', line)
      return

    current_file_number += 1

    file_id, size_str, path = parts
    try:
      size = int(size_str)
    except __HOLE__:
      logging.error('Invalid file list entry from server: invalid size: '
                    '"%s"', size_str)
      return

    StatusUpdate('[%d/%d] %s' % (current_file_number, num_files, path))

    def TryGet():
      """A request to /api/files/get which works with the RetryWithBackoff."""
      try:
        contents = rpcserver.Send('/api/files/get', app_id=app_id,
                                  version=full_version, id=file_id)
        return True, contents
      except urllib2.HTTPError, exc:


        if exc.code == 503:
          return False, exc
        else:
          raise

    def PrintRetryMessage(_, delay):
      StatusUpdate('Server busy.  Will try again in %d seconds.' % delay)

    success, contents = RetryWithBackoff(TryGet, PrintRetryMessage)
    if not success:
      logging.error('Unable to download file "%s".', path)
      num_errors += 1
      continue

    if len(contents) != size:
      logging.error('File "%s": server listed as %d bytes but served '
                    '%d bytes.', path, size, len(contents))
      num_errors += 1

    full_path = os.path.join(out_dir, path)

    if os.path.exists(full_path):
      logging.error('Unable to create file "%s": path conflicts with '
                    'an existing file or directory', path)
      num_errors += 1
      continue

    full_dir = os.path.dirname(full_path)
    try:
      EnsureDir(full_dir)
    except OSError, exc:
      logging.error('Couldn\'t create directory "%s": %s', full_dir, exc)
      num_errors += 1
      continue

    try:
      out_file = open(full_path, 'wb')
    except IOError, exc:
      logging.error('Couldn\'t open file "%s": %s', full_path, exc)
      num_errors += 1
      continue

    try:
      try:
        out_file.write(contents)
      except IOError, exc:
        logging.error('Couldn\'t write to file "%s": %s', full_path, exc)
        num_errors += 1
        continue
    finally:
      out_file.close()

  if num_errors > 0:
    logging.error('Number of errors: %d.  See output for details.', num_errors)
dataset/ETHPy150Open AppScale/appscale/AppServer/google/appengine/tools/appcfg.py/DoDownloadApp