Merge lp:~sinzui/launchpad/non-existent-packages-bug-204119 into lp:launchpad
- non-existent-packages-bug-204119
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Brad Crittenden |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~sinzui/launchpad/non-existent-packages-bug-204119 |
Merge into: | lp:launchpad |
Diff against target: |
326 lines (+105/-46) 10 files modified
lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt (+12/-0) lib/lp/registry/browser/packaging.py (+9/-0) lib/lp/registry/browser/productseries.py (+27/-3) lib/lp/registry/browser/tests/packaging-views.txt (+37/-2) lib/lp/registry/stories/product/xx-product-package-pages.txt (+9/-8) lib/lp/registry/stories/productseries/xx-productseries-index.txt (+1/-4) lib/lp/registry/templates/product-packages.pt (+2/-2) lib/lp/registry/templates/productseries-portlet-packages.pt (+0/-3) lib/lp/registry/templates/productseries-ubuntupkg.pt (+6/-23) lib/lp/testing/factory.py (+2/-1) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/non-existent-packages-bug-204119 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | code | Approve | |
Review via email: mp+17176@code.launchpad.net |
Commit message
Description of the change
Curtis Hovey (sinzui) wrote : | # |
Brad Crittenden (bac) wrote : | # |
Hi Curtis,
Thanks for the branch. It looks good save for a few small items below.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -33,6 +33,9 @@
>
> from zope.component import getUtility
> from zope.app.
> +from zope.formlib import form
> +from zope.schema import Choice
> +from zope.schema.
>
> from z3c.ptcompat import ViewPageTemplat
>
> @@ -366,7 +369,7 @@
>
> class ProductSeriesUb
>
> - field_names = ['sourcepackage
> + field_names = ['sourcepackage
> page_title = 'Ubuntu source packaging'
> label = page_title
>
> @@ -374,8 +377,8 @@
> """Set the static packaging information for this series."""
> super(ProductSe
> context, request)
> - ubuntu = getUtility(
> - self._ubuntu_series = ubuntu.
> + self._ubuntu = getUtility(
> + self._ubuntu_series = self._ubuntu.
> try:
> package = self.context.
> self.default_
> @@ -383,6 +386,28 @@
> # The package has never been set.
> self.default_
>
> + def setUpFields(self):
> + """See `LaunchpadFormV
> +
> + The packaging is restricted to ubuntu series and the default value
> + is the current development series.
> + """
> + super(Packaging
As we discussed on IRC the class name is wrong.
> + series_vocabulary = SimpleVocabulary(
> + [SimpleTerm(series, series.name, series.
> + for series in self._ubuntu.
> + choice = Choice(
> + title=_('Series'),
> + default=
> + vocabulary=
> + description=_(
> + "Select the Ubuntu series that this package is published "
> + "in. The current series is most important to the Ubuntu "
> + "community."),
I think "series where this package is published" sounds better. What
do you think?
> + required=True)
> + field = form.Fields(choice, render_
> + self.form_fields = self.form_
> +
> def setUpWidgets(self):
> """See `LaunchpadFormV
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -18,6 +18,8 @@
> >>> sourcepackagename = factory.
> >>> sourcepackage = factory.make...
Curtis Hovey (sinzui) wrote : | # |
On Tue, 2010-01-12 at 18:45 +0000, Brad Crittenden wrote:
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
...
> > @@ -374,8 +377,8 @@
> > """Set the static packaging information for this series."""
> > super(ProductSe
> > context, request)
> > - ubuntu = getUtility(
> > - self._ubuntu_series = ubuntu.
> > + self._ubuntu = getUtility(
> > + self._ubuntu_series = self._ubuntu.
> > try:
> > package = self.context.
> > self.default_
> > @@ -383,6 +386,28 @@
> > # The package has never been set.
> > self.default_
> >
> > + def setUpFields(self):
> > + """See `LaunchpadFormV
> > +
> > + The packaging is restricted to ubuntu series and the default value
> > + is the current development series.
> > + """
> > + super(Packaging
>
> As we discussed on IRC the class name is wrong.
Fixed
> > + series_vocabulary = SimpleVocabulary(
> > + [SimpleTerm(series, series.name, series.
> > + for series in self._ubuntu.
> > + choice = Choice(
> > + title=_('Series'),
> > + default=
> > + vocabulary=
> > + description=_(
> > + "Select the Ubuntu series that this package is published "
> > + "in. The current series is most important to the Ubuntu "
> > + "community."),
>
> I think "series where this package is published" sounds better. What
> do you think?
Much better. Accepted.
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -18,6 +18,8 @@
> > >>> sourcepackagename = factory.
> > >>> sourcepackage = factory.
> > ... sourcepackagena
> > + >>> spph = factory.
> > + ... sourcepackagena
> > >>> product = factory.
> > >>> productseries = factory.
> > ... product=product, name='hotter')
> > @@ -103,6 +105,22 @@
> > ('sourcepackage
> > You must choose the source package name.
> >
> > +In the case of full functionality distributions like Ubuntu, the source
> > +package must be published in the distro series.
> > +
> > + >>> vapour_spn = factory.
Preview Diff
1 | === modified file 'lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt' |
2 | --- lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt 2009-10-22 01:39:19 +0000 |
3 | +++ lib/lp/bugs/stories/guided-filebug/xx-project-guided-filebug.txt 2010-01-13 15:11:15 +0000 |
4 | @@ -205,6 +205,18 @@ |
5 | If we set up an external bug tracker for Testy Product, this will be reflected |
6 | in the Test Group's filebug page. |
7 | |
8 | + >>> from zope.component import getUtility |
9 | + >>> from canonical.launchpad.interfaces.launchpad import ( |
10 | + ... ILaunchpadCelebrities) |
11 | + |
12 | + >>> login('admin@canonical.com') |
13 | + >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
14 | + >>> hoary = ubuntu.getSeries('hoary') |
15 | + >>> thunderbird_spn = factory.getOrMakeSourcePackageName('thunderbird') |
16 | + >>> spph = factory.makeSourcePackagePublishingHistory( |
17 | + ... sourcepackagename=thunderbird_spn, distroseries=hoary) |
18 | + >>> logout() |
19 | + |
20 | >>> admin_browser.open('http://launchpad.dev/testy/+edit') |
21 | >>> admin_browser.getControl(name='field.bugtracker').value = ['external'] |
22 | >>> admin_browser.getControl( |
23 | |
24 | === modified file 'lib/lp/registry/browser/packaging.py' |
25 | --- lib/lp/registry/browser/packaging.py 2009-10-23 14:23:18 +0000 |
26 | +++ lib/lp/registry/browser/packaging.py 2010-01-13 15:11:15 +0000 |
27 | @@ -47,6 +47,15 @@ |
28 | if sourcepackagename is None: |
29 | message = "You must choose the source package name." |
30 | self.setFieldError('sourcepackagename', message) |
31 | + # Do not allow users it create links to unpublished Ubuntu packages. |
32 | + elif distroseries.distribution.full_functionality: |
33 | + source_package = distroseries.getSourcePackage(sourcepackagename) |
34 | + if source_package.currentrelease is None: |
35 | + message = ("The source package is not published in %s." % |
36 | + distroseries.displayname) |
37 | + self.setFieldError('sourcepackagename', message) |
38 | + else: |
39 | + pass |
40 | packaging_util = getUtility(IPackagingUtil) |
41 | if packaging_util.packagingEntryExists( |
42 | productseries=productseries, |
43 | |
44 | === modified file 'lib/lp/registry/browser/productseries.py' |
45 | --- lib/lp/registry/browser/productseries.py 2009-12-05 18:37:28 +0000 |
46 | +++ lib/lp/registry/browser/productseries.py 2010-01-13 15:11:15 +0000 |
47 | @@ -33,6 +33,9 @@ |
48 | |
49 | from zope.component import getUtility |
50 | from zope.app.form.browser import TextAreaWidget, TextWidget |
51 | +from zope.formlib import form |
52 | +from zope.schema import Choice |
53 | +from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary |
54 | |
55 | from z3c.ptcompat import ViewPageTemplateFile |
56 | |
57 | @@ -366,7 +369,7 @@ |
58 | |
59 | class ProductSeriesUbuntuPackagingView(PackagingAddView): |
60 | |
61 | - field_names = ['sourcepackagename'] |
62 | + field_names = ['sourcepackagename', 'distroseries'] |
63 | page_title = 'Ubuntu source packaging' |
64 | label = page_title |
65 | |
66 | @@ -374,8 +377,8 @@ |
67 | """Set the static packaging information for this series.""" |
68 | super(ProductSeriesUbuntuPackagingView, self).__init__( |
69 | context, request) |
70 | - ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
71 | - self._ubuntu_series = ubuntu.currentseries |
72 | + self._ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
73 | + self._ubuntu_series = self._ubuntu.currentseries |
74 | try: |
75 | package = self.context.getPackage(self._ubuntu_series) |
76 | self.default_sourcepackagename = package.sourcepackagename |
77 | @@ -383,6 +386,27 @@ |
78 | # The package has never been set. |
79 | self.default_sourcepackagename = None |
80 | |
81 | + def setUpFields(self): |
82 | + """See `LaunchpadFormView`. |
83 | + |
84 | + The packaging is restricted to ubuntu series and the default value |
85 | + is the current development series. |
86 | + """ |
87 | + super(ProductSeriesUbuntuPackagingView, self).setUpFields() |
88 | + series_vocabulary = SimpleVocabulary( |
89 | + [SimpleTerm(series, series.name, series.named_version) |
90 | + for series in self._ubuntu.series]) |
91 | + choice = Choice(__name__='distroseries', |
92 | + title=_('Series'), |
93 | + default=self._ubuntu_series, |
94 | + vocabulary=series_vocabulary, |
95 | + description=_( |
96 | + "Series where this package is published. The current series " |
97 | + "is most important to the Ubuntu community."), |
98 | + required=True) |
99 | + field = form.Fields(choice, render_context=self.render_context) |
100 | + self.form_fields = self.form_fields.omit(choice.__name__) + field |
101 | + |
102 | def setUpWidgets(self): |
103 | """See `LaunchpadFormView`. |
104 | |
105 | |
106 | === modified file 'lib/lp/registry/browser/tests/packaging-views.txt' |
107 | --- lib/lp/registry/browser/tests/packaging-views.txt 2009-12-10 20:03:11 +0000 |
108 | +++ lib/lp/registry/browser/tests/packaging-views.txt 2010-01-13 15:11:15 +0000 |
109 | @@ -18,6 +18,8 @@ |
110 | >>> sourcepackagename = factory.makeSourcePackageName('hot') |
111 | >>> sourcepackage = factory.makeSourcePackage( |
112 | ... sourcepackagename=sourcepackagename, distroseries=hoary) |
113 | + >>> spph = factory.makeSourcePackagePublishingHistory( |
114 | + ... sourcepackagename=sourcepackagename, distroseries=hoary) |
115 | >>> product = factory.makeProduct(name="hot", displayname='Hot') |
116 | >>> productseries = factory.makeProductSeries( |
117 | ... product=product, name='hotter') |
118 | @@ -103,6 +105,22 @@ |
119 | ('sourcepackagename', u'Source Package Name', RequiredMissing()) |
120 | You must choose the source package name. |
121 | |
122 | +In the case of full functionality distributions like Ubuntu, the source |
123 | +package must be published in the distro series. |
124 | + |
125 | + >>> vapor_spn = factory.makeSourcePackageName('vapor') |
126 | + >>> form = { |
127 | + ... 'field.distroseries': 'ubuntu/hoary', |
128 | + ... 'field.sourcepackagename': 'vapor', |
129 | + ... 'field.packaging': 'Primary Project', |
130 | + ... 'field.actions.continue': 'Continue', |
131 | + ... } |
132 | + >>> view = create_initialized_view( |
133 | + ... productseries, '+addpackage', form=form) |
134 | + >>> for error in view.errors: |
135 | + ... print error |
136 | + The source package is not published in Hoary. |
137 | + |
138 | The +addpackage view provides the default_distroseries property. It is None |
139 | by default, but subclasses may change it. |
140 | |
141 | @@ -129,16 +147,27 @@ |
142 | Ubuntu source packaging |
143 | |
144 | >>> print view.field_names |
145 | - ['sourcepackagename'] |
146 | + ['sourcepackagename', 'distroseries'] |
147 | |
148 | >>> print view.cancel_url |
149 | http://launchpad.dev/hot/hotter |
150 | |
151 | -The view restricts the packaging to the current Ubuntu series. |
152 | +The view restricts the packaging to Ubuntu series, and default is the current |
153 | +Ubuntu series. |
154 | |
155 | >>> print view.default_distroseries.name |
156 | hoary |
157 | |
158 | + >>> print view.widgets['distroseries']._getDefault().name |
159 | + hoary |
160 | + |
161 | + >>> for term in view.widgets['distroseries'].vocabulary: |
162 | + ... print term.title |
163 | + Breezy Badger Autotest (6.6.6) |
164 | + Grumpy (5.10) |
165 | + Hoary (5.04) |
166 | + Warty (4.10) |
167 | + |
168 | The sourcepackagename is None if the package link was never set. The view's |
169 | packaging history is empty, and the sourcepackagename widget is empty. |
170 | |
171 | @@ -172,6 +201,10 @@ |
172 | |
173 | The package in the current Ubuntu series can be updated. |
174 | |
175 | + >>> thunderbird_spn = factory.getOrMakeSourcePackageName('thunderbird') |
176 | + >>> spph = factory.makeSourcePackagePublishingHistory( |
177 | + ... sourcepackagename=thunderbird_spn, distroseries=hoary) |
178 | + |
179 | >>> form = { |
180 | ... 'field.sourcepackagename': 'thunderbird', |
181 | ... 'field.actions.continue': 'Update', |
182 | @@ -211,6 +244,8 @@ |
183 | >>> login('admin@canonical.com') |
184 | >>> hoary.status = DistroSeriesStatus.CURRENT |
185 | >>> grumpy_series = ubuntu.getSeries('grumpy') |
186 | + >>> spph = factory.makeSourcePackagePublishingHistory( |
187 | + ... sourcepackagename=sourcepackagename, distroseries=grumpy_series) |
188 | >>> grumpy_series.status = DistroSeriesStatus.FROZEN |
189 | |
190 | >>> a_user = factory.makePerson(name="hedgehog") |
191 | |
192 | === modified file 'lib/lp/registry/stories/product/xx-product-package-pages.txt' |
193 | --- lib/lp/registry/stories/product/xx-product-package-pages.txt 2009-11-12 23:24:53 +0000 |
194 | +++ lib/lp/registry/stories/product/xx-product-package-pages.txt 2010-01-13 15:11:15 +0000 |
195 | @@ -35,14 +35,15 @@ |
196 | |
197 | >>> evo_owner.getLink(url='/ubuntu/hoary/+source/evolution') is not None |
198 | True |
199 | - >>> evo_owner.getLink(url='/evolution/trunk/+addpackage') is not None |
200 | - True |
201 | - |
202 | -If you're not the owner or an admin, you still see the packages, but without |
203 | -the link to add packages yourself. |
204 | - |
205 | - >>> browser.open('http://launchpad.dev/evolution/+packages') |
206 | - >>> browser.getLink(url='/evolution/trunk/+addpackage') |
207 | + |
208 | +Any logged in users can still see the links to create a packaging link. |
209 | + |
210 | + >>> user_browser.open('http://launchpad.dev/evolution/+packages') |
211 | + >>> print user_browser.getLink(url='/evolution/trunk/+ubuntupkg').url |
212 | + http://launchpad.dev/evolution/trunk/+ubuntupkg |
213 | + |
214 | + >>> anon_browser.open('http://launchpad.dev/evolution/+packages') |
215 | + >>> anon_browser.getLink(url='/evolution/trunk/+ubuntupkg') |
216 | Traceback (most recent call last): |
217 | ... |
218 | LinkNotFoundError |
219 | |
220 | === modified file 'lib/lp/registry/stories/productseries/xx-productseries-index.txt' |
221 | --- lib/lp/registry/stories/productseries/xx-productseries-index.txt 2009-10-30 15:40:55 +0000 |
222 | +++ lib/lp/registry/stories/productseries/xx-productseries-index.txt 2010-01-13 15:11:15 +0000 |
223 | @@ -130,14 +130,11 @@ |
224 | This series is not packaged in any distribution series. |
225 | |
226 | |
227 | -The driver sees two packaging links near the distribution packaging. |
228 | +The driver sees a packaging link near the distribution packaging. |
229 | |
230 | >>> driver_browser.getLink('Link to Ubuntu package') |
231 | <Link ... url='http://launchpad.dev/firefox/trunk/+ubuntupkg'> |
232 | |
233 | - >>> driver_browser.getLink('Link package') |
234 | - <Link ... url='http://launchpad.dev/firefox/trunk/+addpackage'> |
235 | - |
236 | There is a section that lists related links to this series |
237 | |
238 | >>> anon_browser.getLink('View series for the Mozilla Firefox project') |
239 | |
240 | === modified file 'lib/lp/registry/templates/product-packages.pt' |
241 | --- lib/lp/registry/templates/product-packages.pt 2009-11-12 23:24:53 +0000 |
242 | +++ lib/lp/registry/templates/product-packages.pt 2010-01-13 15:11:15 +0000 |
243 | @@ -85,9 +85,9 @@ |
244 | </table> |
245 | |
246 | <ul class="table-actions" |
247 | - tal:condition="series/menu:overview/add_package/linked"> |
248 | + tal:condition="series/menu:overview/ubuntupkg/linked"> |
249 | <li> |
250 | - <a tal:replace="structure series/menu:overview/add_package/fmt:link" /> |
251 | + <a tal:replace="structure series/menu:overview/ubuntupkg/fmt:link" /> |
252 | </li> |
253 | </ul> |
254 | </div> |
255 | |
256 | === modified file 'lib/lp/registry/templates/productseries-portlet-packages.pt' |
257 | --- lib/lp/registry/templates/productseries-portlet-packages.pt 2009-10-22 19:16:03 +0000 |
258 | +++ lib/lp/registry/templates/productseries-portlet-packages.pt 2010-01-13 15:11:15 +0000 |
259 | @@ -39,8 +39,5 @@ |
260 | <li> |
261 | <a tal:replace="structure context/menu:overview/ubuntupkg/fmt:icon-link" /> |
262 | </li> |
263 | - <li> |
264 | - <a tal:replace="structure context/menu:overview/add_package/fmt:icon-link" /> |
265 | - </li> |
266 | </ul> |
267 | </div> |
268 | |
269 | === modified file 'lib/lp/registry/templates/productseries-ubuntupkg.pt' |
270 | --- lib/lp/registry/templates/productseries-ubuntupkg.pt 2009-10-21 21:16:09 +0000 |
271 | +++ lib/lp/registry/templates/productseries-ubuntupkg.pt 2010-01-13 15:11:15 +0000 |
272 | @@ -17,31 +17,14 @@ |
273 | translations from Ubuntu to the |
274 | <span tal:replace="context/product/displayname">Firefox</span> team. |
275 | </p> |
276 | - |
277 | <p> |
278 | - Use |
279 | - <a tal:replace="structure context/menu:overview/add_package/fmt:link" /> |
280 | - to link this series to packages in other distribution series. |
281 | + Verify that the version packaged in the Ubuntu series is based on |
282 | + the <tal:series replace="context/title" />. Don’t link "HEAD", |
283 | + "MAIN", "MASTER", or "TRUNK" to an Ubuntu package unless you are |
284 | + certain that the package is based on the trunk of development. |
285 | </p> |
286 | - </div> |
287 | - |
288 | - <div class="portlet"> |
289 | - <div metal:use-macro="context/@@launchpad_form/form"> |
290 | - <div metal:fill-slot="extra_info"> |
291 | - <h2> |
292 | - Package in |
293 | - <span tal:replace="view/default_distroseries/title" /> |
294 | - </h2> |
295 | - |
296 | - <p> |
297 | - Ensure both the <tal:series replace="context/title" /> and the |
298 | - Ubuntu package are exactly correct. Don’t link "HEAD", |
299 | - "MAIN", or "TRUNK" to an Ubuntu package unless you are |
300 | - absolutely certain that the package is really based on the |
301 | - trunk of development. |
302 | - </p> |
303 | - </div> |
304 | - </div> |
305 | + |
306 | + <div metal:use-macro="context/@@launchpad_form/form" /> |
307 | </div> |
308 | |
309 | <div class="portlet" |
310 | |
311 | === modified file 'lib/lp/testing/factory.py' |
312 | --- lib/lp/testing/factory.py 2010-01-11 23:43:59 +0000 |
313 | +++ lib/lp/testing/factory.py 2010-01-13 15:11:15 +0000 |
314 | @@ -277,10 +277,11 @@ |
315 | def makeGPGKey(self, owner): |
316 | """Give 'owner' a crappy GPG key for the purposes of testing.""" |
317 | key_id = self.getUniqueHexString(digits=8).upper() |
318 | + fingerprint = key_id + 'A' * 32 |
319 | return getUtility(IGPGKeySet).new( |
320 | owner.id, |
321 | keyid=key_id, |
322 | - fingerprint='A' * 40, |
323 | + fingerprint=fingerprint, |
324 | keysize=self.getUniqueInteger(), |
325 | algorithm=GPGKeyAlgorithm.R, |
326 | active=True, |
This is my branch to prevent packaging links to unpublished Ubuntu packages.
lp:~sinzui/launchpad/non-existent-packages-bug-204119 /bugs.launchpad .net/bugs/ 204119 package- pages.txt \ s-index. txt implementation: beuno, bigjools, flacoste
Diff size: 305
Launchpad bug: https:/
Test command: ./bin/test -vv \
-t packaging-views.txt \
-t xx-product-
-t xx-productserie
Pre-
Target release: 10.01
= Prevent packaging links to unpublished Ubuntu packages =
Launchpad allows users to create links to non-existent packages such as /edge.launchpad .net/ubuntu/ breezy/ +source/ warzone2100.
https:/
The base view does not verify the that the package was published because
it is designed to accommodate any distro. But this feature is intended for
Ubuntu and it would not have been added if it Ubuntu did not want it, so
the rules must favour Ubuntu.
The work in this branch was started months ago. It was stalled by a debate
regarding the motivations for upstream projects linking to Ubuntu, and the
feature's design to accommodate all distros.
It was decided that the feature will never work well for other distros, and
that is why there are less then 100 links after 4 years. The feature must
favour Ubuntu. There is a problem with the Ubuntu-only form; some users
want to select the non-current series, and we should accommodate that before
we remove the generic form.
== Rules ==
* Update the base view to verify the package was published in the series if
the series is full functionality (Ubuntu)
* Update the ubuntu view to show all the Ubuntu series, but retain the
rule to use the current series as the default.
* Update all the project and series pages to use +ubuntupkg instead of
+addpackage.
* ADDENDUM: I improved the instructions to use the ubuntu form.
There will be follow up branches to remove the +addpackage view and a query
to remove the packaging links to the non-existent series package.
== QA ==
On staging (or edge if you are willing to clean up) /edge.launchpad .net/gdp/ +packages -plugins into the source package name field
* https:/
* Verify the action is `(+) Link to Ubuntu package`
* Follow the link
* Verify the form lists all Ubuntu series
* Verify the default series is Lucid
* Enter gedit-developer
and submit
* Verify that the error message is:
The source package is not published in Lucid
== Lint ==
Linting changed files: registry/ browser/ packaging. py registry/ browser/ productseries. py registry/ browser/ tests/packaging -views. txt registry/ stories/ product/ xx-product- package- pages.txt registry/ stories/ productseries/ xx-productserie s-index. txt registry/ templates/ product- packages. pt registry/ templates/ productseries- portlet- packages. pt registry/ templates/ productseries- ubuntupkg. pt testing/ factory. py
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== Test ==
* lib/lp/ registry/ browser/ tests/packaging -views. txt registry/ stories/ product/ xx-product- package- pages.txt registry/ stories/ productseries/ xx-productserie s-index. txt
* lib/lp/
* lib/lp/
== Implementation ==
* lib/lp/ registry/ browser/ pack...