launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04042
[Merge] lp:~jtv/launchpad/pre-800634-800652 into lp:launchpad
Jeroen T. Vermeulen has proposed merging lp:~jtv/launchpad/pre-800634-800652 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #800634 in Launchpad itself: "+queue page doesn't show the overrides for syncs"
https://bugs.launchpad.net/launchpad/+bug/800634
Bug #800652 in Launchpad itself: "+queue page broken for accepting syncs"
https://bugs.launchpad.net/launchpad/+bug/800652
For more details, see:
https://code.launchpad.net/~jtv/launchpad/pre-800634-800652/+merge/65657
= Summary =
This lays some groundwork for another branch I have in flight. It introduces IPackageCopyJobSource, which can take a PackageCopyJob and give you a full-fledged IPackageCopyJob implementation of the right concrete type. The only concrete implementation is PlainPackageCopyJob at the moment, but hard-coding that wasn't actually any easier than generalizing it a tiny bit.
I also ran into some circular imports, and even though it's not the minimal change, chose lp.soyuz.adapters.overrides as the place to cut the cycles. It just seems to me that importing lp.soyuz.model classes into an lp.soyuz.adapters module is in some vague way swimming against the flow.
== Tests ==
I ran more, but this is the main one:
{{{
./bin/test -vvc lp.soyuz.tests.test_packagecopyjob
}}}
== Demo and Q/A ==
Will only be interesting as part of my other branch, which fixes up the display of overrides for sync jobs.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/soyuz/model/queue.py
lib/lp/soyuz/interfaces/queue.py
lib/lp/soyuz/model/packagecopyjob.py
lib/lp/soyuz/configure.zcml
lib/lp/soyuz/interfaces/packagecopyjob.py
lib/lp/soyuz/tests/test_packagecopyjob.py
lib/lp/soyuz/adapters/overrides.py
--
https://code.launchpad.net/~jtv/launchpad/pre-800634-800652/+merge/65657
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/launchpad/pre-800634-800652 into lp:launchpad.
=== modified file 'lib/lp/soyuz/adapters/overrides.py'
--- lib/lp/soyuz/adapters/overrides.py 2011-06-05 08:17:40 +0000
+++ lib/lp/soyuz/adapters/overrides.py 2011-06-23 12:57:43 +0000
@@ -40,13 +40,7 @@
from lp.soyuz.model.binarypackagename import BinaryPackageName
from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
from lp.soyuz.model.component import Component
-from lp.soyuz.model.distroarchseries import DistroArchSeries
-from lp.soyuz.model.publishing import (
- BinaryPackagePublishingHistory,
- SourcePackagePublishingHistory,
- )
from lp.soyuz.model.section import Section
-from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
class IOverride(Interface):
@@ -130,7 +124,7 @@
def __repr__(self):
return ("<BinaryOverride at %x component=%r section=%r "
- "binary_package_name=%r distro_arch_series=%r priority=%r>" %
+ "binary_package_name=%r distro_arch_series=%r priority=%r>" %
(id(self), self.component, self.section, self.binary_package_name,
self.distro_arch_series, self.priority))
@@ -187,7 +181,7 @@
class FromExistingOverridePolicy(BaseOverridePolicy):
"""Override policy that only searches for existing publications.
-
+
Override policy that returns the SourcePackageName, component and
section for the latest published source publication, or the
BinaryPackageName, DistroArchSeries, component, section and priority
@@ -195,10 +189,15 @@
"""
def calculateSourceOverrides(self, archive, distroseries, pocket, spns):
- store = IStore(SourcePackagePublishingHistory)
+ # Avoid circular imports.
+ from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
+ from lp.soyuz.model.publishing import SourcePackagePublishingHistory
+
def eager_load(rows):
bulk.load(Component, (row[1] for row in rows))
bulk.load(Section, (row[2] for row in rows))
+
+ store = IStore(SourcePackagePublishingHistory)
already_published = DecoratedResultSet(
store.find(
(SourcePackageRelease.sourcepackagenameID,
@@ -226,10 +225,15 @@
def calculateBinaryOverrides(self, archive, distroseries, pocket,
binaries):
- store = IStore(BinaryPackagePublishingHistory)
+ # Avoid circular imports.
+ from lp.soyuz.model.distroarchseries import DistroArchSeries
+ from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
+
def eager_load(rows):
bulk.load(Component, (row[2] for row in rows))
bulk.load(Section, (row[3] for row in rows))
+
+ store = IStore(BinaryPackagePublishingHistory)
expanded = calculate_target_das(distroseries, binaries)
candidates = (
make_package_condition(archive, das, bpn)
@@ -266,11 +270,11 @@
class UnknownOverridePolicy(BaseOverridePolicy):
"""Override policy that returns defaults.
-
+
Override policy that assumes everything passed in doesn't exist, so
returns the defaults.
"""
-
+
def calculateSourceOverrides(self, archive, distroseries, pocket,
sources):
default_component = archive.default_component or getUtility(
@@ -291,9 +295,9 @@
class UbuntuOverridePolicy(FromExistingOverridePolicy,
UnknownOverridePolicy):
"""Override policy for Ubuntu.
-
- An override policy that incorporates both the from existing policy
- and the unknown policy.
+
+ An override policy that incorporates both the existing policy and the
+ unknown policy.
"""
def calculateSourceOverrides(self, archive, distroseries, pocket,
@@ -314,9 +318,12 @@
total = set(binaries)
overrides = FromExistingOverridePolicy.calculateBinaryOverrides(
self, archive, distroseries, pocket, binaries)
- existing = set((
- overide.binary_package_name, overide.distro_arch_series.architecturetag)
- for overide in overrides)
+ existing = set(
+ (
+ overide.binary_package_name,
+ overide.distro_arch_series.architecturetag,
+ )
+ for overide in overrides)
missing = total.difference(existing)
if missing:
unknown = UnknownOverridePolicy.calculateBinaryOverrides(
@@ -340,6 +347,8 @@
def make_package_condition(archive, das, bpn):
+ # Avoid circular imports.
+ from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
return And(
BinaryPackagePublishingHistory.archiveID == archive.id,
BinaryPackagePublishingHistory.distroarchseriesID == das.id,
@@ -347,9 +356,13 @@
def id_resolver(lookups):
+ # Avoid circular imports.
+ from lp.soyuz.model.publishing import SourcePackagePublishingHistory
+
def _resolve(row):
store = IStore(SourcePackagePublishingHistory)
return tuple(
(value if cls is None else store.get(cls, value))
for value, cls in zip(row, lookups))
+
return _resolve
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2011-06-15 02:41:34 +0000
+++ lib/lp/soyuz/configure.zcml 2011-06-23 12:57:43 +0000
@@ -914,6 +914,11 @@
<!-- PackageCopyJobSource -->
<securedutility
+ component=".model.packagecopyjob.PackageCopyJob"
+ provides=".interfaces.packagecopyjob.IPackageCopyJobSource">
+ <allow interface=".interfaces.packagecopyjob.IPackageCopyJobSource"/>
+ </securedutility>
+ <securedutility
component=".model.packagecopyjob.PlainPackageCopyJob"
provides=".interfaces.packagecopyjob.IPlainPackageCopyJobSource">
<allow interface=".interfaces.packagecopyjob.IPlainPackageCopyJobSource"/>
=== modified file 'lib/lp/soyuz/interfaces/packagecopyjob.py'
--- lib/lp/soyuz/interfaces/packagecopyjob.py 2011-06-14 06:01:14 +0000
+++ lib/lp/soyuz/interfaces/packagecopyjob.py 2011-06-23 12:57:43 +0000
@@ -5,6 +5,7 @@
__all__ = [
"IPackageCopyJob",
+ "IPackageCopyJobSource",
"IPlainPackageCopyJob",
"IPlainPackageCopyJobSource",
"PackageCopyJobType",
@@ -37,6 +38,21 @@
from lp.soyuz.interfaces.archive import IArchive
+class IPackageCopyJobSource(Interface):
+ """Utility for `IPackageCopyJob`-implementing types."""
+
+ def wrap(package_copy_job):
+ """Wrap a `PackageCopyJob` in its concrete implementation type.
+
+ As a special case, `None` produces `None`.
+
+ :param package_copy_job: A `PackageCopyJob`.
+ :return: An `IPackageCopyJob` implementation based on
+ `package_copy_job`, but of the job's specific concrete type
+ (such as `PlainPackageCopyJob`).
+ """
+
+
class IPackageCopyJob(Interface):
"""A job that copies packages between `IArchive`s."""
@@ -74,6 +90,12 @@
schema=IJob, title=_('The common Job attributes'),
required=True, readonly=True)
+ component_name = TextLine(
+ title=_("Component override name"), required=False, readonly=True)
+
+ section_name = TextLine(
+ title=_("Section override name"), required=False, readonly=True)
+
metadata = Attribute('A dict of data about the job.')
=== modified file 'lib/lp/soyuz/interfaces/queue.py'
--- lib/lp/soyuz/interfaces/queue.py 2011-06-17 05:23:56 +0000
+++ lib/lp/soyuz/interfaces/queue.py 2011-06-23 12:57:43 +0000
@@ -140,6 +140,11 @@
package_copy_job = Reference(
schema=IPackageCopyJob,
description=_("The PackageCopyJob for this upload, if it has one."),
+ title=_("Raw Package Copy Job"), required=False, readonly=True)
+
+ concrete_package_copy_job = Reference(
+ schema=IPackageCopyJob,
+ description=_("Concrete IPackageCopyJob implementation, if any."),
title=_("Package Copy Job"), required=False, readonly=True)
archive = exported(
=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py 2011-06-16 13:21:14 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py 2011-06-23 12:57:43 +0000
@@ -22,6 +22,7 @@
classProvides,
implements,
)
+from zope.security.proxy import removeSecurityProxy
from canonical.database.enumcol import EnumCol
from canonical.database.sqlbase import sqlvalues
@@ -61,6 +62,7 @@
from lp.soyuz.interfaces.copypolicy import ICopyPolicy
from lp.soyuz.interfaces.packagecopyjob import (
IPackageCopyJob,
+ IPackageCopyJobSource,
IPlainPackageCopyJob,
IPlainPackageCopyJobSource,
PackageCopyJobType,
@@ -75,6 +77,7 @@
"""Base class for package copying jobs."""
implements(IPackageCopyJob)
+ classProvides(IPackageCopyJobSource)
__storm_table__ = 'PackageCopyJob'
@@ -99,6 +102,31 @@
_json_data = Unicode('json_data')
+ # Derived concrete classes. The entire class gets one dict for
+ # this; it's not meant to be on an instance.
+ concrete_classes = {}
+
+ @classmethod
+ def registerConcreteClass(cls, new_class):
+ """Register a concrete `IPackageCopyJob`-implementing class."""
+ assert new_class.class_job_type not in cls.concrete_classes, (
+ "Class %s is already registered." % new_class)
+ cls.concrete_classes[new_class.class_job_type] = new_class
+
+ @classmethod
+ def wrap(cls, package_copy_job):
+ """See `IPackageCopyJobSource`."""
+ if package_copy_job is None:
+ return None
+ job_type = removeSecurityProxy(package_copy_job).job_type
+ concrete_class = cls.concrete_classes[job_type]
+ return concrete_class(package_copy_job)
+
+ @classmethod
+ def getByID(cls, pcj_id):
+ """See `IPackageCopyJobSource`."""
+ return cls.wrap(IStore(PackageCopyJob).get(PackageCopyJob, pcj_id))
+
def __init__(self, source_archive, target_archive, target_distroseries,
job_type, metadata, package_name=None, copy_policy=None):
super(PackageCopyJob, self).__init__()
@@ -130,6 +158,16 @@
existing.update(metadata_dict)
self._json_data = self.serializeMetadata(existing)
+ @property
+ def component_name(self):
+ """See `IPackageCopyJob`."""
+ return self.metadata.get("component_override")
+
+ @property
+ def section_name(self):
+ """See `IPackageCopyJob`."""
+ return self.metadata.get("section_override")
+
class PackageCopyJobDerived(BaseRunnableJob):
"""Abstract class for deriving from PackageCopyJob."""
@@ -339,13 +377,13 @@
metadata_dict = dict(
component_override=component,
section_override=section)
- self.context.extendMetadata(metadata_dict)
+ removeSecurityProxy(self.context).extendMetadata(metadata_dict)
def getSourceOverride(self):
"""Fetch an `ISourceOverride` from the metadata."""
name = self.package_name
- component_name = self.metadata.get("component_override")
- section_name = self.metadata.get("section_override")
+ component_name = self.component_name
+ section_name = self.section_name
source_package_name = getUtility(ISourcePackageNameSet)[name]
try:
component = getUtility(IComponentSet)[component_name]
@@ -508,3 +546,6 @@
def getPolicyImplementation(self):
"""Return the `ICopyPolicy` applicable to this job."""
return ICopyPolicy(self.copy_policy)
+
+
+PackageCopyJob.registerConcreteClass(PlainPackageCopyJob)
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2011-06-22 12:03:31 +0000
+++ lib/lp/soyuz/model/queue.py 2011-06-23 12:57:43 +0000
@@ -65,12 +65,14 @@
from lp.registry.model.sourcepackagename import SourcePackageName
from lp.services.mail.signedmessage import strip_pgp_signature
from lp.services.propertycache import cachedproperty
+from lp.soyuz.adapters.overrides import SourceOverride
from lp.soyuz.adapters.notification import notify
from lp.soyuz.enums import (
PackageUploadCustomFormat,
PackageUploadStatus,
)
from lp.soyuz.interfaces.archive import MAIN_ARCHIVE_PURPOSES
+from lp.soyuz.interfaces.packagecopyjob import IPackageCopyJobSource
from lp.soyuz.interfaces.publishing import (
IPublishingSet,
ISourcePackagePublishingHistory,
@@ -853,6 +855,11 @@
existing_components.add(binary.component)
return existing_components
+ @cachedproperty
+ def concrete_package_copy_job(self):
+ """See `IPackageUpload`."""
+ return getUtility(IPackageCopyJobSource).wrap(self.package_copy_job)
+
def overrideSource(self, new_component, new_section, allowed_components):
"""See `IPackageUpload`."""
if new_component is None and new_section is None:
@@ -860,15 +867,9 @@
return False
if self.package_copy_job is not None:
- # We just need to add the required component/section to the
- # job metadata.
- extra_data = {}
- if new_component is not None:
- extra_data['component_override'] = new_component.name
- if new_section is not None:
- extra_data['section_override'] = new_section.name
- self.package_copy_job.extendMetadata(extra_data)
- return
+ self.concrete_package_copy_job.addSourceOverride(SourceOverride(
+ self.package_name, new_component, new_section))
+ return True
if not self.contains_source:
return False
=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py 2011-06-10 11:17:39 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py 2011-06-23 12:57:43 +0000
@@ -17,7 +17,10 @@
from canonical.config import config
from canonical.launchpad.interfaces.lpstorm import IStore
from canonical.launchpad.webapp.testing import verifyObject
-from canonical.testing import LaunchpadZopelessLayer
+from canonical.testing import (
+ LaunchpadZopelessLayer,
+ ZopelessDatabaseLayer,
+ )
from lp.registry.model.distroseriesdifferencecomment import (
DistroSeriesDifferenceComment,
)
@@ -43,6 +46,7 @@
from lp.soyuz.interfaces.component import IComponentSet
from lp.soyuz.interfaces.packagecopyjob import (
IPackageCopyJob,
+ IPackageCopyJobSource,
IPlainPackageCopyJob,
IPlainPackageCopyJobSource,
)
@@ -52,12 +56,14 @@
from lp.soyuz.interfaces.sourcepackageformat import (
ISourcePackageFormatSelectionSet,
)
+from lp.soyuz.model.packagecopyjob import PackageCopyJob
from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
from lp.testing import (
run_script,
TestCaseWithFactory,
)
from lp.testing.mail_helpers import pop_notifications
+from lp.testing.matchers import Provides
from lp.testing.fakemethod import FakeMethod
@@ -298,7 +304,7 @@
archive=breezy_archive)
# The target archive needs ancestry so the package is
# auto-accepted.
- ancestry = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=target_series, sourcename="libc",
version="2.8-0", status=PackagePublishingStatus.PUBLISHED,
archive=target_archive)
@@ -489,7 +495,7 @@
# Publish a package in the source archive with some overridable
# properties set to known values.
- source_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="libc",
component='universe', section='web',
version="2.8-1", status=PackagePublishingStatus.PUBLISHED,
@@ -497,7 +503,7 @@
# Now put the same named package in the target archive with
# different override values.
- ancestry_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="libc",
component='restricted', section='games',
version="2.8-0", status=PackagePublishingStatus.PUBLISHED,
@@ -594,7 +600,7 @@
# Publish a package in the source archive with some overridable
# properties set to known values.
- source_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="copyme",
component='multiverse', section='web',
version="2.8-1", status=PackagePublishingStatus.PUBLISHED,
@@ -645,7 +651,7 @@
source_archive = self.factory.makeArchive()
# Publish a package in the source archive.
- source_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="copyme",
version="2.8-1", status=PackagePublishingStatus.PUBLISHED,
component='multiverse', section='web',
@@ -653,7 +659,7 @@
# Now put the same named package in the target archive so it has
# ancestry.
- ancestry_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="copyme",
version="2.8-0", status=PackagePublishingStatus.PUBLISHED,
component='main', section='games',
@@ -698,7 +704,7 @@
source_archive = self.factory.makeArchive()
# Publish a package in the source archive.
- source_package = publisher.getPubSource(
+ publisher.getPubSource(
distroseries=distroseries, sourcename="copyme",
version="2.8-1", status=PackagePublishingStatus.PUBLISHED,
archive=source_archive)
@@ -838,3 +844,25 @@
transaction.commit()
self.layer.switchDbUser(self.dbuser)
removeSecurityProxy(job).reportFailure(CannotCopy("Mommy it hurts"))
+
+
+class TestPackageCopyJobSource(TestCaseWithFactory):
+ """Test the `IPackageCopyJob` utility."""
+
+ layer = ZopelessDatabaseLayer
+
+ def test_implements_interface(self):
+ job_source = getUtility(IPackageCopyJobSource)
+ self.assertThat(job_source, Provides(IPackageCopyJobSource))
+
+ def test_wrap_accepts_None(self):
+ job_source = getUtility(IPackageCopyJobSource)
+ self.assertIs(None, job_source.wrap(None))
+
+ def test_wrap_wraps_PlainPackageCopyJob(self):
+ original_ppcj = self.factory.makePlainPackageCopyJob()
+ IStore(PackageCopyJob).flush()
+ pcj = IStore(PackageCopyJob).get(PackageCopyJob, original_ppcj.id)
+ self.assertNotEqual(None, pcj)
+ job_source = getUtility(IPackageCopyJobSource)
+ self.assertEqual(original_ppcj, job_source.wrap(pcj))