Merge lp:~wgrant/launchpad/refactor-_dominateBinary into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11181
Proposed branch: lp:~wgrant/launchpad/refactor-_dominateBinary
Merge into: lp:launchpad
Diff against target: 753 lines (+184/-226)
9 files modified
lib/lp/archivepublisher/domination.py (+49/-179)
lib/lp/archivepublisher/tests/test_dominator.py (+13/-15)
lib/lp/registry/doc/distroseries.txt (+3/-3)
lib/lp/soyuz/doc/archive.txt (+2/-7)
lib/lp/soyuz/doc/distroarchseries.txt (+2/-3)
lib/lp/soyuz/doc/publishing.txt (+2/-10)
lib/lp/soyuz/interfaces/publishing.py (+19/-6)
lib/lp/soyuz/model/publishing.py (+91/-1)
lib/lp/soyuz/scripts/ftpmaster.py (+3/-2)
To merge this branch: bzr merge lp:~wgrant/launchpad/refactor-_dominateBinary
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+29667@code.launchpad.net

Commit message

Move per-publication Dominator logic into the model.

Description of the change

This branch shuffles lots of logic from lp.archivepublisher into the model. The diff is horrific, I admit, but the files have ended up much cleaner than before.

Package domination is currently performed by lp.archivepublisher.domination.Dominator. It finds a list of domination candidates, then calls its _dominateSource and _dominateBinary methods to supersede each publication. But there's a a method for that purpose on IPublishing, which I(Source|Binary)PackagePublishingHistory inherit: IPublishing.supersede. Sadly, this method is only used in tests, and doesn't encapsulate the full functionality required to replace _dominateBinary and _dominateSource.

This branch moves the logic from _dominateBinary and _dominateSource into [SB]PPH.supersede, letting Dominator just identify candidates and call a single method on each to let them handle themselves.

SPPH.supersede is simple: it checks that the publication can be superseded, then calls the base method (setting datesuperseded and status), and sets supersededby if a dominant publication is provided.

BPPH.supersede does the same, but also incorporates additional functionality from _dominateBinar*ies*: the atomic arch-indep domination behaviour. As described in bug #48760, this requires that if an architecture-independent publication is superseded, it should immediately supersede the corresponding publications on every other architecture. This is implemented by moving Dominator._getOtherBinaryPublications to BPPH._getOtherPublications, and superseding publications found by it. With that behaviour encapsulated in the model, the Dominator code ends up much cleaner.

With all that code ripped out into the model, some duplication was revealed in Dominator. A couple of methods had distinct source and binary versions, with only simple attribute name differences. I've merged them by passing around a couple of attrgetters. Not pretty, but better than duplicating the code.

All this brings huge potential for test cleanups. But this branch is big enough, so another (slightly oversized) test rewrite branch follows.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

