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

Unified Diff: cms/sources.py

Issue 29555839: Issue 5336 - Allow additional include, page, and template paths using CMS (Closed)
Patch Set: Created Sept. 25, 2017, 7:12 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: cms/sources.py
===================================================================
--- a/cms/sources.py
+++ b/cms/sources.py
@@ -298,8 +298,118 @@
result.append(relpath + filename)
elif os.path.isdir(path):
do_list(path, relpath + filename + '/')
do_list(self.get_path(subdir), '')
return result
def get_cache_dir(self):
return os.path.join(self._dir, 'cache')
+
+
+class MultiSource(Source):
+ """A source that combines the contents of multiple other sources."""
+
+ def __init__(self, base_sources):
+ self._bases = base_sources
+
+ @property
+ def version(self):
+ return self._bases[0].version
+
+ def get_cache_dir(self):
+ return self._bases[0].get_cache_dir()
+
+ def __enter__(self):
+ for base in self._bases:
+ base.__enter__()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ return any(base.__exit__(exc_type, exc_value, tb)
+ for base in self._bases)
+
+ def close(self):
+ for base in self._bases:
+ base.close()
+
+ def has_file(self, filename):
+ for base in self._bases:
Sebastian Noack 2017/09/27 02:47:53 Nit: You could use any() here as well: return a
Vasily Kuznetsov 2017/09/27 11:34:39 Done.
+ if base.has_file(filename):
+ return True
+ return False
+
+ def read_file(self, filename, binary=False):
+ for base in self._bases:
+ if base.has_file(filename):
+ return base.read_file(filename, binary)
+ raise KeyError('File not found {}'.format(filename))
+
+ def list_files(self, subdir):
+ files = set()
+ for base in self._bases:
+ files.update(base.list_files(subdir))
+ return sorted(files)
Sebastian Noack 2017/09/27 02:47:53 Nit: This could be written as a set literal one-li
Vasily Kuznetsov 2017/09/27 11:34:39 Done. Thanks for this suggestion. Having thought
Sebastian Noack 2017/09/27 15:55:22 The type returned here doesn't seem to be importan
Vasily Kuznetsov 2017/09/28 09:08:17 Fair enough. Done.
+
+
+def _memoize(func):
+ """Cache results of functions calls."""
+ memoized = {}
+
+ def wrapper(*args):
+ try:
+ return memoized[args]
+ except KeyError:
+ return memoized.setdefault(args, func(*args))
+ wrapper.cache_clear = memoized.clear
+ return wrapper
+
+
+def create_source(path, static=False, revision='default'):
Sebastian Noack 2017/09/27 02:47:53 I would rather implement this in Source.__new__, s
Vasily Kuznetsov 2017/09/27 11:34:38 Here I prefer my implementation for the following
+ """Create a source from path and optional revision.
+
+ `static` flag determines the type of the source that will be created and
+ its caching behavior:
+ - If `static` is `True`, we use version-control-aware `MercurialSource`,
+ pass the revision and cache the results of most used functions.
+ - If `static` is `False` (as is the case with the preview server), we use
+ simpler `FileSource` and don't cache anything to be able to respond to
+ changes on the filesystem.
+
+ If `settings.ini` in the source contains `[paths]` section with an
+ `additional-paths` key that contains the list of additional root folders,
+ `MultiSource` will be instantiated and its bases will be the original
+ source plus an additional source for each additional root folder.
+ `MultiSource` looks up files in its base sources in the order they are
+ provided, so the files in the additional folders will only be used if the
+ original source doesn't contain that file.
+ """
+ if static:
+ source = MercurialSource(path, revision)
+ else:
+ source = FileSource(path)
+
+ config = source.read_config()
+ try:
+ ap = config.get('paths', 'additional-paths').strip()
+ additional_paths = filter(None, ap.split())
+ except ConfigParser.NoSectionError:
+ additional_paths = []
+
+ if additional_paths:
+ additional_sources = [
+ create_source(os.path.join(path, p))
+ for p in additional_paths
+ ]
+ source = MultiSource([source] + additional_sources)
+
+ if static:
+ for fname in [
+ 'resolve_link',
+ 'read_config',
+ 'read_template',
+ 'read_locale',
+ 'read_include',
+ 'exec_file',
+ ]:
+ setattr(source, fname, _memoize(getattr(source, fname)))
+
+ return source

Powered by Google App Engine
This is Rietveld