Merge lp:~jtv/launchpad/bug-884649-branch-2 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Superseded
Proposed branch: lp:~jtv/launchpad/bug-884649-branch-2
Merge into: lp:launchpad
Diff against target: 949 lines (+490/-194)
3 files modified
lib/lp/archivepublisher/domination.py (+272/-155)
lib/lp/archivepublisher/tests/test_dominator.py (+216/-27)
lib/lp/soyuz/model/publishing.py (+2/-12)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-884649-branch-2
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+81025@code.launchpad.net

This proposal has been superseded by a proposal from 2011-11-02.

Commit message

Move special-casing for publication liveness out of inner domination loop; specialize binary passes.

Description of the change

= Summary =

This is part of the Dominator optimization work needed for bug 884649.

Binary domination needs 2 passes. This is because publications for the "all" architecture, which are copied into each architecture, can't be superseded until all architecture-specific publications coming from the same source package release have been superseded. Only then can we decide whether arch-all publications can be superseded. This is because a binary package from the same source package release may still depend on the arch-all binary package, and may not have finished building yet.

The code for dealing with this subtlety is a bit hard to place, because it's deep down in the inner loop as a special case. The special case is applied to both binary-domination passes, although there's really no point: the first pass may conceivably supersede a few arch-all publications but it won't deal with all of them. Meanwhile we pay a hefty price for the extra check.

== Proposed fix ==

Hoist the decision of which publications must stay live out of dominatePackage. It's a policy decision for the caller; the API lets the caller specify which versions stay live.

With that, we can specialize the passes more, and cut out complexity that is slowing things down.

You'll see three new functions explaining exactly what liveness criteria each domination pass uses. The source pass is easy. The first binary pass deals with architecture-specific publications; arch-all ones must be considered, but they are simply kept alive. And once all architectures have gone through first-pass binary domination, superseding as many arch-specific publications as possible, the second binary-domination pass considers just what needs to happen to those arch-all publications.

== Pre-implementation notes ==

Discussed in detail with Julian; see bug 884649 for the plan we're following in addressing this. We should also be able to re-use liveness decisions across architectures (and probably binary packages building from the same source package).

Raphaƫl suggested another clever improvement: Steve K. has just initialized a new, denormalized column on BinaryPackagePublishingHistory that contains the package name. That should enable a few shortcuts as well.

By the way, note that we can't just dominate arch-all and arch-specific publications separately. Packages may vacillate between the two, producing successive releases that need to be considered together even if domination of some publications may need to be postponed.

== Implementation details ==

I had to ditch _dominatePublications, because the discovery of live versions went into the middle of it. I reasoned that if removing it introduced too much code duplication, it could return with a callback for live-version discovery. But the loss turned out to be pretty small: it's really just a 3-line loop, including log output.

== Tests ==

{{{
./bin/test -vvc lp.archivepublisher.tests.test_dominator
}}}

== Demo and Q/A ==

