~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/apps/branch.py

  • Committer: Andrew Bennetts
  • Date: 2011-06-28 13:13:05 UTC
  • Revision ID: andrew.bennetts@canonical.com-20110628131305-skv4h2z02z3iw8wc
Extract some refactoring of TemplatedBranchView and BranchWSGIApp from the jsonify branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008-2011 Canonical Ltd.
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
#
 
17
"""The WSGI application for serving a Bazaar branch."""
 
18
 
 
19
import logging
 
20
import urllib
 
21
import sys
 
22
 
 
23
import bzrlib.branch
 
24
import bzrlib.errors
 
25
import bzrlib.lru_cache
 
26
 
 
27
from paste import request
 
28
from paste import httpexceptions
 
29
 
 
30
from loggerhead.apps import static_app
 
31
from loggerhead.controllers.annotate_ui import AnnotateUI
 
32
from loggerhead.controllers.view_ui import ViewUI
 
33
from loggerhead.controllers.atom_ui import AtomUI
 
34
from loggerhead.controllers.changelog_ui import ChangeLogUI
 
35
from loggerhead.controllers.diff_ui import DiffUI
 
36
from loggerhead.controllers.download_ui import DownloadUI
 
37
from loggerhead.controllers.filediff_ui import FileDiffUI
 
38
from loggerhead.controllers.inventory_ui import InventoryUI
 
39
from loggerhead.controllers.revision_ui import RevisionUI
 
40
from loggerhead.controllers.revlog_ui import RevLogUI
 
41
from loggerhead.controllers.search_ui import SearchUI
 
42
from loggerhead.history import History
 
43
from loggerhead import util
 
44
 
 
45
 
 
46
_DEFAULT = object()
 
47
 
 
48
class BranchWSGIApp(object):
 
49
 
 
50
    def __init__(self, branch, friendly_name=None, config={},
 
51
                 graph_cache=None, branch_link=None, is_root=False,
 
52
                 served_url=_DEFAULT, use_cdn=False):
 
53
        self.branch = branch
 
54
        self._config = config
 
55
        self.friendly_name = friendly_name
 
56
        self.branch_link = branch_link  # Currently only used in Launchpad
 
57
        self.log = logging.getLogger('loggerhead.%s' % (friendly_name,))
 
58
        if graph_cache is None:
 
59
            graph_cache = bzrlib.lru_cache.LRUCache(10)
 
60
        self.graph_cache = graph_cache
 
61
        self.is_root = is_root
 
62
        self.served_url = served_url
 
63
        self.use_cdn = use_cdn
 
64
 
 
65
    def get_history(self):
 
66
        file_cache = None
 
67
        revinfo_disk_cache = None
 
68
        cache_path = self._config.get('cachepath', None)
 
69
        if cache_path is not None:
 
70
            # Only import the cache if we're going to use it.
 
71
            # This makes sqlite optional
 
72
            try:
 
73
                from loggerhead.changecache import (
 
74
                    FileChangeCache, RevInfoDiskCache)
 
75
            except ImportError:
 
76
                self.log.debug("Couldn't load python-sqlite,"
 
77
                               " continuing without using a cache")
 
78
            else:
 
79
                file_cache = FileChangeCache(cache_path)
 
80
                revinfo_disk_cache = RevInfoDiskCache(cache_path)
 
81
        return History(
 
82
            self.branch, self.graph_cache, file_cache=file_cache,
 
83
            revinfo_disk_cache=revinfo_disk_cache, cache_key=self.friendly_name)
 
84
 
 
85
    def url(self, *args, **kw):
 
86
        if isinstance(args[0], list):
 
87
            args = args[0]
 
88
        qs = []
 
89
        for k, v in kw.iteritems():
 
90
            if v is not None:
 
91
                qs.append('%s=%s' % (k, urllib.quote(v)))
 
92
        qs = '&'.join(qs)
 
93
        path_info = urllib.quote(
 
94
            unicode('/'.join(args)).encode('utf-8'), safe='/~:')
 
95
        if qs:
 
96
            path_info += '?' + qs
 
97
        return self._url_base + path_info
 
98
 
 
99
    def absolute_url(self, *args, **kw):
 
100
        rel_url = self.url(*args, **kw)
 
101
        return request.resolve_relative_url(rel_url, self._environ)
 
102
 
 
103
    def context_url(self, *args, **kw):
 
104
        kw = util.get_context(**kw)
 
105
        return self.url(*args, **kw)
 
106
 
 
107
    def static_url(self, path):
 
108
        return self._static_url_base + path
 
109
 
 
110
    def yui_url(self, path):
 
111
        if self.use_cdn:
 
112
            base = 'http://yui.yahooapis.com/3.0.0pr2/build/'
 
113
        else:
 
114
            base = self.static_url('/static/javascript/yui/build/')
 
115
        return base + path
 
116
 
 
117
    controllers_dict = {
 
118
        '+filediff': FileDiffUI,
 
119
        '+revlog': RevLogUI,
 
120
        'annotate': AnnotateUI,
 
121
        'atom': AtomUI,
 
122
        'changes': ChangeLogUI,
 
123
        'diff': DiffUI,
 
124
        'download': DownloadUI,
 
125
        'files': InventoryUI,
 
126
        'revision': RevisionUI,
 
127
        'search': SearchUI,
 
128
        'view': ViewUI,
 
129
        }
 
130
 
 
131
    def last_updated(self):
 
132
        h = self.get_history()
 
133
        change = h.get_changes([h.last_revid])[0]
 
134
        return change.date
 
135
 
 
136
    def public_branch_url(self):
 
137
        return self.branch.get_config().get_user_option('public_branch')
 
138
 
 
139
    def lookup_app(self, environ):
 
140
        # Check again if the branch is blocked from being served, this is
 
141
        # mostly for tests. It's already checked in apps/transport.py
 
142
        if self.branch.get_config().get_user_option('http_serve') == 'False':
 
143
            raise httpexceptions.HTTPNotFound()
 
144
        self._url_base = environ['SCRIPT_NAME']
 
145
        self._static_url_base = environ.get('loggerhead.static.url')
 
146
        if self._static_url_base is None:
 
147
            self._static_url_base = self._url_base
 
148
        self._environ = environ
 
149
        if self.served_url is _DEFAULT:
 
150
            public_branch = self.public_branch_url()
 
151
            if public_branch is not None:
 
152
                self.served_url = public_branch
 
153
            else:
 
154
                # Loggerhead only supports serving .bzr/ on local branches, so
 
155
                # we shouldn't suggest something that won't work.
 
156
                try:
 
157
                    util.local_path_from_url(self.branch.base)
 
158
                    self.served_url = self.url([])
 
159
                except bzrlib.errors.InvalidURL:
 
160
                    self.served_url = None
 
161
        path = request.path_info_pop(environ)
 
162
        if not path:
 
163
            raise httpexceptions.HTTPMovedPermanently(
 
164
                self.absolute_url('/changes'))
 
165
        if path == 'static':
 
166
            return static_app
 
167
        cls = self.controllers_dict.get(path)
 
168
        if cls is None:
 
169
            raise httpexceptions.HTTPNotFound()
 
170
        return cls(self, self.get_history)
 
171
 
 
172
    def app(self, environ, start_response):
 
173
        self.branch.lock_read()
 
174
        try:
 
175
            try:
 
176
                c = self.lookup_app(environ)
 
177
                return c(environ, start_response)
 
178
            except:
 
179
                environ['exc_info'] = sys.exc_info()
 
180
                environ['branch'] = self
 
181
                raise
 
182
        finally:
 
183
            self.branch.unlock()