← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/db-use-dsp into lp:launchpad/db-devel

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/db-use-dsp into lp:launchpad/db-devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/db-use-dsp/+merge/58624

Stop using IDistroSeries.parent_series in DSDs and DSDJs.

I flailed wildly with multiple sharp instruments to remove its use from the tests, so it isn't pretty, but they all pass.

I've added a parent_series column and attribute onto DSD itself, which means a child may have multiple DSDs per source package -- one per parent, in fact. This means the simple case of just-one-parent is handled by DSD itself, but for more than one, it needs help.

I have yet to test the multiple parents code, but this branch is over-sized as it is.
-- 
https://code.launchpad.net/~stevenk/launchpad/db-use-dsp/+merge/58624
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/db-use-dsp into lp:launchpad/db-devel.
=== modified file 'database/sampledata/current-dev.sql'
--- database/sampledata/current-dev.sql	2011-04-21 05:32:46 +0000
+++ database/sampledata/current-dev.sql	2011-04-21 06:11:12 +0000
@@ -4180,9 +4180,9 @@
 
 ALTER TABLE distroseriesdifference DISABLE TRIGGER ALL;
 
-INSERT INTO distroseriesdifference (id, derived_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (1, 14, 19, NULL, 2, 3, NULL, '1.0.10-4deribuntu1', '1.0.9a-4ubuntu1', '1.0.9a-4ubuntu1');
-INSERT INTO distroseriesdifference (id, derived_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (2, 14, 9, 1, 1, 3, 2, '2.0.8-4deribuntu1', '2.0.9-1ubuntu2', '2.0.7-1ubuntu1');
-INSERT INTO distroseriesdifference (id, derived_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (3, 14, 1, NULL, 2, 3, NULL, '4.1.1-1deribuntu1', '4.1.2-1ubuntu1', '4.1.1-1ubuntu1');
+INSERT INTO distroseriesdifference (id, derived_series, parent_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (1, 14, 1, 19, NULL, 2, 3, NULL, '1.0.10-4deribuntu1', '1.0.9a-4ubuntu1', '1.0.9a-4ubuntu1');
+INSERT INTO distroseriesdifference (id, derived_series, parent_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (2, 14, 1, 9, 1, 1, 3, 2, '2.0.8-4deribuntu1', '2.0.9-1ubuntu2', '2.0.7-1ubuntu1');
+INSERT INTO distroseriesdifference (id, derived_series, parent_series, source_package_name, package_diff, status, difference_type, parent_package_diff, source_version, parent_source_version, base_version) VALUES (3, 14, 1, 1, NULL, 2, 3, NULL, '4.1.1-1deribuntu1', '4.1.2-1ubuntu1', '4.1.1-1ubuntu1');
 
 
 ALTER TABLE distroseriesdifference ENABLE TRIGGER ALL;

=== added file 'database/schema/patch-2208-99-0.sql'
--- database/schema/patch-2208-99-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-99-0.sql	2011-04-21 06:11:12 +0000
@@ -0,0 +1,9 @@
+-- Copyright 2010 Canonical Ltd.  This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+SET client_min_messages=ERROR;
+
+ALTER TABLE DistroSeriesDifference
+    ADD COLUMN parent_series INTEGER NOT NULL
+        CONSTRAINT distroseriesdifference__parentseries__fk REFERENCES distroseries;
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 99, 0);

=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg	2011-04-20 04:05:37 +0000
+++ database/schema/security.cfg	2011-04-21 06:11:12 +0000
@@ -1074,6 +1074,7 @@
 public.distributionjob                          = SELECT
 public.distroseries                             = SELECT
 public.distroseriesdifference                   = SELECT, INSERT, UPDATE
+public.distroseriesparent                       = SELECT
 public.job                                      = SELECT, UPDATE
 public.libraryfilealias                         = SELECT
 public.libraryfilecontent                       = SELECT

=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py	2011-04-17 18:00:45 +0000
+++ lib/lp/registry/interfaces/distroseries.py	2011-04-21 06:11:12 +0000
@@ -235,9 +235,6 @@
         Choice(
             title=_("Status"), required=True,
             vocabulary=SeriesStatus))
-    is_derived_series = Bool(
-        title=u'Is this series a derived series?', readonly=True,
-        description=(u"Whether or not this series is a derived series."))
     is_initialising = Bool(
         title=u'Is this series initialising?', readonly=True,
         description=(u"Whether or not this series is initialising."))

=== modified file 'lib/lp/registry/interfaces/distroseriesdifference.py'
--- lib/lp/registry/interfaces/distroseriesdifference.py	2011-04-19 02:57:38 +0000
+++ lib/lp/registry/interfaces/distroseriesdifference.py	2011-04-21 06:11:12 +0000
@@ -55,8 +55,14 @@
     derived_series = exported(Reference(
         IDistroSeries, title=_("Derived series"), required=True,
         readonly=True, description=_(
-            "The distribution series which, together with its parent, "
-            "identifies the two series with the difference.")))
+            "The distribution series which identifies the derived series "
+            "with the difference.")))
+
+    parent_series = exported(Reference(
+        IDistroSeries, title=_("Parent series"), required=True,
+        readonly=True, description=_(
+            "The distribution series which identifies the parent series "
+            "with the difference.")))
 
     source_package_name = Reference(
         ISourcePackageName,
@@ -253,7 +259,7 @@
 class IDistroSeriesDifferenceSource(Interface):
     """A utility of this interface can be used to create differences."""
 
-    def new(derived_series, source_package_name):
+    def new(derived_series, source_package_name, parent_series=None):
         """Create an `IDistroSeriesDifference`.
 
         :param derived_series: The distribution series which was derived
@@ -263,6 +269,10 @@
         :param source_package_name: A source package name identifying the
             package with a difference.
         :type source_package_name: `ISourcePackageName`.
+        :param parent_series: The distribution series which has the derived
+            series as a child. If there is only one parent, it does not need
+            to be specified.
+        :type parent_series: `IDistroSeries`.
         :raises NotADerivedSeriesError: When the passed distro series
             is not a derived series.
         :return: A new `DistroSeriesDifference` object.

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2011-04-17 18:00:45 +0000
+++ lib/lp/registry/model/distroseries.py	2011-04-21 06:11:12 +0000
@@ -786,13 +786,6 @@
             self.distribution.name.capitalize(), self.name.capitalize())
 
     @property
-    def is_derived_series(self):
-        """See `IDistroSeries`."""
-        # XXX rvb 2011-04-11 bug=754750: This should be cleaned up once
-        # the bug is fixed.
-        return self.parent_series is not None
-
-    @property
     def is_initialising(self):
         """See `IDistroSeries`."""
         return not getUtility(
@@ -2002,17 +1995,12 @@
 
     def getDerivedSeries(self):
         """See `IDistroSeriesPublic`."""
-        # XXX rvb 2011-04-08 bug=754750: The clause
-        # 'DistroSeries.distributionID!=self.distributionID' is only
-        # required because the parent_series attribute has been
-        # (mis-)used to denote other relations than proper derivation
-        # relashionships. We should be rid of this condition once
-        # the bug is fixed.
-        results = Store.of(self).find(
-            DistroSeries,
-            DistroSeries.parent_series==self.id,
-            DistroSeries.distributionID!=self.distributionID)
-        return results.order_by(Desc(DistroSeries.date_created))
+        # Circular imports.
+        from lp.registry.interfaces.distroseriesparent import (
+            IDistroSeriesParentSet,
+            )
+        dsps = getUtility(IDistroSeriesParentSet).getByParentSeries(self)
+        return [dsp.derived_series for dsp in dsps]
 
     def getBugTaskWeightFunction(self):
         """Provide a weight function to determine optimal bug task.

=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py	2011-04-19 11:43:40 +0000
+++ lib/lp/registry/model/distroseriesdifference.py	2011-04-21 06:11:12 +0000
@@ -61,6 +61,7 @@
 from lp.registry.interfaces.distroseriesdifferencecomment import (
     IDistroSeriesDifferenceCommentSource,
     )
+from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.model.distroseries import DistroSeries
 from lp.registry.model.distroseriesdifferencecomment import (
@@ -123,7 +124,7 @@
         ParentDistroSeries = ClassAlias(DistroSeries)
         conditions = And(
             conditions,
-            ParentDistroSeries.id == DistroSeries.parent_seriesID,
+            ParentDistroSeries.id == DistroSeriesDifference.parent_series_id,
             Archive.distributionID == ParentDistroSeries.distributionID,
             Archive.purpose == ArchivePurpose.PRIMARY,
             )
@@ -197,6 +198,9 @@
     derived_series = Reference(
         derived_series_id, 'DistroSeries.id')
 
+    parent_series_id = Int(name='parent_series', allow_none=False)
+    parent_series = Reference(parent_series_id, 'DistroSeries.id')
+
     source_package_name_id = Int(
         name='source_package_name', allow_none=False)
     source_package_name = Reference(
@@ -222,14 +226,21 @@
     base_version = StringCol(dbName='base_version', notNull=False)
 
     @staticmethod
-    def new(derived_series, source_package_name):
+    def new(derived_series, source_package_name, parent_series=None):
         """See `IDistroSeriesDifferenceSource`."""
-        if not derived_series.is_derived_series:
+        dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
+            derived_series)
+        if dsp.is_empty():
             raise NotADerivedSeriesError()
+        if parent_series is None:
+            if dsp.count() > 1:
+                raise NotADerivedSeriesError() # Multiple parents.
+            parent_series = dsp[0].parent_series
 
         store = IMasterStore(DistroSeriesDifference)
         diff = DistroSeriesDifference()
         diff.derived_series = derived_series
+        diff.parent_series = parent_series
         diff.source_package_name = source_package_name
 
         # The status and type is set to default values - they will be
@@ -333,8 +344,7 @@
                     spph = parent_source_pubs_for_release[spn_id]
                     cache.parent_source_package_release = (
                         DistroSeriesSourcePackageRelease(
-                            dsd.derived_series.parent_series,
-                            spph.sourcepackagerelease))
+                            dsd.parent_series, spph.sourcepackagerelease))
                 else:
                     cache.parent_source_package_release = None
                 cache.latest_comment = latest_comment_by_dsd_id.get(dsd.id)
