You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2014/01/10 19:19:50 UTC

[26/32] PEP8 cleanup

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 65b68c8..6e5ef42 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -40,7 +40,7 @@ log = logging.getLogger(__name__)
 
 
 class RepositoryApp(Application):
-    END_OF_REF_ESCAPE='~'
+    END_OF_REF_ESCAPE = '~'
     __version__ = version.__version__
     permissions = [
         'read', 'write', 'create',
@@ -56,19 +56,19 @@ class RepositoryApp(Application):
         ConfigOption('cloned_from_project_id', ObjectId, None),
         ConfigOption('cloned_from_repo_id', ObjectId, None),
         ConfigOption('init_from_url', str, None)
-        ]
-    tool_label='Repository'
-    default_mount_label='Code'
-    default_mount_point='code'
-    relaxed_mount_points=True
-    ordinal=2
-    forkable=False
-    default_branch_name=None # master or default or some such
-    repo=None # override with a property in child class
-    icons={
-        24:'images/code_24.png',
-        32:'images/code_32.png',
-        48:'images/code_48.png'
+    ]
+    tool_label = 'Repository'
+    default_mount_label = 'Code'
+    default_mount_point = 'code'
+    relaxed_mount_points = True
+    ordinal = 2
+    forkable = False
+    default_branch_name = None  # master or default or some such
+    repo = None  # override with a property in child class
+    icons = {
+        24: 'images/code_24.png',
+        32: 'images/code_32.png',
+        48: 'images/code_48.png'
     }
 
     def __init__(self, project, config):
@@ -79,9 +79,9 @@ class RepositoryApp(Application):
         '''Apps should provide their entries to be added to the main nav
         :return: a list of :class:`SitemapEntries <allura.app.SitemapEntry>`
         '''
-        return [ SitemapEntry(
-                self.config.options.mount_label,
-                '.')]
+        return [SitemapEntry(
+            self.config.options.mount_label,
+            '.')]
 
     @property
     @h.exceptionless([], log)
@@ -89,13 +89,17 @@ class RepositoryApp(Application):
         menu_id = self.config.options.mount_label
         with h.push_config(c, app=self):
             return [
-                SitemapEntry(menu_id, '.')[self.sidebar_menu()] ]
+                SitemapEntry(menu_id, '.')[self.sidebar_menu()]]
 
     def admin_menu(self):
-        admin_url = c.project.url()+'admin/'+self.config.options.mount_point+'/'
-        links = [SitemapEntry('Viewable Files', admin_url + 'extensions', className='admin_modal')]
+        admin_url = c.project.url() + 'admin/' + \
+            self.config.options.mount_point + '/'
+        links = [
+            SitemapEntry('Viewable Files', admin_url + 'extensions', className='admin_modal')]
         links.append(SitemapEntry('Refresh Repository',
-                                  c.project.url() + self.config.options.mount_point + '/refresh',
+                                  c.project.url() +
+                                  self.config.options.mount_point +
+                                  '/refresh',
                                   ))
         links += super(RepositoryApp, self).admin_menu()
         [links.remove(l) for l in links[:] if l.label == 'Options']
@@ -105,41 +109,47 @@ class RepositoryApp(Application):
     def sidebar_menu(self):
         if not self.repo or self.repo.status != 'ready':
             return []
-        links = [SitemapEntry('Browse Commits', c.app.url + 'commit_browser', ui_icon=g.icons['folder'])]
+        links = [SitemapEntry('Browse Commits', c.app.url +
+                              'commit_browser', ui_icon=g.icons['folder'])]
         if self.forkable and self.repo.status == 'ready':
-            links.append(SitemapEntry('Fork', c.app.url + 'fork', ui_icon=g.icons['fork']))
-        merge_request_count = self.repo.merge_requests_by_statuses('open').count()
+            links.append(
+                SitemapEntry('Fork', c.app.url + 'fork', ui_icon=g.icons['fork']))
+        merge_request_count = self.repo.merge_requests_by_statuses(
+            'open').count()
         if merge_request_count:
             links += [
                 SitemapEntry(
                     'Merge Requests', c.app.url + 'merge-requests/',
-                    small=merge_request_count) ]
+                    small=merge_request_count)]
         if self.repo.forks:
             links += [
-                SitemapEntry('Forks', c.app.url + 'forks/', small=len(self.repo.forks))
+                SitemapEntry('Forks', c.app.url + 'forks/',
+                             small=len(self.repo.forks))
             ]
         if self.repo.upstream_repo.name:
-            repo_path_parts = self.repo.upstream_repo.name.strip('/').split('/')
+            repo_path_parts = self.repo.upstream_repo.name.strip(
+                '/').split('/')
             links += [
                 SitemapEntry('Clone of'),
                 SitemapEntry('%s / %s' %
-                    (repo_path_parts[1], repo_path_parts[-1]),
-                    self.repo.upstream_repo.name)
-                ]
+                             (repo_path_parts[1], repo_path_parts[-1]),
+                             self.repo.upstream_repo.name)
+            ]
             if not c.app.repo.is_empty() and has_access(c.app.repo, 'admin'):
                 merge_url = c.app.url + 'request_merge'
                 if getattr(c, 'revision', None):
                     merge_url = merge_url + '?branch=' + h.urlquote(c.revision)
                 links.append(SitemapEntry('Request Merge', merge_url,
                              ui_icon=g.icons['merge'],
-                             ))
+                                          ))
             pending_upstream_merges = self.repo.pending_upstream_merges()
             if pending_upstream_merges:
                 links.append(SitemapEntry(
-                        'Pending Merges',
-                        self.repo.upstream_repo.name + 'merge-requests/',
-                        small=pending_upstream_merges))
-        ref_url = self.repo.url_for_commit(self.default_branch_name, url_type='ref')
+                    'Pending Merges',
+                    self.repo.upstream_repo.name + 'merge-requests/',
+                    small=pending_upstream_merges))
+        ref_url = self.repo.url_for_commit(
+            self.default_branch_name, url_type='ref')
         branches = self.repo.get_branches()
         if branches:
             links.append(SitemapEntry('Branches'))
@@ -151,28 +161,28 @@ class RepositoryApp(Application):
             max_branches = 10
             for branch in branches[:max_branches]:
                 links.append(SitemapEntry(
-                        branch.name,
-                        quote(self.repo.url_for_commit(branch.name) + 'tree/')))
+                    branch.name,
+                    quote(self.repo.url_for_commit(branch.name) + 'tree/')))
             if len(branches) > max_branches:
                 links.append(
                     SitemapEntry(
                         'More Branches',
                         ref_url + 'branches/',
-                        ))
+                    ))
         tags = self.repo.get_tags()
         if tags:
             links.append(SitemapEntry('Tags'))
             max_tags = 10
             for b in tags[:max_tags]:
                 links.append(SitemapEntry(
-                        b.name,
-                        quote(self.repo.url_for_commit(b.name) + 'tree/')))
+                    b.name,
+                    quote(self.repo.url_for_commit(b.name) + 'tree/')))
             if len(tags) > max_tags:
                 links.append(
                     SitemapEntry(
                         'More Tags',
                         ref_url + 'tags/',
-                        ))
+                    ))
         return links
 
     def install(self, project):
@@ -191,11 +201,12 @@ class RepositoryApp(Application):
             M.ACE.allow(role_developer, 'moderate'),
             M.ACE.allow(role_admin, 'configure'),
             M.ACE.allow(role_admin, 'admin'),
-            ]
+        ]
 
     def uninstall(self, project):
         allura.tasks.repo_tasks.uninstall.post()
 
+
 class RepoAdminController(DefaultAdminController):
 
     def __init__(self, app):
@@ -224,7 +235,8 @@ class RepoAdminController(DefaultAdminController):
     @expose()
     @require_post()
     def set_extensions(self, **post_data):
-        self.repo.additional_viewable_extensions = post_data['additional_viewable_extensions']
+        self.repo.additional_viewable_extensions = post_data[
+            'additional_viewable_extensions']
 
     @without_trailing_slash
     @expose('jinja:allura:templates/repo/default_branch.html')
@@ -235,4 +247,3 @@ class RepoAdminController(DefaultAdminController):
         else:
             return dict(app=self.app,
                         default_branch_name=self.app.default_branch_name)
-

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/rest_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/rest_api.py b/Allura/allura/lib/rest_api.py
index f0d74f8..dcc0d7d 100644
--- a/Allura/allura/lib/rest_api.py
+++ b/Allura/allura/lib/rest_api.py
@@ -30,6 +30,7 @@ from formencode import variabledecode
 
 log = logging.getLogger(__name__)
 
