~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/apps/branch.py

  • Committer: Max Kanat-Alexander
  • Date: 2010-11-11 03:30:43 UTC
  • Revision ID: mkanat@bugzilla.org-20101111033043-rmxnwrkxfkcrqq60
Tags: loggerhead-1.18
ReleaseĀ 1.18

Show diffs side-by-side

added added

removed removed

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