Merge ~cjwatson/launchpad:move-publishing-queries into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 9934601567f43b71a7c4a999c6f98e50353b3e12
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:move-publishing-queries
Merge into: launchpad:master
Diff against target: 766 lines (+293/-243)
8 files modified
lib/lp/archivepublisher/publishing.py (+7/-5)
lib/lp/registry/doc/distroseries.txt (+0/-72)
lib/lp/registry/interfaces/distroseries.py (+1/-21)
lib/lp/registry/model/distroseries.py (+3/-94)
lib/lp/registry/tests/test_distroseries.py (+1/-48)
lib/lp/soyuz/interfaces/publishing.py (+27/-1)
lib/lp/soyuz/model/publishing.py (+78/-1)
lib/lp/soyuz/tests/test_publishing.py (+176/-1)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+417751@code.launchpad.net

Commit message

Move publishing queries from DistroSeries to PublishingSet

Description of the change

The existence of `DistroSeries.getSourcePackagePublishing` and `DistroSeries.getBinaryPackagePublishing` was a historical anomaly, as this sort of publishing-specific code should live in `lp.soyuz` or `lp.archivepublisher`. Move these to the more appropriate `PublishingSet.getSourcesForPublishing` and `PublishingSet.getBinariesForPublishing`.

As well as cleaning up some minor technical debt, this helps to prepare for Artifactory publishing, where we're likely to need to be able to query for publications in an archive without filtering by series.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/archivepublisher/publishing.py b/lib/lp/archivepublisher/publishing.py
2index a5a0cc4..e280b79 100644
3--- a/lib/lp/archivepublisher/publishing.py
4+++ b/lib/lp/archivepublisher/publishing.py
5@@ -1,4 +1,4 @@
6-# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
7+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 __all__ = [
11@@ -910,8 +910,9 @@ class Publisher:
12 get_sources_path(self._config, suite_name, component),
13 self._config.temproot, distroseries.index_compressors)
14
15- for spp in distroseries.getSourcePackagePublishing(
16- pocket, component, self.archive):
17+ for spp in getUtility(IPublishingSet).getSourcesForPublishing(
18+ archive=self.archive, distroseries=distroseries, pocket=pocket,
19+ component=component):
20 stanza = build_source_stanza_fields(
21 spp.sourcepackagerelease, spp.component, spp.section)
22 source_index.write(stanza.makeOutput().encode('utf-8') + b'\n\n')
23@@ -937,8 +938,9 @@ class Publisher:
24 self._config, suite_name, component, arch, subcomp),
25 self._config.temproot, distroseries.index_compressors)
26
27- for bpp in distroseries.getBinaryPackagePublishing(
28- arch.architecturetag, pocket, component, self.archive):
29+ for bpp in getUtility(IPublishingSet).getBinariesForPublishing(
30+ archive=self.archive, distroarchseries=arch, pocket=pocket,
31+ component=component):
32 subcomp = FORMAT_TO_SUBCOMPONENT.get(
33 bpp.binarypackagerelease.binpackageformat)
34 if subcomp not in indices:
35diff --git a/lib/lp/registry/doc/distroseries.txt b/lib/lp/registry/doc/distroseries.txt
36index 7d202f5..82a8ff8 100644
37--- a/lib/lp/registry/doc/distroseries.txt
38+++ b/lib/lp/registry/doc/distroseries.txt
39@@ -232,7 +232,6 @@ other things) the uploader for validating incoming uploads.
40 devel
41 translations
42
43- >>> from lp.soyuz.interfaces.component import IComponentSet
44 >>> from lp.soyuz.interfaces.section import ISectionSet
45 >>> python = getUtility(ISectionSet).ensure('python')
46
47@@ -490,50 +489,9 @@ upstream.
48 SourcePackagePublishingHistory
49 ------------------------------
50
51-IDistroSeries.getSourcePackagePublishing returns all the ISPPH
52-records for a given status in a given pocket. It makes easy to
53-generate a list of currently published sources for override-check, for
54-instance. it can also be used to generate the archive packages list in
55-the future.
56-
57- >>> ubuntu = getUtility(IDistributionSet)['ubuntu']
58- >>> hoary = ubuntu['hoary']
59- >>> component_main = getUtility(IComponentSet)['main']
60- >>> component_multiverse = getUtility(IComponentSet)['multiverse']
61- >>> debian_archive = getUtility(IDistributionSet)['debian'].main_archive
62-
63- >>> spphs = hoary.getSourcePackagePublishing(
64- ... PackagePublishingPocket.RELEASE, component_main,
65- ... warty.main_archive)
66- >>> spphs.count()
67- 6
68- >>> for name in sorted(set(
69- ... pkgpub.sourcepackagerelease.sourcepackagename.name
70- ... for pkgpub in spphs)):
71- ... print(name)
72- alsa-utils
73- evolution
74- libstdc++
75- linux-source-2.6.15
76- netapplet
77- pmount
78- >>> hoary.getSourcePackagePublishing(
79- ... PackagePublishingPocket.RELEASE, component_multiverse,
80- ... hoary.main_archive).count()
81- 0
82- >>> hoary.getSourcePackagePublishing(
83- ... PackagePublishingPocket.BACKPORTS, component_main,
84- ... hoary.main_archive).count()
85- 0
86- >>> hoary.getSourcePackagePublishing(
87- ... PackagePublishingPocket.RELEASE, component_main,
88- ... debian_archive).count()
89- 0
90-
91 ISPP.getPublishedBinaries returns all the binaries generated by the
92 publication in question:
93
94- >>> warty = ubuntu['warty']
95 >>> warty_pub_source = warty.main_archive.getPublishedSources(
96 ... distroseries=warty, name=u'mozilla-firefox',
97 ... status=PackagePublishingStatus.PUBLISHED).one()
98@@ -566,36 +524,6 @@ publication in question:
99 >>> print(warty_mozilla_pub_bin.section.name)
100 base
101
102-DistroSeries.getBinaryPackagePublishing will return
103-BinaryPackagePublishingHistory objects for the DistroSeries:
104-
105- >>> warty = ubuntu['warty']
106- >>> bpphs = warty.getBinaryPackagePublishing(
107- ... "i386", PackagePublishingPocket.RELEASE, component_main,
108- ... warty.main_archive)
109- >>> bpphs.count()
110- 8
111- >>> 'mozilla-firefox' in set(
112- ... pkgpub.binarypackagerelease.binarypackagename.name
113- ... for pkgpub in bpphs)
114- True
115- >>> warty.getBinaryPackagePublishing(
116- ... "nope", PackagePublishingPocket.RELEASE, component_main,
117- ... warty.main_archive).count()
118- 0
119- >>> warty.getBinaryPackagePublishing(
120- ... "i386", PackagePublishingPocket.RELEASE, component_multiverse,
121- ... warty.main_archive).count()
122- 0
123- >>> warty.getBinaryPackagePublishing(
124- ... "i386", PackagePublishingPocket.BACKPORTS, component_main,
125- ... warty.main_archive).count()
126- 0
127- >>> warty.getBinaryPackagePublishing(
128- ... "i386", PackagePublishingPocket.RELEASE, component_main,
129- ... debian_archive).count()
130- 0
131-
132 getAllPublishedSources will return all publications with status PUBLISHED
133 and in the main archives for this distroseries:
134
135diff --git a/lib/lp/registry/interfaces/distroseries.py b/lib/lp/registry/interfaces/distroseries.py
136index d8c6370..ac2959c 100644
137--- a/lib/lp/registry/interfaces/distroseries.py
138+++ b/lib/lp/registry/interfaces/distroseries.py
139@@ -1,4 +1,4 @@
140-# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
141+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
142 # GNU Affero General Public License version 3 (see the file LICENSE).
143
144 """Interfaces including and related to IDistroSeries."""
145@@ -729,26 +729,6 @@ class IDistroSeriesPublic(
146 def addSection(section):
147 """SQLObject provided method to fill a related join key section."""
148
149- def getBinaryPackagePublishing(archtag, pocket, component, archive):
150- """Get BinaryPackagePublishings in a DistroSeries.
151-
152- Can optionally restrict the results by architecturetag, pocket and/or
153- component.
154-
155- If archive is passed, restricted the results to the given archive,
156- if it is suppressed the results will be restricted to the
157- distribution 'main_archive'.
158- """
159-
160- def getSourcePackagePublishing(pocket, component, archive):
161- """Return a selectResult of ISourcePackagePublishingHistory.
162-
163- According status and pocket.
164- If archive is passed, restricted the results to the given archive,
165- if it is suppressed the results will be restricted to the
166- distribution 'main_archive'.
167- """
168-
169 def searchPackages(text):
170 """Search through the package cache for this distroseries and return
171 DistroSeriesBinaryPackage objects that match the given text.
172diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py
173index cc209a5..8c3d76c 100644
174--- a/lib/lp/registry/model/distroseries.py
175+++ b/lib/lp/registry/model/distroseries.py
176@@ -1,4 +1,4 @@
177-# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
178+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
179 # GNU Affero General Public License version 3 (see the file LICENSE).
180
181 """Database classes for a distribution series."""
182@@ -12,10 +12,7 @@ __all__ = [
183
184 import collections
185 from io import BytesIO
186-from operator import (
187- attrgetter,
188- itemgetter,
189- )
190+from operator import itemgetter
191
192 import apt_pkg
193 from lazr.delegates import delegate_to
194@@ -86,10 +83,6 @@ from lp.registry.model.person import Person
195 from lp.registry.model.series import SeriesMixin
196 from lp.registry.model.sourcepackage import SourcePackage
197 from lp.registry.model.sourcepackagename import SourcePackageName
198-from lp.services.database.bulk import (
199- load_referencing,
200- load_related,
201- )
202 from lp.services.database.constants import (
203 DEFAULT,
204 UTC_NOW,
205@@ -116,10 +109,7 @@ from lp.services.database.stormexpr import (
206 IsTrue,
207 )
208 from lp.services.librarian.interfaces import ILibraryFileAliasSet
209-from lp.services.librarian.model import (
210- LibraryFileAlias,
211- LibraryFileContent,
212- )
213+from lp.services.librarian.model import LibraryFileAlias
214 from lp.services.mail.signedmessage import signed_message_from_bytes
215 from lp.services.propertycache import (
216 cachedproperty,
217@@ -146,9 +136,7 @@ from lp.soyuz.interfaces.queue import (
218 from lp.soyuz.interfaces.sourcepackageformat import (
219 ISourcePackageFormatSelectionSet,
220 )
221-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
222 from lp.soyuz.model.binarypackagename import BinaryPackageName
223-from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
224 from lp.soyuz.model.component import Component
225 from lp.soyuz.model.distributionsourcepackagerelease import (
226 DistributionSourcePackageRelease,
227@@ -159,10 +147,6 @@ from lp.soyuz.model.distroarchseries import (
228 )
229 from lp.soyuz.model.distroseriesbinarypackage import DistroSeriesBinaryPackage
230 from lp.soyuz.model.distroseriespackagecache import DistroSeriesPackageCache
231-from lp.soyuz.model.files import (
232- BinaryPackageFile,
233- SourcePackageReleaseFile,
234- )
235 from lp.soyuz.model.publishing import (
236 BinaryPackagePublishingHistory,
237 get_current_source_releases,
238@@ -173,7 +157,6 @@ from lp.soyuz.model.queue import (
239 PackageUploadQueue,
240 PackageUploadSource,
241 )
242-from lp.soyuz.model.section import Section
243 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
244 from lp.translations.enums import LanguagePackType
245 from lp.translations.model.distroserieslanguage import (
246@@ -1067,80 +1050,6 @@ class DistroSeries(SQLBase, BugTargetBase, HasSpecificationsMixin,
247 """See `IDistroSeries`."""
248 return self._getAllBinaries().find(scheduleddeletiondate=None)
249
250- def getSourcePackagePublishing(self, pocket, component, archive):
251- """See `IDistroSeries`."""
252- spphs = Store.of(self).find(
253- SourcePackagePublishingHistory,
254- SourcePackagePublishingHistory.archive == archive,
255- SourcePackagePublishingHistory.distroseries == self,
256- SourcePackagePublishingHistory.pocket == pocket,
257- SourcePackagePublishingHistory.component == component,
258- SourcePackagePublishingHistory.status ==
259- PackagePublishingStatus.PUBLISHED,
260- SourcePackagePublishingHistory.sourcepackagename ==
261- SourcePackageName.id).order_by(SourcePackageName.name)
262-
263- def eager_load(spphs):
264- # Preload everything which will be used by archivepublisher's
265- # build_source_stanza_fields.
266- load_related(Section, spphs, ["sectionID"])
267- sprs = load_related(
268- SourcePackageRelease, spphs, ["sourcepackagereleaseID"])
269- load_related(SourcePackageName, sprs, ["sourcepackagenameID"])
270- spr_ids = set(map(attrgetter("id"), sprs))
271- sprfs = list(Store.of(self).find(
272- SourcePackageReleaseFile,
273- SourcePackageReleaseFile.sourcepackagereleaseID.is_in(
274- spr_ids)).order_by(SourcePackageReleaseFile.libraryfileID))
275- file_map = collections.defaultdict(list)
276- for sprf in sprfs:
277- file_map[sprf.sourcepackagerelease].append(sprf)
278- for spr, files in file_map.items():
279- get_property_cache(spr).files = files
280- lfas = load_related(LibraryFileAlias, sprfs, ["libraryfileID"])
281- load_related(LibraryFileContent, lfas, ["contentID"])
282-
283- return DecoratedResultSet(spphs, pre_iter_hook=eager_load)
284-
285- def getBinaryPackagePublishing(self, archtag, pocket, component, archive):
286- """See `IDistroSeries`."""
287- bpphs = Store.of(self).find(
288- BinaryPackagePublishingHistory,
289- DistroArchSeries.distroseries == self,
290- DistroArchSeries.architecturetag == archtag,
291- BinaryPackagePublishingHistory.archive == archive,
292- BinaryPackagePublishingHistory.distroarchseries ==
293- DistroArchSeries.id,
294- BinaryPackagePublishingHistory.pocket == pocket,
295- BinaryPackagePublishingHistory.component == component,
296- BinaryPackagePublishingHistory.status ==
297- PackagePublishingStatus.PUBLISHED,
298- BinaryPackagePublishingHistory.binarypackagename ==
299- BinaryPackageName.id).order_by(BinaryPackageName.name)
300-
301- def eager_load(bpphs):
302- # Preload everything which will be used by archivepublisher's
303- # build_binary_stanza_fields.
304- load_related(Section, bpphs, ["sectionID"])
305- bprs = load_related(
306- BinaryPackageRelease, bpphs, ["binarypackagereleaseID"])
307- bpbs = load_related(BinaryPackageBuild, bprs, ["buildID"])
308- sprs = load_related(
309- SourcePackageRelease, bpbs, ["source_package_release_id"])
310- bpfs = load_referencing(
311- BinaryPackageFile, bprs, ["binarypackagereleaseID"])
312- file_map = collections.defaultdict(list)
313- for bpf in bpfs:
314- file_map[bpf.binarypackagerelease].append(bpf)
315- for bpr, files in file_map.items():
316- get_property_cache(bpr).files = files
317- lfas = load_related(LibraryFileAlias, bpfs, ["libraryfileID"])
318- load_related(LibraryFileContent, lfas, ["contentID"])
319- load_related(SourcePackageName, sprs, ["sourcepackagenameID"])
320- load_related(BinaryPackageName, bprs, ["binarypackagenameID"])
321-
322- return DecoratedResultSet(bpphs, pre_iter_hook=eager_load)
323-
324 def getBuildRecords(self, build_state=None, name=None, pocket=None,
325 arch_tag=None, user=None, binary_only=True):
326 """See IHasBuildRecords"""
327diff --git a/lib/lp/registry/tests/test_distroseries.py b/lib/lp/registry/tests/test_distroseries.py
328index f076ecd..a46cdcd 100644
329--- a/lib/lp/registry/tests/test_distroseries.py
330+++ b/lib/lp/registry/tests/test_distroseries.py
331@@ -1,4 +1,4 @@
332-# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
333+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
334 # GNU Affero General Public License version 3 (see the file LICENSE).
335
336 """Tests for distroseries."""
337@@ -7,7 +7,6 @@ __all__ = [
338 'CurrentSourceReleasesMixin',
339 ]
340
341-from functools import partial
342 import json
343
344 from testtools.matchers import Equals
345@@ -16,10 +15,6 @@ from zope.component import getUtility
346 from zope.security.proxy import removeSecurityProxy
347
348 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
349-from lp.archivepublisher.indices import (
350- build_binary_stanza_fields,
351- build_source_stanza_fields,
352- )
353 from lp.registry.errors import NoSuchDistroSeries
354 from lp.registry.interfaces.distroseries import IDistroSeriesSet
355 from lp.registry.interfaces.pocket import PackagePublishingPocket
356@@ -46,7 +41,6 @@ from lp.testing import (
357 api_url,
358 login,
359 person_logged_in,
360- record_two_runs,
361 StormStatementRecorder,
362 TestCase,
363 TestCaseWithFactory,
364@@ -584,47 +578,6 @@ class TestDistroSeriesPackaging(TestCaseWithFactory):
365 expected = ['translatable', 'linked', 'importabletranslatable']
366 self.assertEqual(expected, names)
367
368- def test_getSourcePackagePublishing_query_count(self):
369- # Check that the number of queries required to publish source
370- # packages is constant in the number of source packages.
371- def get_index_stanzas():
372- for spp in self.series.getSourcePackagePublishing(
373- PackagePublishingPocket.RELEASE, self.universe_component,
374- self.series.main_archive):
375- build_source_stanza_fields(
376- spp.sourcepackagerelease, spp.component, spp.section)
377-
378- recorder1, recorder2 = record_two_runs(
379- get_index_stanzas,
380- partial(
381- self.makeSeriesPackage, pocket=PackagePublishingPocket.RELEASE,
382- status=PackagePublishingStatus.PUBLISHED),
383- 5, 5)
384- self.assertThat(recorder1, HasQueryCount(Equals(11)))
385- self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
386-
387- def test_getBinaryPackagePublishing_query_count(self):
388- # Check that the number of queries required to publish binary
389- # packages is constant in the number of binary packages.
390- def get_index_stanzas(das):
391- for bpp in self.series.getBinaryPackagePublishing(
392- das.architecturetag, PackagePublishingPocket.RELEASE,
393- self.universe_component, self.series.main_archive):
394- build_binary_stanza_fields(
395- bpp.binarypackagerelease, bpp.component, bpp.section,
396- bpp.priority, bpp.phased_update_percentage, False)
397-
398- das = self.factory.makeDistroArchSeries(distroseries=self.series)
399- recorder1, recorder2 = record_two_runs(
400- partial(get_index_stanzas, das),
401- partial(
402- self.makeSeriesBinaryPackage, das=das,
403- pocket=PackagePublishingPocket.RELEASE,
404- status=PackagePublishingStatus.PUBLISHED),
405- 5, 5)
406- self.assertThat(recorder1, HasQueryCount(Equals(15)))
407- self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
408-
409
410 class TestDistroSeriesWebservice(TestCaseWithFactory):
411
412diff --git a/lib/lp/soyuz/interfaces/publishing.py b/lib/lp/soyuz/interfaces/publishing.py
413index a1d5dc2..874d1a1 100644
414--- a/lib/lp/soyuz/interfaces/publishing.py
415+++ b/lib/lp/soyuz/interfaces/publishing.py
416@@ -1,4 +1,4 @@
417-# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
418+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
419 # GNU Affero General Public License version 3 (see the file LICENSE).
420
421 """Publishing interfaces."""
422@@ -1133,6 +1133,32 @@ class IPublishingSet(Interface):
423 release in the given `archive`, `distroseries`, and `pocket`.
424 """
425
426+ def getSourcesForPublishing(archive, distroseries, pocket, component):
427+ """Get source publications which are published in a given context.
428+
429+ :param archive: The `Archive` to search.
430+ :param distroseries: The `DistroSeries` to search.
431+ :param pocket: The `PackagePublishingPocket` to search.
432+ :param component: The `Component` to search.
433+ :return: A result set of `SourcePackagePublishingHistory` objects in
434+ the given context and with the `PUBLISHED` status, ordered by
435+ source package name, with associated publisher-relevant objects
436+ preloaded.
437+ """
438+
439+ def getBinariesForPublishing(archive, distroarchseries, pocket, component):
440+ """Get binary publications which are published in a given context.
441+
442+ :param archive: The `Archive` to search.
443+ :param distroarchseries: The `DistroArchSeries` to search.
444+ :param pocket: The `PackagePublishingPocket` to search.
445+ :param component: The `Component` to search.
446+ :return: A result set of `BinaryPackagePublishingHistory` objects in
447+ the given context and with the `PUBLISHED` status, ordered by
448+ binary package name, with associated publisher-relevant objects
449+ preloaded.
450+ """
451+
452 def getChangesFilesForSources(one_or_more_source_publications):
453 """Return all changesfiles for each given source publication.
454
455diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
456index 421e649..ea0b012 100644
457--- a/lib/lp/soyuz/model/publishing.py
458+++ b/lib/lp/soyuz/model/publishing.py
459@@ -1,4 +1,4 @@
460-# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
461+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
462 # GNU Affero General Public License version 3 (see the file LICENSE).
463
464 __all__ = [
465@@ -45,6 +45,7 @@ from lp.app.errors import NotFoundError
466 from lp.buildmaster.enums import BuildStatus
467 from lp.registry.interfaces.person import validate_public_person
468 from lp.registry.interfaces.pocket import PackagePublishingPocket
469+from lp.registry.model.sourcepackagename import SourcePackageName
470 from lp.services.database import bulk
471 from lp.services.database.constants import UTC_NOW
472 from lp.services.database.datetimecol import UtcDateTimeCol
473@@ -112,6 +113,7 @@ from lp.soyuz.model.files import (
474 BinaryPackageFile,
475 SourcePackageReleaseFile,
476 )
477+from lp.soyuz.model.section import Section
478 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
479
480
481@@ -1511,6 +1513,81 @@ class PublishingSet:
482 active_publishing_status),
483 BinaryPackageRelease.architecturespecific == True)
484
485+ def getSourcesForPublishing(self, archive, distroseries, pocket,
486+ component):
487+ """See `IPublishingSet`."""
488+ spphs = IStore(SourcePackagePublishingHistory).find(
489+ SourcePackagePublishingHistory,
490+ SourcePackagePublishingHistory.archive == archive,
491+ SourcePackagePublishingHistory.distroseries == distroseries,
492+ SourcePackagePublishingHistory.pocket == pocket,
493+ SourcePackagePublishingHistory.component == component,
494+ SourcePackagePublishingHistory.status ==
495+ PackagePublishingStatus.PUBLISHED,
496+ SourcePackagePublishingHistory.sourcepackagename ==
497+ SourcePackageName.id).order_by(SourcePackageName.name)
498+
499+ def eager_load(spphs):
500+ # Preload everything which will be used by archivepublisher's
501+ # build_source_stanza_fields.
502+ bulk.load_related(Section, spphs, ["sectionID"])
503+ sprs = bulk.load_related(
504+ SourcePackageRelease, spphs, ["sourcepackagereleaseID"])
505+ bulk.load_related(SourcePackageName, sprs, ["sourcepackagenameID"])
506+ spr_ids = set(map(attrgetter("id"), sprs))
507+ sprfs = list(IStore(SourcePackageReleaseFile).find(
508+ SourcePackageReleaseFile,
509+ SourcePackageReleaseFile.sourcepackagereleaseID.is_in(
510+ spr_ids)).order_by(SourcePackageReleaseFile.libraryfileID))
511+ file_map = defaultdict(list)
512+ for sprf in sprfs:
513+ file_map[sprf.sourcepackagerelease].append(sprf)
514+ for spr, files in file_map.items():
515+ get_property_cache(spr).files = files
516+ lfas = bulk.load_related(
517+ LibraryFileAlias, sprfs, ["libraryfileID"])
518+ bulk.load_related(LibraryFileContent, lfas, ["contentID"])
519+
520+ return DecoratedResultSet(spphs, pre_iter_hook=eager_load)
521+
522+ def getBinariesForPublishing(self, archive, distroarchseries, pocket,
523+ component):
524+ """See `IPublishingSet`."""
525+ bpphs = IStore(BinaryPackagePublishingHistory).find(
526+ BinaryPackagePublishingHistory,
527+ BinaryPackagePublishingHistory.archive == archive,
528+ BinaryPackagePublishingHistory.distroarchseries ==
529+ distroarchseries,
530+ BinaryPackagePublishingHistory.pocket == pocket,
531+ BinaryPackagePublishingHistory.component == component,
532+ BinaryPackagePublishingHistory.status ==
533+ PackagePublishingStatus.PUBLISHED,
534+ BinaryPackagePublishingHistory.binarypackagename ==
535+ BinaryPackageName.id).order_by(BinaryPackageName.name)
536+
537+ def eager_load(bpphs):
538+ # Preload everything which will be used by archivepublisher's
539+ # build_binary_stanza_fields.
540+ bulk.load_related(Section, bpphs, ["sectionID"])
541+ bprs = bulk.load_related(
542+ BinaryPackageRelease, bpphs, ["binarypackagereleaseID"])
543+ bpbs = bulk.load_related(BinaryPackageBuild, bprs, ["buildID"])
544+ sprs = bulk.load_related(
545+ SourcePackageRelease, bpbs, ["source_package_release_id"])
546+ bpfs = bulk.load_referencing(
547+ BinaryPackageFile, bprs, ["binarypackagereleaseID"])
548+ file_map = defaultdict(list)
549+ for bpf in bpfs:
550+ file_map[bpf.binarypackagerelease].append(bpf)
551+ for bpr, files in file_map.items():
552+ get_property_cache(bpr).files = files
553+ lfas = bulk.load_related(LibraryFileAlias, bpfs, ["libraryfileID"])
554+ bulk.load_related(LibraryFileContent, lfas, ["contentID"])
555+ bulk.load_related(SourcePackageName, sprs, ["sourcepackagenameID"])
556+ bulk.load_related(BinaryPackageName, bprs, ["binarypackagenameID"])
557+
558+ return DecoratedResultSet(bpphs, pre_iter_hook=eager_load)
559+
560 def getChangesFilesForSources(self, one_or_more_source_publications):
561 """See `IPublishingSet`."""
562 # Avoid circular imports.
563diff --git a/lib/lp/soyuz/tests/test_publishing.py b/lib/lp/soyuz/tests/test_publishing.py
564index fbb19f0..d645ef8 100644
565--- a/lib/lp/soyuz/tests/test_publishing.py
566+++ b/lib/lp/soyuz/tests/test_publishing.py
567@@ -1,9 +1,10 @@
568-# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
569+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
570 # GNU Affero General Public License version 3 (see the file LICENSE).
571
572 """Test native publication workflow for Soyuz. """
573
574 import datetime
575+from functools import partial
576 import io
577 import operator
578 import os
579@@ -20,6 +21,10 @@ from zope.security.proxy import removeSecurityProxy
580 from lp.app.errors import NotFoundError
581 from lp.archivepublisher.config import getPubConfig
582 from lp.archivepublisher.diskpool import DiskPool
583+from lp.archivepublisher.indices import (
584+ build_binary_stanza_fields,
585+ build_source_stanza_fields,
586+ )
587 from lp.buildmaster.enums import BuildStatus
588 from lp.buildmaster.interfaces.processor import IProcessorSet
589 from lp.registry.interfaces.distribution import IDistributionSet
590@@ -1029,6 +1034,176 @@ class TestPublishingSetLite(TestCaseWithFactory):
591 "override the corresponding deb instead.",
592 debug_bpph.changeOverride, new_phased_update_percentage=20)
593
594+ def makePublishedSourcePackage(self, series, pocket=None, status=None):
595+ # Make a published source package.
596+ name = self.factory.getUniqueUnicode()
597+ sourcepackagename = self.factory.makeSourcePackageName(name)
598+ component = getUtility(IComponentSet)["universe"]
599+ spph = self.factory.makeSourcePackagePublishingHistory(
600+ sourcepackagename=sourcepackagename, distroseries=series,
601+ component=component, pocket=pocket, status=status)
602+ source_package = self.factory.makeSourcePackage(
603+ sourcepackagename=spph.sourcepackagename, distroseries=series)
604+ spr = spph.sourcepackagerelease
605+ for extension in ("dsc", "tar.gz"):
606+ filename = "%s_%s.%s" % (spr.name, spr.version, extension)
607+ spr.addFile(self.factory.makeLibraryFileAlias(
608+ filename=filename, db_only=True))
609+ return source_package
610+
611+ def test_getSourcesForPublishing(self):
612+ # PublisherSet.getSourcesForPublishing returns all the ISPPH records
613+ # in a given publishing context. It is used as part of publishing
614+ # some types of archives.
615+ # XXX cjwatson 2022-03-28: Detach test from sampledata.
616+ ubuntu = getUtility(IDistributionSet)["ubuntu"]
617+ hoary = ubuntu["hoary"]
618+ component_main = getUtility(IComponentSet)["main"]
619+ component_multiverse = getUtility(IComponentSet)["multiverse"]
620+ debian_archive = getUtility(IDistributionSet)["debian"].main_archive
621+ publishing_set = getUtility(IPublishingSet)
622+
623+ spphs = publishing_set.getSourcesForPublishing(
624+ archive=hoary.main_archive, distroseries=hoary,
625+ pocket=PackagePublishingPocket.RELEASE, component=component_main)
626+ self.assertEqual(6, spphs.count())
627+ self.assertContentEqual(
628+ ["alsa-utils", "evolution", "libstdc++", "linux-source-2.6.15",
629+ "netapplet", "pmount"],
630+ {spph.sourcepackagerelease.name for spph in spphs})
631+ self.assertEqual(
632+ 0,
633+ publishing_set.getSourcesForPublishing(
634+ archive=hoary.main_archive, distroseries=hoary,
635+ pocket=PackagePublishingPocket.RELEASE,
636+ component=component_multiverse).count())
637+ self.assertEqual(
638+ 0,
639+ publishing_set.getSourcesForPublishing(
640+ archive=hoary.main_archive, distroseries=hoary,
641+ pocket=PackagePublishingPocket.BACKPORTS,
642+ component=component_main).count())
643+ self.assertEqual(
644+ 0,
645+ publishing_set.getSourcesForPublishing(
646+ archive=debian_archive, distroseries=hoary,
647+ pocket=PackagePublishingPocket.RELEASE,
648+ component=component_main).count())
649+
650+ def test_getSourcesForPublishing_query_count(self):
651+ # Check that the number of queries required to publish source
652+ # packages is constant in the number of source packages.
653+ series = self.factory.makeDistroSeries()
654+ archive = series.main_archive
655+ component_universe = getUtility(IComponentSet)["universe"]
656+
657+ def get_index_stanzas():
658+ for spp in getUtility(IPublishingSet).getSourcesForPublishing(
659+ archive=archive, distroseries=series,
660+ pocket=PackagePublishingPocket.RELEASE,
661+ component=component_universe):
662+ build_source_stanza_fields(
663+ spp.sourcepackagerelease, spp.component, spp.section)
664+
665+ recorder1, recorder2 = record_two_runs(
666+ get_index_stanzas,
667+ partial(
668+ self.makePublishedSourcePackage, series=series,
669+ pocket=PackagePublishingPocket.RELEASE,
670+ status=PackagePublishingStatus.PUBLISHED),
671+ 5, 5)
672+ self.assertThat(recorder1, HasQueryCount(Equals(8)))
673+ self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
674+
675+ def makePublishedBinaryPackage(self, das, pocket=None, status=None):
676+ # Make a published binary package.
677+ source = self.makePublishedSourcePackage(
678+ das.distroseries, pocket=pocket, status=status)
679+ spr = source.distinctreleases[0]
680+ binarypackagename = self.factory.makeBinaryPackageName(source.name)
681+ bpph = self.factory.makeBinaryPackagePublishingHistory(
682+ binarypackagename=binarypackagename, distroarchseries=das,
683+ component=spr.component, section_name=spr.section.name,
684+ status=status, pocket=pocket, source_package_release=spr)
685+ bpr = bpph.binarypackagerelease
686+ filename = "%s_%s_%s.deb" % (
687+ bpr.name, bpr.version, das.architecturetag)
688+ bpr.addFile(self.factory.makeLibraryFileAlias(
689+ filename=filename, db_only=True))
690+ return bpph
691+
692+ def test_getBinariesForPublishing(self):
693+ # PublisherSet.getBinariesForPublishing returns all the IBPPH
694+ # records in a given publishing context. It is used as part of
695+ # publishing some types of archives.
696+ # XXX cjwatson 2022-03-28: Detach test from sampledata.
697+ ubuntu = getUtility(IDistributionSet)["ubuntu"]
698+ warty = ubuntu["warty"]
699+ warty_i386 = warty["i386"]
700+ warty_another = self.factory.makeDistroArchSeries(distroseries=warty)
701+ component_main = getUtility(IComponentSet)["main"]
702+ component_multiverse = getUtility(IComponentSet)["multiverse"]
703+ debian_archive = getUtility(IDistributionSet)["debian"].main_archive
704+ publishing_set = getUtility(IPublishingSet)
705+
706+ bpphs = publishing_set.getBinariesForPublishing(
707+ archive=warty.main_archive, distroarchseries=warty_i386,
708+ pocket=PackagePublishingPocket.RELEASE, component=component_main)
709+ self.assertEqual(8, bpphs.count())
710+ self.assertIn(
711+ "mozilla-firefox",
712+ {bpph.binarypackagerelease.name for bpph in bpphs})
713+ self.assertEqual(
714+ 0,
715+ publishing_set.getBinariesForPublishing(
716+ archive=warty.main_archive, distroarchseries=warty_another,
717+ pocket=PackagePublishingPocket.RELEASE,
718+ component=component_main).count())
719+ self.assertEqual(
720+ 0,
721+ publishing_set.getBinariesForPublishing(
722+ archive=warty.main_archive, distroarchseries=warty_i386,
723+ pocket=PackagePublishingPocket.RELEASE,
724+ component=component_multiverse).count())
725+ self.assertEqual(
726+ 0,
727+ publishing_set.getBinariesForPublishing(
728+ archive=warty.main_archive, distroarchseries=warty_i386,
729+ pocket=PackagePublishingPocket.BACKPORTS,
730+ component=component_main).count())
731+ self.assertEqual(
732+ 0,
733+ publishing_set.getBinariesForPublishing(
734+ archive=debian_archive, distroarchseries=warty_i386,
735+ pocket=PackagePublishingPocket.RELEASE,
736+ component=component_main).count())
737+
738+ def test_getBinariesForPublishing_query_count(self):
739+ # Check that the number of queries required to publish binary
740+ # packages is constant in the number of binary packages.
741+ das = self.factory.makeDistroArchSeries()
742+ archive = das.main_archive
743+ component_universe = getUtility(IComponentSet)["universe"]
744+
745+ def get_index_stanzas():
746+ for bpp in getUtility(IPublishingSet).getBinariesForPublishing(
747+ archive=archive, distroarchseries=das,
748+ pocket=PackagePublishingPocket.RELEASE,
749+ component=component_universe):
750+ build_binary_stanza_fields(
751+ bpp.binarypackagerelease, bpp.component, bpp.section,
752+ bpp.priority, bpp.phased_update_percentage, False)
753+
754+ recorder1, recorder2 = record_two_runs(
755+ get_index_stanzas,
756+ partial(
757+ self.makePublishedBinaryPackage, das=das,
758+ pocket=PackagePublishingPocket.RELEASE,
759+ status=PackagePublishingStatus.PUBLISHED),
760+ 5, 5)
761+ self.assertThat(recorder1, HasQueryCount(Equals(11)))
762+ self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
763+
764
765 class TestSourceDomination(TestNativePublishingBase):
766 """Test SourcePackagePublishingHistory.supersede() operates correctly."""

Subscribers

People subscribed via source and target branches

to status/vote changes: