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