Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: cms/sources.py

Issue 29805580: Issue 6728 - Remove Mercurial dependency (Closed)
Patch Set: Address PS2 comments Created June 15, 2018, 5:19 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/conftest.py » ('j') | tests/test_page_outputs.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # This file is part of the Adblock Plus web scripts, 1 # This file is part of the Adblock Plus web scripts,
2 # Copyright (C) 2006-present eyeo GmbH 2 # Copyright (C) 2006-present eyeo GmbH
3 # 3 #
4 # Adblock Plus is free software: you can redistribute it and/or modify 4 # Adblock Plus is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License version 3 as 5 # it under the terms of the GNU General Public License version 3 as
6 # published by the Free Software Foundation. 6 # published by the Free Software Foundation.
7 # 7 #
8 # Adblock Plus is distributed in the hope that it will be useful, 8 # Adblock Plus is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details. 11 # GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License 13 # You should have received a copy of the GNU General Public License
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
15 15
16 import io 16 import io
17 import collections 17 import collections
18 import ConfigParser 18 import ConfigParser
19 import json 19 import json
20 import os 20 import os
21 from StringIO import StringIO 21 from StringIO import StringIO
22 import subprocess 22 from random import randint
23 import urlparse 23 import urlparse
24 import zipfile
25 import logging 24 import logging
26 25
27 26
28 class Source: 27 class Source:
29 def resolve_link(self, url, locale): 28 def resolve_link(self, url, locale):
30 parsed = urlparse.urlparse(url) 29 parsed = urlparse.urlparse(url)
31 page = parsed.path 30 page = parsed.path
32 if parsed.scheme != '' or page.startswith('/') or page.startswith('.'): 31 if parsed.scheme != '' or page.startswith('/') or page.startswith('.'):
33 # Not a page link 32 # Not a page link
34 return None, None 33 return None, None
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 def include_filename(include, format): 213 def include_filename(include, format):
215 return 'includes/%s.%s' % (include, format) 214 return 'includes/%s.%s' % (include, format)
216 215
217 def has_include(self, include, format): 216 def has_include(self, include, format):
218 return self.has_file(self.include_filename(include, format)) 217 return self.has_file(self.include_filename(include, format))
219 218
220 def read_include(self, include, format): 219 def read_include(self, include, format):
221 return self.read_file(self.include_filename(include, format)) 220 return self.read_file(self.include_filename(include, format))
222 221
223 222
224 class MercurialSource(Source):
225 def __init__(self, repo, revision):
226 command = ['hg', '-R', repo, 'archive', '-r', revision,
227 '-t', 'uzip', '-p', 'root', '-']
228 data = subprocess.check_output(command)
229 self._archive = zipfile.ZipFile(StringIO(data), mode='r')
230
231 command = ['hg', '-R', repo, 'id', '-n', '-r', revision]
232 self.version = subprocess.check_output(command).strip()
233
234 self._name = os.path.basename(repo.rstrip(os.path.sep))
235
236 def __enter__(self):
237 return self
238
239 def __exit__(self, type, value, traceback):
240 self.close()
241 return False
242
243 def close(self):
244 self._archive.close()
245
246 def has_file(self, filename):
247 try:
248 self._archive.getinfo('root/' + filename)
249 except KeyError:
250 return False
251 return True
252
253 def read_file(self, filename, binary=False):
254 data = self._archive.read('root/' + filename)
255 if not binary:
256 data = data.decode('utf-8')
257 return (data, '%s!%s' % (self._name, filename))
258
259 def list_files(self, subdir):
260 prefix = 'root/{}/'.format(subdir)
261 for filename in self._archive.namelist():
262 if filename.startswith(prefix):
263 yield filename[len(prefix):]
264
265 if os.name == 'posix':
266 def get_cache_dir(self):
267 return '/var/cache/' + self._name
268
269
270 class FileSource(Source): 223 class FileSource(Source):
271 def __init__(self, dir): 224 def __init__(self, dir):
272 self._dir = dir 225 self._dir = dir
273 226
274 def __enter__(self): 227 def __enter__(self):
275 return self 228 return self
276 229
277 def __exit__(self, type, value, traceback): 230 def __exit__(self, type, value, traceback):
278 return False 231 return False
279 232
280 def close(self): 233 def close(self):
281 pass 234 pass
282 235
236 @property
237 def version(self):
238 return randint(1, 2 ** 32)
239
283 def get_path(self, filename): 240 def get_path(self, filename):
284 return os.path.join(self._dir, *filename.split('/')) 241 return os.path.join(self._dir, *filename.split('/'))
285 242
286 def has_file(self, filename): 243 def has_file(self, filename):
287 return os.path.isfile(self.get_path(filename)) 244 return os.path.isfile(self.get_path(filename))
288 245
289 def read_file(self, filename, binary=False): 246 def read_file(self, filename, binary=False):
290 path = self.get_path(filename) 247 path = self.get_path(filename)
291 248
292 if binary: 249 if binary:
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 361
405 362
406 def create_source(path, cached=False, revision=None): 363 def create_source(path, cached=False, revision=None):
407 """Create a source from path and optional revision. 364 """Create a source from path and optional revision.
408 365
409 `cached` flag activates caching. This can be used to optimize performance 366 `cached` flag activates caching. This can be used to optimize performance
410 if no changes are expected on the filesystem after the source was created. 367 if no changes are expected on the filesystem after the source was created.
411 This is usually the case with static generation (as opposed to dynamic 368 This is usually the case with static generation (as opposed to dynamic
412 preview). 369 preview).
413 370
414 If `revision` option is provided, the `path` is assumed to be pointing to a
415 Mercurial repository. In this case the source will return the content of
416 selected revision (using `MercurialSource`) instead of the content of the
417 directory. Note that any local changes will be ignored in this case.
418
419 If `settings.ini` in the source contains `[paths]` section with an 371 If `settings.ini` in the source contains `[paths]` section with an
420 `additional-paths` key that contains the list of additional root folders, 372 `additional-paths` key that contains the list of additional root folders,
421 `MultiSource` will be instantiated and its bases will be the original 373 `MultiSource` will be instantiated and its bases will be the original
422 source plus an additional source for each additional root folder. 374 source plus an additional source for each additional root folder.
423 `MultiSource` looks up files in its base sources in the order they are 375 `MultiSource` looks up files in its base sources in the order they are
424 provided, so the files in the additional folders will only be used if the 376 provided, so the files in the additional folders will only be used if the
425 original source doesn't contain that file. 377 original source doesn't contain that file.
426 """ 378 """
427 if revision is not None: 379 source = FileSource(path)
428 source = MercurialSource(path, revision)
429 else:
430 source = FileSource(path)
431 380
432 config = source.read_config() 381 config = source.read_config()
433 try: 382 try:
434 ap = config.get('paths', 'additional-paths').strip() 383 ap = config.get('paths', 'additional-paths').strip()
435 additional_paths = filter(None, ap.split()) 384 additional_paths = filter(None, ap.split())
436 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 385 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
437 additional_paths = [] 386 additional_paths = []
438 387
439 if additional_paths: 388 if additional_paths:
440 additional_sources = [ 389 additional_sources = [
441 create_source(os.path.join(path, p)) 390 create_source(os.path.join(path, p))
442 for p in additional_paths 391 for p in additional_paths
443 ] 392 ]
444 source = MultiSource([source] + additional_sources) 393 source = MultiSource([source] + additional_sources)
445 394
446 if cached: 395 if cached:
447 for fname in [ 396 for fname in [
448 'resolve_link', 397 'resolve_link',
449 'read_config', 398 'read_config',
450 'read_template', 399 'read_template',
451 'read_locale', 400 'read_locale',
452 'read_include', 401 'read_include',
453 'exec_file', 402 'exec_file',
454 ]: 403 ]:
455 setattr(source, fname, _memoize(getattr(source, fname))) 404 setattr(source, fname, _memoize(getattr(source, fname)))
456 405
457 return source 406 return source
OLDNEW
« no previous file with comments | « no previous file | tests/conftest.py » ('j') | tests/test_page_outputs.py » ('J')

Powered by Google App Engine
This is Rietveld