+
 class RestClient(object):
 
     def __init__(self, api_key, secret_key, base_uri,
@@ -49,13 +50,18 @@ class RestClient(object):
             self._opener = urllib2.build_opener(redirect_handler)
 
     def sign_request(self, path, params):
-        if hasattr(params, 'items'): params = params.items()
+        if hasattr(params, 'items'):
+            params = params.items()
         has_api_key = has_api_timestamp = has_api_signature = False
-        for k,v in params:
-            if k == 'api_key': has_api_key = True
-            if k == 'api_timestamp': has_api_timestamp = True
-            if k == 'api_signature': has_api_signature = True
-        if not has_api_key: params.append(('api_key', self._api_key))
+        for k, v in params:
+            if k == 'api_key':
+                has_api_key = True
+            if k == 'api_timestamp':
+                has_api_timestamp = True
+            if k == 'api_signature':
+                has_api_signature = True
+        if not has_api_key:
+            params.append(('api_key', self._api_key))
         if not has_api_timestamp:
             params.append(('api_timestamp', datetime.utcnow().isoformat()))
         if not has_api_signature:
@@ -71,15 +77,17 @@ class RestClient(object):
 
     def _redirect_handler_class(self):
         client = self
+
         class RedirectHandler(urllib2.HTTPRedirectHandler):
+
             def redirect_request(self, req, fp, code, msg, headers, newurl):
                 m = req.get_method()
                 if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
-                    or code in (301, 302, 303) and m == "POST"):
+                        or code in (301, 302, 303) and m == "POST"):
                         newurl = newurl.replace(' ', '%20')
-                        newheaders = dict((k,v) for k,v in req.headers.items()
+                        newheaders = dict((k, v) for k, v in req.headers.items()
                                           if k.lower() not in ("content-length", "content-type")
-                                         )
+                                          )
                         result = urlparse(newurl)
                         log.debug('Redirect to %s' % result.path)
                         return client.Request(
@@ -88,39 +96,51 @@ class RestClient(object):
                             origin_req_host=req.get_origin_req_host(),
                             unverifiable=True)
                 else:
-                        raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
+                        raise urllib2.HTTPError(
+                            req.get_full_url(), code, msg, headers, fp)
         return RedirectHandler
 
     def _request_class(self):
         client = self
+
         class Request(urllib2.Request):
+
             def __init__(self, method, path, params=None, **kwargs):
-                if params is None: params = {}
-                params = variabledecode.variable_encode(params, add_repetitions=False)
+                if params is None:
+                    params = {}
+                params = variabledecode.variable_encode(
+                    params, add_repetitions=False)
                 params = client.sign_request(path, params)
                 self._method = method.upper()
                 if self._method == 'GET':
-                    url = urljoin(client.base_uri, path) + '?' + urlencode(params)
-                    data=None
+                    url = urljoin(client.base_uri, path) + \
+                        '?' + urlencode(params)
+                    data = None
                 else:
                     url = urljoin(client.base_uri, path)
-                    data=urlencode(params)
+                    data = urlencode(params)
                 urllib2.Request.__init__(self, url, data=data, **kwargs)
+
             def get_method(self):
                 return self._method
         return Request
 
+
 def generate_smart_str(params):
-    if isinstance(params, dict): iterparams = params.iteritems()
-    else: iterparams = iter(params)
+    if isinstance(params, dict):
+        iterparams = params.iteritems()
+    else:
+        iterparams = iter(params)
     for key, value in iterparams:
-        if value is None: continue
+        if value is None:
+            continue
         if isinstance(value, (list, tuple)):
             for item in value:
                 yield smart_str(key), smart_str(item)
         else:
             yield smart_str(key), smart_str(value)
 
+
 def urlencode(params):
     """
     A version of Python's urllib.urlencode() function that can operate on

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/search.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/search.py b/Allura/allura/lib/search.py
index d43af9c..419a473 100644
--- a/Allura/allura/lib/search.py
+++ b/Allura/allura/lib/search.py
@@ -34,10 +34,13 @@ from .markdown_extensions import ForgeExtension
 
 log = getLogger(__name__)
 
+
 def solarize(obj):
-    if obj is None: return None
+    if obj is None:
+        return None
     doc = obj.index()
-    if doc is None: return None
+    if doc is None:
+        return None
     # if index() returned doc without text, assume empty text
     if not doc.get('text'):
         doc['text'] = ''
@@ -48,16 +51,19 @@ def solarize(obj):
     doc['text'] = jinja2.Markup.escape(text).striptags()
     return doc
 
+
 class SearchError(SolrError):
     pass
 
+
 def inject_user(q, user=None):
     '''Replace $USER with current user's name.'''
     if user is None:
         user = c.user
     return q.replace('$USER', '"%s"' % user.username) if q else q
 
-def search(q,short_timeout=False,ignore_errors=True,**kw):
+
+def search(q, short_timeout=False, ignore_errors=True, **kw):
     q = inject_user(q)
     try:
         if short_timeout:
@@ -68,7 +74,9 @@ def search(q,short_timeout=False,ignore_errors=True,**kw):
         log.exception('Error in solr search')
         if not ignore_errors:
             match = re.search(r'<pre>(.*)</pre>', str(e))
-            raise SearchError('Error running search query: %s' % (match.group(1) if match else e))
+            raise SearchError('Error running search query: %s' %
+                              (match.group(1) if match else e))
+
 
 def search_artifact(atype, q, history=False, rows=10, short_timeout=False, **kw):
     """Performs SOLR search.
@@ -77,14 +85,15 @@ def search_artifact(atype, q, history=False, rows=10, short_timeout=False, **kw)
     """
     # first, grab an artifact and get the fields that it indexes
     a = atype.query.find().first()
-    if a is None: return # if there are no instance of atype, we won't find anything
+    if a is None:
+        return  # if there are no instance of atype, we won't find anything
     fields = a.index()
     # Now, we'll translate all the fld:
     q = atype.translate_query(q, fields)
     fq = [
         'type_s:%s' % fields['type_s'],
         'project_id_s:%s' % c.project._id,
-        'mount_point_s:%s' % c.app.config.options.mount_point ]
+        'mount_point_s:%s' % c.app.config.options.mount_point]
     if not history:
         fq.append('is_history_b:False')
     return search(q, fq=fq, rows=rows, short_timeout=short_timeout, ignore_errors=False, **kw)
@@ -97,8 +106,10 @@ def search_app(q='', fq=None, app=True, **kw):
     """
     history = kw.pop('history', None)
     if app and kw.pop('project', False):
-        # Used from app's search controller. If `project` is True, redirect to 'entire project search' page
-        redirect(c.project.url() + 'search/?' + urlencode(dict(q=q, history=history)))
+        # Used from app's search controller. If `project` is True, redirect to
+        # 'entire project search' page
+        redirect(c.project.url() + 'search/?' +
+                 urlencode(dict(q=q, history=history)))
     search_comments = kw.pop('search_comments', None)
     limit = kw.pop('limit', None)
     page = kw.pop('page', 0)
@@ -122,10 +133,11 @@ def search_app(q='', fq=None, app=True, **kw):
             allowed_types += ['Post']
         if app:
             fq = [
-                'project_id_s:%s'  % c.project._id,
+                'project_id_s:%s' % c.project._id,
                 'mount_point_s:%s' % c.app.config.options.mount_point,
                 '-deleted_b:true',
-                'type_s:(%s)' % ' OR '.join(['"%s"' % t for t in allowed_types])
+                'type_s:(%s)' % ' OR '.join(
+                    ['"%s"' % t for t in allowed_types])
             ] + fq
         search_params = {
             'qt': 'dismax',
@@ -138,7 +150,7 @@ def search_app(q='', fq=None, app=True, **kw):
             'sort': sort,
         }
         if not history:
-           search_params['fq'].append('is_history_b:False')
+            search_params['fq'].append('is_history_b:False')
         if parser == 'standard':
             search_params.pop('qt', None)
             search_params.pop('qf', None)
@@ -152,11 +164,14 @@ def search_app(q='', fq=None, app=True, **kw):
         if results:
             count = results.hits
             matches = results.highlighting
+
             def historize_urls(doc):
                 if doc.get('type_s', '').endswith(' Snapshot'):
                     if doc.get('url_s'):
-                        doc['url_s'] = doc['url_s'] + '?version=%s' % doc.get('version_i')
+                        doc['url_s'] = doc['url_s'] + \
+                            '?version=%s' % doc.get('version_i')
                 return doc
+
             def add_matches(doc):
                 m = matches.get(doc['id'], {})
                 title = h.get_first(m, 'title')
@@ -172,6 +187,7 @@ def search_app(q='', fq=None, app=True, **kw):
                 doc['title_match'] = title
                 doc['text_match'] = text or h.get_first(doc, 'text')
                 return doc
+
             def paginate_comment_urls(doc):
                 if doc.get('type_s', '') == 'Post':
                     aref = ArtifactReference.query.get(_id=doc.get('id'))
