OLD | NEW |
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 | 8 |
9 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
10 """ | 10 """ |
11 | 11 |
12 # System-level imports | 12 # System-level imports |
13 import argparse | 13 import argparse |
14 import BaseHTTPServer | 14 import BaseHTTPServer |
15 import json | 15 import json |
16 import logging | 16 import logging |
17 import os | 17 import os |
18 import posixpath | 18 import posixpath |
19 import re | 19 import re |
20 import shutil | 20 import shutil |
21 import socket | 21 import socket |
22 import subprocess | 22 import subprocess |
23 import thread | 23 import thread |
24 import threading | 24 import threading |
25 import time | 25 import time |
26 import urlparse | 26 import urlparse |
27 | 27 |
| 28 # Must fix up PYTHONPATH before importing from within Skia |
| 29 # pylint: disable=W0611 |
| 30 import fix_pythonpath |
| 31 # pylint: enable=W0611 |
| 32 |
28 # Imports from within Skia | 33 # Imports from within Skia |
29 import fix_pythonpath # must do this first | |
30 from pyutils import gs_utils | 34 from pyutils import gs_utils |
31 import gm_json | 35 import gm_json |
32 | 36 |
33 # Imports from local dir | 37 # Imports from local dir |
34 # | 38 # |
| 39 # pylint: disable=C0301 |
35 # Note: we import results under a different name, to avoid confusion with the | 40 # Note: we import results under a different name, to avoid confusion with the |
36 # Server.results() property. See discussion at | 41 # Server.results() property. See discussion at |
37 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 | 42 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 |
| 43 # pylint: enable=C0301 |
38 import compare_configs | 44 import compare_configs |
39 import compare_to_expectations | 45 import compare_to_expectations |
40 import download_actuals | 46 import download_actuals |
41 import imagepairset | 47 import imagepairset |
42 import results as results_mod | 48 import results as results_mod |
43 | 49 |
44 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') | 50 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') |
45 | 51 |
46 # A simple dictionary of file name extensions to MIME types. The empty string | 52 # A simple dictionary of file name extensions to MIME types. The empty string |
47 # entry is used as the default when no extension was given or if the extension | 53 # entry is used as the default when no extension was given or if the extension |
48 # has no entry in this dictionary. | 54 # has no entry in this dictionary. |
49 MIME_TYPE_MAP = {'': 'application/octet-stream', | 55 MIME_TYPE_MAP = {'': 'application/octet-stream', |
50 'html': 'text/html', | 56 'html': 'text/html', |
51 'css': 'text/css', | 57 'css': 'text/css', |
52 'png': 'image/png', | 58 'png': 'image/png', |
53 'js': 'application/javascript', | 59 'js': 'application/javascript', |
54 'json': 'application/json' | 60 'json': 'application/json' |
55 } | 61 } |
56 | 62 |
57 # Keys that server.py uses to create the toplevel content header. | 63 # Keys that server.py uses to create the toplevel content header. |
58 # NOTE: Keep these in sync with static/constants.js | 64 # NOTE: Keep these in sync with static/constants.js |
59 KEY__EDITS__MODIFICATIONS = 'modifications' | 65 KEY__EDITS__MODIFICATIONS = 'modifications' |
60 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' | 66 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' |
61 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' | 67 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
| 68 URL_KEY__SCHEMA_VERSION = 'urlSchemaVersion' |
| 69 URL_VALUE__SCHEMA_VERSION__CURRENT = 0 |
| 70 # always interpret as then-current schema version; |
| 71 # used for toplevel links on index page |
| 72 URL_VALUE__SCHEMA_VERSION__ALWAYS_CURRENT = 'current' |
62 | 73 |
63 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR | 74 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
64 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET | 75 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
65 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME | 76 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
66 DEFAULT_PORT = 8888 | 77 DEFAULT_PORT = 8888 |
67 | 78 |
68 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 79 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
69 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) | 80 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) |
70 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 81 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
71 # out live results (not static files). | 82 # out live results (not static files). |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 os.makedirs(dir_path) | 161 os.makedirs(dir_path) |
151 with open(file_path, 'w') as file_handle: | 162 with open(file_path, 'w') as file_handle: |
152 file_handle.write( | 163 file_handle.write( |
153 '<!DOCTYPE html><html>' | 164 '<!DOCTYPE html><html>' |
154 '<head><title>rebaseline_server</title></head>' | 165 '<head><title>rebaseline_server</title></head>' |
155 '<body><ul>') | 166 '<body><ul>') |
156 if SUMMARY_TYPES: | 167 if SUMMARY_TYPES: |
157 file_handle.write('<li>Expectations vs Actuals</li><ul>') | 168 file_handle.write('<li>Expectations vs Actuals</li><ul>') |
158 for summary_type in SUMMARY_TYPES: | 169 for summary_type in SUMMARY_TYPES: |
159 file_handle.write( | 170 file_handle.write( |
160 '<li>' | 171 '<li><a href="/{static_subdir}/view.html#/view.html?' |
161 '<a href="/%s/view.html#/view.html?resultsToLoad=/%s/%s">' | 172 '{version_key}={version_value}&' |
162 '%s</a></li>' % ( | 173 'resultsToLoad=/{results_subdir}/{summary_type}">' |
163 STATIC_CONTENTS_SUBDIR, RESULTS_SUBDIR, | 174 '{summary_type}</a></li>'.format( |
164 summary_type, summary_type)) | 175 results_subdir=RESULTS_SUBDIR, |
| 176 static_subdir=STATIC_CONTENTS_SUBDIR, |
| 177 summary_type=summary_type, |
| 178 version_key=URL_KEY__SCHEMA_VERSION, |
| 179 version_value=URL_VALUE__SCHEMA_VERSION__ALWAYS_CURRENT)) |
165 file_handle.write('</ul>') | 180 file_handle.write('</ul>') |
166 if config_pairs: | 181 if config_pairs: |
167 file_handle.write('<li>Comparing configs within actual results</li><ul>') | 182 file_handle.write('<li>Comparing configs within actual results</li><ul>') |
168 for config_pair in config_pairs: | 183 for config_pair in config_pairs: |
169 file_handle.write('<li>%s vs %s:' % config_pair) | 184 file_handle.write('<li>%s vs %s:' % config_pair) |
170 for summary_type in SUMMARY_TYPES: | 185 for summary_type in SUMMARY_TYPES: |
171 file_handle.write( | 186 file_handle.write( |
172 ' <a href="/%s/view.html#/view.html?' | 187 ' <a href="/%s/view.html#/view.html?' |
173 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( | 188 'resultsToLoad=/%s/%s/%s-vs-%s_%s.json">%s</a>' % ( |
174 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, | 189 STATIC_CONTENTS_SUBDIR, STATIC_CONTENTS_SUBDIR, |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 generated_images_root=os.path.join( | 354 generated_images_root=os.path.join( |
340 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | 355 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, |
341 GENERATED_IMAGES_SUBDIR), | 356 GENERATED_IMAGES_SUBDIR), |
342 diff_base_url=posixpath.join( | 357 diff_base_url=posixpath.join( |
343 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), | 358 os.pardir, STATIC_CONTENTS_SUBDIR, GENERATED_IMAGES_SUBDIR), |
344 builder_regex_list=self._builder_regex_list) | 359 builder_regex_list=self._builder_regex_list) |
345 | 360 |
346 json_dir = os.path.join( | 361 json_dir = os.path.join( |
347 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) | 362 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, GENERATED_JSON_SUBDIR) |
348 if not os.path.isdir(json_dir): | 363 if not os.path.isdir(json_dir): |
349 os.makedirs(json_dir) | 364 os.makedirs(json_dir) |
350 | 365 |
351 for config_pair in self._config_pairs: | 366 for config_pair in self._config_pairs: |
352 config_comparisons = compare_configs.ConfigComparisons( | 367 config_comparisons = compare_configs.ConfigComparisons( |
353 configs=config_pair, | 368 configs=config_pair, |
354 actuals_root=self._actuals_dir, | 369 actuals_root=self._actuals_dir, |
355 generated_images_root=os.path.join( | 370 generated_images_root=os.path.join( |
356 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, | 371 PARENT_DIRECTORY, STATIC_CONTENTS_SUBDIR, |
357 GENERATED_IMAGES_SUBDIR), | 372 GENERATED_IMAGES_SUBDIR), |
358 diff_base_url=posixpath.join( | 373 diff_base_url=posixpath.join( |
359 os.pardir, GENERATED_IMAGES_SUBDIR), | 374 os.pardir, GENERATED_IMAGES_SUBDIR), |
(...skipping 28 matching lines...) Expand all Loading... |
388 if self._export: | 403 if self._export: |
389 server_address = ('', self._port) | 404 server_address = ('', self._port) |
390 host = _get_routable_ip_address() | 405 host = _get_routable_ip_address() |
391 if self._editable: | 406 if self._editable: |
392 logging.warning('Running with combination of "export" and "editable" ' | 407 logging.warning('Running with combination of "export" and "editable" ' |
393 'flags. Users on other machines will ' | 408 'flags. Users on other machines will ' |
394 'be able to modify your GM expectations!') | 409 'be able to modify your GM expectations!') |
395 else: | 410 else: |
396 host = '127.0.0.1' | 411 host = '127.0.0.1' |
397 server_address = (host, self._port) | 412 server_address = (host, self._port) |
| 413 # pylint: disable=W0201 |
398 http_server = BaseHTTPServer.HTTPServer(server_address, HTTPRequestHandler) | 414 http_server = BaseHTTPServer.HTTPServer(server_address, HTTPRequestHandler) |
399 self._url = 'http://%s:%d' % (host, http_server.server_port) | 415 self._url = 'http://%s:%d' % (host, http_server.server_port) |
400 logging.info('Listening for requests on %s' % self._url) | 416 logging.info('Listening for requests on %s' % self._url) |
401 http_server.serve_forever() | 417 http_server.serve_forever() |
402 | 418 |
403 | 419 |
404 class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | 420 class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
405 """ HTTP request handlers for various types of queries this server knows | 421 """ HTTP request handlers for various types of queries this server knows |
406 how to handle (static HTML and Javascript, expected/actual results, etc.) | 422 how to handle (static HTML and Javascript, expected/actual results, etc.) |
407 """ | 423 """ |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
685 json_filename=args.json_filename, | 701 json_filename=args.json_filename, |
686 gm_summaries_bucket=args.gm_summaries_bucket, | 702 gm_summaries_bucket=args.gm_summaries_bucket, |
687 port=args.port, export=args.export, editable=args.editable, | 703 port=args.port, export=args.export, editable=args.editable, |
688 reload_seconds=args.reload, config_pairs=config_pairs, | 704 reload_seconds=args.reload, config_pairs=config_pairs, |
689 builder_regex_list=args.builders) | 705 builder_regex_list=args.builders) |
690 _SERVER.run() | 706 _SERVER.run() |
691 | 707 |
692 | 708 |
693 if __name__ == '__main__': | 709 if __name__ == '__main__': |
694 main() | 710 main() |
OLD | NEW |