Dominate Ubuntu and you'll see.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/archivepublisher/domination.py
  lib/lp/soyuz/model/publishing.py
  lib/lp/archivepublisher/tests/test_dominator.py

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/archivepublisher/domination.py'
--- lib/lp/archivepublisher/domination.py 2011-10-18 11:56:09 +0000
+++ lib/lp/archivepublisher/domination.py 2011-11-02 14:39:30 +0000
@@ -52,7 +52,12 @@
5252
53__all__ = ['Dominator']53__all__ = ['Dominator']
5454
55from collections import defaultdict
55from datetime import timedelta56from datetime import timedelta
57from operator import (
58 attrgetter,
59 itemgetter,
60 )
5661
57import apt_pkg62import apt_pkg
58from storm.expr import (63from storm.expr import (
@@ -67,7 +72,11 @@
67 flush_database_updates,72 flush_database_updates,
68 sqlvalues,73 sqlvalues,
69 )74 )
75from canonical.launchpad.components.decoratedresultset import (
76 DecoratedResultSet,
77 )
70from canonical.launchpad.interfaces.lpstorm import IStore78from canonical.launchpad.interfaces.lpstorm import IStore
79from canonical.launchpad.utilities.orderingcheck import OrderingCheck
71from lp.registry.model.sourcepackagename import SourcePackageName80from lp.registry.model.sourcepackagename import SourcePackageName
72from lp.services.database.bulk import load_related81from lp.services.database.bulk import load_related
73from lp.soyuz.enums import (82from lp.soyuz.enums import (
@@ -188,6 +197,104 @@
188 else:197 else:
189 return version_comparison198 return version_comparison
190199
200 def sortPublications(self, publications):
201 """Sort publications from most to least current versions."""
202 # Listify; we want to iterate this twice, which won't do for a
203 # non-persistent sequence.
204 sorted_publications = list(publications)
205 # Batch-load associated package releases; we'll be needing them
206 # to compare versions.
207 self.load_releases(sorted_publications)
208 # Now sort. This is that second iteration. An in-place sort
209 # won't hurt the original, because we're working on a copy of
210 # the original iterable.
211 sorted_publications.sort(cmp=self.compare, reverse=True)
212 return sorted_publications
213
214
215def find_live_source_versions(publications):
216 """Find versions out of Published `publications` that should stay live.
217
218 This particular notion of liveness applies to source domination: the
219 latest version stays live, and that's it.
220
221 :param publications: An iterable of `SourcePackagePublishingHistory`
222 sorted by descending package version.
223 :return: A list of live versions.
224 """
225 # Given the required sort order, the latest version is at the head
226 # of the list.
227 return [publications[0].sourcepackagerelease.version]
228
229
230def get_binary_versions(binary_publications):
231 """List versions for sequence of `BinaryPackagePublishingHistory`."""
232 return [pub.binarypackagerelease.version for pub in binary_publications]
233
234
235def find_live_binary_versions_first_pass(publications):
236 """Find versions out of Published `publications` that should stay live.
237
238 This particular notion of liveness applies to first-pass binary
239 domination: the latest version stays live, and so do publications of
240 binary packages for the "all" architecture.
241
242 :param publications: An iterable of `BinaryPackagePublishingHistory`,
243 sorted by descending package version.
244 :return: A list of live versions.
245 """
246 publications = list(publications)
247 latest = publications.pop(0)
248 return get_binary_versions(
249 [latest] + [
250 pub for pub in publications if not pub.architecture_specific])
251
252
253def find_live_binary_versions_second_pass(publications):
254 """Find versions out of Published `publications` that should stay live.
255
256 This particular notion of liveness applies to second-pass binary
257 domination: the latest version stays live, and architecture-specific
258 publications stay live (i.e, ones that are not for the "all"
259 architecture).
260
261 More importantly, any publication for binary packages of the "all"
262 architecture stay live if any of the non-"all" binary packages from
263 the same source package release are still active -- even if they are
264 for other architectures.
265
266 This is the raison d'etre for the two-pass binary domination algorithm:
267 to let us see which architecture-independent binary publications can be
268 superseded without rendering any architecture-specific binaries from the
269 same source package release uninstallable.
270
271 (Note that here, "active" includes Published publications but also
272 Pending ones. This is standard nomenclature in Soyuz. Some of the
273 domination code confuses matters by using the term "active" to mean only
274 Published publications).
275
276 :param publications: An iterable of `BinaryPackagePublishingHistory`,
277 sorted by descending package version.
278 :return: A list of live versions.
279 """
280 publications = list(publications)
281 latest = publications.pop(0)
282 is_arch_specific = attrgetter('architecture_specific')
283 arch_specific_pubs = filter(is_arch_specific, publications)
284 arch_indep_pubs = filter(
285 lambda pub: not is_arch_specific(pub),
286 publications)
287
288 # XXX JeroenVermeulen 2011-11-01 bug=884649: This is likely to be
289 # costly, and the result could be reused for all builds of the same
290 # source package release to all architectures.
291 reprieved_pubs = [
292 pub
293 for pub in arch_indep_pubs
294 if pub.getOtherPublicationsForSameSource().any()]
295
296 return get_binary_versions([latest] + arch_specific_pubs + reprieved_pubs)
297
191298
192class Dominator:299class Dominator:
193 """Manage the process of marking packages as superseded.300 """Manage the process of marking packages as superseded.
@@ -205,27 +312,6 @@
205 self.logger = logger312 self.logger = logger
206 self.archive = archive313 self.archive = archive
207314
208 def _checkArchIndep(self, publication):
209 """Return True if the binary publication can be superseded.
210
211 If the publication is an arch-indep binary, we can only supersede
212 it if all the binaries from the same source are also superseded,
213 else those binaries may become uninstallable.
214 See bug 34086.
215 """
216 binary = publication.binarypackagerelease
217 if not binary.architecturespecific:
218 # getOtherPublicationsForSameSource returns PENDING in
219 # addition to PUBLISHED binaries, and we rely on this since
220 # they must also block domination.
221 others = publication.getOtherPublicationsForSameSource()
222 if others.any():
223 # Don't dominate this arch:all binary as there are
224 # other arch-specific binaries from the same build
225 # that are still active.
226 return False
227 return True
228
229 def dominatePackage(self, publications, live_versions, generalization):315 def dominatePackage(self, publications, live_versions, generalization):
230 """Dominate publications for a single package.316 """Dominate publications for a single package.
231317
@@ -243,48 +329,47 @@
243329
244 :param publications: Iterable of publications for the same package,330 :param publications: Iterable of publications for the same package,
245 in the same archive, series, and pocket, all with status331 in the same archive, series, and pocket, all with status
246 `PackagePublishingStatus.PUBLISHED`.332 `PackagePublishingStatus.PUBLISHED`. They must be sorted from
247 :param live_versions: Iterable of version strings that are still333 most current to least current, as would be the result of
248 considered live for this package. The given publications will334 `generalization.sortPublications`.
249 remain active insofar as they represent any of these versions;335 :param live_versions: Iterable of versions that are still considered
250 older publications will be marked as superseded and newer ones336 "live" for this package. For any of these, the latest publication
251 as deleted.337 among `publications` will remain Published. Publications for
338 older releases, as well as older publications of live versions,
339 will be marked as Superseded. Publications of newer versions than
340 are listed in `live_versions` are marked as Deleted.
252 :param generalization: A `GeneralizedPublication` helper representing341 :param generalization: A `GeneralizedPublication` helper representing
253 the kind of publications these are--source or binary.342 the kind of publications these are: source or binary.
254 """343 """
255 publications = list(publications)344 live_versions = frozenset(live_versions)
256 generalization.load_releases(publications)
257
258 # Go through publications from latest version to oldest. This
259 # makes it easy to figure out which release superseded which:
260 # the dominant is always the oldest live release that is newer
261 # than the one being superseded.
262 publications = sorted(
263 publications, cmp=generalization.compare, reverse=True)
264345
265 self.logger.debug(346 self.logger.debug(
266 "Package has %d live publication(s). Live versions: %s",347 "Package has %d live publication(s). Live versions: %s",
267 len(publications), live_versions)348 len(publications), live_versions)
268349
350 # Verify that the publications are really sorted properly.
351 check_order = OrderingCheck(cmp=generalization.compare, reverse=True)
352
269 current_dominant = None353 current_dominant = None
270 dominant_version = None354 dominant_version = None
271355
272 for pub in publications:356 for pub in publications:
357 check_order.check(pub)
358
273 version = generalization.getPackageVersion(pub)359 version = generalization.getPackageVersion(pub)
274 # There should never be two published releases with the same360 # There should never be two published releases with the same
275 # version. So this comparison is really a string361 # version. So it doesn't matter whether this comparison is
276 # comparison, not a version comparison: if the versions are362 # really a string comparison or a version comparison: if the
277 # equal by either measure, they're from the same release.363 # versions are equal by either measure, they're from the same
278 if dominant_version is not None and version == dominant_version:364 # release.
365 if version == dominant_version:
279 # This publication is for a live version, but has been366 # This publication is for a live version, but has been
280 # superseded by a newer publication of the same version.367 # superseded by a newer publication of the same version.
281 # Supersede it.368 # Supersede it.
282 pub.supersede(current_dominant, logger=self.logger)369 pub.supersede(current_dominant, logger=self.logger)
283 self.logger.debug2(370 self.logger.debug2(
284 "Superseding older publication for version %s.", version)371 "Superseding older publication for version %s.", version)
285 elif (version in live_versions or372 elif version in live_versions:
286 (not generalization.is_source and
287 not self._checkArchIndep(pub))):
288 # This publication stays active; if any publications373 # This publication stays active; if any publications
289 # that follow right after this are to be superseded,374 # that follow right after this are to be superseded,
290 # this is the release that they are superseded by.375 # this is the release that they are superseded by.
@@ -303,50 +388,32 @@
303 pub.supersede(current_dominant, logger=self.logger)388 pub.supersede(current_dominant, logger=self.logger)
304 self.logger.debug2("Superseding version %s.", version)389 self.logger.debug2("Superseding version %s.", version)
305390
306 def _dominatePublications(self, pubs, generalization):391 def _sortPackages(self, publications, generalization):
307 """Perform dominations for the given publications.392 """Partition publications by package name, and sort them.
308393
309 Keep the latest published version for each package active,394 The publications are sorted from most current to least current,
310 superseding older versions.395 as required by `dominatePackage` etc.
311396
312 :param pubs: A dict mapping names to a list of publications. Every397 :param publications: An iterable of `SourcePackagePublishingHistory`
313 publication must be PUBLISHED or PENDING, and the first in each398 or of `BinaryPackagePublishingHistory`.
314 list will be treated as dominant (so should be the latest).399 :param generalization: A `GeneralizedPublication` helper representing
315 :param generalization: A `GeneralizedPublication` helper representing400 the kind of publications these are: source or binary.
316 the kind of publications these are--source or binary.401 :return: A dict mapping each package name to a sorted list of
317 """402 publications from `publications`.
318 self.logger.debug("Dominating packages...")403 """
319 for name, publications in pubs.iteritems():404 pubs_by_package = defaultdict(list)
320 assert publications, "Empty list of publications for %s." % name405 for pub in publications:
321 # Since this always picks the latest version as the live406 pubs_by_package[generalization.getPackageName(pub)].append(pub)
322 # one, this dominatePackage call will never result in a407
323 # deletion.408 # Sort the publication lists. This is not an in-place sort, so
324 latest_version = generalization.getPackageVersion(publications[0])409 # it involves altering the dict while we iterate it. Listify
325 self.logger.debug2("Dominating %s" % name)410 # the keys so that we can be sure that we're not altering the
326 self.dominatePackage(411 # iteration order while iteration is underway.
327 publications, [latest_version], generalization)412 for package in list(pubs_by_package.keys()):
328413 pubs_by_package[package] = generalization.sortPublications(
329 def _sortPackages(self, pkglist, generalization):414 pubs_by_package[package])
330 """Map out packages by name, and sort by descending version.415
331416 return pubs_by_package
332 :param pkglist: An iterable of `SourcePackagePublishingHistory` or
333 `BinaryPackagePublishingHistory`.
334 :param generalization: A `GeneralizedPublication` helper representing
335 the kind of publications these are--source or binary.
336 :return: A dict mapping each package name to a list of publications
337 from `pkglist`, newest first.
338 """
339 self.logger.debug("Sorting packages...")
340
341 outpkgs = {}
342 for inpkg in pkglist:
343 key = generalization.getPackageName(inpkg)
344 outpkgs.setdefault(key, []).append(inpkg)
345
346 for package_pubs in outpkgs.itervalues():
347 package_pubs.sort(cmp=generalization.compare, reverse=True)
348
349 return outpkgs
350417
351 def _setScheduledDeletionDate(self, pub_record):418 def _setScheduledDeletionDate(self, pub_record):
352 """Set the scheduleddeletiondate on a publishing record.419 """Set the scheduleddeletiondate on a publishing record.
@@ -442,72 +509,98 @@
442 # always equals to "scheduleddeletiondate - quarantine".509 # always equals to "scheduleddeletiondate - quarantine".
443 pub_record.datemadepending = UTC_NOW510 pub_record.datemadepending = UTC_NOW
444511
512 def findBinariesForDomination(self, distroarchseries, pocket):
513 """Find binary publications that need dominating.
514
515 This is only for traditional domination, where the latest published
516 publication is always kept published. It will ignore publications
517 that have no other publications competing for the same binary package.
518 """
519 # Avoid circular imports.
520 from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
521
522 bpph_location_clauses = [
523 BinaryPackagePublishingHistory.status ==
524 PackagePublishingStatus.PUBLISHED,
525 BinaryPackagePublishingHistory.distroarchseries ==
526 distroarchseries,
527 BinaryPackagePublishingHistory.archive == self.archive,
528 BinaryPackagePublishingHistory.pocket == pocket,
529 ]
530 candidate_binary_names = Select(
531 BinaryPackageName.id,
532 And(
533 BinaryPackageRelease.binarypackagenameID ==
534 BinaryPackageName.id,
535 BinaryPackagePublishingHistory.binarypackagereleaseID ==
536 BinaryPackageRelease.id,
537 bpph_location_clauses,
538 ),
539 group_by=BinaryPackageName.id,
540 having=Count(BinaryPackagePublishingHistory.id) > 1)
541 main_clauses = [
542 BinaryPackageRelease.id ==
543 BinaryPackagePublishingHistory.binarypackagereleaseID,
544 BinaryPackageRelease.binarypackagenameID.is_in(
545 candidate_binary_names),
546 BinaryPackageRelease.binpackageformat !=
547 BinaryPackageFormat.DDEB,
548 ]
549 main_clauses.extend(bpph_location_clauses)
550
551 store = IStore(BinaryPackagePublishingHistory)
552
553 # We're going to access the BPRs as well. Since we make the
554 # database look them up anyway, and since there won't be many
555 # duplications among them, load them alongside the publications.
556 # We'll also want their BinaryPackageNames, but adding those to
557 # the join would complicate the query.
558 query = store.find(
559 (BinaryPackagePublishingHistory, BinaryPackageRelease),
560 *main_clauses)
561 return DecoratedResultSet(query, itemgetter(0))
562
445 def dominateBinaries(self, distroseries, pocket):563 def dominateBinaries(self, distroseries, pocket):
446 """Perform domination on binary package publications.564 """Perform domination on binary package publications.
447565
448 Dominates binaries, restricted to `distroseries`, `pocket`, and566 Dominates binaries, restricted to `distroseries`, `pocket`, and
449 `self.archive`.567 `self.archive`.
450 """568 """
451 # Avoid circular imports.
452 from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
453
454 generalization = GeneralizedPublication(is_source=False)569 generalization = GeneralizedPublication(is_source=False)
455570
456 for distroarchseries in distroseries.architectures:571 for distroarchseries in distroseries.architectures:
457 self.logger.info(572 self.logger.info(
458 "Performing domination across %s/%s (%s)",573 "Performing domination across %s/%s (%s)",
459 distroseries.name, pocket.title,574 distroarchseries.distroseries.name, pocket.title,
460 distroarchseries.architecturetag)575 distroarchseries.architecturetag)
461576
462 bpph_location_clauses = [577 self.logger.info("Finding binaries...")
463 BinaryPackagePublishingHistory.status ==578 bins = self.findBinariesForDomination(distroarchseries, pocket)
464 PackagePublishingStatus.PUBLISHED,579 sorted_packages = self._sortPackages(bins, generalization)
465 BinaryPackagePublishingHistory.distroarchseries ==580 self.logger.info("Dominating binaries...")
466 distroarchseries,581 for name, pubs in sorted_packages.iteritems():
467 BinaryPackagePublishingHistory.archive == self.archive,582 self.logger.debug("Dominating %s" % name)
468 BinaryPackagePublishingHistory.pocket == pocket,583 assert len(pubs) > 0, "Dominating zero binaries!"
469 ]584 live_versions = find_live_binary_versions_first_pass(pubs)
470 candidate_binary_names = Select(585 self.dominatePackage(pubs, live_versions, generalization)
471 BinaryPackageName.id,586
472 And(587 # We need to make a second pass to cover the cases where:
473 BinaryPackageRelease.binarypackagenameID ==588 # * arch-specific binaries were not all dominated before arch-all
474 BinaryPackageName.id,589 # ones that depend on them
475 BinaryPackagePublishingHistory.binarypackagereleaseID ==590 # * An arch-all turned into an arch-specific, or vice-versa
476 BinaryPackageRelease.id,591 # * A package is completely schizophrenic and changes all of
477 bpph_location_clauses,592 # its binaries between arch-all and arch-any (apparently
478 ),593 # occurs sometimes!)
479 group_by=BinaryPackageName.id,594 for distroarchseries in distroseries.architectures:
480 having=Count(BinaryPackagePublishingHistory.id) > 1)595 self.logger.info("Finding binaries...(2nd pass)")
481 main_clauses = [596 bins = self.findBinariesForDomination(distroarchseries, pocket)
482 BinaryPackagePublishingHistory,597 sorted_packages = self._sortPackages(bins, generalization)
483 BinaryPackageRelease.id ==598 self.logger.info("Dominating binaries...(2nd pass)")
484 BinaryPackagePublishingHistory.binarypackagereleaseID,599 for name, pubs in sorted_packages.iteritems():
485 BinaryPackageRelease.binarypackagenameID.is_in(600 self.logger.debug("Dominating %s" % name)
486 candidate_binary_names),601 assert len(pubs) > 0, "Dominating zero binaries in 2nd pass!"
487 BinaryPackageRelease.binpackageformat !=602 live_versions = find_live_binary_versions_second_pass(pubs)
488 BinaryPackageFormat.DDEB,603 self.dominatePackage(pubs, live_versions, generalization)
489 ]
490 main_clauses.extend(bpph_location_clauses)
491
492 def do_domination(pass2_msg=""):
493 msg = "binaries..." + pass2_msg
494 self.logger.info("Finding %s" % msg)
495 bins = IStore(
496 BinaryPackagePublishingHistory).find(*main_clauses)
497 self.logger.info("Dominating %s" % msg)
498 sorted_packages = self._sortPackages(bins, generalization)
499 self._dominatePublications(sorted_packages, generalization)
500
501 do_domination()
502
503 # We need to make a second pass to cover the cases where:
504 # * arch-specific binaries were not all dominated before arch-all
505 # ones that depend on them
506 # * An arch-all turned into an arch-specific, or vice-versa
507 # * A package is completely schizophrenic and changes all of
508 # its binaries between arch-all and arch-any (apparently
509 # occurs sometimes!)
510 do_domination("(2nd pass)")
511604
512 def _composeActiveSourcePubsCondition(self, distroseries, pocket):605 def _composeActiveSourcePubsCondition(self, distroseries, pocket):
513 """Compose ORM condition for restricting relevant source pubs."""606 """Compose ORM condition for restricting relevant source pubs."""
@@ -522,21 +615,16 @@
522 SourcePackagePublishingHistory.pocket == pocket,615 SourcePackagePublishingHistory.pocket == pocket,
523 )616 )
524617
525 def dominateSources(self, distroseries, pocket):618 def findSourcesForDomination(self, distroseries, pocket):
526 """Perform domination on source package publications.619 """Find binary publications that need dominating.
527620
528 Dominates sources, restricted to `distroseries`, `pocket`, and621 This is only for traditional domination, where the latest published
529 `self.archive`.622 publication is always kept published. It will ignore publications
623 that have no other publications competing for the same binary package.
530 """624 """
531 # Avoid circular imports.625 # Avoid circular imports.
532 from lp.soyuz.model.publishing import SourcePackagePublishingHistory626 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
533627
534 generalization = GeneralizedPublication(is_source=True)
535
536 self.logger.debug(
537 "Performing domination across %s/%s (Source)",
538 distroseries.name, pocket.title)
539
540 spph_location_clauses = self._composeActiveSourcePubsCondition(628 spph_location_clauses = self._composeActiveSourcePubsCondition(
541 distroseries, pocket)629 distroseries, pocket)
542 having_multiple_active_publications = (630 having_multiple_active_publications = (
@@ -546,16 +634,44 @@
546 And(join_spph_spr(), join_spr_spn(), spph_location_clauses),634 And(join_spph_spr(), join_spr_spn(), spph_location_clauses),
547 group_by=SourcePackageName.id,635 group_by=SourcePackageName.id,
548 having=having_multiple_active_publications)636 having=having_multiple_active_publications)
549 sources = IStore(SourcePackagePublishingHistory).find(637
550 SourcePackagePublishingHistory,638 # We'll also access the SourcePackageReleases associated with
639 # the publications we find. Since they're in the join anyway,
640 # load them alongside the publications.
641 # Actually we'll also want the SourcePackageNames, but adding
642 # those to the (outer) query would complicate it, and
643 # potentially slow it down.
644 query = IStore(SourcePackagePublishingHistory).find(
645 (SourcePackagePublishingHistory, SourcePackageRelease),
551 join_spph_spr(),646 join_spph_spr(),
552 SourcePackageRelease.sourcepackagenameID.is_in(647 SourcePackageRelease.sourcepackagenameID.is_in(
553 candidate_source_names),648 candidate_source_names),
554 spph_location_clauses)649 spph_location_clauses)
650 return DecoratedResultSet(query, itemgetter(0))
651
652 def dominateSources(self, distroseries, pocket):
653 """Perform domination on source package publications.
654
655 Dominates sources, restricted to `distroseries`, `pocket`, and
656 `self.archive`.
657 """
658 self.logger.debug(
659 "Performing domination across %s/%s (Source)",
660 distroseries.name, pocket.title)
661
662 generalization = GeneralizedPublication(is_source=True)
663
664 self.logger.debug("Finding sources...")
665 sources = self.findSourcesForDomination(distroseries, pocket)
666 sorted_packages = self._sortPackages(sources, generalization)
555667
556 self.logger.debug("Dominating sources...")668 self.logger.debug("Dominating sources...")
557 self._dominatePublications(669 for name, pubs in sorted_packages.iteritems():
558 self._sortPackages(sources, generalization), generalization)670 self.logger.debug("Dominating %s" % name)
671 assert len(pubs) > 0, "Dominating zero binaries!"
672 live_versions = find_live_source_versions(pubs)
673 self.dominatePackage(pubs, live_versions, generalization)
674
559 flush_database_updates()675 flush_database_updates()
560676
561 def findPublishedSourcePackageNames(self, distroseries, pocket):677 def findPublishedSourcePackageNames(self, distroseries, pocket):
@@ -615,6 +731,7 @@
615 """731 """
616 generalization = GeneralizedPublication(is_source=True)732 generalization = GeneralizedPublication(is_source=True)
617 pubs = self.findPublishedSPPHs(distroseries, pocket, package_name)733 pubs = self.findPublishedSPPHs(distroseries, pocket, package_name)
734 pubs = generalization.sortPublications(pubs)
618 self.dominatePackage(pubs, live_versions, generalization)735 self.dominatePackage(pubs, live_versions, generalization)
619736
620 def judge(self, distroseries, pocket):737 def judge(self, distroseries, pocket):
621738
=== modified file 'lib/lp/archivepublisher/tests/test_dominator.py'
--- lib/lp/archivepublisher/tests/test_dominator.py 2011-10-27 11:36:13 +0000
+++ lib/lp/archivepublisher/tests/test_dominator.py 2011-11-02 14:39:30 +0000
@@ -30,6 +30,7 @@
30 StormStatementRecorder,30 StormStatementRecorder,
31 TestCaseWithFactory,31 TestCaseWithFactory,
32 )32 )
33from lp.testing.fakemethod import FakeMethod
33from lp.testing.matchers import HasQueryCount34from lp.testing.matchers import HasQueryCount
3435
3536
@@ -72,13 +73,9 @@
72 is_source=ISourcePackagePublishingHistory.providedBy(dominant))73 is_source=ISourcePackagePublishingHistory.providedBy(dominant))
73 dominator = Dominator(self.logger, self.ubuntutest.main_archive)74 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
7475
75 # The _dominate* test methods require a dictionary where the76 pubs = [dominant, dominated]
76 # package name is the key. The key's value is a list of77 live_versions = [generalization.getPackageVersion(dominant)]
77 # source or binary packages representing dominant, the first element78 dominator.dominatePackage(pubs, live_versions, generalization)
78 # and dominated, the subsequents.
79 pubs = {'foo': [dominant, dominated]}
80
81 dominator._dominatePublications(pubs, generalization)
82 flush_database_updates()79 flush_database_updates()
8380
84 # The dominant version remains correctly published.81 # The dominant version remains correctly published.
@@ -158,16 +155,30 @@
158 [foo_10_source] + foo_10_binaries,155 [foo_10_source] + foo_10_binaries,
159 PackagePublishingStatus.SUPERSEDED)156 PackagePublishingStatus.SUPERSEDED)
160157
161 def testEmptyDomination(self):158 def test_dominateBinaries_rejects_empty_publication_list(self):
162 """Domination asserts for not empty input list."""159 """Domination asserts for non-empty input list."""
163 dominator = Dominator(self.logger, self.ubuntutest.main_archive)160 package = self.factory.makeBinaryPackageName()
164 pubs = {'foo': []}161 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
165 # This isn't a really good exception. It should probably be162 dominator.mapPackages = FakeMethod({package.name: []})
166 # something more indicative of bad input.163 # This isn't a really good exception. It should probably be
167 self.assertRaises(164 # something more indicative of bad input.
168 AssertionError,165 self.assertRaises(
169 dominator._dominatePublications,166 AssertionError,
170 pubs, GeneralizedPublication(True))167 dominator.dominateBinaries,
168 self.factory.makeDistroArchSeries().distroseries,
169 self.factory.getAnyPocket())
170
171 def test_dominateSources_rejects_empty_publication_list(self):
172 """Domination asserts for non-empty input list."""
173 package = self.factory.makeSourcePackageName()
174 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
175 dominator.mapPackages = FakeMethod({package.name: []})
176 # This isn't a really good exception. It should probably be
177 # something more indicative of bad input.
178 self.assertRaises(
179 AssertionError,
180 dominator.dominateSources,
181 self.factory.makeDistroSeries(), self.factory.getAnyPocket())
171182
172 def test_archall_domination(self):183 def test_archall_domination(self):
173 # Arch-all binaries should not be dominated when a new source184 # Arch-all binaries should not be dominated when a new source
@@ -361,7 +372,10 @@
361def make_spphs_for_versions(factory, versions):372def make_spphs_for_versions(factory, versions):
362 """Create publication records for each of `versions`.373 """Create publication records for each of `versions`.
363374
364 They records are created in the same order in which they are specified.375 All these publications will be in the same source package, archive,
376 distroseries, and pocket. They will all be in Published status.
377
378 The records are created in the same order in which they are specified.
365 Make the order irregular to prove that version ordering is not a379 Make the order irregular to prove that version ordering is not a
366 coincidence of object creation order etc.380 coincidence of object creation order etc.
367381
@@ -371,6 +385,7 @@
371 spn = factory.makeSourcePackageName()385 spn = factory.makeSourcePackageName()
372 distroseries = factory.makeDistroSeries()386 distroseries = factory.makeDistroSeries()
373 pocket = factory.getAnyPocket()387 pocket = factory.getAnyPocket()
388 archive = distroseries.main_archive
374 sprs = [389 sprs = [
375 factory.makeSourcePackageRelease(390 factory.makeSourcePackageRelease(
376 sourcepackagename=spn, version=version)391 sourcepackagename=spn, version=version)
@@ -378,11 +393,34 @@
378 return [393 return [
379 factory.makeSourcePackagePublishingHistory(394 factory.makeSourcePackagePublishingHistory(
380 distroseries=distroseries, pocket=pocket,395 distroseries=distroseries, pocket=pocket,
381 sourcepackagerelease=spr,396 sourcepackagerelease=spr, archive=archive,
382 status=PackagePublishingStatus.PUBLISHED)397 status=PackagePublishingStatus.PUBLISHED)
383 for spr in sprs]398 for spr in sprs]
384399
385400
401def make_bpphs_for_versions(factory, versions):
402 """Create publication records for each of `versions`.
403
404 All these publications will be in the same binary package, source
405 package, archive, distroarchseries, and pocket. They will all be in
406 Published status.
407 """
408 bpn = factory.makeBinaryPackageName()
409 spn = factory.makeSourcePackageName()
410 das = factory.makeDistroArchSeries()
411 archive = das.distroseries.main_archive
412 pocket = factory.getAnyPocket()
413 bprs = [
414 factory.makeBinaryPackageRelease(binarypackagename=bpn)
415 for version in versions]
416 return [
417 factory.makeBinaryPackagePublishingHistory(
418 binarypackagerelease=bpr, binarypackagename=bpn,
419 distroarchseries=das, pocket=pocket, archive=archive,
420 sourcepackagename=spn, status=PackagePublishingStatus.PUBLISHED)
421 for bpr in bprs]
422
423
386def list_source_versions(spphs):424def list_source_versions(spphs):
387 """Extract the versions from `spphs` as a list, in the same order."""425 """Extract the versions from `spphs` as a list, in the same order."""
388 return [spph.sourcepackagerelease.version for spph in spphs]426 return [spph.sourcepackagerelease.version for spph in spphs]
@@ -564,9 +602,10 @@
564 def test_dominatePackage_supersedes_older_pub_with_newer_live_pub(self):602 def test_dominatePackage_supersedes_older_pub_with_newer_live_pub(self):
565 # When marking a package as superseded, dominatePackage603 # When marking a package as superseded, dominatePackage
566 # designates a newer live version as the superseding version.604 # designates a newer live version as the superseding version.
605 generalization = GeneralizedPublication(True)
567 pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])606 pubs = make_spphs_for_versions(self.factory, ['1.0', '1.1'])
568 self.makeDominator(pubs).dominatePackage(607 self.makeDominator(pubs).dominatePackage(
569 pubs, ['1.1'], GeneralizedPublication(True))608 generalization.sortPublications(pubs), ['1.1'], generalization)
570 self.assertEqual(PackagePublishingStatus.SUPERSEDED, pubs[0].status)609 self.assertEqual(PackagePublishingStatus.SUPERSEDED, pubs[0].status)
571 self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)610 self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
572 self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)611 self.assertEqual(PackagePublishingStatus.PUBLISHED, pubs[1].status)
@@ -574,10 +613,11 @@
574 def test_dominatePackage_only_supersedes_with_live_pub(self):613 def test_dominatePackage_only_supersedes_with_live_pub(self):
575 # When marking a package as superseded, dominatePackage will614 # When marking a package as superseded, dominatePackage will
576 # only pick a live version as the superseding one.615 # only pick a live version as the superseding one.
616 generalization = GeneralizedPublication(True)
577 pubs = make_spphs_for_versions(617 pubs = make_spphs_for_versions(
578 self.factory, ['1.0', '2.0', '3.0', '4.0'])618 self.factory, ['1.0', '2.0', '3.0', '4.0'])
579 self.makeDominator(pubs).dominatePackage(619 self.makeDominator(pubs).dominatePackage(
580 pubs, ['3.0'], GeneralizedPublication(True))620 generalization.sortPublications(pubs), ['3.0'], generalization)
581 self.assertEqual([621 self.assertEqual([
582 pubs[2].sourcepackagerelease,622 pubs[2].sourcepackagerelease,
583 pubs[2].sourcepackagerelease,623 pubs[2].sourcepackagerelease,
@@ -589,23 +629,27 @@
589 def test_dominatePackage_supersedes_with_oldest_newer_live_pub(self):629 def test_dominatePackage_supersedes_with_oldest_newer_live_pub(self):
590 # When marking a package as superseded, dominatePackage picks630 # When marking a package as superseded, dominatePackage picks
591 # the oldest of the newer, live versions as the superseding one.631 # the oldest of the newer, live versions as the superseding one.
632 generalization = GeneralizedPublication(True)
592 pubs = make_spphs_for_versions(self.factory, ['2.7', '2.8', '2.9'])633 pubs = make_spphs_for_versions(self.factory, ['2.7', '2.8', '2.9'])
593 self.makeDominator(pubs).dominatePackage(634 self.makeDominator(pubs).dominatePackage(
594 pubs, ['2.8', '2.9'], GeneralizedPublication(True))635 generalization.sortPublications(pubs), ['2.8', '2.9'],
636 generalization)
595 self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)637 self.assertEqual(pubs[1].sourcepackagerelease, pubs[0].supersededby)
596638
597 def test_dominatePackage_only_supersedes_with_newer_live_pub(self):639 def test_dominatePackage_only_supersedes_with_newer_live_pub(self):
598 # When marking a package as superseded, dominatePackage only640 # When marking a package as superseded, dominatePackage only
599 # considers a newer version as the superseding one.641 # considers a newer version as the superseding one.
642 generalization = GeneralizedPublication(True)
600 pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2'])643 pubs = make_spphs_for_versions(self.factory, ['0.1', '0.2'])
601 self.makeDominator(pubs).dominatePackage(644 self.makeDominator(pubs).dominatePackage(
602 pubs, ['0.1'], GeneralizedPublication(True))645 generalization.sortPublications(pubs), ['0.1'], generalization)
603 self.assertEqual(None, pubs[1].supersededby)646 self.assertEqual(None, pubs[1].supersededby)
604 self.assertEqual(PackagePublishingStatus.DELETED, pubs[1].status)647 self.assertEqual(PackagePublishingStatus.DELETED, pubs[1].status)
605648
606 def test_dominatePackage_supersedes_replaced_pub_for_live_version(self):649 def test_dominatePackage_supersedes_replaced_pub_for_live_version(self):
607 # Even if a publication record is for a live version, a newer650 # Even if a publication record is for a live version, a newer
608 # one for the same version supersedes it.651 # one for the same version supersedes it.
652 generalization = GeneralizedPublication(True)
609 spr = self.factory.makeSourcePackageRelease()653 spr = self.factory.makeSourcePackageRelease()
610 series = self.factory.makeDistroSeries()654 series = self.factory.makeDistroSeries()
611 pocket = PackagePublishingPocket.RELEASE655 pocket = PackagePublishingPocket.RELEASE
@@ -622,7 +666,8 @@
622 ])666 ])
623667
624 self.makeDominator(pubs).dominatePackage(668 self.makeDominator(pubs).dominatePackage(
625 pubs, [spr.version], GeneralizedPublication(True))669 generalization.sortPublications(pubs), [spr.version],
670 generalization)
626 self.assertEqual([671 self.assertEqual([
627 PackagePublishingStatus.SUPERSEDED,672 PackagePublishingStatus.SUPERSEDED,
628 PackagePublishingStatus.SUPERSEDED,673 PackagePublishingStatus.SUPERSEDED,
@@ -634,12 +679,13 @@
634679
635 def test_dominatePackage_is_efficient(self):680 def test_dominatePackage_is_efficient(self):
636 # dominatePackage avoids issuing too many queries.681 # dominatePackage avoids issuing too many queries.
682 generalization = GeneralizedPublication(True)
637 versions = ["1.%s" % revision for revision in xrange(5)]683 versions = ["1.%s" % revision for revision in xrange(5)]
638 pubs = make_spphs_for_versions(self.factory, versions)684 pubs = make_spphs_for_versions(self.factory, versions)
639 with StormStatementRecorder() as recorder:685 with StormStatementRecorder() as recorder:
640 self.makeDominator(pubs).dominatePackage(686 self.makeDominator(pubs).dominatePackage(
641 pubs, versions[2:-1],687 generalization.sortPublications(pubs), versions[2:-1],
642 GeneralizedPublication(True))688 generalization)
643 self.assertThat(recorder, HasQueryCount(LessThan(5)))689 self.assertThat(recorder, HasQueryCount(LessThan(5)))
644690
645 def test_dominatePackage_advanced_scenario(self):691 def test_dominatePackage_advanced_scenario(self):
@@ -650,6 +696,7 @@
650 # don't just patch up the code or this test. Create unit tests696 # don't just patch up the code or this test. Create unit tests
651 # that specifically cover the difference, then change the code697 # that specifically cover the difference, then change the code
652 # and/or adapt this test to return to harmony.698 # and/or adapt this test to return to harmony.
699 generalization = GeneralizedPublication(True)
653 series = self.factory.makeDistroSeries()700 series = self.factory.makeDistroSeries()
654 package = self.factory.makeSourcePackageName()701 package = self.factory.makeSourcePackageName()
655 pocket = PackagePublishingPocket.RELEASE702 pocket = PackagePublishingPocket.RELEASE
@@ -696,7 +743,8 @@
696743
697 all_pubs = sum(pubs_by_version.itervalues(), [])744 all_pubs = sum(pubs_by_version.itervalues(), [])
698 Dominator(DevNullLogger(), series.main_archive).dominatePackage(745 Dominator(DevNullLogger(), series.main_archive).dominatePackage(
699 all_pubs, live_versions, GeneralizedPublication(True))746 generalization.sortPublications(all_pubs), live_versions,
747 generalization)
700748
701 for version in reversed(versions):749 for version in reversed(versions):
702 pubs = pubs_by_version[version]750 pubs = pubs_by_version[version]
@@ -921,3 +969,144 @@
921 [],969 [],
922 dominator.findPublishedSPPHs(970 dominator.findPublishedSPPHs(
923 spph.distroseries, spph.pocket, other_package.name))971 spph.distroseries, spph.pocket, other_package.name))
972
973 def test_findBinariesForDomination_finds_published_publications(self):
974 bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
975 dominator = self.makeDominator(bpphs)
976 self.assertContentEqual(
977 bpphs, dominator.findBinariesForDomination(
978 bpphs[0].distroarchseries, bpphs[0].pocket))
979
980 def test_findBinariesForDomination_skips_single_pub_packages(self):
981 # The domination algorithm that uses findBinariesForDomination
982 # always keeps the latest version live. Thus, a single
983 # publication isn't worth dominating. findBinariesForDomination
984 # won't return it.
985 bpphs = make_bpphs_for_versions(self.factory, ['1.0'])
986 dominator = self.makeDominator(bpphs)
987 self.assertContentEqual(
988 [], dominator.findBinariesForDomination(
989 bpphs[0].distroarchseries, bpphs[0].pocket))
990
991 def test_findBinariesForDomination_ignores_other_distroseries(self):
992 bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
993 dominator = self.makeDominator(bpphs)
994 das = bpphs[0].distroarchseries
995 other_series = self.factory.makeDistroSeries(
996 distribution=das.distroseries.distribution)
997 other_das = self.factory.makeDistroArchSeries(
998 distroseries=other_series, architecturetag=das.architecturetag,
999 processorfamily=das.processorfamily)
1000 self.assertContentEqual(
1001 [], dominator.findBinariesForDomination(
1002 other_das, bpphs[0].pocket))
1003
1004 def test_findBinariesForDomination_ignores_other_architectures(self):
1005 bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
1006 dominator = self.makeDominator(bpphs)
1007 other_das = self.factory.makeDistroArchSeries(
1008 distroseries=bpphs[0].distroseries)
1009 self.assertContentEqual(
1010 [], dominator.findBinariesForDomination(
1011 other_das, bpphs[0].pocket))
1012
1013 def test_findBinariesForDomination_ignores_other_archive(self):
1014 bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
1015 dominator = self.makeDominator(bpphs)
1016 dominator.archive = self.factory.makeArchive()
1017 self.assertContentEqual(
1018 [], dominator.findBinariesForDomination(
1019 bpphs[0].distroarchseries, bpphs[0].pocket))
1020
1021 def test_findBinariesForDomination_ignores_other_pocket(self):
1022 bpphs = make_bpphs_for_versions(self.factory, ['1.0', '1.1'])
1023 dominator = self.makeDominator(bpphs)
1024 for bpph in bpphs:
1025 removeSecurityProxy(bpph).pocket = PackagePublishingPocket.UPDATES
1026 self.assertContentEqual(
1027 [], dominator.findBinariesForDomination(
1028 bpphs[0].distroarchseries, PackagePublishingPocket.SECURITY))
1029
1030 def test_findBinariesForDomination_ignores_other_status(self):
1031 # If we have one BPPH for each possible status, plus one
1032 # Published one to stop findBinariesForDomination from skipping
1033 # the package, findBinariesForDomination returns only the
1034 # Published ones.
1035 versions = [
1036 '1.%d' % self.factory.getUniqueInteger()
1037 for status in PackagePublishingStatus.items] + ['0.9']
1038 bpphs = make_bpphs_for_versions(self.factory, versions)
1039 dominator = self.makeDominator(bpphs)
1040
1041 for bpph, status in zip(bpphs, PackagePublishingStatus.items):
1042 bpph.status = status
1043
1044 # These are the Published publications. The other ones will all
1045 # be ignored.
1046 published_bpphs = [
1047 bpph
1048 for bpph in bpphs
1049 if bpph.status == PackagePublishingStatus.PUBLISHED]
1050
1051 self.assertContentEqual(
1052 published_bpphs,
1053 dominator.findBinariesForDomination(
1054 bpphs[0].distroarchseries, bpphs[0].pocket))
1055
1056 def test_findSourcesForDomination_finds_published_publications(self):
1057 spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
1058 dominator = self.makeDominator(spphs)
1059 self.assertContentEqual(
1060 spphs, dominator.findSourcesForDomination(
1061 spphs[0].distroseries, spphs[0].pocket))
1062
1063 def test_findSourcesForDomination_skips_single_pub_packages(self):
1064 # The domination algorithm that uses findSourcesForDomination
1065 # always keeps the latest version live. Thus, a single
1066 # publication isn't worth dominating. findSourcesForDomination
1067 # won't return it.
1068 spphs = make_spphs_for_versions(self.factory, ['2.0'])
1069 dominator = self.makeDominator(spphs)
1070 self.assertContentEqual(
1071 [], dominator.findSourcesForDomination(
1072 spphs[0].distroseries, spphs[0].pocket))
1073
1074 def test_findSourcesForDomination_ignores_other_distroseries(self):
1075 spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
1076 dominator = self.makeDominator(spphs)
1077 other_series = self.factory.makeDistroSeries(
1078 distribution=spphs[0].distroseries.distribution)
1079 self.assertContentEqual(
1080 [], dominator.findSourcesForDomination(
1081 other_series, spphs[0].pocket))
1082
1083 def test_findSourcesForDomination_ignores_other_pocket(self):
1084 spphs = make_spphs_for_versions(self.factory, ['2.0', '2.1'])
1085 dominator = self.makeDominator(spphs)
1086 for spph in spphs:
1087 removeSecurityProxy(spph).pocket = PackagePublishingPocket.UPDATES
1088 self.assertContentEqual(
1089 [], dominator.findSourcesForDomination(
1090 spphs[0].distroseries, PackagePublishingPocket.SECURITY))
1091
1092 def test_findSourcesForDomination_ignores_other_status(self):
1093 versions = [
1094 '1.%d' % self.factory.getUniqueInteger()
1095 for status in PackagePublishingStatus.items] + ['0.9']
1096 spphs = make_spphs_for_versions(self.factory, versions)
1097 dominator = self.makeDominator(spphs)
1098
1099 for spph, status in zip(spphs, PackagePublishingStatus.items):
1100 spph.status = status
1101
1102 # These are the Published publications. The other ones will all
1103 # be ignored.
1104 published_spphs = [
1105 spph
1106 for spph in spphs
1107 if spph.status == PackagePublishingStatus.PUBLISHED]
1108
1109 self.assertContentEqual(
1110 published_spphs,
1111 dominator.findSourcesForDomination(
1112 spphs[0].distroseries, spphs[0].pocket))
9241113
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2011-10-23 02:58:56 +0000
+++ lib/lp/soyuz/model/publishing.py 2011-11-02 14:39:30 +0000
@@ -33,7 +33,6 @@
33 Desc,33 Desc,
34 LeftJoin,34 LeftJoin,
35 Or,35 Or,
36 Select,
37 Sum,36 Sum,
38 )37 )
39from storm.store import Store38from storm.store import Store
@@ -1154,19 +1153,10 @@
1154 # Avoid circular wotsits.1153 # Avoid circular wotsits.
1155 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild1154 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
1156 from lp.soyuz.model.distroarchseries import DistroArchSeries1155 from lp.soyuz.model.distroarchseries import DistroArchSeries
1157 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease1156
1158 source_select = Select(
1159 SourcePackageRelease.id,
1160 And(
1161 BinaryPackageBuild.source_package_release_id ==
1162 SourcePackageRelease.id,
1163 BinaryPackageRelease.build == BinaryPackageBuild.id,
1164 self.binarypackagereleaseID == BinaryPackageRelease.id,
1165 ))
1166 pubs = [1157 pubs = [
1167 BinaryPackageBuild.source_package_release_id ==1158 BinaryPackageBuild.source_package_release_id ==
1168 SourcePackageRelease.id,1159 self.binarypackagerelease.build.source_package_release_id,
1169 SourcePackageRelease.id.is_in(source_select),
1170 BinaryPackageRelease.build == BinaryPackageBuild.id,1160 BinaryPackageRelease.build == BinaryPackageBuild.id,
1171 BinaryPackagePublishingHistory.binarypackagereleaseID ==1161 BinaryPackagePublishingHistory.binarypackagereleaseID ==
1172 BinaryPackageRelease.id,1162 BinaryPackageRelease.id,