@@ -211,4 +227,4 @@ def find_shortlinks(text):
         output_format='html4')
     md.convert(text)
     link_index = md.treeprocessors['links'].alinks
-    return [ link for link in link_index if link is not None]
+    return [link for link in link_index if link is not None]

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/security.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 49d6415..a0d6c1e 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -31,7 +31,9 @@ from allura.lib.utils import TruthyCallable
 
 log = logging.getLogger(__name__)
 
+
 class Credentials(object):
+
     '''
     Role graph logic & caching
     '''
@@ -58,7 +60,8 @@ class Credentials(object):
 
     def clear_user(self, user_id, project_id=None):
         if project_id == '*':
-            to_remove = [(uid, pid) for uid, pid in self.users if uid == user_id]
+            to_remove = [(uid, pid)
+                         for uid, pid in self.users if uid == user_id]
         else:
             to_remove = [(user_id, project_id)]
         for uid, pid in to_remove:
@@ -68,8 +71,10 @@ class Credentials(object):
     def load_user_roles(self, user_id, *project_ids):
         '''Load the credentials with all user roles for a set of projects'''
         # Don't reload roles
-        project_ids = [ pid for pid in project_ids if self.users.get((user_id, pid)) is None ]
-        if not project_ids: return
+        project_ids = [
+            pid for pid in project_ids if self.users.get((user_id, pid)) is None]
+        if not project_ids:
+            return
         if user_id is None:
             q = self.project_role.find({
                 'user_id': None,
@@ -94,8 +99,10 @@ class Credentials(object):
     def load_project_roles(self, *project_ids):
         '''Load the credentials with all user roles for a set of projects'''
         # Don't reload roles
-        project_ids = [ pid for pid in project_ids if self.projects.get(pid) is None ]
-        if not project_ids: return
+        project_ids = [
+            pid for pid in project_ids if self.projects.get(pid) is None]
+        if not project_ids:
+            return
         q = self.project_role.find({
             'project_id': {'$in': project_ids}})
         roles_by_project = dict((pid, []) for pid in project_ids)
@@ -134,17 +141,18 @@ class Credentials(object):
 
     def user_has_any_role(self, user_id, project_id, role_ids):
         user_roles = self.user_roles(user_id=user_id, project_id=project_id)
-        return bool(set(role_ids)  & user_roles.reaching_ids_set)
+        return bool(set(role_ids) & user_roles.reaching_ids_set)
 
     def users_with_named_role(self, project_id, name):
         """ returns in sorted order """
         roles = self.project_roles(project_id)
-        return sorted(RoleCache(self, roles.find(name=name)).users_that_reach, key=lambda u:u.username)
+        return sorted(RoleCache(self, roles.find(name=name)).users_that_reach, key=lambda u: u.username)
 
     def userids_with_named_role(self, project_id, name):
         roles = self.project_roles(project_id)
         return RoleCache(self, roles.find(name=name)).userids_that_reach
 
+
 class RoleCache(object):
 
     def __init__(self, cred, q):
@@ -153,19 +161,23 @@ class RoleCache(object):
 
     def find(self, **kw):
         tests = kw.items()
+
         def _iter():
             for r in self:
-                for k,v in tests:
+                for k, v in tests:
                     val = r.get(k)
                     if callable(v):
-                        if not v(val): break
-                    elif v != val: break
+                        if not v(val):
+                            break
+                    elif v != val:
+                        break
                 else:
                     yield r
         return RoleCache(self.cred, _iter())
 
     def get(self, **kw):
-        for x in self.find(**kw): return x
+        for x in self.find(**kw):
+            return x
         return None
 
     def __iter__(self):
@@ -199,10 +211,12 @@ class RoleCache(object):
             to_visit = list(self)
             while to_visit:
                 r = to_visit.pop(0)
-                if r['_id'] in visited: continue
+                if r['_id'] in visited:
+                    continue
                 visited.add(r['_id'])
                 yield r
-                pr_rindex = self.cred.project_roles(r['project_id']).reverse_index
+                pr_rindex = self.cred.project_roles(
+                    r['project_id']).reverse_index
                 to_visit += pr_rindex[r['_id']]
         return RoleCache(self.cred, _iter())
 
@@ -214,7 +228,7 @@ class RoleCache(object):
 
     @LazyProperty
     def userids_that_reach(self):
-        return [ r['user_id'] for r in self.roles_that_reach ]
+        return [r['user_id'] for r in self.roles_that_reach]
 
     @LazyProperty
     def reaching_roles(self):
@@ -223,7 +237,8 @@ class RoleCache(object):
             visited = set()
             while to_visit:
                 (rid, role) = to_visit.pop()
-                if rid in visited: continue
+                if rid in visited:
+                    continue
                 yield role
                 pr_index = self.cred.project_roles(role['project_id']).index
                 if rid in pr_index:
@@ -234,12 +249,13 @@ class RoleCache(object):
 
     @LazyProperty
     def reaching_ids(self):
-        return [ r['_id'] for r in self.reaching_roles ]
+        return [r['_id'] for r in self.reaching_roles]
 
     @LazyProperty
     def reaching_ids_set(self):
         return set(self.reaching_ids)
 
+
 def has_access(obj, permission, user=None, project=None):
     '''Return whether the given user has the permission name on the given object.
 
@@ -280,11 +296,13 @@ def has_access(obj, permission, user=None, project=None):
       3. Otherwise, DENY access to the resource.
     '''
     from allura import model as M
+
     def predicate(obj=obj, user=user, project=project, roles=None):
         if obj is None:
             return False
         if roles is None:
-            if user is None: user = c.user
+            if user is None:
+                user = c.user
             assert user, 'c.user should always be at least M.User.anonymous()'
             cred = Credentials.get()
             if project is None:
@@ -298,12 +316,13 @@ def has_access(obj, permission, user=None, project=None):
                 else:
                     project = getattr(obj, 'project', None) or c.project
                     project = project.root_project
-            roles = cred.user_roles(user_id=user._id, project_id=project._id).reaching_ids
+            roles = cred.user_roles(
+                user_id=user._id, project_id=project._id).reaching_ids
 
         # TODO: move deny logic into loop below; see ticket [#6715]
         if user != M.User.anonymous():
             user_roles = Credentials.get().user_roles(user_id=user._id,
-                    project_id=project.root_project._id)
+                                                      project_id=project.root_project._id)
             for r in user_roles:
                 deny_user = M.ACE.deny(r['_id'], permission)
                 if M.ACL.contains(deny_user, obj.acl):
@@ -337,6 +356,7 @@ def has_access(obj, permission, user=None, project=None):
         return result
     return TruthyCallable(predicate)
 
+
 def all_allowed(obj, user_or_role=None, project=None):
     '''
     List all the permission names that a given user or named role
@@ -380,7 +400,8 @@ def all_allowed(obj, user_or_role=None, project=None):
         roles += [anon]  # auth inherits from anon
     else:
         roles += [auth, anon]  # named group or user inherits from auth + anon
-    role_ids = RoleCache(Credentials.get(), roles).reaching_ids  # match rules applicable to us
+    # match rules applicable to us
+    role_ids = RoleCache(Credentials.get(), roles).reaching_ids
     perms = set()
     denied = defaultdict(set)
     while obj:  # traverse parent contexts
@@ -395,13 +416,15 @@ def all_allowed(obj, user_or_role=None, project=None):
                     else:
                         # explicit DENY overrides any ALLOW for this permission
                         # for this role_id in this ACL or parent(s) (but an ALLOW
-                        # for a different role could still grant this permission)
+                        # for a different role could still grant this
+                        # permission)
                         denied[role_id].add(ace.permission)
         obj = obj.parent_security_context()
     if M.ALL_PERMISSIONS in perms:
         return set([M.ALL_PERMISSIONS])
     return perms
 
+
 def require(predicate, message=None):
     '''
     Example: require(has_access(c.app, 'read'))
@@ -412,7 +435,8 @@ def require(predicate, message=None):
     '''
 
     from allura import model as M
-    if predicate(): return
+    if predicate():
+        return
     if not message:
         message = """You don't have permission to do that.
                      You must ask a project administrator for rights to perform this task.
@@ -423,12 +447,15 @@ def require(predicate, message=None):
     else:
         raise exc.HTTPUnauthorized()
 
+
 def require_access(obj, permission, **kwargs):
     if obj is not None:
         predicate = has_access(obj, permission, **kwargs)
         return require(predicate, message='%s access required' % permission.capitalize())
     else:
-        raise exc.HTTPForbidden(detail="Could not verify permissions for this page.")
+        raise exc.HTTPForbidden(
+            detail="Could not verify permissions for this page.")
+
 
 def require_authenticated():
     '''
@@ -438,12 +465,15 @@ def require_authenticated():
     if c.user == M.User.anonymous():
         raise exc.HTTPUnauthorized()
 
+
 def simple_grant(acl, role_id, permission):
     from allura.model.types import ACE
     for ace in acl:
-        if ace.role_id == role_id and ace.permission == permission: return
+        if ace.role_id == role_id and ace.permission == permission:
+            return
     acl.append(ACE.allow(role_id, permission))
 
+
 def simple_revoke(acl, role_id, permission):
     remove = []
     for i, ace in enumerate(acl):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/solr.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/solr.py b/Allura/allura/lib/solr.py
index 4fa0c77..22adbe6 100644
--- a/Allura/allura/lib/solr.py
+++ b/Allura/allura/lib/solr.py
@@ -37,6 +37,7 @@ def make_solr_from_config(push_servers, query_server=None, **kwargs):
 
 
 class Solr(object):
+
     """Solr interface that pushes updates to multiple solr instances.
 
     `push_servers`: list of servers to push to.
@@ -88,6 +89,7 @@ class Solr(object):
 class MockSOLR(object):
 
     class MockHits(list):
+
         @property
         def hits(self):
             return len(self)
@@ -113,7 +115,8 @@ class MockSOLR(object):
         # Parse query
         preds = []
         q_parts = shlex.split(q)
-        if fq: q_parts += fq
+        if fq:
+            q_parts += fq
         for part in q_parts:
             if part == '&&':
                 continue

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/__init__.py b/Allura/allura/lib/spam/__init__.py
index cfb5c41..4bf3917 100644
--- a/Allura/allura/lib/spam/__init__.py
+++ b/Allura/allura/lib/spam/__init__.py
@@ -23,7 +23,9 @@ log = logging.getLogger(__name__)
 
 
 class SpamFilter(object):
+
     """Defines the spam checker interface and provides a default no-op impl."""
+
     def __init__(self, config):
         pass
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/akismetfilter.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/akismetfilter.py b/Allura/allura/lib/spam/akismetfilter.py
index 7020632..48f24cc 100644
--- a/Allura/allura/lib/spam/akismetfilter.py
+++ b/Allura/allura/lib/spam/akismetfilter.py
@@ -34,6 +34,7 @@ log = logging.getLogger(__name__)
 
 
 class AkismetSpamFilter(SpamFilter):
+
     """Spam checking implementation via Akismet service.
 
     To enable Akismet spam filtering in your Allura instance, first
@@ -47,10 +48,12 @@ class AkismetSpamFilter(SpamFilter):
         spam.method = akismet
         spam.key = <your Akismet key here>
     """
+
     def __init__(self, config):
         if not AKISMET_AVAILABLE:
             raise ImportError('akismet not available')
-        self.service = akismet.Akismet(config.get('spam.key'), config.get('base_url'))
+        self.service = akismet.Akismet(
+            config.get('spam.key'), config.get('base_url'))
         self.service.verify_key()
 
     def get_data(self, text, artifact=None, user=None, content_type='comment', **kw):
@@ -62,7 +65,8 @@ class AkismetSpamFilter(SpamFilter):
         user = user or c.user
         if user:
             kw['comment_author'] = user.display_name or user.username
-            kw['comment_author_email'] = user.email_addresses[0] if user.email_addresses else ''
+            kw['comment_author_email'] = user.email_addresses[
+                0] if user.email_addresses else ''
         user_ip = request.headers.get('X_FORWARDED_FOR', request.remote_addr)
         kw['user_ip'] = user_ip.split(',')[0].strip()
         kw['user_agent'] = request.headers.get('USER_AGENT')
@@ -93,8 +97,8 @@ class AkismetSpamFilter(SpamFilter):
 
     def submit_ham(self, text, artifact=None, user=None, content_type='comment'):
         self.service.submit_ham(text,
-                                 data=self.get_data(text=text,
-                                                    artifact=artifact,
-                                                    user=user,
-                                                    content_type=content_type),
-                                 build_data=False)
+                                data=self.get_data(text=text,
+                                                   artifact=artifact,
+                                                   user=user,
+                                                   content_type=content_type),
+                                build_data=False)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/spam/mollomfilter.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/mollomfilter.py b/Allura/allura/lib/spam/mollomfilter.py
index b00f978..d76ff59 100644
--- a/Allura/allura/lib/spam/mollomfilter.py
+++ b/Allura/allura/lib/spam/mollomfilter.py
@@ -34,6 +34,7 @@ log = logging.getLogger(__name__)
 
 
 class MollomSpamFilter(SpamFilter):
+
     """Spam checking implementation via Mollom service.
 
     To enable Mollom spam filtering in your Allura instance, first
@@ -48,6 +49,7 @@ class MollomSpamFilter(SpamFilter):
         spam.public_key = <your Mollom public key here>
         spam.private_key = <your Mollom private key here>
     """
+
     def __init__(self, config):
         if not MOLLOM_AVAILABLE:
             raise ImportError('Mollom not available')
@@ -71,7 +73,8 @@ class MollomSpamFilter(SpamFilter):
         user = user or c.user
         if user:
             kw['authorName'] = user.display_name or user.username
-            kw['authorMail'] = user.email_addresses[0] if user.email_addresses else ''
+            kw['authorMail'] = user.email_addresses[
+                0] if user.email_addresses else ''
         user_ip = request.headers.get('X_FORWARDED_FOR', request.remote_addr)
         kw['authorIP'] = user_ip.split(',')[0].strip()
         # kw will be urlencoded, need to utf8-encode
@@ -79,7 +82,7 @@ class MollomSpamFilter(SpamFilter):
             kw[k] = h.really_unicode(v).encode('utf8')
         cc = self.service.checkContent(**kw)
         res = cc['spam'] == 2
-        artifact.spam_check_id = cc.get('session_id','')
+        artifact.spam_check_id = cc.get('session_id', '')
         log.info("spam=%s (mollom): %s" % (str(res), log_msg))
         return res
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/stats.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/stats.py b/Allura/allura/lib/stats.py
index ef6dc01..9864e30 100644
--- a/Allura/allura/lib/stats.py
+++ b/Allura/allura/lib/stats.py
@@ -20,6 +20,7 @@ from time import time
 from contextlib import contextmanager
 from pylons import request
 
+
 class StatsRecord(object):
 
     def __init__(self, request, active):
@@ -34,8 +35,8 @@ class StatsRecord(object):
 
     def __repr__(self):
         stats = ' '.join(
-            ('%s=%.0fms' % (k,v*1000))
-            for k,v in sorted(self.timers.iteritems()))
+            ('%s=%.0fms' % (k, v * 1000))
+            for k, v in sorted(self.timers.iteritems()))
         return '%s: %s' % (self.url, stats)
 
     def asdict(self):
@@ -53,12 +54,14 @@ class StatsRecord(object):
                 yield
             finally:
                 end = time()
-                self.timers[name] += end-begin
+                self.timers[name] += end - begin
                 self._now_timing.remove(name)
         else:
             yield
 
+
 class timing(object):
+
     '''Decorator to time a method call'''
 
     def __init__(self, timer):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index d35b17e..95622c9 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -74,6 +74,7 @@ def guess_mime_type(filename):
 
 
 class ConfigProxy(object):
+
     '''Wrapper for loading config values at module-scope so we don't
     have problems when a module is imported before tg.config is initialized
     '''
@@ -92,6 +93,7 @@ class ConfigProxy(object):
 
 
 class lazy_logger(object):
+
     '''Lazy instatiation of a logger, to ensure that it does not get
     created before logging is configured (which would make it disabled)'''
 
@@ -103,15 +105,18 @@ class lazy_logger(object):
         return logging.getLogger(self._name)
 
     def __getattr__(self, name):
-        if name.startswith('_'): raise AttributeError, name
+        if name.startswith('_'):
+            raise AttributeError, name
         return getattr(self._logger, name)
 
+
 class TimedRotatingHandler(logging.handlers.BaseRotatingHandler):
 
     def __init__(self, strftime_pattern):
         self.pattern = strftime_pattern
         self.last_filename = self.current_filename()
-        logging.handlers.BaseRotatingHandler.__init__(self, self.last_filename, 'a')
+        logging.handlers.BaseRotatingHandler.__init__(
+            self, self.last_filename, 'a')
 
     def current_filename(self):
         return os.path.abspath(datetime.datetime.utcnow().strftime(self.pattern))
@@ -128,9 +133,11 @@ class TimedRotatingHandler(logging.handlers.BaseRotatingHandler):
         else:
             self.stream = open(self.baseFilename, 'w')
 
+
 class StatsHandler(TimedRotatingHandler):
-    fields=('action', 'action_type', 'tool_type', 'tool_mount', 'project', 'neighborhood',
-            'username', 'url', 'ip_address')
+    fields = (
+        'action', 'action_type', 'tool_type', 'tool_mount', 'project', 'neighborhood',
+        'username', 'url', 'ip_address')
 
     def __init__(self,
                  strftime_pattern,
@@ -151,13 +158,14 @@ class StatsHandler(TimedRotatingHandler):
             kwpairs[name] = getattr(record, name, None)
         kwpairs.update(getattr(record, 'kwpairs', {}))
         record.kwpairs = ','.join(
-            '%s=%s' % (k,v) for k,v in sorted(kwpairs.iteritems())
+            '%s=%s' % (k, v) for k, v in sorted(kwpairs.iteritems())
             if v is not None)
-        record.exc_info = None # Never put tracebacks in the rtstats log
+        record.exc_info = None  # Never put tracebacks in the rtstats log
         TimedRotatingHandler.emit(self, record)
 
 
 class CustomWatchedFileHandler(logging.handlers.WatchedFileHandler):
+
     """Custom log handler for Allura"""
 
     def format(self, record):
@@ -179,7 +187,8 @@ def chunked_find(cls, query=None, pagesize=1024, sort_key='_id', sort_dir=1):
     Pass an indexed sort_key for efficient queries.  Default _id should work
     in most cases.
     '''
-    if query is None: query = {}
+    if query is None:
+        query = {}
     page = 0
     max_id = None
     while True:
@@ -200,6 +209,7 @@ def chunked_find(cls, query=None, pagesize=1024, sort_key='_id', sort_dir=1):
         yield results
         page += 1
 
+
 def lsub_utf8(s, n):
     '''Useful for returning n bytes of a UTF-8 string, rather than characters'''
     while len(s) > n:
@@ -209,22 +219,26 @@ def lsub_utf8(s, n):
         return s[:k]
     return s
 
+
 def chunked_list(l, n):
     """ Yield successive n-sized chunks from l.
     """
     for i in xrange(0, len(l), n):
-        yield l[i:i+n]
+        yield l[i:i + n]
+
 
 def chunked_iter(iterable, max_size):
     '''return iterable 'chunks' from the iterable of max size max_size'''
     eiter = enumerate(iterable)
-    keyfunc = lambda (i,x): i//max_size
+    keyfunc = lambda (i, x): i // max_size
     for _, chunk in groupby(eiter, keyfunc):
-        yield (x for i,x in chunk)
+        yield (x for i, x in chunk)
+
 
 class AntiSpam(object):
+
     '''Helper class for bot-protecting forms'''
-    honey_field_template=string.Template('''<p class="$honey_class">
+    honey_field_template = string.Template('''<p class="$honey_class">
     <label for="$fld_id">You seem to have CSS turned off.
         Please don't fill out this field.</label><br>
     <input id="$fld_id" name="$fld_name" type="text"><br></p>''')
@@ -244,7 +258,7 @@ class AntiSpam(object):
             self.timestamp = int(self.timestamp_text)
             self.spinner = self._unwrap(self.spinner_text)
         self.spinner_ord = map(ord, self.spinner)
-        self.random_padding = [ random.randint(0,255) for x in self.spinner ]
+        self.random_padding = [random.randint(0, 255) for x in self.spinner]
         self.honey_class = self.enc(self.spinner_text, css_safe=True)
 
         # The counter is to ensure that multiple forms in the same page
@@ -289,10 +303,10 @@ class AntiSpam(object):
         '''
         # Plain starts with its length, includes the ordinals for its
         #   characters, and is padded with random data
-        plain = ([ len(plain) ]
+        plain = ([len(plain)]
                  + map(ord, plain)
                  + self.random_padding[:len(self.spinner_ord) - len(plain) - 1])
-        enc = ''.join(chr(p^s) for p, s in zip(plain, self.spinner_ord))
+        enc = ''.join(chr(p ^ s) for p, s in zip(plain, self.spinner_ord))
         enc = self._wrap(enc)
         if css_safe:
             enc = ''.join(ch for ch in enc if ch.isalpha())
@@ -301,8 +315,8 @@ class AntiSpam(object):
     def dec(self, enc):
         enc = self._unwrap(enc)
         enc = list(map(ord, enc))
-        plain = [e^s for e,s in zip(enc, self.spinner_ord)]
-        plain = plain[1:1+plain[0]]
+        plain = [e ^ s for e, s in zip(enc, self.spinner_ord)]
+        plain = plain[1:1 + plain[0]]
         plain = ''.join(map(chr, plain))
         return plain
 
@@ -313,15 +327,17 @@ class AntiSpam(object):
             fld_name = self.enc('honey%d' % (fldno))
             fld_id = self.enc('honey%d%d' % (self.counter, fldno))
             yield literal(self.honey_field_template.substitute(
-                    honey_class=self.honey_class,
-                    fld_id=fld_id,
-                    fld_name=fld_name))
+                honey_class=self.honey_class,
+                fld_id=fld_id,
+                fld_name=fld_name))
         self.counter += 1
 
     def make_spinner(self, timestamp=None):
-        if timestamp is None: timestamp = self.timestamp
+        if timestamp is None:
+            timestamp = self.timestamp
         try:
-            client_ip = self.request.headers.get('X_FORWARDED_FOR', self.request.remote_addr)
+            client_ip = self.request.headers.get(
+                'X_FORWARDED_FOR', self.request.remote_addr)
             client_ip = client_ip.split(',')[0].strip()
         except (TypeError, AttributeError), err:
             client_ip = '127.0.0.1'
@@ -331,17 +347,20 @@ class AntiSpam(object):
 
     @classmethod
     def validate_request(cls, request=None, now=None, params=None):
-        if request is None: request = pylons.request
-        if params is None: params = request.params
+        if request is None:
+            request = pylons.request
+        if params is None:
+            params = request.params
         new_params = dict(params)
         if not request.method == 'GET':
             new_params.pop('timestamp', None)
             new_params.pop('spinner', None)
             obj = cls(request)
-            if now is None: now = time.time()
+            if now is None:
+                now = time.time()
             if obj.timestamp > now + 5:
                 raise ValueError, 'Post from the future'
-            if now - obj.timestamp > 24*60*60:
+            if now - obj.timestamp > 24 * 60 * 60:
                 raise ValueError, 'Post from the distant past'
             if obj.spinner != obj.make_spinner(obj.timestamp):
                 raise ValueError, 'Bad spinner value'
@@ -365,21 +384,27 @@ class AntiSpam(object):
                 raise Invalid(error_msg, params, None)
         return before_validate(antispam_hook)
 
+
 class TruthyCallable(object):
+
     '''
     Wraps a callable to make it truthy in a boolean context.
 
     Assumes the callable returns a truthy value and can be called with no args.
     '''
+
     def __init__(self, callable):
         self.callable = callable
+
     def __call__(self, *args, **kw):
         return self.callable(*args, **kw)
+
     def __nonzero__(self):
         return self.callable()
 
 
 class TransformedDict(collections.MutableMapping):
+
     """
     A dictionary which applies an arbitrary
     key-altering function before accessing the keys.
@@ -389,7 +414,7 @@ class TransformedDict(collections.MutableMapping):
 
     def __init__(self, *args, **kwargs):
         self.store = dict()
-        self.update(dict(*args, **kwargs)) # use the free update to set keys
+        self.update(dict(*args, **kwargs))  # use the free update to set keys
 
     def __getitem__(self, key):
         return self.store[self.__keytransform__(key)]
@@ -416,17 +441,20 @@ class CaseInsensitiveDict(TransformedDict):
         return key.lower()
 
 
-def postmortem_hook(etype, value, tb): # pragma no cover
-    import sys, pdb, traceback
+def postmortem_hook(etype, value, tb):  # pragma no cover
+    import sys
+    import pdb
+    import traceback
     try:
-        from IPython.ipapi import make_session; make_session()
+        from IPython.ipapi import make_session
+        make_session()
         from IPython.Debugger import Pdb
         sys.stderr.write('Entering post-mortem IPDB shell\n')
         p = Pdb(color_scheme='Linux')
         p.reset()
         p.setup(None, tb)
         p.print_stack_trace()
-        sys.stderr.write('%s: %s\n' % ( etype, value))
+        sys.stderr.write('%s: %s\n' % (etype, value))
         p.cmdloop()
         p.forget()
         # p.interaction(None, tb)
@@ -435,7 +463,9 @@ def postmortem_hook(etype, value, tb): # pragma no cover
         traceback.print_exception(etype, value, tb)
         pdb.post_mortem(tb)
 
+
 class LineAnchorCodeHtmlFormatter(HtmlFormatter):
+
     def _wrap_pre(self, inner):
         style = []
         if self.prestyles:
@@ -451,6 +481,7 @@ class LineAnchorCodeHtmlFormatter(HtmlFormatter):
             num += 1
         yield 0, '</pre>'
 
+
 def generate_code_stats(blob):
     stats = {'line_count': 0,
              'code_size': 0,
@@ -486,7 +517,8 @@ def serve_file(fp, filename, content_type, last_modified=None, cache_expires=Non
         etag_cache(etag)
     pylons.response.headers['Content-Type'] = ''
     pylons.response.content_type = content_type.encode('utf-8')
-    pylons.response.cache_expires = cache_expires or asint(tg.config.get('files_expires_header_secs', 60 * 60))
+    pylons.response.cache_expires = cache_expires or asint(
+        tg.config.get('files_expires_header_secs', 60 * 60))
     pylons.response.last_modified = last_modified
     if size:
         pylons.response.content_length = size

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/validators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/validators.py b/Allura/allura/lib/validators.py
index 3556fe0..1c0acfd 100644
--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -23,6 +23,7 @@ from pylons import tmpl_context as c
 from . import helpers as h
 from datetime import datetime
 
+
 class Ming(fev.FancyValidator):
 
     def __init__(self, cls, **kw):
@@ -41,15 +42,18 @@ class Ming(fev.FancyValidator):
     def _from_python(self, value, state):
         return value._id
 
+
 class UniqueOAuthApplicationName(fev.UnicodeString):
 
     def _to_python(self, value, state):
         from allura import model as M
         app = M.OAuthConsumerToken.query.get(name=value)
         if app is not None:
-            raise fe.Invalid('That name is already taken, please choose another', value, state)
+            raise fe.Invalid(
+                'That name is already taken, please choose another', value, state)
         return value
 
+
 class NullValidator(fev.Validator):
 
     def to_python(self, value, state):
@@ -61,21 +65,25 @@ class NullValidator(fev.Validator):
     def validate(self, value, state):
         return value
 
+
 class MaxBytesValidator(fev.FancyValidator):
-    max=255
+    max = 255
 
     def _to_python(self, value, state):
         value = h.really_unicode(value or '').encode('utf-8')
         if len(value) > self.max:
-            raise fe.Invalid("Please enter a value less than %s bytes long." % self.max, value, state)
+            raise fe.Invalid("Please enter a value less than %s bytes long." %
+                             self.max, value, state)
         return value
 
     def from_python(self, value, state):
         return h.really_unicode(value or '')
 
+
 class MountPointValidator(fev.UnicodeString):
+
     def __init__(self, app_class,
-            reserved_mount_points=('feed', 'index', 'icon', '_nav.json'), **kw):
+                 reserved_mount_points=('feed', 'index', 'icon', '_nav.json'), **kw):
         super(self.__class__, self).__init__(**kw)
         self.app_class = app_class
         self.reserved_mount_points = reserved_mount_points
@@ -86,13 +94,14 @@ class MountPointValidator(fev.UnicodeString):
             mount_point = mount_point.lower()
         if not App.validate_mount_point(mount_point):
             raise fe.Invalid('Mount point "%s" is invalid' % mount_point,
-                    value, state)
+                             value, state)
         if mount_point in self.reserved_mount_points:
             raise fe.Invalid('Mount point "%s" is reserved' % mount_point,
-                    value, state)
+                             value, state)
         if c.project and c.project.app_instance(mount_point) is not None:
-            raise fe.Invalid('Mount point "%s" is already in use' % mount_point,
-                    value, state)
+            raise fe.Invalid(
+                'Mount point "%s" is already in use' % mount_point,
+                value, state)
         return mount_point
 
     def empty_value(self, value):
@@ -104,13 +113,15 @@ class MountPointValidator(fev.UnicodeString):
             mount_point = base_mount_point + '-%d' % i
             i += 1
 
+
 class TaskValidator(fev.FancyValidator):
+
     def _to_python(self, value, state):
         try:
             mod, func = value.rsplit('.', 1)
         except ValueError:
             raise fe.Invalid('Invalid task name. Please provide the full '
-                    'dotted path to the python callable.', value, state)
+                             'dotted path to the python callable.', value, state)
         try:
             mod = __import__(mod, fromlist=[str(func)])
         except ImportError:
@@ -119,13 +130,16 @@ class TaskValidator(fev.FancyValidator):
         try:
             task = getattr(mod, func)
         except AttributeError:
-            raise fe.Invalid('Module has no attribute "%s"' % func, value, state)
+            raise fe.Invalid('Module has no attribute "%s"' %
+                             func, value, state)
 
         if not hasattr(task, 'post'):
             raise fe.Invalid('"%s" is not a task.' % value, value, state)
         return task
 
+
 class UserValidator(fev.FancyValidator):
+
     def _to_python(self, value, state):
         from allura import model as M
         user = M.User.by_username(value)
@@ -133,14 +147,16 @@ class UserValidator(fev.FancyValidator):
             raise fe.Invalid('Invalid username', value, state)
         return user
 
+
 class PathValidator(fev.FancyValidator):
+
     def _to_python(self, value, state):
         from allura import model as M
 
         parts = value.strip('/').split('/')
         if len(parts) < 2:
             raise fe.Invalid("You must specify at least a neighborhood and "
-                "project, i.e. '/nbhd/project'", value, state)
+                             "project, i.e. '/nbhd/project'", value, state)
         elif len(parts) == 2:
             nbhd_name, project_name, app_name = parts[0], parts[1], None
         elif len(parts) > 2:
@@ -150,24 +166,31 @@ class PathValidator(fev.FancyValidator):
         nbhd_url_prefix = '/%s/' % nbhd_name
         nbhd = M.Neighborhood.query.get(url_prefix=nbhd_url_prefix)
         if not nbhd:
-            raise fe.Invalid('Invalid neighborhood: %s' % nbhd_url_prefix, value, state)
+            raise fe.Invalid('Invalid neighborhood: %s' %
+                             nbhd_url_prefix, value, state)
 
