Changeset - 52bbeb1e813f
[Not reviewed]
default
0 3 0
Marcin Kuzminski - 16 years ago 2010-05-21 02:44:40
marcin@python-works.com
Added universal cache invalidator for two cached functions.
added invalidation when repository was added or deleted, and another invalidation when there was a mercurial command involved.
3 files changed with 24 insertions and 8 deletions:
0 comments (0 inline, 0 general)
pylons_app/controllers/admin.py
Show inline comments
 
import logging
 
import os
 

	
 
from pylons import request, response, session, tmpl_context as c, url, app_globals as g
 
from pylons.controllers.util import abort, redirect
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.lib import auth
 
from pylons_app.model.forms import LoginForm
 
import formencode
 
import formencode.htmlfill as htmlfill
 
from pylons_app.model import meta
 
from pylons_app.model.db import Users, UserLogs
 
from webhelpers.paginate import Page
 
from pylons_app.lib.utils import check_repo, invalidate_cache
 

	
 
log = logging.getLogger(__name__)
 

	
 
class AdminController(BaseController):
 

	
 
    def __before__(self):
 
        c.admin_user = session.get('admin_user', False)
 
        c.admin_username = session.get('admin_username')
 
        
 
    def index(self):
 
        # Return a rendered template
 
        if request.POST:
 
            #import Login Form validator class
 
            login_form = LoginForm()
 

	
 
            try:
 
                c.form_result = login_form.to_python(dict(request.params))
 
                if auth.admin_auth(c.form_result['username'], c.form_result['password']):
 
                    session['admin_user'] = True
 
                    session['admin_username'] = c.form_result['username']
 
                    session.save()
 
                    return redirect(url('admin_home'))
 
                else:
 
                    raise formencode.Invalid('Login Error', None, None,
 
                                             error_dict={'username':'invalid login',
 
                                                         'password':'invalid password'})
 
                                      
 
            except formencode.Invalid, error:
 
                c.form_result = error.value
 
                c.form_errors = error.error_dict or {}
 
                html = render('admin/admin.html')
 

	
 
                return htmlfill.render(
 
                    html,
 
                    defaults=c.form_result,
 
                    encoding="UTF-8"
 
                )
 
        if c.admin_user:
 
            sa = meta.Session
 
                             
 
            users_log = sa.query(UserLogs)\
 
                .order_by(UserLogs.action_date.desc())
 
            p = int(request.params.get('page', 1))
 
            c.users_log = Page(users_log, page=p, items_per_page=10)
 
            c.log_data = render('admin/admin_log.html')
 
            if request.params.get('partial'):
 
                return c.log_data
 
        return render('admin/admin.html')
 

	
 
    def hgrc(self, dirname):
 
        filename = os.path.join(dirname, '.hg', 'hgrc')
 
        return filename
 

	
 
    def add_repo(self, new_repo):
 
        
 

	
 
        #extra check it can be add since it's the command
 
        if new_repo == '_admin':
 
            c.msg = 'DENIED'
 
            c.new_repo = ''
 
            return render('admin/add.html')
 

	
 
        new_repo = new_repo.replace(" ", "_")
 
        new_repo = new_repo.replace("-", "_")
 

	
 
        try:
 
            self._create_repo(new_repo)
 
            c.new_repo = new_repo
 
            c.msg = 'added repo'
 
            #clear our cached list for refresh with new repo
 
            invalidate_cache('repo_list_2')
 
            invalidate_cache('cached_repo_list')
 
        except Exception as e:
 
            c.new_repo = 'Exception when adding: %s' % new_repo
 
            c.msg = str(e)
 

	
 
        return render('admin/add.html')
 

	
 

	
 
    def _create_repo(self, repo_name):
 
        if repo_name in [None, '', 'add']:
 
            raise Exception('undefined repo_name of repo')
 
        repo_path = os.path.join(g.base_path, repo_name)
 
        if check_repo(repo_name, g.base_path):
 
            log.info('creating repo %s in %s', repo_name, repo_path)
 
            from vcs.backends.hg import MercurialRepository
 
            MercurialRepository(repo_path, create=True)        
 
                
pylons_app/lib/simplehg.py
Show inline comments
 
import os
 
from mercurial.hgweb import hgweb
 
from mercurial.hgweb.request import wsgiapplication
 
from pylons_app.lib.utils import make_ui
 
from pylons_app.lib.utils import make_ui, invalidate_cache
 
from pylons.controllers.util import abort
 
from webob.exc import HTTPNotFound
 
class SimpleHg(object):
 

	
 
    def __init__(self, application, config):
 
        self.application = application
 
        self.config = config
 
        
 
    def __call__(self, environ, start_response):
 
        if not is_mercurial(environ):
 
            return self.application(environ, start_response)
 
        else:
 
            try:
 
                repo_name = environ['PATH_INFO'].split('/')[1]
 
            except:
 
                return HTTPNotFound()(environ, start_response)
 
            
 
            #since we wrap into hgweb, just reset the path
 
            environ['PATH_INFO'] = '/'
 
            self.baseui = make_ui()
 
            self.basepath = self.baseui.configitems('paths')[0][1].replace('*', '')
 
            self.basepath = self.baseui.configitems('paths')[0][1]\
 
                                                            .replace('*', '')
 
            self.repo_path = os.path.join(self.basepath, repo_name)
 
            try:
 
                app = wsgiapplication(self._make_app)
 
            except Exception as e:
 
                return HTTPNotFound()(environ, start_response)
 
            
 
            """we know that some change was made to repositories and we should
 
            invalidate the cache to see the changes right away"""
 
            invalidate_cache('full_changelog', repo_name)
 
            return app(environ, start_response)            
 

	
 
    def _make_app(self):
 
        hgserve = hgweb(self.repo_path)
 
        return  self.load_web_settings(hgserve)
 
        
 
                
 
    def load_web_settings(self, hgserve):
 
        repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
 
        #set the global ui for hgserve
 
        hgserve.repo.ui = self.baseui
 
        
 
        if repoui:
 
            #set the repository based config
 
            hgserve.repo.ui = repoui
 
            
 
        return hgserve
 
                                
 
