| 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 |