Merge lp:~bac/launchpad/bug-487965 into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Edwin Grubbs
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~bac/launchpad/bug-487965
Merge into: lp:launchpad
Diff against target: 136 lines (+68/-17)
2 files modified
lib/lp/registry/browser/product.py (+24/-17)
lib/lp/registry/browser/tests/packaging-views.txt (+44/-0)
To merge this branch: bzr merge lp:~bac/launchpad/bug-487965
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+15251@code.launchpad.net

Commit message

Fix an error in the product/+packages view code that is causing an OOPS.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

= Summary =

Bug 487965 reports an OOPS that occurs when attempting to look at
packages for a product when there are more than one distribution
represented.

== Proposed fix ==

The dictionaries representing the distros and packaging used to have an
entry with the distribution name in it. Since the name was used as the
key for the containing dictionary it, having the name in the distro
dictionary looked redundant and it was inadvertently removed.

While I could've just stuffed it back in I though restructuring the code
to be less fragile was a better solution.

== Pre-implementation notes ==

None.

== Implementation details ==

As above.

== Tests ==

bin/test -vvt packaging-views.txt

== Demo and Q/A ==

Visit the product in the bug report and ensure it doesn't OOPS.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/registry/browser/product.py
  lib/lp/registry/browser/tests/packaging-views.txt

== Pylint notices ==

lib/lp/registry/browser/product.py
    54: [F0401] Unable to import 'lazr.delegates' (No module named
delegates)

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (3.4 KiB)

Hi Brad,

This is a nice improvement.

merge-conditional

-Edwin

>=== modified file 'lib/lp/registry/browser/product.py'
>--- lib/lp/registry/browser/product.py 2009-11-23 14:52:25 +0000
>+++ lib/lp/registry/browser/product.py 2009-11-25 18:30:38 +0000
>@@ -474,9 +474,9 @@
>
> def _sort_distros(a, b):

Since this function doesn't actually sort a & b, it should be
called _compare_distros.

> """Put Ubuntu first, otherwise in alpha order."""
>- if a['name'] == 'ubuntu':
>+ if a == 'ubuntu':
> return -1

This only works if 'ubuntu' is always passed in as the first
parameter. There needs to be a corresponding if-statement for
b=='ubuntu'.