def is_mercurial(environ):
 
    """
 
    Returns True if request's target is mercurial server - header
 
    ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
 
    """
 
    http_accept = environ.get('HTTP_ACCEPT')
 
    if http_accept and http_accept.startswith('application/mercurial'):
 
        return True
 
    return False
 

	
 

	
pylons_app/lib/utils.py
Show inline comments
 
import os
 
import logging
 
from mercurial import ui, config, hg
 
from mercurial.error import RepoError
 
log = logging.getLogger(__name__)
 

	
 

	
 
def get_repo_slug(request):
 
    path_info = request.environ.get('PATH_INFO')
 
    uri_lst = path_info.split('/')   
 
    repo_name = uri_lst[1]
 
    return repo_name
 

	
 
def is_mercurial(environ):
 
    """
 
    Returns True if request's target is mercurial server - header
 
    ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
 
    """
 
    http_accept = environ.get('HTTP_ACCEPT')
 
    if http_accept and http_accept.startswith('application/mercurial'):
 
        return True
 
    return False
 

	
 
def check_repo_dir(paths):
 
    repos_path = paths[0][1].split('/')
 
    if repos_path[-1] in ['*', '**']:
 
        repos_path = repos_path[:-1]
 
    if repos_path[0] != '/':
 
        repos_path[0] = '/'
 
    if not os.path.isdir(os.path.join(*repos_path)):
 
        raise Exception('Not a valid repository in %s' % paths[0][1])
 

	
 
def check_repo(repo_name, base_path):
 

	
 
    repo_path = os.path.join(base_path, repo_name)
 

	
 
    try:
 
        r = hg.repository(ui.ui(), repo_path)
 
        hg.verify(r)
 
        #here we hnow that repo exists it was verified
 
        log.info('%s repo is already created', repo_name)
 
        return False
 
        #raise Exception('Repo exists')
 
    except RepoError:
 
        log.info('%s repo is free for creation', repo_name)
 
        #it means that there is no valid repo there...
 
        return True
 
                
 
def make_ui(path='hgwebdir.config', checkpaths=True):        
 
    """
 
    A funcion that will read python rc files and make an ui from read options
 
    
 
    @param path: path to mercurial config file
 
    """
 
    if not os.path.isfile(path):
 
        log.error('Unable to read config file %s' % path)
 
        return False
 
    #propagated from mercurial documentation
 
    sections = [
 
                'alias',
 
                'auth',
 
                'decode/encode',
 
                'defaults',
 
                'diff',
 
                'email',
 
                'extensions',
 
                'format',
 
                'merge-patterns',
 
                'merge-tools',
 
                'hooks',
 
                'http_proxy',
 
                'smtp',
 
                'patch',
 
                'paths',
 
                'profiling',
 
                'server',
 
                'trusted',
 
                'ui',
 
                'web',
 
                ]
 

	
 
    baseui = ui.ui()
 
    cfg = config.config()
 
    cfg.read(path)
 
    if checkpaths:check_repo_dir(cfg.items('paths'))
 

	
 
    for section in sections:
 
        for k, v in cfg.items(section):
 
            baseui.setconfig(section, k, v)
 
    
 
    return baseui
 

	
 
def invalidate_cache(name):
 
def invalidate_cache(name, *args):
 
    from beaker.cache import region_invalidate
 
    if name == 'repo_list_2':
 
        log.info('INVALIDATING CACHE FOR %s', name)
 
        from pylons_app.lib.base import _get_repos
 
        #clear our cached list for refresh with new repo
 
        region_invalidate(_get_repos, None, 'repo_list_2')
 
    
 
    """propaget our arguments to make sure invalidation works. First
 
    argument has to be the name of cached func name give to cache decorator
 
    without that the invalidation would not work"""
 
    tmp = [name]
 
    tmp.extend(args)
 
    args = tuple(tmp)
 
    
 
    if name == 'cached_repo_list':
 
        from pylons_app.lib.base import _get_repos_cached
 
        region_invalidate(_get_repos_cached, None, *args)
 
        
 
    if name == 'full_changelog':
 
        from pylons_app.controllers.changelog import _full_changelog_cached
 
        region_invalidate(_full_changelog_cached, None, *args)
 

	
 
from vcs.backends.base import BaseChangeset
 
from vcs.utils.lazy import LazyProperty
 
class EmptyChangeset(BaseChangeset):
 
    
 
    revision = -1
 

	
 
    @LazyProperty
 
    def raw_id(self):
 
        """
 
        Returns raw string identifing this changeset, useful for web
 
        representation.
 
        """
 
        return '0' * 12
 

	
0 comments (0 inline, 0 general)