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: Address comments on PS2, improve docstring of create_source Created Sept. 28, 2017, 9 a.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
« no previous file with comments | « cms/bin/test_server.py ('k') | tests/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cms/sources.py
===================================================================
--- a/cms/sources.py
+++ b/cms/sources.py
@@ -298,8 +298,115 @@
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):
+ return any(base.has_file(filename) for base in self._bases)
+
+ 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):
+ return {f for base in self._bases for f in base.list_files(subdir)}
+
+
+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'):
Wladimir Palant 2017/09/29 08:50:44 The default revision should be 'master', not 'defa
Vasily Kuznetsov 2017/09/29 10:49:09 Changed to `None`, following your next comment. Do
+ """Create a source from path and optional revision.
+
+ `static` flag indicates that the website source under `path` can be assumed
+ to not change after the source was created and any changes can be safely
+ ignored.
+ - If `static` is `True`, no interim changes are expected and caching will
+ be used to optimize performance (source directory is also assumed to be a
+ Mercurial repository, and `revision` parameter can be used to select a
+ source revision from which the files will be read).
+ - If `static` is `False`, changes on the filesystem are expected and will
+ be reflected in the outputs of the calls. This mode can be used for live
+ preview.
+
+ 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:
Wladimir Palant 2017/09/29 08:50:44 Should this flag really determine which type of so
Vasily Kuznetsov 2017/09/29 10:49:10 Yeah, you're right. `static` parameter should cont
+ 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:
Wladimir Palant 2017/09/29 08:50:44 What about NoOptionError?
Vasily Kuznetsov 2017/09/29 10:49:09 Thank you. Done.
+ additional_paths = []
+
+ if additional_paths:
+ additional_sources = [
+ create_source(os.path.join(path, p))
Sebastian Noack 2017/09/28 20:48:21 This seems to be equivalent to FileSource(os.path.
Wladimir Palant 2017/09/29 08:50:44 Not quite equivalent. The directory we link to mig
Vasily Kuznetsov 2017/09/29 10:49:09 Yes, nested dependencies are the reason why I made
Sebastian Noack 2017/09/29 15:55:07 I agree with Wladimir. His suggestion seems to be
+ 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
« no previous file with comments | « cms/bin/test_server.py ('k') | tests/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld