Merge lp:~jelmer/loggerhead/controller-hook into lp:loggerhead

Proposed by Jelmer Vernooij
Status: Merged
Merged at revision: 458
Proposed branch: lp:~jelmer/loggerhead/controller-hook
Merge into: lp:loggerhead
Diff against target: 129 lines (+60/-7)
3 files modified
NEWS (+3/-0)
loggerhead/apps/branch.py (+28/-3)
loggerhead/tests/test_controllers.py (+29/-4)
To merge this branch: bzr merge lp:~jelmer/loggerhead/controller-hook
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+80972@code.launchpad.net

Description of the change

Add a hook 'controller' to BranchWSGIApp, allowing other bzr plugins to provide custom branch-specific controllers.

This can be used for several things. It makes it possible to e.g. have bzr stats provide a custom page with branch statistics.

bzr-git/bzr-svn can hook into loggerhead and allow access to bzr branches using their respective protocols. I have a working version of this with bzr-git, using the branch at lp:~jelmer/bzr-git/loggerhead-integration:

$ cd /my/bzr/branch
$ echo http_git=True >> .bzr/branch/branch.conf
$ bzr serve --http &
$ git clone git://localhost:8080/ gitclone

To post a comment you must log in.
460. By Jelmer Vernooij

Add bug #

Revision history for this message
Martin Pool (mbp) wrote :

  vote approve

review: Approve
461. By Jelmer Vernooij

Fix comment.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2011-09-08 00:33:28 +0000
3+++ NEWS 2011-11-02 01:59:24 +0000
4@@ -42,6 +42,9 @@
5 - Fix support for displaying foreign revision ids.
6 (Jelmer Vernooij, #736026)
7
8+ - Add hook 'controller' to BranchWSGIApp, allowing other bzr plugins
9+ to provide custom branch-specific controllers. (Jelmer Vernooij, #585822)
10+
11
12 1.18.1 [24Mar2011]
13 ------------------
14
15=== modified file 'loggerhead/apps/branch.py'
16--- loggerhead/apps/branch.py 2011-08-17 11:04:00 +0000
17+++ loggerhead/apps/branch.py 2011-11-02 01:59:24 +0000
18@@ -22,6 +22,7 @@
19
20 import bzrlib.branch
21 import bzrlib.errors
22+from bzrlib.hooks import Hooks
23 import bzrlib.lru_cache
24
25 from paste import request
26@@ -155,6 +156,10 @@
27 self.served_url = self.url([])
28 except bzrlib.errors.InvalidURL:
29 self.served_url = None
30+ for hook in self.hooks['controller']:
31+ controller = hook(self, environ)
32+ if controller is not None:
33+ return controller
34 path = request.path_info_pop(environ)
35 if not path:
36 raise httpexceptions.HTTPMovedPermanently(
37@@ -165,9 +170,9 @@
38 environ['loggerhead.as_json'] = True
39 path = request.path_info_pop(environ)
40 cls = self.controllers_dict.get(path)
41- if cls is None:
42- raise httpexceptions.HTTPNotFound()
43- return cls(self, self.get_history)
44+ if cls is not None:
45+ return cls(self, self.get_history)
46+ raise httpexceptions.HTTPNotFound()
47
48 def app(self, environ, start_response):
49 self.branch.lock_read()
50@@ -181,3 +186,23 @@
51 raise
52 finally:
53 self.branch.unlock()
54+
55+
56+class BranchWSGIAppHooks(Hooks):
57+ """A dictionary mapping hook name to a list of callables for WSGI app branch hooks.
58+ """
59+
60+ def __init__(self):
61+ """Create the default hooks.
62+ """
63+ Hooks.__init__(self, "bzrlib.plugins.loggerhead.apps.branch",
64+ "BranchWSGIApp.hooks")
65+ self.add_hook('controller',
66+ "Invoked when looking for the controller to use for a "
67+ "branch subpage. The api signature is (branch_app, environ)."
68+ "If a hook can provide a controller, it should return one, "
69+ "as a standard WSGI app. If it can't provide a controller, "
70+ "it should return None", (1, 19))
71+
72+
73+BranchWSGIApp.hooks = BranchWSGIAppHooks()
74
75=== modified file 'loggerhead/tests/test_controllers.py'
76--- loggerhead/tests/test_controllers.py 2011-09-02 09:43:08 +0000
77+++ loggerhead/tests/test_controllers.py 2011-11-02 01:59:24 +0000
78@@ -1,11 +1,7 @@
79-import simplejson
80-
81 from loggerhead.apps.branch import BranchWSGIApp
82 from loggerhead.controllers.annotate_ui import AnnotateUI
83 from loggerhead.controllers.inventory_ui import InventoryUI
84-from loggerhead.controllers.revision_ui import RevisionUI
85 from loggerhead.tests.test_simple import BasicTests, consume_app
86-from loggerhead import util
87
88
89 class TestInventoryUI(BasicTests):
90@@ -168,6 +164,7 @@
91 annotated = annotate_info['annotated']
92 self.assertEqual(0, len(annotated))
93
94+
95 class TestFileDiffUI(BasicTests):
96
97 def make_branch_app_for_filediff_ui(self):
98@@ -233,3 +230,31 @@
99 revlog_ui = branch_app.lookup_app(env)
100 self.assertOkJsonResponse(revlog_ui, env)
101
102+
103+class TestControllerHooks(BasicTests):
104+
105+ def test_dummy_hook(self):
106+ return
107+ # A hook that returns None doesn't influence the searching for
108+ # a controller.
109+ env = {'SCRIPT_NAME': '', 'PATH_INFO': '/custom'}
110+ myhook = lambda app, environ: None
111+ branch = self.make_branch('.')
112+ self.addCleanup(branch.lock_read().unlock)
113+ app = self.make_branch_app(branch)
114+ self.addCleanup(BranchWSGIApp.hooks.uninstall_named_hook, 'controller',
115+ 'captain hook')
116+ BranchWSGIApp.hooks.install_named_hook('controller', myhook, "captain hook")
117+ self.assertRaises(KeyError, app.lookup_app, env)
118+
119+ def test_working_hook(self):
120+ # A hook can provide an app to use for a particular request.
121+ env = {'SCRIPT_NAME': '', 'PATH_INFO': '/custom'}
122+ myhook = lambda app, environ: "I am hooked"
123+ branch = self.make_branch('.')
124+ self.addCleanup(branch.lock_read().unlock)
125+ app = self.make_branch_app(branch)
126+ self.addCleanup(BranchWSGIApp.hooks.uninstall_named_hook, 'controller',
127+ 'captain hook')
128+ BranchWSGIApp.hooks.install_named_hook('controller', myhook, "captain hook")
129+ self.assertEquals("I am hooked", app.lookup_app(env))

Subscribers

People subscribed via source and target branches