You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by jo...@apache.org on 2013/06/05 00:17:28 UTC

[24/26] git commit: [#6325] pep8 of app_cfg

[#6325] pep8 of app_cfg


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/2d39c619
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/2d39c619
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/2d39c619

Branch: refs/heads/cj/6325
Commit: 2d39c619a7b21edbee64e9af401edd4c92205810
Parents: bf6db7f
Author: Nicholas Bollweg (Nick) <ni...@gmail.com>
Authored: Tue Feb 7 18:00:26 2012 -0500
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Tue Jun 4 20:51:31 2013 +0000

----------------------------------------------------------------------
 Allura/allura/config/app_cfg.py          |   22 +--
 Allura/allura/lib/package_path_loader.py |  229 +++++++++++++++++++++++++
 2 files changed, 233 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2d39c619/Allura/allura/config/app_cfg.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/app_cfg.py b/Allura/allura/config/app_cfg.py
index 86c65b4..854ad0b 100644
--- a/Allura/allura/config/app_cfg.py
+++ b/Allura/allura/config/app_cfg.py
@@ -30,9 +30,6 @@ convert them into boolean, for example, you should use the
     setting = asbool(global_conf.get('the_setting'))
 
 """
-import logging
-import pkg_resources
-
 import tg
 import jinja2
 import pylons
@@ -45,8 +42,8 @@ import ew
 import allura
 # needed for tg.configuration to work
 from allura.lib import app_globals, helpers
+from allura.lib.package_path_loader import PackagePathLoader
 
-log = logging.getLogger(__name__)
 
 class ForgeConfig(AppConfig):
 
@@ -54,13 +51,12 @@ class ForgeConfig(AppConfig):
         AppConfig.__init__(self)
         self.root_controller = root_controller
         self.package = allura
-        self.renderers = [ 'json', 'genshi', 'jinja' ]
+        self.renderers = ['json', 'genshi', 'mako', 'jinja']
         self.default_renderer = 'genshi'
         self.use_sqlalchemy = False
         self.use_toscawidgets = True
         self.use_transaction_manager = False
-        # self.handle_status_codes = [ 403, 404 ]
-        self.handle_status_codes = [ 403, 404 ]
+        self.handle_status_codes = [403, 404]
         self.disable_request_extensions = True
 
     def after_init_config(self):
@@ -108,6 +104,7 @@ class ForgeConfig(AppConfig):
         config['pylons.strict_c'] = True
         self.render_functions.jinja = tg.render.render_jinja
 
+
 class JinjaEngine(ew.TemplateEngine):
 
     @property
@@ -129,15 +126,4 @@ class JinjaEngine(ew.TemplateEngine):
             text = template.render(**context)
             return literal(text)
 
-class PackagePathLoader(jinja2.BaseLoader):
-
-    def __init__(self):
-        self.fs_loader = jinja2.FileSystemLoader(['/'])
-
-    def get_source(self, environment, template):
-        package, path = template.split(':')
-        filename = pkg_resources.resource_filename(package, path)
-        return self.fs_loader.get_source(environment, filename)
-
-
 base_config = ForgeConfig()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2d39c619/Allura/allura/lib/package_path_loader.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/package_path_loader.py b/Allura/allura/lib/package_path_loader.py
new file mode 100644
index 0000000..a88a061
--- /dev/null
+++ b/Allura/allura/lib/package_path_loader.py
@@ -0,0 +1,229 @@
+'''
+A Jinja template loader which allows for:
+ - dotted-notation package loading
+ - search-path-based overriding of same
+
+## Dotted notation
+- Allow a Tool implementer to use a dotted-notation module name
+  (as occuring in the PYTONPATH), then the given path within the
+  module:
+
+        @expose('jinja:module.name:path/within/module.html>')
+
+  e.g.
+
+        @expose('jinja:allura:templates/repo/file.html')
+
+## Overriding dotted notation
+Allow a Tool implementer to override the theme baseline (or any
+other Tool's) templates. This can be lighter-weight than subclassing
+allura.plugin.ThemeProvider, plus will allow for more fine-grained
+changes.
+
+This will also override `extends` and `import` Jinja tags.
+
+This approach uses a:
+
+- setup.py entry point to a class with...
+- _magic_ files and...
+- (optionally) a class property to specify ordering
+
+### File Structure for Overriding dotted notation
+For the examples, assume the following directory structure:
+
+    NewTool/
+    |- setup.py                     <- entry point specified here
+    |- newtool/
+       |- app.py                    <- entry point target here
+       |- templates/
+          |- index.html             <- Tool's regular templates
+          |- allura/                <- magic directory named after module
+             |- templates/
+                |- repo/
+                   |- file.html     <- actual template
+
+To override the above example, a Tool implementer would
+add the following line to their Tool's setup.py:
+
+    [theme.override]
+    newtool = newtool.app:NewToolApp
+
+Then, in the neighbor path (see below) for the file containing the
+Tool class, add the following path/file:
+
+    templates/allura/templates/repo/file.html
+
+The template will be overridden. Note that after changing
+setup.py, it would be required to re-initialize with setuptools:
+
+    python setup.py develop
+
+###  Specifying search path order with template_path_rules
+If a highly specific ordering is required, such as if multiple Tools
+are trying to override the same template, the entry point target
+class can also contain a class property template_path_rules:
+
+    class NewToolApp(Application):
+        template_path_rules = [
+            ['>', 'old-tool'],
+        ]
+
+Each rule specifies a postioner and an entry point or "signpost".
+If no rule is provided, the default is ['>', 'allura'].
+
+The "signposts" are:
+
+- site-theme
+- allura (you probably shouldn't do this)
+- project-theme NOT IMPLEMENTED
+- tool-theme NOT IMPLEMENTED
+
+The positioners are:
+- >
+    - This overrider will be found BEFORE the specified entry point
+- <
+    - This overrider will be found AFTER the specified entry point... not
+      exectly sure why you would use this.
+- =
+    - This will replace one of the "signpost" entry points... if multiple
+      entry points try to do this, the result is undefined.
+      TODO: Support multiple partial themes
+'''
+import pkg_resources
+import os
+
+import jinja2
+
+
+class PackagePathLoader(jinja2.BaseLoader):
+    '''
+    Implements the following extensions to the BaseLoader for locating
+    templates: dotted-notation module-based template loading, and overriding
+    the same with other Tools.
+    '''
+    def __init__(self, override_entrypoint='allura.theme.override',
+                default_paths=None):
+        '''
+        Set up initial values... defaults are for Allura.
+        '''
+        # TODO: How does one handle project-theme?
+        if default_paths is None:
+            default_paths = [
+                    #['projec-theme', None],
+                    ['site-theme', None],
+                    ['allura', '/'],
+                ]
+
+        self.override_entrypoint = override_entrypoint
+        self.default_paths = default_paths
+
+        # Finally instantiate the loader
+        self.fs_loader = jinja2.FileSystemLoader(self.init_paths())
+
+    def init_paths(self):
+        '''
+        Set up the setuptools entry point-based paths.
+        '''
+        paths = self.default_paths[:]
+
+        '''
+        Iterate through the overriders.
+        TODO: Can this be moved to allura.app_globals.Globals, or is this
+              executed before that is available?
+        '''
+        epoints = pkg_resources.iter_entry_points(self.override_entrypoint)
+        for epoint in epoints:
+            overrider = epoint.load()
+            # Get the path of the module
+            tmpl_path = pkg_resources.resource_filename(
+                overrider.__module__,
+                ""
+            )
+            # Default insert position is right before allura(/)
+            insert_position = len(paths) - 1
+
+            rules = getattr(overrider, 'template_path_rules', [])
+
+            # Check each of the rules for this overrider
+            for direction, signpost in rules:
+                sp_location = None
+
+                # Find the signpost
+                try:
+                    sp_location = [path[0] for path in paths].index(signpost)
+                except ValueError:
+                    # Couldn't find it, hope they specified another one, or
+                    # that the default is ok.
+                    continue
+
+                if direction == '=':
+                    # Set a signpost. Behavior if already set is undetermined,
+                    # as entry point ordering is undetermined
+                    paths[sp_location][1] = tmpl_path
+                    # already inserted! our work is done here
+                    insert_position = None
+                    break
+                elif direction == '>':
+                    # going to put it right before the signpost
+                    insert_position = min(sp_location, insert_position)
+                elif direction == '<':
+                    # going to put it right after the signpost
+                    insert_position = min(sp_location + 1, insert_position)
+                else:
+                    # don't know what that is!
+                    raise Exception('Unknown template path rule in %s: %s' % (
+                        overrider, direction))
+
+            # in the case that we've already replaced a signpost, carry on
+            if insert_position is not None:
+                # TODO: wouldn't OrderedDict be better? the allura.lib one
+                #       doesn't support ordering like the markdown one
+                paths.insert(insert_position, (epoint.name, tmpl_path))
+
+        # Get rid of None paths... not useful
+        return [path for name, path in paths if path is not None]
+
+    def get_source(self, environment, template):
+        '''
+        Returns the source for jinja2 rendered templates. Can understand...
+        - path/to/template.html
+        - module:path/to/template.html
+        '''
+        package, path = None, None
+        src = None
+        bits = template.split(':')
+        if len(bits) == 2:
+            # splitting out the Python module name from the template string...
+            # the default allura behavior
+            package, path = template.split(':')
+            # TODO: is there a better way to do this?
+            path_fragment = os.path.join('templates', package, path)
+        elif len(bits) == 1:
+            # TODO: is this even useful?
+            path = bits[0]
+            path_fragment = os.path.join('templates', path)
+        else:
+            raise Exception('malformed template path')
+
+        # look in all of the customized search locations...
+        try:
+            src = self.fs_loader.get_source(environment, path_fragment)
+        except Exception:
+            # no Tool implemented an override... not even sure if this will
+            # throw an error, but we're ready for it!
+            pass
+
+        # ...but if you don't find anything, fall back to the explicit package
+        # approach
+        if src is None and package is not None:
+            # gets the absolute filename of the template
+            filename = pkg_resources.resource_filename(package, path)
+            # get the filename relative to the fs root (/).. if this fails
+            # this error is not caught, so should get propagated normally
+            src = self.fs_loader.get_source(environment, filename)
+        elif src is None:
+            raise Exception(('Template %s not found in search path ' +
+                  'and no module specified') % (
+                    path,
+                  ))
+        return src