Merge lp:~mkanat/loggerhead/launchpad into lp:~launchpad-pqm/loggerhead/devel

Proposed by Max Kanat-Alexander
Status: Merged
Merged at revision: 178
Proposed branch: lp:~mkanat/loggerhead/launchpad
Merge into: lp:~launchpad-pqm/loggerhead/devel
Diff against target: 598 lines (+211/-101)
15 files modified
NEWS (+2/-2)
__init__.py (+19/-7)
info.py (+13/-0)
loggerhead/__init__.py (+1/-1)
loggerhead/apps/branch.py (+2/-0)
loggerhead/controllers/annotate_ui.py (+54/-0)
loggerhead/controllers/view_ui.py (+33/-48)
loggerhead/highlight.py (+4/-2)
loggerhead/static/css/view.css (+14/-12)
loggerhead/templatefunctions.py (+2/-2)
loggerhead/templates/inventory.pt (+8/-8)
loggerhead/templates/revision.pt (+2/-2)
loggerhead/templates/view.pt (+30/-17)
loggerhead/tests/__init__.py (+26/-0)
loggerhead/trace.py (+1/-0)
To merge this branch: bzr merge lp:~mkanat/loggerhead/launchpad
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle Approve
Launchpad PQM Bot Pending
Review via email: mp+42338@code.launchpad.net

Description of the change