-        project = M.Project.query.get(shortname=nbhd.shortname_prefix + project_name,
-                neighborhood_id=nbhd._id)
+        project = M.Project.query.get(
+            shortname=nbhd.shortname_prefix + project_name,
+            neighborhood_id=nbhd._id)
         if not project:
-            raise fe.Invalid('Invalid project: %s' % project_name, value, state)
+            raise fe.Invalid('Invalid project: %s' %
+                             project_name, value, state)
 
         path_parts['project'] = project
         if app_name:
             app = project.app_instance(app_name)
             if not app:
-                raise fe.Invalid('Invalid app mount point: %s' % app_name, value, state)
+                raise fe.Invalid('Invalid app mount point: %s' %
+                                 app_name, value, state)
             path_parts['app'] = app
 
         return path_parts
 
+
 class JsonValidator(fev.FancyValidator):
+
     """Validates a string as JSON and returns the original string"""
+
     def _to_python(self, value, state):
         try:
             json.loads(value)
@@ -175,8 +198,11 @@ class JsonValidator(fev.FancyValidator):
             raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
         return value
 
+
 class JsonConverter(fev.FancyValidator):
+
     """Deserializes a string to JSON and returns a Python object"""
+
     def _to_python(self, value, state):
         try:
             obj = json.loads(value)
@@ -184,14 +210,19 @@ class JsonConverter(fev.FancyValidator):
             raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
         return obj
 
+
 class JsonFile(fev.FieldStorageUploadConverter):
+
     """Validates that a file is JSON and returns the deserialized Python object
 
     """
+
     def _to_python(self, value, state):
         return JsonConverter.to_python(value.value)
 
+
 class UserMapJsonFile(JsonFile):
+
     """Validates that a JSON file conforms to this format:
 
     {str:str, ...}
@@ -199,6 +230,7 @@ class UserMapJsonFile(JsonFile):
     and returns a deserialized or stringified copy of it.
 
     """
+
     def __init__(self, as_string=False):
         self.as_string = as_string
 
@@ -211,8 +243,9 @@ class UserMapJsonFile(JsonFile):
             return json.dumps(value) if self.as_string else value
         except:
             raise fe.Invalid(
-                    'User map file must contain mapping of {str:str, ...}',
-                    value, state)
+                'User map file must contain mapping of {str:str, ...}',
+                value, state)
+
 
 class CreateTaskSchema(fe.Schema):
     task = TaskValidator(not_empty=True, strip=True)
@@ -220,7 +253,9 @@ class CreateTaskSchema(fe.Schema):
     user = UserValidator(strip=True, if_missing=None)
     path = PathValidator(strip=True, if_missing={}, if_empty={})
 
+
 class DateValidator(fev.FancyValidator):
+
     def _to_python(self, value, state):
         value = convertDate(value)
         if not value:
@@ -229,7 +264,9 @@ class DateValidator(fev.FancyValidator):
                 value, state)
         return value
 
+
 class TimeValidator(fev.FancyValidator):
+
     def _to_python(self, value, state):
         value = convertTime(value)
         if not value:
@@ -238,8 +275,10 @@ class TimeValidator(fev.FancyValidator):
                 value, state)
         return value
 
+
 class OneOfValidator(fev.FancyValidator):