@@ -390,7 +400,7 @@
         """Helper to keep source_pub/parent_source_pub DRY."""
         distro_series = self.derived_series
         if for_parent:
-            distro_series = self.derived_series.parent_series
+            distro_series = self.parent_series
 
         pubs = distro_series.getPublishedSources(
             self.source_package_name, include_pending=True)
@@ -405,7 +415,7 @@
     def base_source_pub(self):
         """See `IDistroSeriesDifference`."""
         if self.base_version is not None:
-            parent = self.derived_series.parent_series
+            parent = self.parent_series
             result = parent.main_archive.getPublishedSources(
                 name=self.source_package_name.name,
                 version=self.base_version).first()
@@ -427,7 +437,7 @@
     @property
     def title(self):
         """See `IDistroSeriesDifference`."""
-        parent_name = self.derived_series.parent_series.displayname
+        parent_name = self.parent_series.displayname
         return ("Difference between distroseries '%(parent_name)s' and "
                 "'%(derived_name)s' for package '%(pkg_name)s' "
                 "(%(parent_version)s/%(source_version)s)" % {
@@ -482,14 +492,8 @@
 
     def getParentPackageSets(self):
         """See `IDistroSeriesDifference`."""
-        has_parent_series = self.derived_series is not None and (
-            self.derived_series.parent_series is not None)
-        if has_parent_series:
-            return getUtility(IPackagesetSet).setsIncludingSource(
-                self.source_package_name,
-                self.derived_series.parent_series)
-        else:
-            return []
+        return getUtility(IPackagesetSet).setsIncludingSource(
+            self.source_package_name, self.parent_series)
 
     @property
     def package_diff_status(self):
@@ -510,14 +514,12 @@
     @cachedproperty
     def parent_source_package_release(self):
         return self._package_release(
-            self.derived_series.parent_series,
-            self.parent_source_version)
+            self.parent_series, self.parent_source_version)
 
     @cachedproperty
     def source_package_release(self):
         return self._package_release(
-            self.derived_series,
-            self.source_version)
+            self.derived_series, self.source_version)
 
     def _package_release(self, distro_series, version):
         statuses = (

=== modified file 'lib/lp/registry/templates/distroseriesdifference-listing-extra.pt'
--- lib/lp/registry/templates/distroseriesdifference-listing-extra.pt	2011-04-18 14:23:22 +0000
+++ lib/lp/registry/templates/distroseriesdifference-listing-extra.pt	2011-04-21 06:11:12 +0000
@@ -14,7 +14,7 @@
 
     <metal:macro-parent metal:define-macro="base-to-parent">
       <span tal:replace="context/base_version">1.1.1</span> to
-      <span tal:replace="context/derived_series/parent_series/displayname">
+      <span tal:replace="context/parent_series/displayname">
         Lucid</span> version:
       <span tal:replace="context/parent_source_version">1.2.4</span>
     </metal:macro-parent>

=== modified file 'lib/lp/registry/tests/test_distroseriesdifference.py'
--- lib/lp/registry/tests/test_distroseriesdifference.py	2011-04-19 12:00:38 +0000
+++ lib/lp/registry/tests/test_distroseriesdifference.py	2011-04-21 06:11:12 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Model tests for the DistroSeriesDifference class."""
@@ -111,8 +111,7 @@
         self.assertEqual(
             'foonew', ds_diff.parent_source_pub.source_package_name)
         self.assertEqual(
-            ds_diff.derived_series.parent_series,
-            ds_diff.parent_source_pub.distroseries)
+            ds_diff.parent_series, ds_diff.parent_source_pub.distroseries)
 
     def test_parent_source_pub_gets_latest_pending(self):
         # The most recent publication is always returned, even if its pending.
@@ -120,7 +119,7 @@
             source_package_name_str="foonew")
         pending_pub = self.factory.makeSourcePackagePublishingHistory(
             sourcepackagename=ds_diff.source_package_name,
-            distroseries=ds_diff.derived_series.parent_series,
+            distroseries=ds_diff.parent_series,
             status=PackagePublishingStatus.PENDING)
 
         self.assertEqual(pending_pub, ds_diff.parent_source_pub)
@@ -269,7 +268,7 @@
                 DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
         new_parent_pub = self.factory.makeSourcePackagePublishingHistory(
             sourcepackagename=ds_diff.source_package_name,
-            distroseries=ds_diff.derived_series.parent_series,
+            distroseries=ds_diff.parent_series,
             status=PackagePublishingStatus.PENDING,
             version='1.1')
 
@@ -332,8 +331,9 @@
     def test_title(self):
         # The title is a friendly description of the difference.
         parent_series = self.factory.makeDistroSeries(name="lucid")
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=parent_series, name="derilucid")
+        derived_series = self.factory.makeDistroSeries(name="derilucid")
+        dsp = self.factory.makeDistroSeriesParent(
+            derived_series=derived_series, parent_series=parent_series)
         ds_diff = self.factory.makeDistroSeriesDifference(
             source_package_name_str="foonew", derived_series=derived_series,
             versions={
@@ -413,7 +413,7 @@
         # All parent's packagesets are returned ordered alphabetically.
         ds_diff = self.factory.makeDistroSeriesDifference()
         packagesets = self._setupPackageSets(
-            ds_diff, ds_diff.derived_series.parent_series, 5)
+            ds_diff, ds_diff.parent_series, 5)
         parent_packagesets = ds_diff.getParentPackageSets()
         self.assertEquals(
             sorted([packageset.name for packageset in packagesets]),
@@ -508,16 +508,15 @@
     def test_base_version_none(self):
         # The attribute is set to None if there is no common base version.
         # Publish different versions in the series.
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
         source_package_name = self.factory.getOrMakeSourcePackageName('foo')
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series,
+            distroseries=dsp.derived_series,
             version='1.0deri1',
             sourcepackagename=source_package_name,
             status=PackagePublishingStatus.PUBLISHED)
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series.parent_series,
+            distroseries=dsp.parent_series,
             version='1.0ubu2',
             sourcepackagename=source_package_name,
             status=PackagePublishingStatus.PUBLISHED)
@@ -527,8 +526,7 @@
 
     def test_base_version_multiple(self):
         # The latest common base version is set as the base-version.
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
         source_package_name = self.factory.getOrMakeSourcePackageName('foo')
         # Create changelogs for both.
         changelog_lfa = self.factory.makeChangelog('foo', ['1.2', '1.1'])
@@ -536,7 +534,7 @@
         transaction.commit() # Yay, librarian.
 
         ds_diff = self.factory.makeDistroSeriesDifference(
-            derived_series=derived_series, source_package_name_str='foo',
+            derived_series=dsp.derived_series, source_package_name_str='foo',
             versions={
                 'derived': '1.2',
                 'parent': '1.3',
@@ -550,8 +548,7 @@
     def test_base_version_invalid(self):
         # If the maximum base version is invalid, it is discarded and not
         # set as the base version.
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
         source_package_name = self.factory.getOrMakeSourcePackageName('foo')
         # Create changelogs for both.
         changelog_lfa = self.factory.makeChangelog(
@@ -561,7 +558,7 @@
         transaction.commit() # Yay, librarian.
 
         ds_diff = self.factory.makeDistroSeriesDifference(
-            derived_series=derived_series, source_package_name_str='foo',
+            derived_series=dsp.derived_series, source_package_name_str='foo',
             versions={
                 'derived': '1:2.0-1',
                 'parent': '1:2.0-2',
@@ -600,8 +597,7 @@
 
         base_pub = ds_diff.base_source_pub
         self.assertEqual('1.0', base_pub.source_package_version)
-        self.assertEqual(
-            ds_diff.derived_series.parent_series, base_pub.distroseries)
+        self.assertEqual(ds_diff.parent_series, base_pub.distroseries)
 
     def test_base_source_pub_not_published(self):
         # If the base version isn't published, the base version is
@@ -741,24 +737,23 @@
     def test_source_package_release_pending(self):
         # source_package_release returns the package release of version
         # source_version with status PUBLISHED or PENDING.
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
         source_package_name = self.factory.getOrMakeSourcePackageName('foo')
         versions = {'derived': u'1.2', 'parent': u'1.3'}
 
         ds_diff = self.factory.makeDistroSeriesDifference(
-            derived_series=derived_series,
+            derived_series=dsp.derived_series,
             source_package_name_str=source_package_name.name,
             versions=versions)
 
         # Create pending source package releases.
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series,
+            distroseries=dsp.derived_series,
             version='1.4',
             sourcepackagename=source_package_name,
             status=PackagePublishingStatus.PENDING)
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series.parent_series,
+            distroseries=dsp.parent_series,
             version='1.5',
             sourcepackagename=source_package_name,
             status=PackagePublishingStatus.PENDING)
@@ -810,7 +805,7 @@
     def _initDiffWithMultiplePendingPublications(self, versions, parent):
         ds_diff = self.factory.makeDistroSeriesDifference(versions=versions)
         if parent:
-            series = ds_diff.derived_series.parent_series
+            series = ds_diff.parent_series
             version = versions.get('parent')
         else:
             series = ds_diff.derived_series
@@ -898,8 +893,8 @@
 
     def makeDerivedSeries(self):
         # Keep tests DRY.
-        return self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        return dsp.derived_series
 
     def test_getForDistroSeries_default(self):
         # By default all differences needing attention for the given
@@ -991,11 +986,10 @@
     layer = DatabaseFunctionalLayer
 
     def test_most_recent_comments(self):
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
         dsds = set(
             self.factory.makeDistroSeriesDifference(
-                derived_series=derived_series) for index in xrange(5))
+                derived_series=dsp.derived_series) for index in xrange(5))
         expected_comments = set()
         for dsd in dsds:
             # Add a couple of comments.
@@ -1026,8 +1020,8 @@
         return dsd
 
     def test_simple(self):
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        derived_series = dsp.derived_series
         dsds = [
             self.create_difference(derived_series),
             self.create_difference(derived_series),
@@ -1056,8 +1050,8 @@
             parent_source_pubs_by_spn_id_found)
 
     def test_statuses(self):
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        derived_series = dsp.derived_series
         dsd = self.create_difference(derived_series)
         # Change the derived source publication to DELETED.
         removeSecurityProxy(dsd.source_pub).status = (
@@ -1078,8 +1072,8 @@
         # When match_version is True, the version of the publications (well,
         # the release) must exactly match those recorded on the
         # DistroSeriesDifference.
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        derived_series = dsp.derived_series
         dsd = self.create_difference(derived_series)
         # Modify the release version.
         removeSecurityProxy(

=== modified file 'lib/lp/soyuz/model/distroseriesdifferencejob.py'
--- lib/lp/soyuz/model/distroseriesdifferencejob.py	2011-04-13 15:20:12 +0000
+++ lib/lp/soyuz/model/distroseriesdifferencejob.py	2011-04-21 06:11:12 +0000
@@ -18,6 +18,7 @@
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifferenceSource,
     )
+from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.model.distroseriesdifference import DistroSeriesDifference
 from lp.registry.model.sourcepackagename import SourcePackageName
@@ -92,12 +93,14 @@
     """
     if distroseries is None:
         return False
-    parent_series = distroseries.parent_series
-    if parent_series is None:
-        return False
-    if parent_series.distribution == distroseries.distribution:
-        # Differences within a distribution are not tracked.
-        return False
+    dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
+        distroseries)
+    if dsp.count() == 0:
+        return False
+    for parent in dsp:
+        if parent.parent_series.distribution == distroseries.distribution:
+            # Differences within a distribution are not tracked.
+            return False
     return find_waiting_jobs(distroseries, sourcepackagename).is_empty()
 
 
