← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:schema-circular-imports-decentralize-registry into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:schema-circular-imports-decentralize-registry into launchpad:master.

Commit message:
Move circular import workarounds to lp.registry.interfaces.webservice

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427074

I moved some patches to `lp.registry.interfaces.distroseries` instead where they were entirely local to that module, and removed an unnecessary bit of circular import avoidance in `lp.registry.interfaces.distroseriesdifferencecomment`.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:schema-circular-imports-decentralize-registry into launchpad:master.
diff --git a/lib/lp/_schema_circular_imports.py b/lib/lp/_schema_circular_imports.py
index ba34be6..667b7d6 100644
--- a/lib/lp/_schema_circular_imports.py
+++ b/lib/lp/_schema_circular_imports.py
@@ -11,46 +11,12 @@ types are defined.
 
 __all__ = []
 
-
-from lazr.restful.fields import Reference
-
 from lp.bugs.interfaces.bugtask import IBugTask
-from lp.bugs.interfaces.vulnerability import IVulnerability
 from lp.buildmaster.interfaces.builder import IBuilder
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
-from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.gitrepository import IGitRepository
-from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
-from lp.registry.interfaces.commercialsubscription import (
-    ICommercialSubscription,
-    )
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.distributionmirror import IDistributionMirror
-from lp.registry.interfaces.distributionsourcepackage import (
-    IDistributionSourcePackage,
-    )
-from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.distroseriesdifference import (
-    IDistroSeriesDifference,
-    )
-from lp.registry.interfaces.distroseriesdifferencecomment import (
-    IDistroSeriesDifferenceComment,
-    )
-from lp.registry.interfaces.ociproject import IOCIProject
-from lp.registry.interfaces.person import (
-    IPerson,
-    IPersonEditRestricted,
-    IPersonLimitedView,
-    IPersonViewRestricted,
-    )
-from lp.registry.interfaces.product import IProduct
-from lp.registry.interfaces.productseries import IProductSeries
-from lp.registry.interfaces.sourcepackage import (
-    ISourcePackage,
-    ISourcePackageEdit,
-    ISourcePackagePublic,
-    )
+from lp.registry.interfaces.person import IPerson
 from lp.services.auth.interfaces import IAccessToken
 from lp.services.comments.interfaces.conversation import IComment
 from lp.services.messages.interfaces.message import (
@@ -61,117 +27,19 @@ from lp.services.messages.interfaces.message import (
 from lp.services.messages.interfaces.messagerevision import IMessageRevision
 from lp.services.webservice.apihelpers import (
     patch_collection_property,
-    patch_collection_return_type,
-    patch_entry_return_type,
-    patch_list_parameter_type,
-    patch_plain_parameter_type,
     patch_reference_property,
     )
-from lp.soyuz.interfaces.archive import IArchive
-from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber
-from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
-from lp.soyuz.interfaces.queue import IPackageUpload
 
 
 # IBuilder
 patch_reference_property(IBuilder, 'current_build', IBuildFarmJob)
 
-patch_reference_property(IPersonViewRestricted, 'archive', IArchive)
-patch_collection_property(IPersonViewRestricted, 'ppas', IArchive)
-patch_plain_parameter_type(
-    IPersonLimitedView, 'getPPAByName', 'distribution', IDistribution)
-patch_entry_return_type(IPersonLimitedView, 'getPPAByName', IArchive)
-patch_plain_parameter_type(
-    IPersonEditRestricted, 'createPPA', 'distribution', IDistribution)
-patch_entry_return_type(IPersonEditRestricted, 'createPPA', IArchive)
-
-patch_reference_property(ISourcePackagePublic, 'distroseries', IDistroSeries)
-patch_reference_property(ISourcePackagePublic, 'productseries', IProductSeries)
-patch_entry_return_type(ISourcePackagePublic, 'getBranch', IBranch)
-patch_plain_parameter_type(ISourcePackageEdit, 'setBranch', 'branch', IBranch)
-patch_reference_property(ISourcePackage, 'distribution', IDistribution)
-
-# IPerson
-patch_entry_return_type(IPerson, 'createRecipe', ISourcePackageRecipe)
-patch_list_parameter_type(IPerson, 'createRecipe', 'distroseries',
-                          Reference(schema=IDistroSeries))
-patch_plain_parameter_type(IPerson, 'createRecipe', 'daily_build_archive',
-                           IArchive)
-patch_plain_parameter_type(IPerson, 'getArchiveSubscriptionURL', 'archive',
-                           IArchive)
-patch_collection_return_type(
-    IPerson, 'getArchiveSubscriptions', IArchiveSubscriber)
-patch_entry_return_type(IPerson, 'getRecipe', ISourcePackageRecipe)
-patch_collection_return_type(IPerson, 'getOwnedProjects', IProduct)
-
 # IBuildFarmJob
 patch_reference_property(IBuildFarmJob, 'buildqueue_record', IBuildQueue)
 
 # IComment
 patch_reference_property(IComment, 'comment_author', IPerson)
 
-# ICommercialSubscription
-patch_reference_property(ICommercialSubscription, 'product', IProduct)
-patch_reference_property(
-    ICommercialSubscription, 'distribution', IDistribution)
-
-# IDistribution
-patch_collection_property(IDistribution, 'series', IDistroSeries)
-patch_collection_property(IDistribution, 'derivatives', IDistroSeries)
-patch_reference_property(IDistribution, 'currentseries', IDistroSeries)
-patch_entry_return_type(IDistribution, 'getArchive', IArchive)
-patch_entry_return_type(IDistribution, 'getSeries', IDistroSeries)
-patch_collection_return_type(
-    IDistribution, 'getDevelopmentSeries', IDistroSeries)
-patch_entry_return_type(
-    IDistribution, 'getSourcePackage', IDistributionSourcePackage)
-patch_entry_return_type(IDistribution, 'getOCIProject', IOCIProject)
-patch_collection_return_type(
-    IDistribution, 'searchSourcePackages', IDistributionSourcePackage)
-patch_reference_property(IDistribution, 'main_archive', IArchive)
-patch_collection_property(IDistribution, 'all_distro_archives', IArchive)
-patch_entry_return_type(IDistribution, 'newOCIProject', IOCIProject)
-patch_collection_return_type(
-    IDistribution, 'searchOCIProjects', IOCIProject)
-patch_collection_property(IDistribution, 'vulnerabilities', IVulnerability)
-
-
-# IDistributionMirror
-patch_reference_property(IDistributionMirror, 'distribution', IDistribution)
-
-
-# IDistroSeries
-patch_entry_return_type(
-    IDistroSeries, 'getDistroArchSeries', IDistroArchSeries)
-patch_reference_property(IDistroSeries, 'main_archive', IArchive)
-patch_collection_property(
-    IDistroSeries, 'enabled_architectures', IDistroArchSeries)
-patch_reference_property(IDistroSeries, 'distribution', IDistribution)
-patch_plain_parameter_type(
-    IDistroSeries, 'getPackageUploads', 'archive', IArchive)
-patch_collection_return_type(
-    IDistroSeries, 'getPackageUploads', IPackageUpload)
-patch_reference_property(IDistroSeries, 'previous_series', IDistroSeries)
-patch_reference_property(
-    IDistroSeries, 'nominatedarchindep', IDistroArchSeries)
-patch_collection_return_type(IDistroSeries, 'getDerivedSeries', IDistroSeries)
-patch_collection_return_type(IDistroSeries, 'getParentSeries', IDistroSeries)
-patch_plain_parameter_type(
-    IDistroSeries, 'getDifferencesTo', 'parent_series', IDistroSeries)
-patch_collection_return_type(
-    IDistroSeries, 'getDifferencesTo', IDistroSeriesDifference)
-patch_collection_return_type(
-    IDistroSeries, 'getDifferenceComments', IDistroSeriesDifferenceComment)
-
-
-# IDistroSeriesDifference
-patch_reference_property(
-    IDistroSeriesDifference, 'latest_comment', IDistroSeriesDifferenceComment)
-
-# IDistroSeriesDifferenceComment
-patch_reference_property(
-    IDistroSeriesDifferenceComment, 'comment_author', IPerson)
-
 # IIndexedMessage
 patch_reference_property(IIndexedMessage, 'inside', IBugTask)
 
@@ -183,12 +51,5 @@ patch_collection_property(IMessage, 'revisions', IMessageRevision)
 patch_reference_property(IUserToUserEmail, 'sender', IPerson)
 patch_reference_property(IUserToUserEmail, 'recipient', IPerson)
 
-# IPerson
-patch_collection_return_type(
-    IPerson, 'getBugSubscriberPackages', IDistributionSourcePackage)
-
-# IProductSeries
-patch_reference_property(IProductSeries, 'product', IProduct)
-
 # IAccessToken
 patch_reference_property(IAccessToken, 'git_repository', IGitRepository)
diff --git a/lib/lp/registry/interfaces/commercialsubscription.py b/lib/lp/registry/interfaces/commercialsubscription.py
index 147138d..e6170cd 100644
--- a/lib/lp/registry/interfaces/commercialsubscription.py
+++ b/lib/lp/registry/interfaces/commercialsubscription.py
@@ -44,7 +44,7 @@ class ICommercialSubscription(Interface):
             required=False,
             readonly=True,
             vocabulary='Product',
-            # Really IProduct, patched in _schema_circular_imports.py.
+            # Really IProduct, patched in lp.registry.interfaces.webservice.
             schema=Interface,
             description=_(
                 "Project for which this commercial subscription is "
@@ -56,7 +56,8 @@ class ICommercialSubscription(Interface):
             required=False,
             readonly=True,
             vocabulary='Distribution',
-            # Really IDistribution, patched in _schema_circular_imports.py.
+            # Really IDistribution, patched in
+            # lp.registry.interfaces.webservice.
             schema=Interface,
             description=_(
                 "Distribution for which this commercial subscription is "
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index 46e6e46..b5f3e08 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -226,7 +226,7 @@ class IDistributionLimitedView(IHasIcon, IHasLogo, IHasOwner, ILaunchpadUsage):
 
     @operation_parameters(
         name=TextLine(title=_("OCI project name"), required=True))
-    # Really returns IOCIProject, see _schema_circular_imports.py.
+    # Really IOCIProject, patched in lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("devel")
@@ -343,12 +343,14 @@ class IDistributionView(
     series = exported(doNotSnapshot(
         CollectionField(
             title=_("DistroSeries inside this Distribution"),
-            # Really IDistroSeries, see _schema_circular_imports.py.
+            # Really IDistroSeries, patched in
+            # lp.registry.interfaces.webservice.
             value_type=Reference(schema=Interface))))
     derivatives = exported(doNotSnapshot(
         CollectionField(
             title=_("This Distribution's derivatives"),
-            # Really IDistroSeries, see _schema_circular_imports.py.
+            # Really IDistroSeries, patched in
+            # lp.registry.interfaces.webservice.
             value_type=Reference(schema=Interface))))
     architectures = List(
         title=_("DistroArchSeries inside this Distribution"))
@@ -366,7 +368,8 @@ class IDistributionView(
     # properties
     currentseries = exported(
         Reference(
-            # Really IDistroSeries, see _schema_circular_imports.py.
+            # Really IDistroSeries, patched in
+            # lp.registry.interfaces.webservice.
             Interface,
             title=_("Current series"),
             description=_(
@@ -404,7 +407,7 @@ class IDistributionView(
     main_archive = exported(
         Reference(
             title=_('Distribution Main Archive.'), readonly=True,
-            # Really IArchive, see _schema_circular_imports.py.
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
             schema=Interface))
 
     all_distro_archives = exported(doNotSnapshot(
@@ -413,8 +416,8 @@ class IDistributionView(
                 "A sequence of the distribution's primary, "
                 "partner and debug archives."),
             readonly=True, required=False,
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
             value_type=Reference(schema=Interface))),
-                # Really IArchive, see _schema_circular_imports.py.
         exported_as='archives')
 
     all_distro_archive_ids = Attribute(
@@ -494,7 +497,8 @@ class IDistributionView(
         doNotSnapshot(CollectionField(
             description=_("Vulnerabilities in this distribution."),
             readonly=True,
-            # Really IVulnerability, see _schema_circular_imports.py.
+            # Really IVulnerability, patched in
+            # lp.registry.interfaces.webservice.
             value_type=Reference(schema=Interface)))
         )
 
@@ -515,6 +519,7 @@ class IDistributionView(
 
     @operation_parameters(
         name=TextLine(title=_("Archive name"), required=True))
+    # Really IArchive, patched in lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -526,7 +531,7 @@ class IDistributionView(
         :param name: The name of the archive, e.g. 'partner'
         """
 
-    # Really IDistroSeries, see _schema_circular_imports.py.
+    # Really IDistroSeries, patched in lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_operation_as(name="getDevelopmentSeries")
     @export_read_operation()
@@ -546,7 +551,7 @@ class IDistributionView(
 
     @operation_parameters(
         name_or_version=TextLine(title=_("Name or version"), required=True))
-    # Really IDistroSeries, see _schema_circular_imports.py.
+    # Really IDistroSeries, patched in lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @call_with(follow_aliases=True)
     @export_read_operation()
@@ -635,8 +640,7 @@ class IDistributionView(
 
     @operation_parameters(
         text=TextLine(title=_("OCI title substring match "), required=False))
-    # Really returns IOCIProject, see
-    # _schema_circular_imports.py.
+    # Really IOCIProject, patched in lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("devel")
@@ -645,8 +649,8 @@ class IDistributionView(
 
     @operation_parameters(
         name=TextLine(title=_("Package name"), required=True))
-    # Really returns IDistributionSourcePackage, see
-    # _schema_circular_imports.py.
+    # Really IDistributionSourcePackage, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -686,8 +690,8 @@ class IDistributionView(
     @operation_parameters(
         text=TextLine(title=_("Source package name substring match"),
                       required=True))
-    # Really returns IDistributionSourcePackage, see
-    # _schema_circular_imports.py.
+    # Really IDistributionSourcePackage, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -814,7 +818,7 @@ class IDistributionView(
             description=_("A short description of this OCI project."),
             required=False)
     )
-    # Interface is actually IOCIProject. Fixed at _schema_circular_imports
+    # Really IOCIProject, patched in lp.registry.interfaces.webservice.
     @export_factory_operation(Interface, [])
     @operation_for_version("devel")
     def newOCIProject(registrant, name, description=None):
diff --git a/lib/lp/registry/interfaces/distributionmirror.py b/lib/lp/registry/interfaces/distributionmirror.py
index ab784f7..8eb22e1 100644
--- a/lib/lp/registry/interfaces/distributionmirror.py
+++ b/lib/lp/registry/interfaces/distributionmirror.py
@@ -347,8 +347,8 @@ class IDistributionMirror(Interface):
     distribution = exported(
         Reference(
             Interface,
-            # Really IDistribution, circular import fixed in
-            # _schema_circular_imports.
+            # Really IDistribution, patched in
+            # lp.registry.interfaces.webservice.
             title=_("Distribution"), required=True, readonly=True,
             description=_("The distribution that is mirrored")))
     name = exported(DistributionMirrorNameField(
diff --git a/lib/lp/registry/interfaces/distroseries.py b/lib/lp/registry/interfaces/distroseries.py
index b6d99db..3bfd444 100644
--- a/lib/lp/registry/interfaces/distroseries.py
+++ b/lib/lp/registry/interfaces/distroseries.py
@@ -92,7 +92,11 @@ from lp.services.fields import (
     Title,
     UniqueField,
     )
-from lp.services.webservice.apihelpers import patch_plain_parameter_type
+from lp.services.webservice.apihelpers import (
+    patch_collection_return_type,
+    patch_plain_parameter_type,
+    patch_reference_property,
+    )
 from lp.soyuz.enums import (
     IndexCompressionType,
     PackageUploadCustomFormat,
@@ -223,7 +227,9 @@ class IDistroSeriesPublic(
             description=_("The version string for this series.")))
     distribution = exported(
         Reference(
-            Interface,  # Really IDistribution, see circular import fix below.
+            # Really IDistribution, patched in
+            # lp.registry.interfaces.webservice.
+            Interface,
             title=_("Distribution"), required=True,
             description=_("The distribution for which this is a series.")))
     distributionID = Attribute('The distribution ID.')
@@ -252,7 +258,9 @@ class IDistroSeriesPublic(
         ReferenceChoice(
             title=_("Parent series"),
             description=_("The series from which this one was branched."),
-            required=True, schema=Interface,  # Really IDistroSeries
+            required=True,
+            # Really IDistroSeries, patched below.
+            schema=Interface,
             vocabulary='DistroSeries'),
         ("devel", dict(exported_as="previous_series")),
         ("1.0", dict(exported_as="parent_series")),
@@ -294,7 +302,9 @@ class IDistroSeriesPublic(
         "series.")
     nominatedarchindep = exported(
         Reference(
-            Interface,  # IDistroArchSeries.
+            # Really IDistroArchSeries, patched in
+            # lp.registry.interfaces.webservice.
+            Interface,
             title=_("DistroArchSeries designed to build "
                     "architecture-independent packages whithin this "
                     "distroseries context."),
@@ -434,7 +444,8 @@ class IDistroSeriesPublic(
 
     main_archive = exported(
         Reference(
-            Interface,  # Really IArchive, see below for circular import fix.
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
+            Interface,
             title=_('Distribution Main Archive')))
 
     supported = exported(
@@ -466,7 +477,9 @@ class IDistroSeriesPublic(
             title=_("Enabled architectures"),
             description=_("All architectures in this series with the "
                           "'enabled' flag set."),
-            value_type=Reference(schema=Interface),  # IDistroArchSeries
+            # Really IDistroArchSeries, patched in
+            # lp.registry.interfaces.webservice.
+            value_type=Reference(schema=Interface),
             readonly=True)),
         exported_as="architectures")
 
@@ -495,6 +508,8 @@ class IDistroSeriesPublic(
     @operation_parameters(
         archtag=TextLine(
             title=_("The architecture tag"), required=True))
+    # Really IDistroArchSeries, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -558,7 +573,7 @@ class IDistroSeriesPublic(
             description=_("Return only items that have this status."),
             required=False),
         archive=Reference(
-            # Really IArchive, patched in _schema_circular_imports.py
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
             schema=Interface,
             title=_("Archive"),
             description=_("Return only items for this archive."),
@@ -582,7 +597,7 @@ class IDistroSeriesPublic(
                           "matching."),
             required=False),
         )
-    # Really IPackageUpload, patched in _schema_circular_imports.py
+    # Really IPackageUpload, patched in lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -792,12 +807,14 @@ class IDistroSeriesPublic(
         :param format: The SourcePackageFormat to check.
         """
 
+    # Really IDistroSeries, patched below.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("beta")
     def getDerivedSeries():
         """Get all `DistroSeries` derived from this one."""
 
+    # Really IDistroSeries, patched below.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -806,7 +823,8 @@ class IDistroSeriesPublic(
 
     @operation_parameters(
         parent_series=Reference(
-            schema=Interface,  # IDistroSeries
+            # Really IDistroSeries, patched below.
+            schema=Interface,
             title=_("The parent series to consider."),
             required=False),
         difference_type=Choice(
@@ -825,6 +843,8 @@ class IDistroSeriesPublic(
                     "is higher than the parent's."),
             required=False),
         )
+    # Really IDistroSeriesDifference, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version('devel')
@@ -872,6 +892,8 @@ class IDistroSeriesPublic(
             title=_("Name of source package"),
             description=_("Only return comments for this source package."),
             required=False))
+    # Really IDistroSeriesDifferenceComment, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version('devel')
@@ -973,6 +995,12 @@ class IDistroSeries(IDistroSeriesEditRestricted, IDistroSeriesPublic,
     """A series of an operating system distribution."""
 
 
+patch_reference_property(IDistroSeries, 'previous_series', IDistroSeries)
+patch_collection_return_type(IDistroSeries, 'getDerivedSeries', IDistroSeries)
+patch_collection_return_type(IDistroSeries, 'getParentSeries', IDistroSeries)
+patch_plain_parameter_type(
+    IDistroSeries, 'getDifferencesTo', 'parent_series', IDistroSeries)
+
 # We assign the schema for an `IHasBugs` method argument here
 # in order to avoid circular dependencies.
 patch_plain_parameter_type(
diff --git a/lib/lp/registry/interfaces/distroseriesdifference.py b/lib/lp/registry/interfaces/distroseriesdifference.py
index 66ab583..966d59f 100644
--- a/lib/lp/registry/interfaces/distroseriesdifference.py
+++ b/lib/lp/registry/interfaces/distroseriesdifference.py
@@ -216,7 +216,9 @@ class IDistroSeriesDifferencePublic(Interface):
         """
 
     latest_comment = Reference(
-        Interface,  # IDistroSeriesDifferenceComment
+        # Really IDistroSeriesDifferenceComment, patched in
+        # lp.registry.interfaces.webservice.
+        Interface,
         title=_("The latest comment"),
         readonly=True)
 
diff --git a/lib/lp/registry/interfaces/distroseriesdifferencecomment.py b/lib/lp/registry/interfaces/distroseriesdifferencecomment.py
index 158b285..121d8ed 100644
--- a/lib/lp/registry/interfaces/distroseriesdifferencecomment.py
+++ b/lib/lp/registry/interfaces/distroseriesdifferencecomment.py
@@ -26,6 +26,7 @@ from lp import _
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifference,
     )
+from lp.registry.interfaces.person import IPerson
 from lp.services.messages.interfaces.message import IMessage
 
 
@@ -49,8 +50,7 @@ class IDistroSeriesDifferenceComment(Interface):
             "The comment text for the related distro series difference.")))
 
     comment_author = exported(Reference(
-        # Really IPerson.
-        Interface, title=_("The author of the comment."),
+        IPerson, title=_("The author of the comment."),
         readonly=True))
 
     comment_date = exported(Datetime(
diff --git a/lib/lp/registry/interfaces/person.py b/lib/lp/registry/interfaces/person.py
index 2ebbdf5..faa05d7 100644
--- a/lib/lp/registry/interfaces/person.py
+++ b/lib/lp/registry/interfaces/person.py
@@ -704,9 +704,11 @@ class IPersonLimitedView(IHasIcon, IHasLogo):
         Bool(title=_("Is this a probationary user?"), readonly=True))
 
     @operation_parameters(
+        # Really IDistribution, patched in lp.registry.interfaces.webservice.
         distribution=Reference(schema=Interface, required=False),
         name=TextLine(required=True, constraint=name_validator))
-    @operation_returns_entry(Interface)  # Really IArchive.
+    # Really IArchive, patched in lp.registry.interfaces.webservice.
+    @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
     def getPPAByName(distribution, name):
@@ -948,7 +950,7 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
             title=_("Default PPA"),
             description=_("The PPA named 'ppa' owned by this person."),
             readonly=True, required=False,
-            # Really IArchive, see archive.py
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
             schema=Interface))
 
     ppas = exported(doNotSnapshot(
@@ -957,7 +959,7 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
             description=_(
                 "PPAs owned by the context person ordered by name."),
             readonly=True, required=False,
-            # Really IArchive, see archive.py
+            # Really IArchive, patched in lp.registry.interfaces.webservice.
             value_type=Reference(schema=Interface))))
 
     structural_subscriptions = Attribute(
@@ -1032,12 +1034,16 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
     @call_with(registrant=REQUEST_USER)
     @operation_parameters(
         description=Text(),
+        # Really IDistroSeries, patched in lp.registry.interfaces.webservice.
         distroseries=List(value_type=Reference(schema=Interface)),
         name=TextLine(),
         recipe_text=Text(),
+        # Really IArchive, patched in lp.registry.interfaces.webservice.
         daily_build_archive=Reference(schema=Interface),
         build_daily=Bool(),
         )
+    # Really ISourcePackageRecipe, patched in
+    # lp.registry.interfaces.webservice.
     @export_factory_operation(Interface, [])
     @operation_for_version("beta")
     def createRecipe(name, description, recipe_text, distroseries,
@@ -1055,7 +1061,9 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
         """
 
     @operation_parameters(name=TextLine(required=True))
-    @operation_returns_entry(Interface)  # Really ISourcePackageRecipe.
+    # Really ISourcePackageRecipe, patched in
+    # lp.registry.interfaces.webservice.
+    @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
     def getRecipe(name):
@@ -1063,7 +1071,8 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
 
     @call_with(requester=REQUEST_USER)
     @export_read_operation()
-    @operation_returns_collection_of(Interface)  # Really IArchiveSubscriber
+    # Really IArchiveSubscriber, patched in lp.registry.interfaces.webservice.
+    @operation_returns_collection_of(Interface)
     @operation_for_version('devel')
     def getArchiveSubscriptions(requester):
         """Return (private) archives subscription for this person."""
@@ -1081,7 +1090,8 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
 
     @call_with(requester=REQUEST_USER)
     @operation_parameters(
-        archive=Reference(schema=Interface))  # Really IArchive
+        # Really IArchive, patched in lp.registry.interfaces.webservice.
+        archive=Reference(schema=Interface))
     @export_write_operation()
     @operation_for_version("beta")
     def getArchiveSubscriptionURL(requester, archive):
@@ -1118,6 +1128,8 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
         The results are ordered using Person.sortingColumns.
         """
 
+    # Really IDistributionSourcePackage, patched in
+    # lp.registry.interfaces.webservice.
     @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -1195,7 +1207,8 @@ class IPersonViewRestricted(IHasBranches, IHasSpecifications,
         """
 
     @call_with(user=REQUEST_USER)
-    @operation_returns_collection_of(Interface)  # Really IProduct.
+    # Really IProduct, patched in lp.registry.interfaces.webservice.
+    @operation_returns_collection_of(Interface)
     @export_read_operation()
     @operation_for_version("devel")
     def getOwnedProjects(match_name=None, transitive=False, user=None):
@@ -1745,6 +1758,7 @@ class IPersonEditRestricted(Interface):
         """
 
     @operation_parameters(
+        # Really IDistribution, patched in lp.registry.interfaces.webservice.
         distribution=Reference(schema=Interface, required=False),
         name=TextLine(required=True, constraint=name_validator),
         displayname=TextLine(required=False),
@@ -1752,7 +1766,8 @@ class IPersonEditRestricted(Interface):
         private=Bool(required=False),
         suppress_subscription_notifications=Bool(required=False),
         )
-    @export_factory_operation(Interface, [])  # Really IArchive.
+    # Really IArchive, patched in lp.registry.interfaces.webservice.
+    @export_factory_operation(Interface, [])
     @operation_for_version("beta")
     def createPPA(distribution=None, name=None, displayname=None,
                   description=None, private=False,
diff --git a/lib/lp/registry/interfaces/productseries.py b/lib/lp/registry/interfaces/productseries.py
index 2378216..99fba48 100644
--- a/lib/lp/registry/interfaces/productseries.py
+++ b/lib/lp/registry/interfaces/productseries.py
@@ -151,7 +151,9 @@ class IProductSeriesLimitedView(Interface):
 
     product = exported(
         ReferenceChoice(title=_('Project'), required=True,
-            vocabulary='Product', schema=Interface),  # really IProduct
+            vocabulary='Product',
+            # Really IProduct, patched in lp.registry.interfaces.webservice.
+            schema=Interface),
         exported_as='project')
     productID = Attribute('The product ID.')
 
diff --git a/lib/lp/registry/interfaces/sourcepackage.py b/lib/lp/registry/interfaces/sourcepackage.py
index 6a7e707..0492b7f 100644
--- a/lib/lp/registry/interfaces/sourcepackage.py
+++ b/lib/lp/registry/interfaces/sourcepackage.py
@@ -104,8 +104,8 @@ class ISourcePackagePublic(IBugTarget, IHasBranches, IHasMergeProposals,
     distribution = exported(
         Reference(
             Interface,
-            # Really IDistribution, circular import fixed in
-            # _schema_circular_imports.
+            # Really IDistribution, patched in
+            # lp.registry.interfaces.webservice.
             title=_("Distribution"), required=True, readonly=True,
             description=_("The distribution for this source package.")))
 
@@ -113,7 +113,10 @@ class ISourcePackagePublic(IBugTarget, IHasBranches, IHasMergeProposals,
     # cause circular imports. Set in _schema_circular_imports.
     distroseries = exported(
         Reference(
-            Interface, title=_("Distribution Series"), required=True,
+            # Really IDistroSeries, patched in
+            # lp.registry.interfaces.webservice.
+            Interface,
+            title=_("Distribution Series"), required=True,
             readonly=True,
             description=_("The DistroSeries for this SourcePackage")))
 
@@ -124,6 +127,8 @@ class ISourcePackagePublic(IBugTarget, IHasBranches, IHasMergeProposals,
         ReferenceChoice(
             title=_("Project series"), required=False,
             vocabulary="ProductSeries", readonly=True,
+            # Really IProductSeries, patched in
+            # lp.registry.interfaces.webservice.
             schema=Interface,
             description=_(
                 "The registered project series that this source package "
@@ -247,8 +252,7 @@ class ISourcePackagePublic(IBugTarget, IHasBranches, IHasMergeProposals,
         pocket=Choice(
             title=_("Pocket"), required=True,
             vocabulary=PackagePublishingPocket))
-    # Actually returns an IBranch, but we say Interface here to avoid circular
-    # imports. Correct interface specified in _schema_circular_imports.
+    # Really IBranch, patched in lp.registry.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
     @operation_for_version("beta")
@@ -303,13 +307,11 @@ class ISourcePackagePublic(IBugTarget, IHasBranches, IHasMergeProposals,
 class ISourcePackageEdit(Interface):
     """SourcePackage attributes requiring launchpad.Edit."""
 
-    # 'branch' should be IBranch, but we use the base class to avoid
-    # circular imports. Correct interface specific in
-    # _schema_circular_imports.
     @operation_parameters(
         pocket=Choice(
             title=_("Pocket"), required=True,
             vocabulary=PackagePublishingPocket),
+        # Really IBranch, patched in lp.registry.interfaces.webservice.
         branch=Reference(Interface, title=_("Branch"), required=False))
     @call_with(registrant=REQUEST_USER)
     @export_write_operation()
diff --git a/lib/lp/registry/interfaces/webservice.py b/lib/lp/registry/interfaces/webservice.py
index c27edda..1b5528d 100644
--- a/lib/lp/registry/interfaces/webservice.py
+++ b/lib/lp/registry/interfaces/webservice.py
@@ -43,10 +43,12 @@ __all__ = [
     'IWikiName',
     ]
 
-# XXX: JonathanLange 2010-11-09 bug=673083: Legacy work-around for circular
-# import bugs.  Break this up into a per-package thing.
-from lp import _schema_circular_imports
+from lazr.restful.fields import Reference
+
 from lp.app.interfaces.services import IServiceFactory
+from lp.bugs.interfaces.vulnerability import IVulnerability
+from lp.code.interfaces.branch import IBranch
+from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
 from lp.registry.interfaces.commercialsubscription import (
     ICommercialSubscription,
     )
@@ -76,9 +78,13 @@ from lp.registry.interfaces.milestone import (
     IMilestone,
     IProjectGroupMilestone,
     )
+from lp.registry.interfaces.ociproject import IOCIProject
 from lp.registry.interfaces.person import (
     IPerson,
+    IPersonEditRestricted,
+    IPersonLimitedView,
     IPersonSet,
+    IPersonViewRestricted,
     ITeam,
     )
 from lp.registry.interfaces.pillar import (
@@ -106,11 +112,108 @@ from lp.registry.interfaces.projectgroup import (
     IProjectGroupSet,
     )
 from lp.registry.interfaces.sharingservice import ISharingService
-from lp.registry.interfaces.sourcepackage import ISourcePackage
+from lp.registry.interfaces.sourcepackage import (
+    ISourcePackage,
+    ISourcePackageEdit,
+    ISourcePackagePublic,
+    )
 from lp.registry.interfaces.sourcepackagename import ISourcePackageName
 from lp.registry.interfaces.ssh import ISSHKey
 from lp.registry.interfaces.teammembership import ITeamMembership
 from lp.registry.interfaces.wikiname import IWikiName
+from lp.services.webservice.apihelpers import (
+    patch_collection_property,
+    patch_collection_return_type,
+    patch_entry_return_type,
+    patch_list_parameter_type,
+    patch_plain_parameter_type,
+    patch_reference_property,
+    )
+from lp.soyuz.interfaces.archive import IArchive
+from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber
+from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
+from lp.soyuz.interfaces.queue import IPackageUpload
+
+
+# ICommercialSubscription
+patch_reference_property(ICommercialSubscription, 'product', IProduct)
+patch_reference_property(
+    ICommercialSubscription, 'distribution', IDistribution)
+
+# IDistribution
+patch_collection_property(IDistribution, 'series', IDistroSeries)
+patch_collection_property(IDistribution, 'derivatives', IDistroSeries)
+patch_reference_property(IDistribution, 'currentseries', IDistroSeries)
+patch_entry_return_type(IDistribution, 'getArchive', IArchive)
+patch_entry_return_type(IDistribution, 'getSeries', IDistroSeries)
+patch_collection_return_type(
+    IDistribution, 'getDevelopmentSeries', IDistroSeries)
+patch_entry_return_type(
+    IDistribution, 'getSourcePackage', IDistributionSourcePackage)
+patch_entry_return_type(IDistribution, 'getOCIProject', IOCIProject)
+patch_collection_return_type(
+    IDistribution, 'searchSourcePackages', IDistributionSourcePackage)
+patch_reference_property(IDistribution, 'main_archive', IArchive)
+patch_collection_property(IDistribution, 'all_distro_archives', IArchive)
+patch_entry_return_type(IDistribution, 'newOCIProject', IOCIProject)
+patch_collection_return_type(
+    IDistribution, 'searchOCIProjects', IOCIProject)
+patch_collection_property(IDistribution, 'vulnerabilities', IVulnerability)
+
+# IDistributionMirror
+patch_reference_property(IDistributionMirror, 'distribution', IDistribution)
+
+# IDistroSeries
+patch_entry_return_type(
+    IDistroSeries, 'getDistroArchSeries', IDistroArchSeries)
+patch_reference_property(IDistroSeries, 'main_archive', IArchive)
+patch_collection_property(
+    IDistroSeries, 'enabled_architectures', IDistroArchSeries)
+patch_reference_property(IDistroSeries, 'distribution', IDistribution)
+patch_plain_parameter_type(
+    IDistroSeries, 'getPackageUploads', 'archive', IArchive)
+patch_collection_return_type(
+    IDistroSeries, 'getPackageUploads', IPackageUpload)
+patch_reference_property(
+    IDistroSeries, 'nominatedarchindep', IDistroArchSeries)
+patch_collection_return_type(
+    IDistroSeries, 'getDifferencesTo', IDistroSeriesDifference)
+patch_collection_return_type(
+    IDistroSeries, 'getDifferenceComments', IDistroSeriesDifferenceComment)
+
+# IDistroSeriesDifference
+patch_reference_property(
+    IDistroSeriesDifference, 'latest_comment', IDistroSeriesDifferenceComment)
+
+# IPerson
+patch_reference_property(IPersonViewRestricted, 'archive', IArchive)
+patch_collection_property(IPersonViewRestricted, 'ppas', IArchive)
+patch_plain_parameter_type(
+    IPersonLimitedView, 'getPPAByName', 'distribution', IDistribution)
+patch_entry_return_type(IPersonLimitedView, 'getPPAByName', IArchive)
+patch_plain_parameter_type(
+    IPersonEditRestricted, 'createPPA', 'distribution', IDistribution)
+patch_entry_return_type(IPersonEditRestricted, 'createPPA', IArchive)
+patch_entry_return_type(IPerson, 'createRecipe', ISourcePackageRecipe)
+patch_list_parameter_type(IPerson, 'createRecipe', 'distroseries',
+                          Reference(schema=IDistroSeries))
+patch_plain_parameter_type(IPerson, 'createRecipe', 'daily_build_archive',
+                           IArchive)
+patch_plain_parameter_type(IPerson, 'getArchiveSubscriptionURL', 'archive',
+                           IArchive)
+patch_collection_return_type(
+    IPerson, 'getArchiveSubscriptions', IArchiveSubscriber)
+patch_entry_return_type(IPerson, 'getRecipe', ISourcePackageRecipe)
+patch_collection_return_type(IPerson, 'getOwnedProjects', IProduct)
+patch_collection_return_type(
+    IPerson, 'getBugSubscriberPackages', IDistributionSourcePackage)
 
+# IProductSeries
+patch_reference_property(IProductSeries, 'product', IProduct)
 
-_schema_circular_imports
+# ISourcePackage
+patch_reference_property(ISourcePackagePublic, 'distroseries', IDistroSeries)
+patch_reference_property(ISourcePackagePublic, 'productseries', IProductSeries)
+patch_entry_return_type(ISourcePackagePublic, 'getBranch', IBranch)
+patch_plain_parameter_type(ISourcePackageEdit, 'setBranch', 'branch', IBranch)
+patch_reference_property(ISourcePackage, 'distribution', IDistribution)