-    def __init__(self, validvalues, not_empty = True):
+
+    def __init__(self, validvalues, not_empty=True):
         self.validvalues = validvalues
         self.not_empty = not_empty
         super(OneOfValidator, self).__init__()
@@ -257,12 +296,14 @@ class OneOfValidator(fev.FancyValidator):
                     allowed = allowed + ', '
                 allowed = allowed + '"%s"' % v
             raise fe.Invalid(
-                "Invalid value. The allowed values are %s." %allowed,
+                "Invalid value. The allowed values are %s." % allowed,
                 value, state)
         return value
 
+
 class MapValidator(fev.FancyValidator):
-    def __init__(self, mapvalues, not_empty = True):
+
+    def __init__(self, mapvalues, not_empty=True):
         self.map = mapvalues
         self.not_empty = not_empty
         super(MapValidator, self).__init__()
@@ -280,25 +321,27 @@ class MapValidator(fev.FancyValidator):
                 value, state)
         return conv_value
 
+
 def convertDate(datestring):
     formats = ['%Y-%m-%d', '%Y.%m.%d', '%Y/%m/%d', '%Y\%m\%d', '%Y %m %d',
                '%d-%m-%Y', '%d.%m.%Y', '%d/%m/%Y', '%d\%m\%Y', '%d %m %Y']
 
     for f in formats:
         try:
-            date = datetime.strptime(datestring, f)       
+            date = datetime.strptime(datestring, f)
             return date
         except:
             pass
     return None
 
+
 def convertTime(timestring):
     formats = ['%H:%M', '%H.%M', '%H %M', '%H,%M']
 
     for f in formats:
         try:
-            time = datetime.strptime(timestring, f)       
-            return {'h':time.hour, 'm':time.minute}
+            time = datetime.strptime(timestring, f)
+            return {'h': time.hour, 'm': time.minute}
         except:
             pass
     return None

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/analytics.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/analytics.py b/Allura/allura/lib/widgets/analytics.py
index cf31715..c2e85c5 100644
--- a/Allura/allura/lib/widgets/analytics.py
+++ b/Allura/allura/lib/widgets/analytics.py
@@ -17,8 +17,9 @@
 
 import ew
 
+
 class GoogleAnalytics(ew.Widget):
-    template='jinja:allura:templates/widgets/analytics.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/analytics.html'
+    defaults = dict(
         ew.Widget.defaults,
         accounts=['UA-XXXXX-X'])

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/auth_widgets.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/auth_widgets.py b/Allura/allura/lib/widgets/auth_widgets.py
index 93e8826..f6c1a28 100644
--- a/Allura/allura/lib/widgets/auth_widgets.py
+++ b/Allura/allura/lib/widgets/auth_widgets.py
@@ -28,9 +28,10 @@ from .forms import ForgeForm
 from allura.lib import plugin
 from allura import model as M
 
+
 class LoginForm(ForgeForm):
-    submit_text='Login'
-    style='wide'
+    submit_text = 'Login'
+    style = 'wide'
 
     @property
     def fields(self):
@@ -40,7 +41,8 @@ class LoginForm(ForgeForm):
         ]
         if plugin.AuthenticationProvider.get(request).forgotten_password_process:
             # only show link if auth provider has method of recovering password
-            fields.append(ew.HTMLField(name='link', text='<a href="forgotten_password">Forgot password?</a>'))
+            fields.append(
+                ew.HTMLField(name='link', text='<a href="forgotten_password">Forgot password?</a>'))
         return fields
 
     class hidden_fields(ew_core.NameList):
@@ -49,7 +51,8 @@ class LoginForm(ForgeForm):
     @validator
     def validate(self, value, state=None):
         try:
-            value['username'] = plugin.AuthenticationProvider.get(request).login()
+            value['username'] = plugin.AuthenticationProvider.get(
+                request).login()
         except exc.HTTPUnauthorized:
             msg = 'Invalid login'
             raise Invalid(
@@ -60,8 +63,8 @@ class LoginForm(ForgeForm):
 
 
 class ForgottenPasswordForm(ForgeForm):
-    submit_text='Recover password'
-    style='wide'
+    submit_text = 'Recover password'
+    style = 'wide'
 
     class fields(ew_core.NameList):
         email = ew.TextField(label='Your e-mail')
@@ -73,6 +76,6 @@ class ForgottenPasswordForm(ForgeForm):
         user = M.User.by_email_address(email)
         if user is None or not email_record.confirmed:
             raise Invalid(
-                    'Unable to recover password for this email',
-                    {'email': email}, None)
+                'Unable to recover password for this email',
+                {'email': email}, None)
         return value

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c93733ac/Allura/allura/lib/widgets/discuss.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/discuss.py b/Allura/allura/lib/widgets/discuss.py
index 151a0c2..30dc128 100644
--- a/Allura/allura/lib/widgets/discuss.py
+++ b/Allura/allura/lib/widgets/discuss.py
@@ -27,34 +27,44 @@ from allura.lib.widgets import form_fields as ffw
 from allura.lib.widgets import forms as ff
 from allura import model as M
 
+
 class NullValidator(fev.FancyValidator):
-    perform_validation=True
+    perform_validation = True
+
+    def _to_python(self, value, state):
+        return value
 
-    def _to_python(self, value, state): return value
-    def _from_python(self, value, state): return value
+    def _from_python(self, value, state):
+        return value
 
 # Discussion forms
+
+
 class ModerateThread(ff.CsrfForm):
-    defaults=dict(
+    defaults = dict(
         ew.SimpleForm.defaults,
         submit_text=None)
+
     class buttons(ew_core.NameList):
-        delete=ew.SubmitButton(label='Delete Thread')
+        delete = ew.SubmitButton(label='Delete Thread')
+
 
 class ModeratePost(ew.SimpleForm):
-    template='jinja:allura:templates/widgets/moderate_post.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/moderate_post.html'
+    defaults = dict(
         ew.SimpleForm.defaults,
         submit_text=None)
 
+
 class FlagPost(ew.SimpleForm):
-    template='jinja:allura:templates/widgets/flag_post.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/flag_post.html'
+    defaults = dict(
         ew.SimpleForm.defaults,
         submit_text=None)
 
+
 class AttachPost(ff.ForgeForm):
-    defaults=dict(
+    defaults = dict(
         ff.ForgeForm.defaults,
         submit_text='Attach File',
         enctype='multipart/form-data')
@@ -62,17 +72,21 @@ class AttachPost(ff.ForgeForm):
     @property
     def fields(self):
         fields = [
-            ew.InputField(name='file_info', field_type='file', label='New Attachment')
+            ew.InputField(name='file_info', field_type='file',
+                          label='New Attachment')
         ]
         return fields
 
+
 class ModeratePosts(ew.SimpleForm):
-    template='jinja:allura:templates/widgets/moderate_posts.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/moderate_posts.html'
+    defaults = dict(
         ew.SimpleForm.defaults,
         submit_text=None)
+
     def resources(self):
-        for r in super(ModeratePosts, self).resources(): yield r
+        for r in super(ModeratePosts, self).resources():
+            yield r
         yield ew.JSScript('''
       (function($){
           var tbl = $('form table');
@@ -86,8 +100,9 @@ class ModeratePosts(ew.SimpleForm):
           });
       }(jQuery));''')
 
+
 class PostFilter(ff.ForgeForm):
-    defaults=dict(
+    defaults = dict(
         ew.SimpleForm.defaults,
         submit_text=None,
         method='GET')
@@ -96,22 +111,24 @@ class PostFilter(ff.ForgeForm):
             name='page',
             validator=fev.Int()),
         ew.FieldSet(label='Post Filter', fields=[
-                ew.SingleSelectField(
+            ew.SingleSelectField(
                     name='status',
                     label='Show posts with status',
                     options=[
                         ew.Option(py_value='-', label='Any'),
                         ew.Option(py_value='spam', label='Spam'),
-                        ew.Option(py_value='pending', label='Pending moderation'),
+                        ew.Option(py_value='pending',
+                                  label='Pending moderation'),
                         ew.Option(py_value='ok', label='Ok')],
                     if_missing='pending'),
-                ew.IntField(name='flag',
-                            label='Show posts with at least "n" flags',
-                            css_class='text',
-                            if_missing=0),
-                ew.SubmitButton(label='Filter Posts')
-                ])
-        ]
+            ew.IntField(name='flag',
+                        label='Show posts with at least "n" flags',
+                        css_class='text',
+                        if_missing=0),
+            ew.SubmitButton(label='Filter Posts')
+        ])
+    ]
+
 
 class TagPost(ff.ForgeForm):
 
@@ -123,15 +140,17 @@ class TagPost(ff.ForgeForm):
         result['buttons'] = [submit_button]
         return result
 
-    fields=[ffw.LabelEdit(label='Labels',name='labels', className='title')]
+    fields = [ffw.LabelEdit(label='Labels', name='labels', className='title')]
 
     def resources(self):
