page.py revision 58537e28ecd584eab876aee8be7156509866d23a
1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4import os
5import re
6import urlparse
7
8
9def _UrlPathJoin(*args):
10  """Joins each path in |args| for insertion into a URL path.
11
12  This is distinct from os.path.join in that:
13  1. Forward slashes are always used.
14  2. Paths beginning with '/' are not treated as absolute.
15
16  For example:
17    _UrlPathJoin('a', 'b') => 'a/b'
18    _UrlPathJoin('a/', 'b') => 'a/b'
19    _UrlPathJoin('a', '/b') => 'a/b'
20    _UrlPathJoin('a/', '/b') => 'a/b'
21  """
22  if not args:
23    return ''
24  if len(args) == 1:
25    return str(args[0])
26  else:
27    args = [str(arg).replace('\\', '/') for arg in args]
28    work = [args[0]]
29    for arg in args[1:]:
30      if not arg:
31        continue
32      if arg.startswith('/'):
33        work.append(arg[1:])
34      else:
35        work.append(arg)
36    joined = reduce(os.path.join, work)
37  return joined.replace('\\', '/')
38
39class Page(object):
40  def __init__(self, url, page_set, attributes=None, base_dir=None):
41    parsed_url = urlparse.urlparse(url)
42    if not parsed_url.scheme:
43      abspath = os.path.abspath(os.path.join(base_dir, parsed_url.path))
44      if os.path.exists(abspath):
45        url = 'file://%s' % os.path.abspath(os.path.join(base_dir, url))
46      else:
47        raise Exception('URLs must be fully qualified: %s' % url)
48    self.url = url
49    self.page_set = page_set
50    self.base_dir = base_dir
51
52    # These attributes can be set dynamically by the page.
53    self.credentials = None
54    self.disabled = False
55    self.name = None
56    self.script_to_evaluate_on_commit = None
57
58    if attributes:
59      for k, v in attributes.iteritems():
60        setattr(self, k, v)
61
62  def __getattr__(self, name):
63    if self.page_set and hasattr(self.page_set, name):
64      return getattr(self.page_set, name)
65
66    raise AttributeError()
67
68  @property
69  def is_file(self):
70    parsed_url = urlparse.urlparse(self.url)
71    return parsed_url.scheme == 'file'
72
73  @property
74  def is_local(self):
75    parsed_url = urlparse.urlparse(self.url)
76    return parsed_url.scheme == 'file' or parsed_url.scheme == 'chrome'
77
78  @property
79  def serving_dirs_and_file(self):
80    parsed_url = urlparse.urlparse(self.url)
81    path = _UrlPathJoin(self.base_dir, parsed_url.netloc, parsed_url.path)
82
83    if hasattr(self.page_set, 'serving_dirs'):
84      url_base_dir = os.path.commonprefix(self.page_set.serving_dirs)
85      base_path = _UrlPathJoin(self.base_dir, url_base_dir)
86      return ([_UrlPathJoin(self.base_dir, d)
87               for d in self.page_set.serving_dirs],
88              path.replace(base_path, ''))
89
90    return os.path.split(path)
91
92  @property
93  def file_safe_name(self):
94    """A version of display_name that's safe to use as a filename."""
95    # Just replace all special characters in the url with underscore.
96    return re.sub('[^a-zA-Z0-9]', '_', self.display_name)
97
98  @property
99  def display_name(self):
100    if self.name:
101      return self.name
102    if not self.is_local:
103      return self.url
104    url_paths = ['/'.join(p.url.strip('/').split('/')[:-1])
105                 for p in self.page_set if p.is_file]
106    common_prefix = os.path.commonprefix(url_paths)
107    return self.url[len(common_prefix):].strip('/')
108
109  @property
110  def archive_path(self):
111    return self.page_set.WprFilePathForPage(self)
112
113  def __str__(self):
114    return self.url
115