Brings in the fix for Bug 568148, which gives us some important performance improvements in the default file view that we believe will make a significant difference for Launchpad. In addition, this syncs up codebrowse with the 1.18 stable branch of loggerhead (although that doesn't bring in any code that will actually affect LP, as far as I know).

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I approve of this branch, but can't set the mp status. Oh well. Do you want me to merge it too?

review: Approve
Revision history for this message
Max Kanat-Alexander (mkanat) wrote :

Yeah, if you could merge it, that would be great.

lp:~mkanat/loggerhead/launchpad updated
178. By Launchpad PQM Bot

[r=mwhudson] bring in the fix for bug 568148,
 which stops the default file content view being the annotated view

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-05-10 13:48:25 +0000
3+++ NEWS 2010-12-01 08:41:33 +0000
4@@ -1,8 +1,8 @@
5 What's changed in loggerhead?
6 =============================
7
8-dev [future]
9-------------
10+1.18 [10Nov2010]
11+----------------
12
13 - Syntax highlighting is no longer applied for files greater than 512K,
14 reducing codebrowse.launchpad.net overloading.
15
16=== modified file '__init__.py'
17--- __init__.py 2010-04-22 08:52:59 +0000
18+++ __init__.py 2010-12-01 08:41:33 +0000
19@@ -30,15 +30,16 @@
20 starts a web server to browse the contents of a branch.
21 """
22
23-version_info = (1, 17, 0)
24+from info import (
25+ bzr_plugin_version as version_info,
26+ bzr_compatible_versions,
27+ )
28
29 if __name__ == 'bzrlib.plugins.loggerhead':
30 import bzrlib
31 from bzrlib.api import require_any_api
32
33- require_any_api(bzrlib, [
34- (1, 13, 0), (1, 15, 0), (1, 16, 0), (1, 17, 0), (1, 18, 0),
35- (2, 0, 0), (2, 1, 0), (2, 2, 0)])
36+ require_any_api(bzrlib, bzr_compatible_versions)
37
38 # NB: Normally plugins should lazily load almost everything, but this
39 # seems reasonable to have in-line here: bzrlib.commands and options are
40@@ -69,10 +70,9 @@
41 logging.getLogger('simpleTAL').addHandler(handler)
42 logging.getLogger('simpleTALES').addHandler(handler)
43
44- def serve_http(transport, host=None, port=None, inet=None):
45- from paste.httpexceptions import HTTPExceptionHandler
46- from paste.httpserver import serve
47
48+ def _ensure_loggerhead_path():
49+ """Ensure that you can 'import loggerhead' and get the root."""
50 # loggerhead internal code will try to 'import loggerhead', so
51 # let's put it on the path if we can't find it in the existing path
52 try:
53@@ -81,6 +81,12 @@
54 import os.path, sys
55 sys.path.append(os.path.dirname(__file__))
56
57+ def serve_http(transport, host=None, port=None, inet=None):
58+ from paste.httpexceptions import HTTPExceptionHandler
59+ from paste.httpserver import serve
60+
61+ _ensure_loggerhead_path()
62+
63 from loggerhead.apps.transport import BranchesFromTransportRoot
64 from loggerhead.config import LoggerheadConfig
65
66@@ -132,3 +138,9 @@
67 super(cmd_serve, self).run(*args, **kw)
68
69 register_command(cmd_serve)
70+
71+ def load_tests(standard_tests, module, loader):
72+ _ensure_loggerhead_path()
73+ standard_tests.addTests(loader.loadTestsFromModuleNames(
74+ ['bzrlib.plugins.loggerhead.loggerhead.tests']))
75+ return standard_tests
76
77=== added file 'info.py'
78--- info.py 1970-01-01 00:00:00 +0000
79+++ info.py 2010-12-01 08:41:33 +0000
80@@ -0,0 +1,13 @@
81+#!/usr/bin/env python
82+# API Info for loggerhead
83+
84+bzr_plugin_name = "loggerhead"
85+
86+bzr_plugin_version = (1, 18, 0)
87+
88+bzr_compatible_versions = [
89+ (1, 17, 0), (1, 18, 0), (2, 0, 0), (2, 1, 0), (2, 2, 0), (2, 3, 0)]
90+
91+bzr_minimum_version = bzr_compatible_versions[0]
92+
93+bzr_maximum_version = bzr_compatible_versions[-1]
94
95=== modified file 'loggerhead/__init__.py'
96--- loggerhead/__init__.py 2009-08-20 13:30:37 +0000
97+++ loggerhead/__init__.py 2010-12-01 08:41:33 +0000
98@@ -22,7 +22,7 @@
99
100 import pkg_resources
101
102-__version__ = '1.17'
103+__version__ = '1.18'
104 required_bzrlib = (1, 17)
105
106 pkg_resources.get_distribution('Paste>=1.6')
107
108=== modified file 'loggerhead/apps/branch.py'
109--- loggerhead/apps/branch.py 2010-05-05 18:28:54 +0000
110+++ loggerhead/apps/branch.py 2010-12-01 08:41:33 +0000
111@@ -29,6 +29,7 @@
112
113 from loggerhead.apps import static_app
114 from loggerhead.controllers.annotate_ui import AnnotateUI
115+from loggerhead.controllers.view_ui import ViewUI
116 from loggerhead.controllers.atom_ui import AtomUI
117 from loggerhead.controllers.changelog_ui import ChangeLogUI
118 from loggerhead.controllers.diff_ui import DiffUI
119@@ -124,6 +125,7 @@
120 'files': InventoryUI,
121 'revision': RevisionUI,
122 'search': SearchUI,
123+ 'view': ViewUI,
124 }
125
126 def last_updated(self):
127
128=== added file 'loggerhead/controllers/annotate_ui.py'
129--- loggerhead/controllers/annotate_ui.py 1970-01-01 00:00:00 +0000
130+++ loggerhead/controllers/annotate_ui.py 2010-12-01 08:41:33 +0000
131@@ -0,0 +1,54 @@
132+#
133+# Copyright (C) 2010 Canonical Ltd.
134+#
135+# This program is free software; you can redistribute it and/or modify
136+# it under the terms of the GNU General Public License as published by
137+# the Free Software Foundation; either version 2 of the License, or
138+# (at your option) any later version.
139+#
140+# This program is distributed in the hope that it will be useful,
141+# but WITHOUT ANY WARRANTY; without even the implied warranty of
142+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
143+# GNU General Public License for more details.
144+#
145+# You should have received a copy of the GNU General Public License
146+# along with this program; if not, write to the Free Software
147+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
148+#
149+
150+from loggerhead.controllers.view_ui import ViewUI
151+from loggerhead import util
152+
153+class AnnotateUI(ViewUI):
154+
155+ def annotate_file(self, info):
156+ file_id = info['file_id']
157+ revid = info['change'].revid
158+
159+ tree = self.tree_for(file_id, revid)
160+
161+ change_cache = {}
162+ last_line_revid = None
163+ parity = 1
164+ for line_revid, text in tree.annotate_iter(file_id):
165+ if line_revid == last_line_revid:
166+ # remember which lines have a new revno and which don't
167+ new_rev = False
168+ else:
169+ new_rev = True
170+ parity ^= 1
171+ last_line_revid = line_revid
172+ if line_revid in change_cache:
173+ change = change_cache[line_revid]
174+ else:
175+ change = self._history.get_changes([line_revid])[0]
176+ change_cache[line_revid] = change
177+
178+ yield util.Container(
179+ parity=parity, new_rev=new_rev, change=change)
180+
181+ def get_values(self, path, kwargs, headers):
182+ values = super(AnnotateUI, self).get_values(path, kwargs, headers)
183+ values['annotated'] = self.annotate_file(values)
184+
185+ return values
186
187=== renamed file 'loggerhead/controllers/annotate_ui.py' => 'loggerhead/controllers/view_ui.py'
188--- loggerhead/controllers/annotate_ui.py 2009-10-17 06:35:33 +0000
189+++ loggerhead/controllers/view_ui.py 2010-12-01 08:41:33 +0000
190@@ -35,20 +35,18 @@
191 from loggerhead import util
192
193
194-class AnnotateUI(TemplatedBranchView):
195-
196- template_path = 'loggerhead.templates.annotate'
197-
198- def annotate_file(self, file_id, revid):
199- z = time.time()
200- lineno = 1
201- parity = 0
202-
203+class ViewUI(TemplatedBranchView):
204+
205+ template_path = 'loggerhead.templates.view'
206+
207+ def tree_for(self, file_id, revid):
208 file_revid = self._history.get_inventory(revid)[file_id].revision
209- tree = self._history._branch.repository.revision_tree(file_revid)
210+ return self._history._branch.repository.revision_tree(file_revid)
211
212+ def text_lines(self, file_id, revid):
213 file_name = os.path.basename(self._history.get_path(revid, file_id))
214-
215+
216+ tree = self.tree_for(file_id, revid)
217 file_text = tree.get_file_text(file_id)
218 encoding = 'utf-8'
219 try:
220@@ -58,44 +56,27 @@
221 file_text = file_text.decode(encoding)
222
223 file_lines = bzrlib.osutils.split_lines(file_text)
224+ # This can throw bzrlib.errors.BinaryFile (which our caller catches).
225+ bzrlib.textfile.check_text_lines(file_lines)
226+
227+ if highlight is not None:
228+ hl_lines = highlight(file_name, file_text, encoding)
229+ # highlight strips off extra newlines at the end of the file.
230+ extra_lines = len(file_lines) - len(hl_lines)
231+ hl_lines.extend([u''] * extra_lines)
232+ else:
233+ hl_lines = map(cgi.escape, file_lines)
234+
235+ return hl_lines;
236
237+ def file_contents(self, file_id, revid):
238 try:
239- bzrlib.textfile.check_text_lines(file_lines)
240+ file_lines = self.text_lines(file_id, revid)
241 except bzrlib.errors.BinaryFile:
242 # bail out; this isn't displayable text
243- yield util.Container(parity=0, lineno=1, status='same',
244- text='(This is a binary file.)',
245- change=util.Container())
246- else:
247- if highlight is not None:
248- hl_lines = highlight(file_name, file_text, encoding)
249- hl_lines.extend([u''] * (len(file_lines) - len(hl_lines)))
250- else:
251- hl_lines = map(cgi.escape, file_lines)
252-
253- change_cache = {}
254-
255- last_line_revid = None
256- for line_revid, text in tree.annotate_iter(file_id):
257- if line_revid == last_line_revid:
258- # remember which lines have a new revno and which don't
259- status = 'same'
260- else:
261- status = 'changed'
262- parity ^= 1
263- last_line_revid = line_revid
264- if line_revid in change_cache:
265- change = change_cache[line_revid]
266- else:
267- change = self._history.get_changes([line_revid])[0]
268- change_cache[line_revid] = change
269-
270- yield util.Container(
271- parity=parity, lineno=lineno, status=status,
272- change=change, text=hl_lines[lineno - 1])
273- lineno += 1
274-
275- self.log.debug('annotate: %r secs' % (time.time() - z,))
276+ return ['(This is a binary file.)']
277+
278+ return file_lines
279
280 def get_values(self, path, kwargs, headers):
281 history = self._history
282@@ -105,7 +86,7 @@
283 file_id = kwargs.get('file_id', None)
284 if (file_id is None) and (path is None):
285 raise HTTPBadRequest('No file_id or filename '
286- 'provided to annotate')
287+ 'provided to view')
288
289 if file_id is None:
290 file_id = history.get_file_id(revid, path)
291@@ -140,13 +121,17 @@
292 branch_breadcrumbs = util.branch_breadcrumbs(path, inv, 'files')
293
294 return {
295+ # In AnnotateUI, "annotated" is a generator giving revision
296+ # numbers per lines, but the template checks if "annotated" is
297+ # true or not before using it, so we have to define it here also.
298+ 'annotated': False,
299 'revno_url': revno_url,
300 'file_id': file_id,
301- 'path': path,
302+ 'file_path': path,
303 'filename': filename,
304 'navigation': navigation,
305 'change': change,
306- 'contents': list(self.annotate_file(file_id, revid)),
307+ 'contents': self.file_contents(file_id, revid),
308 'fileview_active': True,
309 'directory_breadcrumbs': directory_breadcrumbs,
310 'branch_breadcrumbs': branch_breadcrumbs,
311
312=== modified file 'loggerhead/highlight.py'
313--- loggerhead/highlight.py 2010-04-22 03:23:55 +0000
314+++ loggerhead/highlight.py 2010-12-01 08:41:33 +0000
315@@ -16,6 +16,7 @@
316 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
317 #
318
319+import bzrlib.osutils
320 import cgi
321
322 from pygments import highlight as _highlight_func
323@@ -36,7 +37,7 @@
324 """
325
326 if len(text) > MAX_HIGHLIGHT_SIZE:
327- return map(cgi.escape, text.split('\n'))
328+ return map(cgi.escape, bzrlib.osutils.split_lines(text))
329
330 formatter = HtmlFormatter(style=style, nowrap=True, classprefix='pyg-')
331
332@@ -48,6 +49,7 @@
333 except (ClassNotFound, ValueError):
334 lexer = TextLexer(encoding=encoding)
335
336- hl_lines = _highlight_func(text, lexer, formatter).split('\n')
337+ hl_lines = _highlight_func(text, lexer, formatter)
338+ hl_lines = bzrlib.osutils.split_lines(hl_lines)
339
340 return hl_lines
341
342=== renamed file 'loggerhead/static/css/annotate.css' => 'loggerhead/static/css/view.css'
343--- loggerhead/static/css/annotate.css 2009-04-30 10:39:05 +0000
344+++ loggerhead/static/css/view.css 2010-12-01 08:41:33 +0000
345@@ -1,29 +1,31 @@
346 /*table*/
347-.annoLineTit, .annoLine, .annoRevTit, .annoRev, .annoComm, .annoCommTit, .annoContTit, .annoCont {
348- width:45px;
349+.viewLineTit, .viewLine, .viewRevTit, .viewRev,
350+.viewContTit, .viewCont
351+{
352 border:1px solid #d2d2d2;
353 }
354-.annoLine, .annoRev, .annoComm, .annoCont {
355+.viewLine, .viewRev, .viewComm, .viewCont {
356 border:none;
357 }
358-.annoLine {
359+.viewLine, .viewLineTit {
360+ padding-right: .2em;
361+}
362+.viewLine {
363 width:37px;
364 }
365-.annoContTit, .annoCont {
366+.viewContTit, .viewCont {
367 width:auto;
368+ padding-left: .3em;
369 }
370-.annoRevTit, .annoRev {
371+.viewRevTit, .viewRev {
372 width:70px;
373 text-align:center;
374 }
375-.annoComm, .annoCommTit {
376- width:200px;
377-}
378-.annoLine, .annoCont {
379+.viewLine, .viewCont {
380 font:normal 12px/normal monospace;
381 whitespace: pre;
382 }
383-.annoCont pre { margin: 0; }
384-.annoLine {
385+.viewCont pre { margin: 0; }
386+.viewLine {
387 text-align:right;
388 }
389
390=== modified file 'loggerhead/templatefunctions.py'
391--- loggerhead/templatefunctions.py 2009-10-17 08:47:38 +0000
392+++ loggerhead/templatefunctions.py 2010-12-01 08:41:33 +0000
393@@ -121,9 +121,9 @@
394
395
396 @templatefunc
397-def annotate_link(url, revno, path):
398+def view_link(url, revno, path):
399 return '<a href="%s" title="Annotate %s">%s</a>' % (
400- url(['/annotate', revno, path]), cgi.escape(path), cgi.escape(path))
401+ url(['/view', revno, path]), cgi.escape(path), cgi.escape(path))
402
403 @templatefunc
404 def revision_link(url, revno, path, frag=''):
405
406=== modified file 'loggerhead/templates/inventory.pt'
407--- loggerhead/templates/inventory.pt 2009-10-17 06:55:25 +0000
408+++ loggerhead/templates/inventory.pt 2010-12-01 08:41:33 +0000
409@@ -89,11 +89,11 @@
410
411 <!-- Show this if it's a symlink -->
412 <tr tal:attributes="class string:blueRow${repeat/file/even}" tal:condition="python:file.kind=='symlink'">
413- <td class="autcell"><a tal:attributes="href python:url(['/annotate', change.revno, file.absolutepath])">
414+ <td class="autcell"><a tal:attributes="href python:url(['/view', change.revno, file.absolutepath])">
415 <img tal:attributes="src python:branch.static_url('/static/images/ico_file_flecha.gif')" alt="Symlink" />
416 </a>
417
418- <a tal:attributes="href python:url(['/annotate', revno_url, file.absolutepath])"
419+ <a tal:attributes="href python:url(['/view', revno_url, file.absolutepath])"
420 tal:content="file/filename" class="link"></a>
421 </td>
422 <td class="date"><a tal:attributes="href python:url(['/revision', file.change.revno]);
423@@ -113,17 +113,17 @@
424
425 <!-- Show this if it's a regular file -->
426 <tr tal:attributes="class string:blueRow${repeat/file/even}" tal:condition="python:file.kind=='file'">
427- <td class="autcell"><a tal:attributes="href python:url(['/annotate', revno_url, file.absolutepath])">
428+ <td class="autcell"><a tal:attributes="href python:url(['/view', revno_url, file.absolutepath])">
429 <img tal:attributes="src python:branch.static_url('/static/images/ico_file.gif');
430- title string:Annotate ${file/filename}"
431+ title string:View ${file/filename}"
432 tal:condition="python:file.executable is False" />
433 <!-- Show a different icon id the file executable -->
434 <img tal:attributes="src python:branch.static_url('/static/images/ico_file_modify.gif');
435- title string:Annotate ${file/filename}"
436+ title string:View ${file/filename}"
437 tal:condition="python:file.executable is True" alt="File" />
438 </a>
439
440- <a tal:attributes="href python:url(['/annotate', revno_url, file.absolutepath])"
441+ <a tal:attributes="href python:url(['/view', revno_url, file.absolutepath])"
442 tal:content="file/filename" class="link"></a></td>
443 <td class="date"><a tal:attributes="href python:url(['/revision', file.change.revno]);
444 title string:Show revision ${file/change/revno}"
445@@ -131,8 +131,8 @@
446 </td>
447 <td class="date" tal:content="python:util.date_time(file.change.date)"></td>
448 <td class="timedate2" tal:content="python:util.human_size(file.size)"></td>
449- <td class="expcell"><a tal:attributes="href python:url(['/annotate', revno_url, file.absolutepath]);
450- title string:Annotate ${file/filename}">
451+ <td class="expcell"><a tal:attributes="href python:url(['/view', revno_url, file.absolutepath]);
452+ title string:View ${file/filename}">
453 <img tal:attributes="src python:branch.static_url('/static/images/ico_planilla.gif')" alt="Diff" />
454 </a>
455 </td>
456
457=== modified file 'loggerhead/templates/revision.pt'
458--- loggerhead/templates/revision.pt 2009-07-21 04:39:33 +0000
459+++ loggerhead/templates/revision.pt 2010-12-01 08:41:33 +0000
460@@ -41,7 +41,7 @@
461 </tal:compare-to>
462 </span>
463 <span class="breadcrumb" tal:condition="specific_path">
464- : <tal:annotate content="structure python:annotate_link(url, change.revno, specific_path)" />
465+ : <tal:annotate content="structure python:view_link(url, change.revno, specific_path)" />
466 </span>
467 </h1>
468 <tal:branch-info replace="structure python:branchinfo(branch)" />
469@@ -54,7 +54,7 @@
470 </a>
471 </p>
472 <p tal:condition="specific_path">
473- Viewing changes to <tal:annotate content="structure python:annotate_link(url, change.revno, specific_path)" />
474+ Viewing changes to <tal:annotate content="structure python:view_link(url, change.revno, specific_path)" />
475 </p>
476 <ul id="submenuTabs">
477 <li id="first"><a tal:attributes="href python:url(['/files', change.revno]);
478
479=== renamed file 'loggerhead/templates/annotate.pt' => 'loggerhead/templates/view.pt'
480--- loggerhead/templates/annotate.pt 2009-04-30 10:39:05 +0000
481+++ loggerhead/templates/view.pt 2010-12-01 08:41:33 +0000
482@@ -3,12 +3,12 @@
483 <html xmlns="http://www.w3.org/1999/xhtml" metal:use-macro="macros/main">
484 <head>
485 <title metal:fill-slot="title"
486- tal:content="string:${branch/friendly_name} : contents of ${path}
487+ tal:content="string:${branch/friendly_name} : contents of ${file_path}
488 at revision ${change/revno}">
489 </title>
490 <metal:block fill-slot="header_extras">
491 <link rel="stylesheet" type="text/css" media="all"
492- tal:attributes="href python:branch.static_url('/static/css/annotate.css')"/>
493+ tal:attributes="href python:branch.static_url('/static/css/view.css')"/>
494 <link rel="stylesheet" type="text/css" media="all"
495 tal:attributes="href python:branch.static_url('/static/css/highlight.css')"/>
496 </metal:block>
497@@ -36,6 +36,14 @@
498 <li id="first">
499 <a tal:attributes="href python:url(['/files', change.revno], clear=1)">browse files</a>
500 </li>
501+ <li tal:condition="not:annotated">
502+ <a tal:attributes="href python:url(['/annotate', revno_url, file_path], clear=1)"
503+ >view revision numbers per line</a>
504+ </li>
505+ <li tal:condition="annotated">
506+ <a tal:attributes="href python:url(['/view', revno_url, file_path], clear=1)"
507+ >view without revision numbers</a>
508+ </li>
509 <li>
510 <a tal:attributes="href python:url(['/revision', change.revno], clear=1)">view revision</a>
511 </li>
512@@ -50,25 +58,30 @@
513 </li>
514 </ul>
515
516- <div class="annotate">
517+ <div class="view">
518 <table id="logentries">
519 <tr class="logheader">
520- <td class="annoLineTit">Line</td>
521- <td class="annoRevTit">Revision</td>
522- <td class="annoContTit">Contents</td>
523+ <td class="viewLineTit">Line</td>
524+ <td class="viewRevTit" tal:condition="annotated">Revision</td>
525+ <td class="viewContTit">Contents</td>
526 </tr>
527
528- <tr tal:repeat="line contents"
529- tal:attributes="class string:blueRow${line/parity}">
530- <td class="annoLine"><a tal:attributes="id string:L${line/lineno}; href string:#L${line/lineno}" tal:content="line/lineno">1</a></td>
531- <td class="annoRev">
532- <a tal:condition="python:line.status=='changed'"
533- tal:content="python:util.trunc(line.change.revno)"
534- tal:attributes="href python:url(['/revision', line.change.revno], clear=1);
535- title python:'%s by %s, on %s (%s)'%(line.change.revno, ', '.join(util.hide_emails(line.change.authors)), line.change.date.strftime('%d %b %Y %H:%M'), util.date_time(line.change.date))"></a>
536- </td>
537- <td class="annoCont"><pre tal:content="structure line/text"></pre></td>
538- </tr>
539+ <tal:rep tal:repeat="line contents">
540+ <tr tal:define="anno python:annotated and annotated.next()"
541+ tal:attributes="class python:anno and 'blueRow' + str(anno.parity) or None">
542+ <td class="viewLine">
543+ <a tal:attributes="id string:L${repeat/line/number}; href string:#L${repeat/line/number}"
544+ tal:content="repeat/line/number">1</a>
545+ </td>
546+ <td class="viewRev" tal:condition="annotated">
547+ <a tal:condition="python:anno.new_rev"
548+ tal:content="python:util.trunc(anno.change.revno)"
549+ tal:attributes="href python:url(['/revision', anno.change.revno], clear=1);
550+ title python:'%s by %s, on %s (%s)' % (anno.change.revno, ', '.join(util.hide_emails(anno.change.authors)), anno.change.date.strftime('%d %b %Y %H:%M'), util.date_time(anno.change.date))"></a>
551+ </td>
552+ <td class="viewCont"><pre tal:content="structure line"></pre></td>
553+ </tr>
554+ </tal:rep>
555 </table>
556 </div>
557
558
559=== modified file 'loggerhead/tests/__init__.py'
560--- loggerhead/tests/__init__.py 2006-12-11 06:44:19 +0000
561+++ loggerhead/tests/__init__.py 2010-12-01 08:41:33 +0000
562@@ -0,0 +1,26 @@
563+# Copyright 2006, 2010 Canonical Ltd
564+#
565+# This program is free software; you can redistribute it and/or modify
566+# it under the terms of the GNU General Public License as published by
567+# the Free Software Foundation; either version 2 of the License, or
568+# (at your option) any later version.
569+#
570+# This program is distributed in the hope that it will be useful,
571+# but WITHOUT ANY WARRANTY; without even the implied warranty of
572+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
573+# GNU General Public License for more details.
574+#
575+# You should have received a copy of the GNU General Public License
576+# along with this program; if not, write to the Free Software
577+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
578+
579+
580+def load_tests(standard_tests, module, loader):
581+ standard_tests.addTests(loader.loadTestsFromModuleNames([
582+ (__name__ + '.' + x) for x in [
583+ 'test_controllers',
584+ 'test_corners',
585+ 'test_simple',
586+ 'test_templating',
587+ ]]))
588+ return standard_tests
589
590=== modified file 'loggerhead/trace.py'
591--- loggerhead/trace.py 2008-10-24 02:26:05 +0000
592+++ loggerhead/trace.py 2010-12-01 08:41:33 +0000
593@@ -22,6 +22,7 @@
594
595 import os
596 import logging
597+import logging.handlers
598 import sys
599
600

Subscribers

People subscribed via source and target branches