your VWS in supersede doesn't really gel for me - please remove or comment

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/domination.py'
2--- lib/lp/archivepublisher/domination.py 2010-05-18 10:30:12 +0000
3+++ lib/lp/archivepublisher/domination.py 2010-07-20 09:44:57 +0000
4@@ -52,9 +52,12 @@
5
6 __all__ = ['Dominator']
7
8-import apt_pkg
9 from datetime import timedelta
10+import functools
11 import gc
12+import operator
13+
14+import apt_pkg
15
16 from lp.archivepublisher import ELIGIBLE_DOMINATION_STATES
17 from canonical.database.constants import UTC_NOW
18@@ -73,38 +76,23 @@
19 clear_current_connection_cache()
20 gc.collect()
21
22-PENDING = PackagePublishingStatus.PENDING
23-PUBLISHED = PackagePublishingStatus.PUBLISHED
24-SUPERSEDED = PackagePublishingStatus.SUPERSEDED
25-DELETED = PackagePublishingStatus.DELETED
26-OBSOLETE = PackagePublishingStatus.OBSOLETE
27
28 # Ugly, but works
29 apt_pkg.InitSystem()
30
31-def _compare_source_packages_by_version_and_date(p1, p2):
32- """Compare packages p1 and p2 by their version; using Debian rules.
33-
34- If the comparison is the same sourcepackagerelease, compare by datecreated
35- instead. So later records beat earlier ones.
36- """
37- if p1.sourcepackagerelease.id == p2.sourcepackagerelease.id:
38- return cmp(p1.datecreated, p2.datecreated)
39-
40- return apt_pkg.VersionCompare(p1.sourcepackagerelease.version,
41- p2.sourcepackagerelease.version)
42-
43-def _compare_binary_packages_by_version_and_date(p1, p2):
44- """Compare packages p1 and p2 by their version; using Debian rules
45-
46- If the comparison is the same binarypackagerelease, compare by datecreated
47- instead. So later records beat earlier ones.
48- """
49- if p1.binarypackagerelease.id == p2.binarypackagerelease.id:
50- return cmp(p1.datecreated, p2.datecreated)
51-
52- return apt_pkg.VersionCompare(p1.binarypackagerelease.version,
53- p2.binarypackagerelease.version)
54+
55+def _compare_packages_by_version_and_date(get_release, p1, p2):
56+ """Compare publications p1 and p2 by their version; using Debian rules.
57+
58+ If the publications are for the same package, compare by datecreated
59+ instead. This lets newer records win.
60+ """
61+ if get_release(p1).id == get_release(p2).id:
62+ return cmp(p1.datecreated, p2.datecreated)
63+
64+ return apt_pkg.VersionCompare(get_release(p1).version,
65+ get_release(p2).version)
66+
67
68 class Dominator:
69 """ Manage the process of marking packages as superseded.
70@@ -123,131 +111,22 @@
71 self.archive = archive
72 self.debug = self._logger.debug
73
74- def _dominateSource(self, sourceinput):
75- """
76- Perform dominations for source.
77- """
78- self.debug("Dominating sources...")
79- for sourcename in sourceinput.keys():
80- # source is a list of versions ordered most-recent-first
81- # basically skip the first entry because that is
82- # never dominated by us, then just set subsequent entries
83- # to SUPERSEDED unless they're already there or pending
84- # removal
85- assert sourceinput[sourcename], (
86- "Empty list of publications for %s" % sourcename)
87- super_release = sourceinput[sourcename][0].sourcepackagerelease
88- super_release_name = super_release.sourcepackagename.name
89- for pubrec in sourceinput[sourcename][1:]:
90- if pubrec.status == PUBLISHED or pubrec.status == PENDING:
91- this_release = pubrec.sourcepackagerelease
92-
93- this_release_name = this_release.sourcepackagename.name
94- self.debug(
95- "%s/%s has been judged as superseded by %s/%s" %
96- (this_release_name, this_release.version,
97- super_release_name, super_release.version))
98-
99- pubrec.status = SUPERSEDED
100- pubrec.datesuperseded = UTC_NOW
101- pubrec.supersededby = super_release
102-
103- def _getOtherBinaryPublications(self, dominated):
104- """Return remaining publications of the same binarypackagerelease.
105-
106- It only considers binary publications in the same distroseries,
107- pocket and archive context, which is the limit where the domination
108- should happen.
109-
110- Return only binaries published or pending in the same component,
111- section and priority, this way the just-made override will be
112- preserved.
113- """
114- # Avoid circular imports.
115- from lp.soyuz.model.publishing import (
116- BinaryPackagePublishingHistory)
117-
118- dominated_series = dominated.distroarchseries.distroseries
119- available_architectures = [
120- das.id for das in dominated_series.architectures]
121- query = """
122- BinaryPackagePublishingHistory.status IN %s AND
123- BinaryPackagePublishingHistory.distroarchseries IN %s AND
124- BinaryPackagePublishingHistory.binarypackagerelease = %s AND
125- BinaryPackagePublishingHistory.pocket = %s AND
126- BinaryPackagePublishingHistory.archive = %s AND
127- BinaryPackagePublishingHistory.component = %s AND
128- BinaryPackagePublishingHistory.section = %s AND
129- BinaryPackagePublishingHistory.priority = %s
130- """ % sqlvalues([PUBLISHED, PENDING], available_architectures,
131- dominated.binarypackagerelease, dominated.pocket,
132- dominated.archive, dominated.component,
133- dominated.section, dominated.priority)
134- return BinaryPackagePublishingHistory.select(query)
135-
136- def _dominateBinary(self, dominated, dominant):
137- """Dominate the given binarypackagerelease publication."""
138- # At this point only PUBLISHED (ancient versions) or PENDING (
139- # multiple overrides/copies) publications should be given. We
140- # tolerate SUPERSEDED architecture-independent binaries, because
141- # they are dominated automatically once the first publication is
142- # processed.
143- if dominated.status not in [PUBLISHED, PENDING]:
144- arch_independent = (
145- dominated.binarypackagerelease.architecturespecific == False)
146- assert arch_independent, (
147- "Should not dominate unpublished architecture specific "
148- "binary %s (%s)" % (
149- dominated.binarypackagerelease.title,
150- dominated.distroarchseries.architecturetag))
151- return
152-
153- dominant_build = dominant.binarypackagerelease.build
154- distroarchseries = dominant_build.distro_arch_series
155- self.debug(
156- "The %s build of %s has been judged as superseded by the build "
157- "of %s. Arch-specific == %s" % (
158- distroarchseries.architecturetag,
159- dominated.binarypackagerelease.title,
160- dominant.binarypackagerelease.build.source_package_release.title,
161- dominated.binarypackagerelease.architecturespecific))
162- dominated.status = SUPERSEDED
163- dominated.datesuperseded = UTC_NOW
164- # Binary package releases are superseded by the new build,
165- # not the new binary package release. This is because
166- # there may not *be* a new matching binary package -
167- # source packages can change the binaries they build
168- # between releases.
169- dominated.supersededby = dominant_build
170-
171- def _dominateBinaries(self, binaryinput):
172- """Perform dominations for binaries."""
173- self.debug("Dominating binaries...")
174- for binaryname in binaryinput.keys():
175- # binary is a list of versions ordered most-recent-first
176- # basically skip the first entry because that is
177- # never dominated by us, then just set subsequent entries
178- # to SUPERSEDED unless they're already there or pending
179- # removal
180- assert binaryinput[binaryname], (
181- "Empty list of publications for %s" % binaryname)
182- # At some future point, this code might automatically locate
183- # binaries which are no longer built from source (NBS).
184- # Currently this is done in archive cruft check.
185- dominant = binaryinput[binaryname][0]
186- for dominated in binaryinput[binaryname][1:]:
187- # Dominate all publications of architecture independent
188- # binaries altogether in this distroseries and pocket.
189- if not dominated.binarypackagerelease.architecturespecific:
190- other_publications = self._getOtherBinaryPublications(
191- dominated)
192- for dominated in other_publications:
193- self._dominateBinary(dominated, dominant)
194- else:
195- self._dominateBinary(dominated, dominant)
196-
197-
198- def _sortPackages(self, pkglist, isSource=True):
199+ def _dominatePublications(self, pubs):
200+ """Perform dominations for the given publications.
201+
202+ :param pubs: A dict mapping names to a list of publications. Every
203+ publication must be PUBLISHED or PENDING, and the first in each
204+ list will be treated as dominant (so should be the latest).
205+ """
206+ self.debug("Dominating packages...")
207+
208+ for name in pubs.keys():
209+ assert pubs[name], (
210+ "Empty list of publications for %s" % name)
211+ for pubrec in pubs[name][1:]:
212+ pubrec.supersede(pubs[name][0], self)
213+
214+ def _sortPackages(self, pkglist, is_source=True):
215 # pkglist is a list of packages with the following
216 # * sourcepackagename or packagename as appropriate
217 # * version
218@@ -255,32 +134,22 @@
219 # Don't care about any other attributes
220 outpkgs = {}
221
222- if isSource:
223- self.debug("Sorting sources...")
224- else:
225- self.debug("Sorting binaries...")
226+ self.debug("Sorting packages...")
227+
228+ attr_prefix = 'source' if is_source else 'binary'
229+ get_release = operator.attrgetter(attr_prefix + 'packagerelease')
230+ get_name = operator.attrgetter(attr_prefix + 'packagename')
231
232 for inpkg in pkglist:
233- if isSource:
234- L = outpkgs.setdefault(
235- inpkg.sourcepackagerelease.sourcepackagename.name.encode(
236- 'utf-8'), [])
237- else:
238- L = outpkgs.setdefault(
239- inpkg.binarypackagerelease.binarypackagename.name.encode(
240- 'utf-8'), [])
241-
242+ L = outpkgs.setdefault(
243+ get_name(get_release(inpkg)).name.encode('utf-8'), [])
244 L.append(inpkg)
245
246 for pkgname in outpkgs:
247 if len(outpkgs[pkgname]) > 1:
248- if isSource:
249- outpkgs[pkgname].sort(
250- _compare_source_packages_by_version_and_date)
251- else:
252- outpkgs[pkgname].sort(
253- _compare_binary_packages_by_version_and_date)
254-
255+ outpkgs[pkgname].sort(
256+ functools.partial(
257+ _compare_packages_by_version_and_date, get_release))
258 outpkgs[pkgname].reverse()
259
260 return outpkgs
261@@ -443,11 +312,12 @@
262 binarypackagerelease.id
263 AND binarypackagerelease.binarypackagename IN (
264 SELECT name FROM PubDomHelper WHERE count > 1)"""
265- % sqlvalues (distroarchseries, self.archive,
266- pocket, PackagePublishingStatus.PUBLISHED),
267+ % sqlvalues(distroarchseries, self.archive,
268+ pocket, PackagePublishingStatus.PUBLISHED),
269 clauseTables=['BinaryPackageRelease'])
270
271- self._dominateBinaries(self._sortPackages(binaries, False))
272+ self.debug("Dominating binaries...")
273+ self._dominatePublications(self._sortPackages(binaries, False))
274 if do_clear_cache:
275 self.debug("Flushing SQLObject cache.")
276 clear_cache()
277@@ -464,7 +334,8 @@
278 sources = SourcePackagePublishingHistory.selectBy(
279 distroseries=dr, archive=self.archive, pocket=pocket,
280 status=PackagePublishingStatus.PUBLISHED)
281- self._dominateSource(self._sortPackages(sources))
282+ self.debug("Dominating sources...")
283+ self._dominatePublications(self._sortPackages(sources))
284 flush_database_updates()
285
286 sources = SourcePackagePublishingHistory.select("""
287@@ -492,4 +363,3 @@
288
289 self.debug("Domination for %s/%s finished" %
290 (dr.name, pocket.title))
291-
292
293=== modified file 'lib/lp/archivepublisher/tests/test_dominator.py'
294--- lib/lp/archivepublisher/tests/test_dominator.py 2010-07-18 00:24:06 +0000
295+++ lib/lp/archivepublisher/tests/test_dominator.py 2010-07-20 09:44:57 +0000
296@@ -75,7 +75,7 @@
297 # and dominated, the subsequents.
298 source_input = {'foo': [dominant_source, dominated_source]}
299
300- dominator._dominateSource(source_input)
301+ dominator._dominatePublications(source_input)
302 flush_database_updates()
303
304 # The dominant version remains correctly published.
305@@ -96,7 +96,7 @@
306 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
307 source_input = {'foo': []}
308 self.assertRaises(
309- AssertionError, dominator._dominateSource, source_input)
310+ AssertionError, dominator._dominatePublications, source_input)
311
312 def testBinariesDomination(self):
313 """Test overall binary domination procedure."""
314@@ -108,7 +108,7 @@
315 # See comment about domination input format and ordering above.
316 binary_input = {'foo-bin': [dominant, dominated]}
317
318- dominator._dominateBinaries(binary_input)
319+ dominator._dominatePublications(binary_input)
320 flush_database_updates()
321
322 # Dominant version remains correctly published.
323@@ -129,7 +129,7 @@
324 dominator = Dominator(self.logger, self.ubuntutest.main_archive)
325 binary_input = {'foo-bin': []}
326 self.assertRaises(
327- AssertionError, dominator._dominateBinaries, binary_input)
328+ AssertionError, dominator._dominatePublications, binary_input)
329
330 def testBinaryDomination(self):
331 """Test binary domination unit procedure."""
332@@ -138,7 +138,7 @@
333 [dominant_source, dominant, dominated_source,
334 dominated] = self.createSimpleDominationContext()
335
336- dominator._dominateBinary(dominated, dominant)
337+ dominated.supersede(dominant)
338 flush_database_updates()
339
340 dominated = self.checkBinaryPublication(
341@@ -150,7 +150,7 @@
342 def testBinaryDominationAssertsPendingOrPublished(self):
343 """Test binary domination asserts coherent dominated status.
344
345- Normally _dominateBinary only accepts domination candidates in
346+ Normally supersede() only accepts domination candidates in
347 PUBLISHED or PENDING status, a exception is opened for architecture
348 independent binaries because during the iteration they might have
349 been already SUPERSEDED with its first publication, when it happens
350@@ -165,7 +165,7 @@
351 dominated] = self.createSimpleDominationContext()
352
353 # Let's modify the domination candidate, so it will look wrong to
354- # _dominateBinary which will raise because it's a architecture
355+ # supersede() which will raise because it's a architecture
356 # specific binary publication not in PENDING or PUBLISHED state.
357 dominated.status = PackagePublishingStatus.SUPERSEDED
358 manual_domination_date = datetime.datetime(
359@@ -176,7 +176,7 @@
360 # An error like that in production clearly indicates that something
361 # is wrong in the Dominator look-up methods.
362 self.assertRaises(
363- AssertionError, dominator._dominateBinary, dominated, dominant)
364+ AssertionError, dominated.supersede, dominant)
365
366 # The refused publishing record remains the same.
367 dominated = self.checkBinaryPublication(
368@@ -188,7 +188,7 @@
369 dominated.binarypackagerelease.architecturespecific = False
370 flush_database_updates()
371
372- dominator._dominateBinary(dominated, dominant)
373+ dominated.supersede(dominant)
374 flush_database_updates()
375 dominated = self.checkBinaryPublication(
376 dominated, PackagePublishingStatus.SUPERSEDED)
377@@ -197,14 +197,12 @@
378 def testOtherBinaryPublications(self):
379 """Check the basis of architecture independent binary domination.
380
381- We use _getOtherBinaryPublications to identify other publications of
382- the same binarypackagerelease in other architectures (architecture
383+ We use _getOtherPublications to identify other publications of the
384+ same binarypackagerelease in other architectures (architecture
385 independent binaries), they will be dominated during a single step.
386
387 See overall details in `testDominationOfOldArchIndepBinaries`.
388 """
389- dominator = Dominator(self.logger, self.ubuntutest.main_archive)
390-
391 # Create architecture independent publications for foo-bin_1.0
392 # in i386 & hppa.
393 pub_source_archindep = self.getPubSource(
394@@ -232,7 +230,7 @@
395
396 # Check if we can reach the i386 publication using
397 # _getOtherBinaryPublications over the hppa binary.
398- [found] = list(dominator._getOtherBinaryPublications(hppa_pub))
399+ [found] = list(hppa_pub._getOtherPublications())
400 self.assertEqual(i386_pub, found)
401
402 # Create architecture specific publications for foo-bin_1.1 in
403@@ -251,7 +249,7 @@
404 # Check if there is no other publication of the hppa binary package
405 # release.
406 self.assertEqual(
407- dominator._getOtherBinaryPublications(hppa_pub).count(),
408+ hppa_pub._getOtherPublications().count(),
409 0)
410
411 def testDominationOfOldArchIndepBinaries(self):
412
413=== modified file 'lib/lp/registry/doc/distroseries.txt'
414--- lib/lp/registry/doc/distroseries.txt 2010-06-22 19:37:43 +0000
415+++ lib/lp/registry/doc/distroseries.txt 2010-07-20 09:44:57 +0000
416@@ -593,17 +593,17 @@
417 Supersede previous netapplet publication:
418
419 >>> last_published = netapplet_srcrel.publishing_history[1]
420- >>> superseded_netapplet = last_published.supersede()
421+ >>> last_published.supersede()
422 >>> flush_database_updates()
423
424 >>> netapplet_srcrel.publishing_history.count()
425 2
426
427- >>> print superseded_netapplet.status.name
428+ >>> print last_published.status.name
429 SUPERSEDED
430
431 >>> from canonical.database.sqlbase import get_transaction_timestamp
432- >>> superseded_netapplet.datesuperseded == get_transaction_timestamp()
433+ >>> last_published.datesuperseded == get_transaction_timestamp()
434 True
435
436
437
438=== modified file 'lib/lp/soyuz/doc/archive.txt'
439--- lib/lp/soyuz/doc/archive.txt 2010-07-02 21:25:36 +0000
440+++ lib/lp/soyuz/doc/archive.txt 2010-07-20 09:44:57 +0000
441@@ -913,10 +913,8 @@
442
443 >>> cprov_archive.number_of_sources
444 3
445- >>> superseded = cprov_archive.getPublishedSources(
446+ >>> cprov_archive.getPublishedSources(
447 ... name='cdrkit')[0].supersede()
448- >>> from canonical.launchpad.ftests import syncUpdate
449- >>> syncUpdate(superseded)
450
451 >>> cprov_archive.number_of_sources
452 2
453@@ -926,9 +924,8 @@
454
455 >>> cprov_archive.number_of_binaries
456 3
457- >>> superseded = cprov_archive.getAllPublishedBinaries(
458+ >>> cprov_archive.getAllPublishedBinaries(
459 ... name='mozilla-firefox')[0].supersede()
460- >>> syncUpdate(superseded)
461
462 >>> cprov_archive.number_of_binaries
463 2
464@@ -982,7 +979,6 @@
465
466 >>> login("celso.providelo@canonical.com")
467 >>> removal_candidate.requestDeletion(cprov, 'go away !')
468- >>> syncUpdate(removal_candidate)
469
470 >>> cprov_archive.getSourcesForDeletion(name='ice').count()
471 1
472@@ -1022,7 +1018,6 @@
473
474 >>> for bin in removal_candidate.getPublishedBinaries():
475 ... bin.requestDeletion(cprov, 'go away !')
476- ... syncUpdate(bin)
477
478 >>> cprov_archive.getSourcesForDeletion(name='ice').count()
479 0
480
481=== modified file 'lib/lp/soyuz/doc/distroarchseries.txt'
482--- lib/lp/soyuz/doc/distroarchseries.txt 2010-05-13 20:01:58 +0000
483+++ lib/lp/soyuz/doc/distroarchseries.txt 2010-07-20 09:44:57 +0000
484@@ -123,12 +123,11 @@
485
486 Supersede current publication:
487
488- >>> pmount_pubrec = pmount_hoary_i386_released.current_publishing_record
489- >>> superseded_pmount = pmount_pubrec.supersede()
490+ >>> pub = pmount_hoary_i386_released.current_publishing_record
491+ >>> pub.supersede()
492 >>> pmount_hoary_i386.publishing_history.count()
493 3
494
495- >>> pub = superseded_pmount
496 >>> print pub.status.name, pub.datesuperseded is not None
497 SUPERSEDED True
498
499
500=== modified file 'lib/lp/soyuz/doc/publishing.txt'
501--- lib/lp/soyuz/doc/publishing.txt 2010-05-27 22:18:16 +0000
502+++ lib/lp/soyuz/doc/publishing.txt 2010-07-20 09:44:57 +0000
503@@ -543,11 +543,8 @@
504 excluded from the getPublishedBinaries() results, but not from the
505 getBuiltBinaries() result.
506
507- >>> from canonical.launchpad.ftests import syncUpdate
508-
509 >>> a_binary = source.getPublishedBinaries()[0]
510- >>> superseded = a_binary.supersede()
511- >>> syncUpdate(superseded)
512+ >>> a_binary.supersede()
513
514 >>> len(source.getPublishedBinaries())
515 1
516@@ -561,7 +558,6 @@
517 >>> deletable = source.getPublishedBinaries()[0]
518 >>> deletable.requestDeletion(mark, "go")
519 >>> deleted = deletable
520- >>> syncUpdate(deleted)
521
522 >>> len(source.getPublishedBinaries())
523 0
524@@ -577,7 +573,6 @@
525
526 >>> for bin in copied_source.getPublishedBinaries():
527 ... obsoleted = bin.requestObsolescence()
528- ... syncUpdate(obsoleted)
529
530 >>> len(copied_source.getPublishedBinaries())
531 0
532@@ -596,7 +591,6 @@
533 >>> for pub in copied_source.getBuiltBinaries():
534 ... pub.status = PackagePublishingStatus.PUBLISHED
535 ... pub.scheduleddeletiondate = None
536- ... syncUpdate(pub)
537
538 Now we override the first binary publication, the hppa one, to
539 component 'universe'.
540@@ -631,10 +625,8 @@
541 We have to re-publish the superseded and the deleted publications above
542 because it's used below.
543
544- >>> superseded.status = PackagePublishingStatus.PUBLISHED
545- >>> syncUpdate(superseded)
546+ >>> a_binary.status = PackagePublishingStatus.PUBLISHED
547 >>> deleted.status = PackagePublishingStatus.PUBLISHED
548- >>> syncUpdate(deleted)
549
550
551 Copying and inspecting architecture independent binaries
552
553=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
554--- lib/lp/soyuz/interfaces/publishing.py 2010-03-11 02:32:07 +0000
555+++ lib/lp/soyuz/interfaces/publishing.py 2010-07-20 09:44:57 +0000
556@@ -278,12 +278,7 @@
557 """
558
559 def supersede():
560- """Supersede this publication.
561-
562- :return: The superseded publishing records, either a
563- `ISourcePackagePublishingHistory` or
564- `IBinaryPackagePublishingHistory`.
565- """
566+ """Supersede this publication."""
567
568 def requestObsolescence():
569 """Make this publication obsolete.
570@@ -632,6 +627,15 @@
571 :return: a list of `ILibraryFileAlias`.
572 """
573
574+ def supersede(dominant=None, logger=None):
575+ """Supersede this publication.
576+
577+ :param dominant: optional `ISourcePackagePublishingHistory` which is
578+ triggering the domination.
579+ :param logger: optional object to which debug information will be
580+ logged.
581+ """
582+
583 def changeOverride(new_component=None, new_section=None):
584 """Change the component and/or section of this publication
585
586@@ -838,6 +842,15 @@
587 title=_("Priority Name"),
588 required=False, readonly=True))
589
590+ def supersede(dominant=None, logger=None):
591+ """Supersede this publication.
592+
593+ :param dominant: optional `IBinaryPackagePublishingHistory` which is
594+ triggering the domination.
595+ :param logger: optional object to which debug information will be
596+ logged.
597+ """
598+
599 def changeOverride(new_component=None, new_section=None,
600 new_priority=None):
601 """Change the component, section and/or priority of this publication.
602
603=== modified file 'lib/lp/soyuz/model/publishing.py'
604--- lib/lp/soyuz/model/publishing.py 2010-07-20 09:16:14 +0000
605+++ lib/lp/soyuz/model/publishing.py 2010-07-20 09:44:57 +0000
606@@ -37,6 +37,7 @@
607 from canonical.database.enumcol import EnumCol
608 from canonical.launchpad.components.decoratedresultset import (
609 DecoratedResultSet)
610+from canonical.launchpad.interfaces.lpstorm import IMasterStore
611 from canonical.launchpad.webapp.interfaces import (
612 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
613 from canonical.launchpad.webapp.interfaces import NotFoundError
614@@ -70,6 +71,10 @@
615 from lp.soyuz.scripts.changeoverride import ArchiveOverriderError
616
617
618+PENDING = PackagePublishingStatus.PENDING
619+PUBLISHED = PackagePublishingStatus.PUBLISHED
620+
621+
622 # XXX cprov 2006-08-18: move it away, perhaps archivepublisher/pool.py
623 def makePoolPath(source_name, component_name):
624 """Return the pool path for a given source name and component name."""
625@@ -271,7 +276,6 @@
626 """See `IPublishing`."""
627 self.status = PackagePublishingStatus.SUPERSEDED
628 self.datesuperseded = UTC_NOW
629- return self
630
631 def requestDeletion(self, removed_by, removal_comment=None):
632 """See `IPublishing`."""
633@@ -673,6 +677,25 @@
634
635 return fields
636
637+ def supersede(self, dominant=None, logger=None):
638+ """See `ISourcePackagePublishingHistory`."""
639+ assert self.status in [PUBLISHED, PENDING], (
640+ "Should not dominate unpublished source %s" %
641+ self.sourcepackagerelease.title)
642+
643+ super(SourcePackagePublishingHistory, self).supersede()
644+
645+ if dominant is not None:
646+ if logger is not None:
647+ logger.debug(
648+ "%s/%s has been judged as superseded by %s/%s" %
649+ (self.sourcepackagerelease.sourcepackagename.name,
650+ self.sourcepackagerelease.version,
651+ dominant.sourcepackagerelease.sourcepackagename.name,
652+ dominant.sourcepackagerelease.version))
653+
654+ self.supersededby = dominant.sourcepackagerelease
655+
656 def changeOverride(self, new_component=None, new_section=None):
657 """See `ISourcePackagePublishingHistory`."""
658 # Check we have been asked to do something
659@@ -927,6 +950,73 @@
660
661 return fields
662
663+ def _getOtherPublications(self):
664+ """Return remaining publications with the same overrides.
665+
666+ Only considers binary publications in the same archive, distroseries,
667+ pocket, component, section and priority context. These publications
668+ are candidates for domination if this is an architecture-independent
669+ package.
670+
671+ The override match is critical -- it prevents a publication created
672+ by new overrides from superseding itself.
673+ """
674+ available_architectures = [
675+ das.id for das in self.distroarchseries.distroseries.architectures]
676+ return IMasterStore(BinaryPackagePublishingHistory).find(
677+ BinaryPackagePublishingHistory,
678+ BinaryPackagePublishingHistory.status.is_in(
679+ [PUBLISHED, PENDING]),
680+ BinaryPackagePublishingHistory.distroarchseries in (
681+ available_architectures),
682+ binarypackagerelease=self.binarypackagerelease,
683+ archive=self.archive,
684+ pocket=self.pocket,
685+ component=self.component,
686+ section=self.section,
687+ priority=self.priority)
688+
689+ def supersede(self, dominant=None, logger=None):
690+ """See `IBinaryPackagePublishingHistory`."""
691+ # At this point only PUBLISHED (ancient versions) or PENDING (
692+ # multiple overrides/copies) publications should be given. We
693+ # tolerate SUPERSEDED architecture-independent binaries, because
694+ # they are dominated automatically once the first publication is
695+ # processed.
696+ if self.status not in [PUBLISHED, PENDING]:
697+ assert not self.binarypackagerelease.architecturespecific, (
698+ "Should not dominate unpublished architecture specific "
699+ "binary %s (%s)" % (
700+ self.binarypackagerelease.title,
701+ self.distroarchseries.architecturetag))
702+ return
703+
704+ super(BinaryPackagePublishingHistory, self).supersede()
705+
706+ if dominant is not None:
707+ dominant_build = dominant.binarypackagerelease.build
708+ distroarchseries = dominant_build.distro_arch_series
709+ if logger is not None:
710+ logger.debug(
711+ "The %s build of %s has been judged as superseded by the "
712+ "build of %s. Arch-specific == %s" % (
713+ distroarchseries.architecturetag,
714+ self.binarypackagerelease.title,
715+ dominant_build.source_package_release.title,
716+ self.binarypackagerelease.architecturespecific))
717+ # Binary package releases are superseded by the new build,
718+ # not the new binary package release. This is because
719+ # there may not *be* a new matching binary package -
720+ # source packages can change the binaries they build
721+ # between releases.
722+ self.supersededby = dominant_build
723+
724+ # If this is architecture-independet, all publications with the same
725+ # context and overrides should be dominated simultaneously.
726+ if not self.binarypackagerelease.architecturespecific:
727+ for dominated in self._getOtherPublications():
728+ dominated.supersede(dominant, logger)
729+
730 def changeOverride(self, new_component=None, new_section=None,
731 new_priority=None):
732 """See `IBinaryPackagePublishingHistory`."""
733
734=== modified file 'lib/lp/soyuz/scripts/ftpmaster.py'
735--- lib/lp/soyuz/scripts/ftpmaster.py 2010-04-13 14:28:58 +0000
736+++ lib/lp/soyuz/scripts/ftpmaster.py 2010-07-20 09:44:57 +0000
737@@ -451,13 +451,14 @@
738 dasbp = distroarchseries.getBinaryPackage(binarypackagename)
739 dasbpr = dasbp.currentrelease
740 try:
741- sbpph = dasbpr.current_publishing_record.supersede()
742+ bpph = dasbpr.current_publishing_record
743+ bpph.supersede()
744 # We're blindly removing for all arches, if it's not there
745 # for some, that's fine ...
746 except NotFoundError:
747 pass
748 else:
749- version = sbpph.binarypackagerelease.version
750+ version = bpph.binarypackagerelease.version
751 self.logger.info ("Removed %s_%s from %s/%s ... "
752 % (package, version,
753 self.distroseries.name,