OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import logging | 5 import logging |
6 import re | 6 import re |
7 import posixpath | 7 import posixpath |
8 import xml.dom.minidom as xml | 8 import xml.dom.minidom as xml |
9 from xml.parsers.expat import ExpatError | 9 from xml.parsers.expat import ExpatError |
10 | 10 |
11 from appengine_url_fetcher import AppEngineUrlFetcher | 11 from appengine_url_fetcher import AppEngineUrlFetcher |
12 from docs_server_utils import StringIdentity | 12 from docs_server_utils import StringIdentity |
13 from file_system import FileSystem, FileNotFoundError, StatInfo, ToUnicode | 13 from file_system import FileSystem, FileNotFoundError, StatInfo, ToUnicode |
14 from future import Future | 14 from future import Future |
15 import svn_constants | 15 import svn_constants |
16 import url_constants | 16 import url_constants |
17 | 17 |
18 class _AsyncFetchFuture(object): | 18 class _AsyncFetchFuture(object): |
19 def __init__(self, paths, fetcher, binary): | 19 def __init__(self, paths, fetcher, binary, args=None): |
| 20 def apply_args(path): |
| 21 return path if args is None else '%s?%s' % (path, args) |
20 # A list of tuples of the form (path, Future). | 22 # A list of tuples of the form (path, Future). |
21 self._fetches = [(path, fetcher.FetchAsync(path)) | 23 self._fetches = [(path, fetcher.FetchAsync(apply_args(path))) |
22 for path in paths] | 24 for path in paths] |
23 self._value = {} | 25 self._value = {} |
24 self._error = None | 26 self._error = None |
25 self._binary = binary | 27 self._binary = binary |
26 | 28 |
27 def _ListDir(self, directory): | 29 def _ListDir(self, directory): |
28 dom = xml.parseString(directory) | 30 dom = xml.parseString(directory) |
29 files = [elem.childNodes[0].data for elem in dom.getElementsByTagName('a')] | 31 files = [elem.childNodes[0].data for elem in dom.getElementsByTagName('a')] |
30 if '..' in files: | 32 if '..' in files: |
31 files.remove('..') | 33 files.remove('..') |
(...skipping 15 matching lines...) Expand all Loading... |
47 else: | 49 else: |
48 self._value[path] = result.content | 50 self._value[path] = result.content |
49 if self._error is not None: | 51 if self._error is not None: |
50 raise self._error | 52 raise self._error |
51 return self._value | 53 return self._value |
52 | 54 |
53 class SubversionFileSystem(FileSystem): | 55 class SubversionFileSystem(FileSystem): |
54 '''Class to fetch resources from src.chromium.org. | 56 '''Class to fetch resources from src.chromium.org. |
55 ''' | 57 ''' |
56 @staticmethod | 58 @staticmethod |
57 def Create(branch): | 59 def Create(branch, revision=None): |
58 if branch == 'trunk': | 60 if branch == 'trunk': |
59 svn_path = 'trunk/src/%s' % svn_constants.EXTENSIONS_PATH | 61 svn_path = 'trunk/src/%s' % svn_constants.EXTENSIONS_PATH |
60 else: | 62 else: |
61 svn_path = 'branches/%s/src/%s' % (branch, | 63 svn_path = 'branches/%s/src/%s' % (branch, |
62 svn_constants.EXTENSIONS_PATH) | 64 svn_constants.EXTENSIONS_PATH) |
63 return SubversionFileSystem( | 65 return SubversionFileSystem( |
64 AppEngineUrlFetcher('%s/%s' % (url_constants.SVN_URL, svn_path)), | 66 AppEngineUrlFetcher('%s/%s' % (url_constants.SVN_URL, svn_path)), |
65 AppEngineUrlFetcher('%s/%s' % (url_constants.VIEWVC_URL, svn_path)), | 67 AppEngineUrlFetcher('%s/%s' % (url_constants.VIEWVC_URL, svn_path)), |
66 svn_path) | 68 svn_path, |
| 69 revision=revision) |
67 | 70 |
68 def __init__(self, file_fetcher, stat_fetcher, svn_path): | 71 def __init__(self, file_fetcher, stat_fetcher, svn_path, revision=None): |
69 self._file_fetcher = file_fetcher | 72 self._file_fetcher = file_fetcher |
70 self._stat_fetcher = stat_fetcher | 73 self._stat_fetcher = stat_fetcher |
71 self._svn_path = svn_path | 74 self._svn_path = svn_path |
| 75 self._revision = revision |
72 | 76 |
73 def Read(self, paths, binary=False): | 77 def Read(self, paths, binary=False): |
| 78 args = None |
| 79 if self._revision is not None: |
| 80 # |fetcher| gets from svn.chromium.org which uses p= for version. |
| 81 args = 'p=%s' % self._revision |
74 return Future(delegate=_AsyncFetchFuture(paths, | 82 return Future(delegate=_AsyncFetchFuture(paths, |
75 self._file_fetcher, | 83 self._file_fetcher, |
76 binary)) | 84 binary, |
| 85 args=args)) |
77 | 86 |
78 def _ParseHTML(self, html): | 87 def _ParseHTML(self, html): |
79 '''Unfortunately, the viewvc page has a stray </div> tag, so this takes care | 88 '''Unfortunately, the viewvc page has a stray </div> tag, so this takes care |
80 of all mismatched tags. | 89 of all mismatched tags. |
81 ''' | 90 ''' |
82 try: | 91 try: |
83 return xml.parseString(html) | 92 return xml.parseString(html) |
84 except ExpatError as e: | 93 except ExpatError as e: |
85 return self._ParseHTML('\n'.join( | 94 return self._ParseHTML('\n'.join( |
86 line for (i, line) in enumerate(html.split('\n')) | 95 line for (i, line) in enumerate(html.split('\n')) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 dict((path, str(version)) | 144 dict((path, str(version)) |
136 for path, version in child_versions.iteritems())) | 145 for path, version in child_versions.iteritems())) |
137 | 146 |
138 # Bleh, but, this data is so unreliable. There are actually some empty file | 147 # Bleh, but, this data is so unreliable. There are actually some empty file |
139 # listings caused by git/svn/something not cleaning up empty dirs. | 148 # listings caused by git/svn/something not cleaning up empty dirs. |
140 return StatInfo('0', {}) | 149 return StatInfo('0', {}) |
141 | 150 |
142 def Stat(self, path): | 151 def Stat(self, path): |
143 directory, filename = posixpath.split(path) | 152 directory, filename = posixpath.split(path) |
144 directory += '/' | 153 directory += '/' |
| 154 if self._revision is not None: |
| 155 # |stat_fetch| uses viewvc which uses pathrev= for version. |
| 156 directory += '?pathrev=%s' % self._revision |
145 result = self._stat_fetcher.Fetch(directory) | 157 result = self._stat_fetcher.Fetch(directory) |
146 if result.status_code == 404: | 158 if result.status_code == 404: |
147 raise FileNotFoundError( | 159 raise FileNotFoundError( |
148 'Got 404 when fetching %s from %s for Stat' % (path, directory)) | 160 'Got 404 when fetching %s from %s for Stat' % (path, directory)) |
149 stat_info = self._CreateStatInfo(result.content) | 161 stat_info = self._CreateStatInfo(result.content) |
150 if path.endswith('/'): | 162 if path.endswith('/'): |
151 return stat_info | 163 return stat_info |
152 if filename not in stat_info.child_versions: | 164 if filename not in stat_info.child_versions: |
153 raise FileNotFoundError('%s was not in child versions' % filename) | 165 raise FileNotFoundError('%s was not in child versions' % filename) |
154 return StatInfo(stat_info.child_versions[filename]) | 166 return StatInfo(stat_info.child_versions[filename]) |
155 | 167 |
156 def GetIdentity(self): | 168 def GetIdentity(self): |
| 169 # NOTE: no revision here, consider it just an implementation detail of the |
| 170 # file version that is handled by Stat. |
157 return '@'.join((self.__class__.__name__, StringIdentity(self._svn_path))) | 171 return '@'.join((self.__class__.__name__, StringIdentity(self._svn_path))) |
OLD | NEW |