launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02645
[Merge] lp:~wallyworld/launchpad/add-link-to-noneformatter into lp:launchpad
Ian Booth has proposed merging lp:~wallyworld/launchpad/add-link-to-noneformatter into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
#714519 LocationError: 'link' on SourcePackageRecipeBuild:+index
https://bugs.launchpad.net/bugs/714519
For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/add-link-to-noneformatter/+merge/50088
See bug 714519. The tales link formatter should be able to render objects with value None.
== Implementation ==
Enhance NoneFormatter to support traversing the "link" attribute of None objects. Returns "None".
Enhance format_link() helper to handle None objects, allowing a specified "None" value to be returned. Default is "None".
== Tests ==
There were no tests for NoneFormatter so I added these. Also added a test_none() to TestXHTMLRepresentations
bin/test -vvt test_noneformatter
bin/test -vvt test_web_service
== Lint ==
Linting changed files:
lib/lp/app/browser/tales.py
lib/lp/app/browser/webservice.py
lib/lp/app/browser/tests/test_noneformatter.py
lib/lp/app/browser/tests/test_webservice.py
lib/lp/code/browser/sourcepackagerecipe.py
./lib/lp/app/browser/tests/test_webservice.py
37: E501 line too long (80 characters)
37: Line exceeds 78 characters.
--
https://code.launchpad.net/~wallyworld/launchpad/add-link-to-noneformatter/+merge/50088
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/add-link-to-noneformatter into lp:launchpad.
=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py 2011-02-14 23:20:14 +0000
+++ lib/lp/app/browser/tales.py 2011-02-17 02:02:16 +0000
@@ -75,8 +75,10 @@
SEPARATOR = ' : '
-def format_link(obj, view_name=None):
+def format_link(obj, view_name=None, empty_value='None'):
"""Return the equivalent of obj/fmt:link as a string."""
+ if obj is None:
+ return empty_value
adapter = queryAdapter(obj, IPathAdapter, 'fmt')
link = getattr(adapter, 'link', None)
if link is None:
@@ -487,6 +489,8 @@
# and not another traversal command.
furtherPath.pop()
return ''
+ elif name == 'link':
+ return 'None'
elif name in self.allowed_names:
return ''
else:
=== added file 'lib/lp/app/browser/tests/test_noneformatter.py'
--- lib/lp/app/browser/tests/test_noneformatter.py 1970-01-01 00:00:00 +0000
+++ lib/lp/app/browser/tests/test_noneformatter.py 2011-02-17 02:02:16 +0000
@@ -0,0 +1,65 @@
+# Copyright 2011 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for lp.registry.browser.tales.NoneFormatter"""
+
+__metaclass__ = type
+
+
+from zope.component import queryAdapter
+from zope.traversing.interfaces import (
+ IPathAdapter,
+ TraversalError,
+ )
+
+from canonical.testing.layers import FunctionalLayer
+from lp.testing import TestCaseWithFactory
+
+
+class TestXHTMLRepresentations(TestCaseWithFactory):
+
+ layer = FunctionalLayer
+
+ def test_valid_traversal(self):
+ adapter = queryAdapter(None, IPathAdapter, 'fmt')
+ traverse = getattr(adapter, 'traverse', None)
+
+ allowed_names = set([
+ 'approximatedate',
+ 'approximateduration',
+ 'break-long-words',
+ 'date',
+ 'datetime',
+ 'displaydate',
+ 'isodate',
+ 'email-to-html',
+ 'exactduration',
+ 'lower',
+ 'nice_pre',
+ 'nl_to_br',
+ 'pagetitle',
+ 'rfc822utcdatetime',
+ 'text-to-html',
+ 'time',
+ 'url',
+ ])
+
+ for name in allowed_names:
+ self.assertEqual('', traverse(name, []))
+
+ def test_invalid_traversal(self):
+ adapter = queryAdapter(None, IPathAdapter, 'fmt')
+ traverse = getattr(adapter, 'traverse', None)
+ self.failUnlessRaises(TraversalError, traverse, "foo", [])
+
+ def test_link(self):
+ adapter = queryAdapter(None, IPathAdapter, 'fmt')
+ traverse = getattr(adapter, 'traverse', None)
+ self.assertEqual('None', traverse('link', []))
+
+ def test_shorten_traversal(self):
+ adapter = queryAdapter(None, IPathAdapter, 'fmt')
+ traverse = getattr(adapter, 'traverse', None)
+ extra = ['1', '2']
+ self.assertEqual('', traverse('shorten', extra))
+ self.assertEqual(['1'], extra)
=== modified file 'lib/lp/app/browser/tests/test_webservice.py'
--- lib/lp/app/browser/tests/test_webservice.py 2011-01-20 22:46:06 +0000
+++ lib/lp/app/browser/tests/test_webservice.py 2011-02-17 02:02:16 +0000
@@ -20,6 +20,11 @@
layer = DatabaseFunctionalLayer
+ def test_none(self):
+ # Test the XHTML representation of None
+ self.assertEqual(format_link(None), 'None')
+ self.assertEqual(format_link(None, empty_value=''), '')
+
def test_person(self):
# Test the XHTML representation of a person.
eric = self.factory.makePerson()
=== modified file 'lib/lp/app/browser/webservice.py'
--- lib/lp/app/browser/webservice.py 2011-01-27 20:55:40 +0000
+++ lib/lp/app/browser/webservice.py 2011-02-17 02:02:16 +0000
@@ -30,13 +30,10 @@
def render(value):
# The value is a webservice link to the object, we want field value.
obj = getattr(context, field.__name__, None)
- if obj is None:
- return ''
- else:
- try:
- return format_link(obj)
- except NotImplementedError:
- return value
+ try:
+ return format_link(obj, empty_value='')
+ except NotImplementedError:
+ return value
return render
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2011-02-15 11:31:56 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2011-02-17 02:02:16 +0000
@@ -253,10 +253,7 @@
@property
def archive_picker(self):
ppa = self.context.daily_build_archive
- if ppa is None:
- initial_html = 'None'
- else:
- initial_html = format_link(ppa)
+ initial_html = format_link(ppa)
field = ISourcePackageEditSchema['daily_build_archive']
return InlineEditPickerWidget(
self.context, field, initial_html,
Follow ups