>- return cmp(a['name'], b['name'])
>+ return cmp(a, b)
>
>
> class ProductSetBreadcrumb(Breadcrumb):
>@@ -947,29 +947,33 @@
> title, and an attribute "packagings" which is a list of the relevant
> packagings for this distro and product.
> """
>- distros = {}
>- # first get a list of all relevant packagings
>+ # First get a list of all relevant packagings.
> all_packagings = []
> for series in self.context.series:
> for packaging in series.packagings:
> all_packagings.append(packaging)
>- # we sort it so that the packagings will always be displayed in the
>- # distroseries version, then productseries name order
>+ # We sort it so that the packagings will always be displayed in the
>+ # distroseries version, then productseries name order.
> all_packagings.sort(key=lambda a: (a.distroseries.version,
> a.productseries.name, a.id))
>+
>+ distros = {}
> for packaging in all_packagings:
>- if distros.has_key(packaging.distroseries.distribution.name):
>- distro = distros[packaging.distroseries.distribution.name]
>+ distribution = packaging.distroseries.distribution
>+ if distribution.name in distros:
>+ distro = distros[distribution.name]
> else:
>- distro = {}
>- distro['distribution'] = packaging.distroseries.distribution
>- distro['packagings'] = []
>+ # Create a dictionary for the distribution.
>+ distro = dict(
>+ distribution=packaging.distroseries.distribution,

Since you created the new distribution variable, this could be shortened to:
  distribution=distribution

>+ packagings=[])
> distros[packaging.distroseries.distribution.name] = distro

The index could be shortened to just distribution.name.
Alternatively, the entire if-else-statement could be replaced with:
    distro = distros.setdefault(
        distribution.name, dict(distribution=distribution, packaging=[]))
but that is up to you, since you might find it less readable.

> distro['packagings'].append(packaging)
>- # now we sort the resulting set of "distro" objects, and return that
>- result = distros.values()
>- result.sort(cmp=_sort_distros)
>- return result
>+ # Now we sort the resulting list of "distro" objects, and re...

Read more...

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/product.py'
2--- lib/lp/registry/browser/product.py 2009-11-23 14:52:25 +0000
3+++ lib/lp/registry/browser/product.py 2009-11-25 19:24:17 +0000
4@@ -472,11 +472,14 @@
5 links = ['listall', 'doc', 'assignments', 'new', 'register_sprint']
6
7
8-def _sort_distros(a, b):
9+def _cmp_distros(a, b):
10 """Put Ubuntu first, otherwise in alpha order."""
11- if a['name'] == 'ubuntu':
12+ if a == 'ubuntu':
13 return -1
14- return cmp(a['name'], b['name'])
15+ elif b == 'ubuntu':
16+ return 1
17+ else:
18+ return cmp(a, b)
19
20
21 class ProductSetBreadcrumb(Breadcrumb):
22@@ -947,29 +950,33 @@
23 title, and an attribute "packagings" which is a list of the relevant
24 packagings for this distro and product.
25 """
26- distros = {}
27- # first get a list of all relevant packagings
28+ # First get a list of all relevant packagings.
29 all_packagings = []
30 for series in self.context.series:
31 for packaging in series.packagings:
32 all_packagings.append(packaging)
33- # we sort it so that the packagings will always be displayed in the
34- # distroseries version, then productseries name order
35+ # We sort it so that the packagings will always be displayed in the
36+ # distroseries version, then productseries name order.
37 all_packagings.sort(key=lambda a: (a.distroseries.version,
38 a.productseries.name, a.id))
39+
40+ distros = {}
41 for packaging in all_packagings:
42- if distros.has_key(packaging.distroseries.distribution.name):
43- distro = distros[packaging.distroseries.distribution.name]
44+ distribution = packaging.distroseries.distribution
45+ if distribution.name in distros:
46+ distro = distros[distribution.name]
47 else:
48- distro = {}
49- distro['distribution'] = packaging.distroseries.distribution
50- distro['packagings'] = []
51- distros[packaging.distroseries.distribution.name] = distro
52+ # Create a dictionary for the distribution.
53+ distro = dict(
54+ distribution=distribution,
55+ packagings=[])
56+ distros[distribution.name] = distro
57 distro['packagings'].append(packaging)
58- # now we sort the resulting set of "distro" objects, and return that
59- result = distros.values()
60- result.sort(cmp=_sort_distros)
61- return result
62+ # Now we sort the resulting list of "distro" objects, and return that.
63+ distro_names = distros.keys()
64+ distro_names.sort(cmp=_cmp_distros)
65+ results = [distros[name] for name in distro_names]
66+ return results
67
68
69 class ProductDownloadFilesView(LaunchpadView,
70
71=== modified file 'lib/lp/registry/browser/tests/packaging-views.txt'
72--- lib/lp/registry/browser/tests/packaging-views.txt 2009-11-19 22:16:06 +0000
73+++ lib/lp/registry/browser/tests/packaging-views.txt 2009-11-25 19:24:17 +0000
74@@ -274,6 +274,40 @@
75 <input type="hidden" name="field.packaging" .../>
76 trunk
77
78+The view provides the distro_packaging property that is a list of
79+dictionaries for the distributions and their packaging. The list is
80+sorted by distribution with Ubuntu first and the rest in alphabetic
81+order.
82+
83+ >>> form = {
84+ ... 'field.distroseries': 'redhat/7.0',
85+ ... 'field.sourcepackagename': 'hot',
86+ ... 'field.packaging': 'Primary Project',
87+ ... 'field.actions.continue': 'Continue',
88+ ... }
89+
90+ >>> view = create_initialized_view(
91+ ... productseries, '+addpackage', form=form)
92+ >>> view.errors
93+ []
94+ >>> form = {
95+ ... 'field.distroseries': 'debian/sarge',
96+ ... 'field.sourcepackagename': 'hot',
97+ ... 'field.packaging': 'Primary Project',
98+ ... 'field.actions.continue': 'Continue',
99+ ... }
100+
101+ >>> view = create_initialized_view(
102+ ... productseries, '+addpackage', form=form)
103+ >>> view.errors
104+ []
105+ >>> view = create_initialized_view(product, name='+packages')
106+ >>> for distro_dict in view.distro_packaging:
107+ ... print distro_dict['distribution'].name
108+ ubuntu
109+ debian
110+ redhat
111+
112 The +packages named view descends from PackagingDeleteView to provide remove
113 link actions for the product's linked packages.
114
115@@ -285,6 +319,14 @@
116 A packaging link can be deleted if the owner believes it is an error. The
117 package linked to hoary is wrong; thunderbird is the wrong sourcepackage.
118
119+ >>> view = create_initialized_view(product, name='+packages')
120+ >>> for package in view.all_packaging:
121+ ... print package.distroseries.name, package.productseries.name
122+ sarge hotter
123+ 7.0 hotter
124+ grumpy hotter
125+ hoary hotter
126+
127 >>> [hoary_package] = [package for package in view.all_packaging
128 ... if package.distroseries.name == 'hoary']
129 >>> form = {
130@@ -296,4 +338,6 @@
131 []
132 >>> for package in view.all_packaging:
133 ... print package.distroseries.name, package.productseries.name
134+ sarge hotter
135+ 7.0 hotter
136 grumpy hotter