-        for r in ffw.LabelEdit(name='labels').resources(): yield r
+        for r in ffw.LabelEdit(name='labels').resources():
+            yield r
+
 
 class EditPost(ff.ForgeForm):
-    template='jinja:allura:templates/widgets/edit_post.html'
-    antispam=True
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/edit_post.html'
+    antispam = True
+    defaults = dict(
         ff.ForgeForm.defaults,
         show_subject=False,
         value=None,
@@ -142,12 +161,13 @@ class EditPost(ff.ForgeForm):
         fields = ew_core.NameList()
         fields.append(ffw.MarkdownEdit(
             name='text',
-            attrs={'style':'height:7em; width:97%'}))
+            attrs={'style': 'height:7em; width:97%'}))
         fields.append(ew.HiddenField(name='forum', if_missing=None))
         if ew_core.widget_context.widget:
             # we are being displayed
             if ew_core.widget_context.render_context.get('show_subject', self.show_subject):
-                fields.append(ew.TextField(name='subject',attrs=dict(style="width:97%")))
+                fields.append(
+                    ew.TextField(name='subject', attrs=dict(style="width:97%")))
         else:
             # We are being validated
             validator = fev.UnicodeString(not_empty=True, if_missing='')
@@ -156,8 +176,10 @@ class EditPost(ff.ForgeForm):
         return fields
 
     def resources(self):
-        for r in ew.TextField(name='subject').resources(): yield r
-        for r in ffw.MarkdownEdit(name='text').resources(): yield r
+        for r in ew.TextField(name='subject').resources():
+            yield r
+        for r in ffw.MarkdownEdit(name='text').resources():
+            yield r
         yield ew.JSScript('''$(document).ready(function () {
             $("a.attachment_form_add_button").click(function(evt){
                 $(this).hide();
@@ -171,48 +193,57 @@ class EditPost(ff.ForgeForm):
             });
          });''')
 
+
 class NewTopicPost(EditPost):
-    template='jinja:allura:templates/widgets/new_topic_post.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/new_topic_post.html'
+    defaults = dict(
         EditPost.defaults,
-        show_subject = True,
+        show_subject=True,
         forums=None)
 
+
 class _ThreadsTable(ew.TableField):
-    template='jinja:allura:templates/widgets/threads_table.html'
+    template = 'jinja:allura:templates/widgets/threads_table.html'
+
     class fields(ew_core.NameList):
-        _id=ew.HiddenField(validator=V.Ming(M.Thread))
-        subscription=ew.Checkbox(suppress_label=True)
-        subject=ffw.DisplayOnlyField(label='Topic')
-        url=ffw.DisplayOnlyField()
-        num_replies=ffw.DisplayOnlyField(label='Posts')
-        num_views=ffw.DisplayOnlyField(label='Views')
-        last_post=ffw.DisplayOnlyField(label='Last Post')
+        _id = ew.HiddenField(validator=V.Ming(M.Thread))
+        subscription = ew.Checkbox(suppress_label=True)
+        subject = ffw.DisplayOnlyField(label='Topic')
+        url = ffw.DisplayOnlyField()
+        num_replies = ffw.DisplayOnlyField(label='Posts')
+        num_views = ffw.DisplayOnlyField(label='Views')
+        last_post = ffw.DisplayOnlyField(label='Last Post')
+
 
 class SubscriptionForm(ew.SimpleForm):
-    template='jinja:allura:templates/widgets/subscription_form.html'
-    value=None
-    threads=None
-    show_subject=False
-    allow_create_thread=False
-    limit=None
-    page=0
-    count=0
-    submit_text='Update Subscriptions'
-    params=['value', 'threads', 'limit', 'page', 'count',
-            'show_subject', 'allow_create_thread']
+    template = 'jinja:allura:templates/widgets/subscription_form.html'
+    value = None
+    threads = None
+    show_subject = False
+    allow_create_thread = False
+    limit = None
+    page = 0
+    count = 0
+    submit_text = 'Update Subscriptions'
+    params = ['value', 'threads', 'limit', 'page', 'count',
+              'show_subject', 'allow_create_thread']
+
     class fields(ew_core.NameList):
-        page_list=ffw.PageList()
-        page_size=ffw.PageSize()
-        threads=_ThreadsTable()
+        page_list = ffw.PageList()
+        page_size = ffw.PageSize()
+        threads = _ThreadsTable()
+
     def resources(self):
-        for r in super(SubscriptionForm, self).resources(): yield r
+        for r in super(SubscriptionForm, self).resources():
+            yield r
         yield ew.JSScript('''
         $(window).load(function () {
             $('tbody').children(':even').addClass('even');
         });''')
 
 # Widgets
+
+
 class HierWidget(ew_core.Widget):
     widgets = {}
 
@@ -228,37 +259,41 @@ class HierWidget(ew_core.Widget):
             for r in w.resources():
                 yield r
 
+
 class Attachment(ew_core.Widget):
-    template='jinja:allura:templates/widgets/attachment.html'
-    params=['value', 'post']
-    value=None
-    post=None
+    template = 'jinja:allura:templates/widgets/attachment.html'
+    params = ['value', 'post']
+    value = None
+    post = None
+
 
 class DiscussionHeader(HierWidget):
-    template='jinja:allura:templates/widgets/discussion_header.html'
-    params=['value']
-    value=None
-    widgets=dict(
+    template = 'jinja:allura:templates/widgets/discussion_header.html'
+    params = ['value']
+    value = None
+    widgets = dict(
         edit_post=EditPost(submit_text='New Thread'))
 
+
 class ThreadHeader(HierWidget):
-    template='jinja:allura:templates/widgets/thread_header.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/thread_header.html'
+    defaults = dict(
         HierWidget.defaults,
         value=None,
         page=None,
         limit=None,
         count=None,
         show_moderate=False)
-    widgets=dict(
+    widgets = dict(
         page_list=ffw.PageList(),
         page_size=ffw.PageSize(),
         moderate_thread=ModerateThread(),
         tag_post=TagPost())
 
+
 class Post(HierWidget):
-    template='jinja:allura:templates/widgets/post_widget.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/post_widget.html'
+    defaults = dict(
         HierWidget.defaults,
         value=None,
         indent=0,
@@ -266,13 +301,15 @@ class Post(HierWidget):
         limit=25,
         show_subject=False,
         suppress_promote=False)
-    widgets=dict(
+    widgets = dict(
         moderate_post=ModeratePost(),
         edit_post=EditPost(submit_text='Post'),
         attach_post=AttachPost(submit_text='Attach'),
         attachment=Attachment())
+
     def resources(self):
-        for r in super(Post, self).resources(): yield r
+        for r in super(Post, self).resources():
+            yield r
         for w in self.widgets.itervalues():
             for r in w.resources():
                 yield r
@@ -362,9 +399,10 @@ class Post(HierWidget):
         }());
         ''')
 
+
 class PostThread(ew_core.Widget):
-    template='jinja:allura:templates/widgets/post_thread.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/post_thread.html'
+    defaults = dict(
         ew_core.Widget.defaults,
         value=None,
         indent=0,
@@ -375,10 +413,11 @@ class PostThread(ew_core.Widget):
         parent=None,
         children=None)
 
+
 class Thread(HierWidget):
-    template='jinja:allura:templates/widgets/thread_widget.html'
-    name='thread'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/thread_widget.html'
+    name = 'thread'
+    defaults = dict(
         HierWidget.defaults,
         value=None,
         page=None,
@@ -386,14 +425,16 @@ class Thread(HierWidget):
         count=None,
         show_subject=False,
         new_post_text='+ New Comment')
-    widgets=dict(
+    widgets = dict(
         page_list=ffw.PageList(),
         thread_header=ThreadHeader(),
         post_thread=PostThread(),
         post=Post(),
         edit_post=EditPost(submit_text='Submit'))
+
     def resources(self):
-        for r in super(Thread, self).resources(): yield r
+        for r in super(Thread, self).resources():
+            yield r
         for w in self.widgets.itervalues():
             for r in w.resources():
                 yield r
@@ -441,18 +482,20 @@ class Thread(HierWidget):
         });
         ''')
 
+
 class Discussion(HierWidget):
-    template='jinja:allura:templates/widgets/discussion.html'
-    defaults=dict(
+    template = 'jinja:allura:templates/widgets/discussion.html'
+    defaults = dict(
         HierWidget.defaults,
         value=None,
         threads=None,
         show_subject=False,
         allow_create_thread=False)
-    widgets=dict(
+    widgets = dict(
         discussion_header=DiscussionHeader(),
         edit_post=EditPost(submit_text='New Topic'),
         subscription_form=SubscriptionForm())
 
     def resources(self):
-        for r in super(Discussion, self).resources(): yield r
+        for r in super(Discussion, self).resources():
+            yield r