Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(469)

Side by Side Diff: gm/rebaseline_server/download_actuals.py

Issue 143653006: new tool: download all GM images for a given builder, ready for skdiff (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: final touches Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gm/gm_json.py ('k') | gm/rebaseline_server/download_actuals_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 """
4 Copyright 2014 Google Inc.
5
6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file.
8
9 Download actual GM results for a particular builder.
10 """
11
12 # System-level imports
13 import contextlib
14 import optparse
15 import os
16 import posixpath
17 import re
18 import shutil
19 import sys
20 import urllib
21 import urllib2
22 import urlparse
23
24 # Imports from within Skia
25 #
26 # We need to add the 'gm' and 'tools' directories, so that we can import
27 # gm_json.py and buildbot_globals.py.
28 #
29 # Make sure that these dirs are in the PYTHONPATH, but add them at the *end*
30 # so any dirs that are already in the PYTHONPATH will be preferred.
31 #
32 # TODO(epoger): Is it OK for this to depend on the 'tools' dir, given that
33 # the tools dir is dependent on the 'gm' dir (to import gm_json.py)?
34 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
35 GM_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'gm')
36 TOOLS_DIRECTORY = os.path.join(TRUNK_DIRECTORY, 'tools')
37 if GM_DIRECTORY not in sys.path:
38 sys.path.append(GM_DIRECTORY)
39 if TOOLS_DIRECTORY not in sys.path:
40 sys.path.append(TOOLS_DIRECTORY)
41 import buildbot_globals
42 import gm_json
43
44 DEFAULT_ACTUALS_BASE_URL = posixpath.join(
45 buildbot_globals.Get('autogen_svn_url'), 'gm-actual')
46 DEFAULT_JSON_FILENAME = 'actual-results.json'
47
48
49 class Download(object):
50
51 def __init__(self, actuals_base_url=DEFAULT_ACTUALS_BASE_URL,
52 json_filename=DEFAULT_JSON_FILENAME,
53 gm_actuals_root_url=gm_json.GM_ACTUALS_ROOT_HTTP_URL):
54 """
55 Args:
56 actuals_base_url: URL pointing at the root directory
57 containing all actual-results.json files, e.g.,
58 http://domain.name/path/to/dir OR
59 file:///absolute/path/to/localdir
60 json_filename: The JSON filename to read from within each directory.
61 gm_actuals_root_url: Base URL under which the actually-generated-by-bots
62 GM images are stored.
63 """
64 self._actuals_base_url = actuals_base_url
65 self._json_filename = json_filename
66 self._gm_actuals_root_url = gm_actuals_root_url
67 self._image_filename_re = re.compile(gm_json.IMAGE_FILENAME_PATTERN)
68
69 def fetch(self, builder_name, dest_dir):
70 """ Downloads actual GM results for a particular builder.
71
72 Args:
73 builder_name: which builder to download results of
74 dest_dir: path to directory where the image files will be written;
75 if the directory does not exist yet, it will be created
76
77 TODO(epoger): Display progress info. Right now, it can take a long time
78 to download all of the results, and there is no indication of progress.
79
80 TODO(epoger): Download multiple images in parallel to speed things up.
81 """
82 json_url = posixpath.join(self._actuals_base_url, builder_name,
83 self._json_filename)
84 json_contents = urllib2.urlopen(json_url).read()
85 results_dict = gm_json.LoadFromString(json_contents)
86
87 actual_results_dict = results_dict[gm_json.JSONKEY_ACTUALRESULTS]
88 for result_type in sorted(actual_results_dict.keys()):
89 results_of_this_type = actual_results_dict[result_type]
90 if not results_of_this_type:
91 continue
92 for image_name in sorted(results_of_this_type.keys()):
93 (test, config) = self._image_filename_re.match(image_name).groups()
94 (hash_type, hash_digest) = results_of_this_type[image_name]
95 source_url = gm_json.CreateGmActualUrl(
96 test_name=test, hash_type=hash_type, hash_digest=hash_digest,
97 gm_actuals_root_url=self._gm_actuals_root_url)
98 dest_path = os.path.join(dest_dir, config, test + '.png')
99 copy_contents(source_url=source_url, dest_path=dest_path,
100 create_subdirs_if_needed=True)
101
102
103 def create_filepath_url(filepath):
104 """ Returns a file:/// URL pointing at the given filepath on local disk.
105
106 For now, this is only used by unittests, but I anticipate it being useful
107 in production, as a way for developers to run rebaseline_server over locally
108 generated images.
109
110 TODO(epoger): Move this function, and copy_contents(), into a shared
111 utility module. They are generally useful.
112
113 Args:
114 filepath: string; path to a file on local disk (may be absolute or relative,
115 and the file does not need to exist)
116
117 Returns:
118 A file:/// URL pointing at the file. Regardless of whether filepath was
119 specified as a relative or absolute path, the URL will contain an
120 absolute path to the file.
121
122 Raises:
123 An Exception, if filepath is already a URL.
124 """
125 if urlparse.urlparse(filepath).scheme:
126 raise Exception('"%s" is already a URL' % filepath)
127 return urlparse.urljoin(
128 'file:', urllib.pathname2url(os.path.abspath(filepath)))
129
130
131 def copy_contents(source_url, dest_path, create_subdirs_if_needed=False):
132 """ Copies the full contents of the URL 'source_url' into
133 filepath 'dest_path'.
134
135 Args:
136 source_url: string; complete URL to read from
137 dest_path: string; complete filepath to write to (may be absolute or
138 relative)
139 create_subdirs_if_needed: boolean; whether to create subdirectories as
140 needed to create dest_path
141
142 Raises:
143 Some subclass of Exception if unable to read source_url or write dest_path.
144 """
145 if create_subdirs_if_needed:
146 dest_dir = os.path.dirname(dest_path)
147 if not os.path.exists(dest_dir):
148 os.makedirs(dest_dir)
149 with contextlib.closing(urllib.urlopen(source_url)) as source_handle:
150 with open(dest_path, 'wb') as dest_handle:
151 shutil.copyfileobj(fsrc=source_handle, fdst=dest_handle)
152
153
154 def main():
155 parser = optparse.OptionParser()
156 required_params = []
157 parser.add_option('--actuals-base-url',
158 action='store', type='string',
159 default=DEFAULT_ACTUALS_BASE_URL,
160 help=('Base URL from which to read files containing JSON '
161 'summaries of actual GM results; defaults to '
162 '"%default". To get a specific revision (useful for '
163 'trybots) replace "svn" with "svn-history/r123".'))
164 # TODO(epoger): Rather than telling the user to run "svn ls" to get the list
165 # of builders, add a --list-builders option that will print the list.
166 required_params.append('builder')
167 parser.add_option('--builder',
168 action='store', type='string',
169 help=('REQUIRED: Which builder to download results for. '
170 'To see a list of builders, run "svn ls %s".' %
171 DEFAULT_ACTUALS_BASE_URL))
172 required_params.append('dest_dir')
173 parser.add_option('--dest-dir',
174 action='store', type='string',
175 help=('REQUIRED: Directory where all images should be '
176 'written. If this directory does not exist yet, it '
177 'will be created.'))
178 parser.add_option('--json-filename',
179 action='store', type='string',
180 default=DEFAULT_JSON_FILENAME,
181 help=('JSON summary filename to read for each builder; '
182 'defaults to "%default".'))
183 (params, remaining_args) = parser.parse_args()
184
185 # Make sure all required options were set,
186 # and that there were no items left over in the command line.
187 for required_param in required_params:
188 if not getattr(params, required_param):
189 raise Exception('required option \'%s\' was not set' % required_param)
190 if len(remaining_args) is not 0:
191 raise Exception('extra items specified in the command line: %s' %
192 remaining_args)
193
194 downloader = Download(actuals_base_url=params.actuals_base_url)
195 downloader.fetch(builder_name=params.builder,
196 dest_dir=params.dest_dir)
197
198
199
200 if __name__ == '__main__':
201 main()
OLDNEW
« no previous file with comments | « gm/gm_json.py ('k') | gm/rebaseline_server/download_actuals_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698