@@ -148,7 +151,9 @@
         in a packageset that the derived series also has.
         """
         derived_series = self.distroseries
-        parent_series = derived_series.parent_series
+        dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
+            derived_series)
+        parent_series = dsp[0].parent_series
         if has_package(derived_series, self.sourcepackagename):
             return True
         if not has_package(parent_series, self.sourcepackagename):

=== modified file 'lib/lp/soyuz/tests/test_distroseriesdifferencejob.py'
--- lib/lp/soyuz/tests/test_distroseriesdifferencejob.py	2011-04-18 05:25:38 +0000
+++ lib/lp/soyuz/tests/test_distroseriesdifferencejob.py	2011-04-21 06:11:12 +0000
@@ -52,8 +52,8 @@
         return getUtility(IDistroSeriesDifferenceJobSource)
 
     def makeDerivedDistroSeries(self):
-        return self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        return dsp.derived_series
 
     def test_baseline(self):
         verifyObject(IDistroSeriesDifferenceJobSource, self.getJobSource())
@@ -147,23 +147,24 @@
             [], find_waiting_jobs(distroseries, sourcepackagename))
 
     def test_createForPackagedPublication_creates_jobs_for_its_child(self):
-        derived_series = self.factory.makeDistroSeries(
-            parent_series=self.makeDerivedDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        parent_dsp = self.factory.makeDistroSeriesParent(
+            derived_series=dsp.parent_series)
         package = self.factory.makeSourcePackageName()
         # Create a job for the derived_series parent, which should create
         # two jobs. One for derived_series, and the other for its child.
         self.getJobSource().createForPackagePublication(
-            derived_series.parent_series, package,
+            dsp.parent_series, package,
             PackagePublishingPocket.RELEASE)
         jobs = (list(
-            find_waiting_jobs(derived_series.parent_series, package)) +
-            list(find_waiting_jobs(derived_series, package)))
+            find_waiting_jobs(dsp.parent_series, package)) +
+            list(find_waiting_jobs(dsp.derived_series, package)))
         self.assertEqual(2, len(jobs))
         self.assertEqual(package.id, jobs[0].metadata['sourcepackagename'])
         self.assertEqual(package.id, jobs[1].metadata['sourcepackagename'])
         # Lastly, a job was not created for the grandparent.
         jobs = list(
-            find_waiting_jobs(derived_series.parent_series.parent_series,
+            find_waiting_jobs(parent_dsp.parent_series,
                 package))
         self.assertEqual(0, len(jobs))
 
@@ -242,82 +243,77 @@
         self.assertEqual(1, ds_diff.count())
 
     def test_packageset_filter_passes_inherited_packages(self):
-        derived_series = self.makeDerivedDistroSeries()
-        parent_series = derived_series.parent_series
+        dsp = self.factory.makeDistroSeriesParent()
         # Parent must have a packageset or the filter will pass anyway.
-        self.factory.makePackageset(distroseries=parent_series)
+        self.factory.makePackageset(distroseries=dsp.parent_series)
         package = self.factory.makeSourcePackageName()
         # Package is not in the packageset _but_ both the parent and
         # derived series have it.
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=parent_series, sourcepackagename=package)
+            distroseries=dsp.parent_series, sourcepackagename=package)
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series, sourcepackagename=package)
-        job = create_job(derived_series, package)
+            distroseries=dsp.derived_series, sourcepackagename=package)
+        job = create_job(dsp.derived_series, package)
         self.assertTrue(job.passesPackagesetFilter())
 
     def test_packageset_filter_passes_packages_unique_to_derived_series(self):
-        derived_series = self.makeDerivedDistroSeries()
-        parent_series = derived_series.parent_series
+        dsp = self.factory.makeDistroSeriesParent()
         # Parent must have a packageset or the filter will pass anyway.
-        self.factory.makePackageset(distroseries=parent_series)
+        self.factory.makePackageset(distroseries=dsp.parent_series)
         package = self.factory.makeSourcePackageName()
         # Package exists in the derived series but not in the parent
         # series.
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=derived_series, sourcepackagename=package)
-        job = create_job(derived_series, package)
+            distroseries=dsp.derived_series, sourcepackagename=package)
+        job = create_job(dsp.derived_series, package)
         self.assertTrue(job.passesPackagesetFilter())
 
     def test_packageset_filter_passes_all_if_parent_has_no_packagesets(self):
         # Debian in particular has no packagesets.  If the parent series
         # has no packagesets, the packageset filter passes all packages.
-        derived_series = self.makeDerivedDistroSeries()
-        parent_series = derived_series.parent_series
+        dsp = self.factory.makeDistroSeriesParent()
         package = self.factory.makeSourcePackageName()
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=parent_series, sourcepackagename=package)
-        job = create_job(derived_series, package)
+            distroseries=dsp.parent_series, sourcepackagename=package)
+        job = create_job(dsp.derived_series, package)
         self.assertTrue(job.passesPackagesetFilter())
 
-    def makeInheritedPackageSet(self, derived_series, packages=()):
+    def makeInheritedPackageSet(self, distro_series_parent, packages=()):
         """Simulate an inherited `Packageset`.
 
         Creates a packageset in the parent that has an equivalent in
         `derived_series`.
         """
-        parent_series = derived_series.parent_series
         parent_packageset = self.factory.makePackageset(
-            distroseries=parent_series, packages=packages)
+            distroseries=distro_series_parent.parent_series,
+            packages=packages)
         derived_packageset = self.factory.makePackageset(
-            distroseries=derived_series, packages=packages,
-            name=parent_packageset.name, owner=parent_packageset.owner,
-            related_set=parent_packageset)
+            distroseries=distro_series_parent.derived_series,
+            packages=packages, name=parent_packageset.name,
+            owner=parent_packageset.owner, related_set=parent_packageset)
 
     def test_packageset_filter_passes_package_in_inherited_packageset(self):
-        derived_series = self.makeDerivedDistroSeries()
-        parent_series = derived_series.parent_series
+        dsp = self.factory.makeDistroSeriesParent()
         # Package is in a packageset on the parent that the derived
         # series also has.
         package = self.factory.makeSourcePackageName()
-        self.makeInheritedPackageSet(derived_series, [package])
+        self.makeInheritedPackageSet(dsp, [package])
         # Package is in parent series and in a packageset that the
         # derived series inherited.
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=parent_series, sourcepackagename=package)
-        job = create_job(derived_series, package)
+            distroseries=dsp.parent_series, sourcepackagename=package)
+        job = create_job(dsp.derived_series, package)
         self.assertTrue(job.passesPackagesetFilter())
 
     def test_packageset_filter_blocks_unwanted_parent_package(self):
-        derived_series = self.makeDerivedDistroSeries()
-        parent_series = derived_series.parent_series
-        self.makeInheritedPackageSet(derived_series)
+        dsp = self.factory.makeDistroSeriesParent()
+        self.makeInheritedPackageSet(dsp)
         package = self.factory.makeSourcePackageName()
         # Package is in the parent series but not in a packageset shared
         # between the derived series and the parent series.
         self.factory.makeSourcePackagePublishingHistory(
-            distroseries=parent_series, sourcepackagename=package)
-        job = create_job(derived_series, package)
+            distroseries=dsp.parent_series, sourcepackagename=package)
+        job = create_job(dsp.derived_series, package)
         self.assertFalse(job.passesPackagesetFilter())
 
 
@@ -334,8 +330,8 @@
         return getUtility(IDistroSeriesDifferenceJobSource)
 
     def makeDerivedDistroSeries(self):
-        return self.factory.makeDistroSeries(
-            parent_series=self.factory.makeDistroSeries())
+        dsp = self.factory.makeDistroSeriesParent()
+        return dsp
 
     def createPublication(self, source_package_name, versions, distroseries,
                           archive=None):
@@ -373,12 +369,13 @@
     def test_parent_gets_newer(self):
         # When a new source package is uploaded to the parent distroseries,
         # a job is created that updates the relevant DSD.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-1derived1', '1.0-1'], derived_series)
         self.createPublication(
-            source_package_name, ['1.0-1'], derived_series.parent_series)
+            source_package_name, ['1.0-1'], dsp.parent_series)
         # Creating the SPPHs has created jobs for us, so grab it off the
         # queue.
         jobs = find_waiting_jobs(derived_series, source_package_name)
@@ -391,7 +388,7 @@
         # Now create a 1.0-2 upload to the parent.
         self.createPublication(
             source_package_name, ['1.0-2', '1.0-1'],
-            derived_series.parent_series)
+            dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         # And the DSD we have a hold of will have updated.
@@ -402,12 +399,13 @@
     def test_child_gets_newer(self):
         # When a new source is uploaded to the child distroseries, the DSD is
         # updated.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-1'], derived_series)
         self.createPublication(
-            source_package_name, ['1.0-1'], derived_series.parent_series)
+            source_package_name, ['1.0-1'], dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         ds_diff = self.findDSD(derived_series, source_package_name)
@@ -424,13 +422,13 @@
     def test_child_is_synced(self):
         # If the source package gets 'synced' to the child from the parent,
         # the job correctly updates the DSD.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-1derived1', '1.0-1'], derived_series)
         self.createPublication(
-            source_package_name, ['1.0-2', '1.0-1'],
-            derived_series.parent_series)
+            source_package_name, ['1.0-2', '1.0-1'], dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         ds_diff = self.findDSD(derived_series, source_package_name)
@@ -445,7 +443,8 @@
     def test_only_in_child(self):
         # If a source package only exists in the child distroseries, the DSD
         # is created with the right type.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-0derived1'], derived_series)
@@ -459,11 +458,11 @@
     def test_only_in_parent(self):
         # If a source package only exists in the parent distroseries, the DSD
         # is created with the right type.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
-            source_package_name, ['1.0-1'],
-            derived_series.parent_series)
+            source_package_name, ['1.0-1'], dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         ds_diff = self.findDSD(derived_series, source_package_name)
@@ -474,12 +473,13 @@
     def test_deleted_in_parent(self):
         # If a source package is deleted in the parent, a job is created, and
         # the DSD is updated correctly.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-1'], derived_series)
         spph = self.createPublication(
-            source_package_name, ['1.0-1'], derived_series.parent_series)
+            source_package_name, ['1.0-1'], dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         ds_diff = self.findDSD(derived_series, source_package_name)
@@ -495,12 +495,13 @@
     def test_deleted_in_child(self):
         # If a source package is deleted in the child, a job is created, and
         # the DSD is updated correctly.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         spph = self.createPublication(
             source_package_name, ['1.0-1'], derived_series)
         self.createPublication(
-            source_package_name, ['1.0-1'], derived_series.parent_series)
+            source_package_name, ['1.0-1'], dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         ds_diff = self.findDSD(derived_series, source_package_name)
@@ -515,7 +516,8 @@
 
     def test_no_job_for_PPA(self):
         # If a source package is uploaded to a PPA, a job is not created.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         ppa = self.factory.makeArchive()
         self.createPublication(
@@ -525,7 +527,8 @@
 
     def test_no_job_for_PPA_with_deleted_source(self):
         # If a source package is deleted from a PPA, no job is created.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         ppa = self.factory.makeArchive()
         spph = self.createPublication(
@@ -536,19 +539,19 @@
 
     def test_update_deletes_diffs(self):
         # When a DSD is updated, the diffs are invalidated.
-        derived_series = self.makeDerivedDistroSeries()
+        dsp = self.makeDerivedDistroSeries()
+        derived_series = dsp.derived_series
         source_package_name = self.factory.makeSourcePackageName()
         self.createPublication(
             source_package_name, ['1.0-1derived1', '1.0-1'], derived_series)
         self.createPublication(
-            source_package_name, ['1.0-2', '1.0-1'],
-            derived_series.parent_series)
+            source_package_name, ['1.0-2', '1.0-1'], dsp.parent_series)
         spr = self.factory.makeSourcePackageRelease(
             sourcepackagename=source_package_name, version='1.0-1')
         self.factory.makeSourcePackagePublishingHistory(
             sourcepackagerelease=spr,
-            archive=derived_series.parent_series.main_archive,
-            distroseries=derived_series.parent_series,
+            archive=dsp.parent_series.main_archive,
+            distroseries=dsp.parent_series,
             status=PackagePublishingStatus.SUPERSEDED)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
@@ -558,7 +561,7 @@
         self.assertIsNot(None, ds_diff[0].parent_package_diff)
         self.createPublication(
             source_package_name, ['1.0-3', '1.0-2', '1.0-1'],
-            derived_series.parent_series)
+            dsp.parent_series)
         jobs = find_waiting_jobs(derived_series, source_package_name)
         self.runJob(jobs[0])
         # Since the diff showing the changes from 1.0-1 to 1.0-1derived1 is

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2011-04-19 11:43:40 +0000
+++ lib/lp/testing/factory.py	2011-04-21 06:11:12 +0000
@@ -2315,9 +2315,17 @@
         changelogs=None, set_base_version=False):
         """Create a new distro series source package difference."""
         if derived_series is None:
-            parent_series = self.makeDistroSeries()
-            derived_series = self.makeDistroSeries(
-                parent_series=parent_series)
+            dsp = self.makeDistroSeriesParent()
+            derived_series = dsp.derived_series
+            parent_series = dsp.parent_series
+        else:
+            dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
+                derived_series)
+            if dsp.count() == 0:
+                new_dsp = self.makeDistroSeriesParent()
+                parent_series = new_dsp.parent_series
+            else:
+                parent_series = dsp[0].parent_series
 
         if source_package_name_str is None:
             source_package_name_str = self.getUniqueString('src-name')
@@ -2332,7 +2340,7 @@
 
         base_version = versions.get('base')
         if base_version is not None:
-            for series in [derived_series, derived_series.parent_series]:
+            for series in [derived_series, parent_series]:
                 spr = self.makeSourcePackageRelease(
                     sourcepackagename=source_package_name,
                     version=base_version)
@@ -2357,7 +2365,7 @@
                 version=versions.get('parent'),
                 changelog=changelogs.get('parent'))
             self.makeSourcePackagePublishingHistory(
-                distroseries=derived_series.parent_series,
+                distroseries=parent_series,
                 sourcepackagerelease=spr,
                 status = PackagePublishingStatus.PUBLISHED)