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

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

Issue 26659002: rebaseline_server: extend returned JSON dict to allow for result-editing in coming CL (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: oppa_python_style Created 7 years, 2 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 | Annotate | Revision Log
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/loader.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 ''' 3 """
4 Copyright 2013 Google Inc. 4 Copyright 2013 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 '''
9 8
10 '''
11 HTTP server for our HTML rebaseline viewer. 9 HTTP server for our HTML rebaseline viewer.
12 ''' 10 """
13 11
14 # System-level imports 12 # System-level imports
15 import argparse 13 import argparse
16 import BaseHTTPServer 14 import BaseHTTPServer
17 import json 15 import json
18 import os 16 import os
19 import posixpath 17 import posixpath
20 import re 18 import re
21 import shutil 19 import shutil
22 import sys 20 import sys
(...skipping 30 matching lines...) Expand all
53 'json': 'application/json' 51 'json': 'application/json'
54 } 52 }
55 53
56 DEFAULT_ACTUALS_DIR = '.gm-actuals' 54 DEFAULT_ACTUALS_DIR = '.gm-actuals'
57 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm') 55 DEFAULT_EXPECTATIONS_DIR = os.path.join(TRUNK_DIRECTORY, 'expectations', 'gm')
58 DEFAULT_PORT = 8888 56 DEFAULT_PORT = 8888
59 57
60 _SERVER = None # This gets filled in by main() 58 _SERVER = None # This gets filled in by main()
61 59
62 class Server(object): 60 class Server(object):
63 """ HTTP server for our HTML rebaseline viewer. 61 """ HTTP server for our HTML rebaseline viewer. """
64 62
65 params:
66 actuals_dir: directory under which we will check out the latest actual
67 GM results
68 expectations_dir: directory under which to find GM expectations (they
69 must already be in that directory)
70 port: which TCP port to listen on for HTTP requests
71 export: whether to allow HTTP clients on other hosts to access this server
72 """
73 def __init__(self, 63 def __init__(self,
74 actuals_dir=DEFAULT_ACTUALS_DIR, 64 actuals_dir=DEFAULT_ACTUALS_DIR,
75 expectations_dir=DEFAULT_EXPECTATIONS_DIR, 65 expectations_dir=DEFAULT_EXPECTATIONS_DIR,
76 port=DEFAULT_PORT, export=False): 66 port=DEFAULT_PORT, export=False):
67 """
68 Args:
69 actuals_dir: directory under which we will check out the latest actual
70 GM results
71 expectations_dir: directory under which to find GM expectations (they
72 must already be in that directory)
73 port: which TCP port to listen on for HTTP requests
74 export: whether to allow HTTP clients on other hosts to access this server
75 """
77 self._actuals_dir = actuals_dir 76 self._actuals_dir = actuals_dir
78 self._expectations_dir = expectations_dir 77 self._expectations_dir = expectations_dir
79 self._port = port 78 self._port = port
80 self._export = export 79 self._export = export
81 80
81 def is_exported(self):
82 """ Returns true iff HTTP clients on other hosts are allowed to access
83 this server. """
84 return self._export
85
82 def fetch_results(self): 86 def fetch_results(self):
83 """ Create self.results, based on the expectations in 87 """ Create self.results, based on the expectations in
84 self._expectations_dir and the latest actuals from skia-autogen. 88 self._expectations_dir and the latest actuals from skia-autogen.
85 89
86 TODO(epoger): Add a new --browseonly mode setting. In that mode, 90 TODO(epoger): Add a new --browseonly mode setting. In that mode,
87 the gm-actuals and expectations will automatically be updated every few 91 the gm-actuals and expectations will automatically be updated every few
88 minutes. See discussion in https://codereview.chromium.org/24274003/ . 92 minutes. See discussion in https://codereview.chromium.org/24274003/ .
89 """ 93 """
90 print 'Checking out latest actual GM results from %s into %s ...' % ( 94 print 'Checking out latest actual GM results from %s into %s ...' % (
91 ACTUALS_SVN_REPO, self._actuals_dir) 95 ACTUALS_SVN_REPO, self._actuals_dir)
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 do_GET_* dispatcher. """ 128 do_GET_* dispatcher. """
125 if self.path == '' or self.path == '/' or self.path == '/index.html' : 129 if self.path == '' or self.path == '/' or self.path == '/index.html' :
126 self.redirect_to('/static/view.html') 130 self.redirect_to('/static/view.html')
127 return 131 return
128 if self.path == '/favicon.ico' : 132 if self.path == '/favicon.ico' :
129 self.redirect_to('/static/favicon.ico') 133 self.redirect_to('/static/favicon.ico')
130 return 134 return
131 135
132 # All requests must be of this form: 136 # All requests must be of this form:
133 # /dispatcher/remainder 137 # /dispatcher/remainder
134 # where "dispatcher" indicates which do_GET_* dispatcher to run 138 # where 'dispatcher' indicates which do_GET_* dispatcher to run
135 # and "remainder" is the remaining path sent to that dispatcher. 139 # and 'remainder' is the remaining path sent to that dispatcher.
136 normpath = posixpath.normpath(self.path) 140 normpath = posixpath.normpath(self.path)
137 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups() 141 (dispatcher_name, remainder) = PATHSPLIT_RE.match(normpath).groups()
138 dispatchers = { 142 dispatchers = {
139 'results': self.do_GET_results, 143 'results': self.do_GET_results,
140 'static': self.do_GET_static, 144 'static': self.do_GET_static,
141 } 145 }
142 dispatcher = dispatchers[dispatcher_name] 146 dispatcher = dispatchers[dispatcher_name]
143 dispatcher(remainder) 147 dispatcher(remainder)
144 148
145 def do_GET_results(self, result_type): 149 def do_GET_results(self, result_type):
146 """ Handle a GET request for GM results. 150 """ Handle a GET request for GM results.
147 For now, we ignore the remaining path info, because we only know how to 151 For now, we ignore the remaining path info, because we only know how to
148 return all results. 152 return all results.
149 153
154 Args:
155 result_type: currently unused
156
150 TODO(epoger): Unless we start making use of result_type, remove that 157 TODO(epoger): Unless we start making use of result_type, remove that
151 parameter.""" 158 parameter."""
152 print 'do_GET_results: sending results of type "%s"' % result_type 159 print 'do_GET_results: sending results of type "%s"' % result_type
160 # TODO(epoger): Cache response_dict rather than the results object, to save
161 # time on subsequent fetches (no need to regenerate the header, etc.)
153 response_dict = _SERVER.results.GetAll() 162 response_dict = _SERVER.results.GetAll()
154 if response_dict: 163 if response_dict:
164 response_dict['header'] = {
165 # Hash of testData, which the client must return with any edits--
166 # this ensures that the edits were made to a particular dataset.
167 'data-hash': str(hash(repr(response_dict['testData']))),
168
169 # Whether the server will accept edits back.
170 # TODO(epoger): Not yet implemented, so hardcoding to False;
171 # once we implement the 'browseonly' mode discussed in
172 # https://codereview.chromium.org/24274003/#msg6 , this value will vary.
173 'isEditable': False,
174
175 # Whether the service is accessible from other hosts.
176 'isExported': _SERVER.is_exported(),
177 }
155 self.send_json_dict(response_dict) 178 self.send_json_dict(response_dict)
156 else: 179 else:
157 self.send_error(404) 180 self.send_error(404)
158 181
159 def do_GET_static(self, path): 182 def do_GET_static(self, path):
160 """ Handle a GET request for a file under the 'static' directory. 183 """ Handle a GET request for a file under the 'static' directory.
161 Only allow serving of files within the 'static' directory that is a 184 Only allow serving of files within the 'static' directory that is a
162 filesystem sibling of this script. """ 185 filesystem sibling of this script.
186
187 Args:
188 path: path to file (under static directory) to retrieve
189 """
163 print 'do_GET_static: sending file "%s"' % path 190 print 'do_GET_static: sending file "%s"' % path
164 static_dir = os.path.realpath(os.path.join(PARENT_DIRECTORY, 'static')) 191 static_dir = os.path.realpath(os.path.join(PARENT_DIRECTORY, 'static'))
165 full_path = os.path.realpath(os.path.join(static_dir, path)) 192 full_path = os.path.realpath(os.path.join(static_dir, path))
166 if full_path.startswith(static_dir): 193 if full_path.startswith(static_dir):
167 self.send_file(full_path) 194 self.send_file(full_path)
168 else: 195 else:
169 print ('Attempted do_GET_static() of path [%s] outside of static dir [%s]' 196 print ('Attempted do_GET_static() of path [%s] outside of static dir [%s]'
170 % (full_path, static_dir)) 197 % (full_path, static_dir))
171 self.send_error(404) 198 self.send_error(404)
172 199
173 def redirect_to(self, url): 200 def redirect_to(self, url):
174 """ Redirect the HTTP client to a different url. """ 201 """ Redirect the HTTP client to a different url.
202
203 Args:
204 url: URL to redirect the HTTP client to
205 """
175 self.send_response(301) 206 self.send_response(301)
176 self.send_header('Location', url) 207 self.send_header('Location', url)
177 self.end_headers() 208 self.end_headers()
178 209
179 def send_file(self, path): 210 def send_file(self, path):
180 """ Send the contents of the file at this path, with a mimetype based 211 """ Send the contents of the file at this path, with a mimetype based
181 on the filename extension. """ 212 on the filename extension.
213
214 Args:
215 path: path of file whose contents to send to the HTTP client
216 """
182 # Grab the extension if there is one 217 # Grab the extension if there is one
183 extension = os.path.splitext(path)[1] 218 extension = os.path.splitext(path)[1]
184 if len(extension) >= 1: 219 if len(extension) >= 1:
185 extension = extension[1:] 220 extension = extension[1:]
186 221
187 # Determine the MIME type of the file from its extension 222 # Determine the MIME type of the file from its extension
188 mime_type = MIME_TYPE_MAP.get(extension, MIME_TYPE_MAP['']) 223 mime_type = MIME_TYPE_MAP.get(extension, MIME_TYPE_MAP[''])
189 224
190 # Open the file and send it over HTTP 225 # Open the file and send it over HTTP
191 if os.path.isfile(path): 226 if os.path.isfile(path):
192 with open(path, 'rb') as sending_file: 227 with open(path, 'rb') as sending_file:
193 self.send_response(200) 228 self.send_response(200)
194 self.send_header('Content-type', mime_type) 229 self.send_header('Content-type', mime_type)
195 self.end_headers() 230 self.end_headers()
196 self.wfile.write(sending_file.read()) 231 self.wfile.write(sending_file.read())
197 else: 232 else:
198 self.send_error(404) 233 self.send_error(404)
199 234
200 def send_json_dict(self, json_dict): 235 def send_json_dict(self, json_dict):
201 """ Send the contents of this dictionary in JSON format, with a JSON 236 """ Send the contents of this dictionary in JSON format, with a JSON
202 mimetype. """ 237 mimetype.
238
239 Args:
240 json_dict: dictionary to send
241 """
203 self.send_response(200) 242 self.send_response(200)
204 self.send_header('Content-type', 'application/json') 243 self.send_header('Content-type', 'application/json')
205 self.end_headers() 244 self.end_headers()
206 json.dump(json_dict, self.wfile) 245 json.dump(json_dict, self.wfile)
207 246
208 247
209 def main(): 248 def main():
210 parser = argparse.ArgumentParser() 249 parser = argparse.ArgumentParser()
211 parser.add_argument('--actuals-dir', 250 parser.add_argument('--actuals-dir',
212 help=('Directory into which we will check out the latest ' 251 help=('Directory into which we will check out the latest '
(...skipping 15 matching lines...) Expand all
228 'defaults to %(default)s'), 267 'defaults to %(default)s'),
229 default=DEFAULT_PORT) 268 default=DEFAULT_PORT)
230 args = parser.parse_args() 269 args = parser.parse_args()
231 global _SERVER 270 global _SERVER
232 _SERVER = Server(expectations_dir=args.expectations_dir, 271 _SERVER = Server(expectations_dir=args.expectations_dir,
233 port=args.port, export=args.export) 272 port=args.port, export=args.export)
234 _SERVER.run() 273 _SERVER.run()
235 274
236 if __name__ == '__main__': 275 if __name__ == '__main__':
237 main() 276 main()
OLDNEW
« no previous file with comments | « gm/rebaseline_server/results.py ('k') | gm/rebaseline_server/static/loader.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698