← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~adiroiban/launchpad/bug-532239 into lp:launchpad/devel

 

Adi Roiban has proposed merging lp:~adiroiban/launchpad/bug-532239 into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #532239 Move name attribute from IDistroSeries and IProductSeries to SeriesMixin
  https://bugs.launchpad.net/bugs/532239


= Bug 532239 =
Since for solving bug 531261, moving name attribute would make the diff to big, I'm opening this bug to be fixed in a different branch.

We will also need to merge DistroSeriesNameField , NoSuchDistroSeries and ProductSeriesNameField , NoSuchProductSeries and the expcetion are used in many places.

== Proposed fix ==

Move name attribute in the common Series interface and mixin.

== Pre-implementation notes ==

I had a short chat with Sinzui and he agreed that merging NoSuchDistroSeries and NoSuchProductSeries into NoSuchSeries is a good idea.
He also volunteer to review this branch in case someone else thing it is too big.

== Implementation details ==

Name attribute is attached to a name validation which was raising 

== Tests ==
./bin/test -t test_branchlookup -t test_branchnamespace -t test_linkedbranch -t test_branch -t test_distroseries -t test_distribution -t test_distribution -t vocabularie -t xx-distribution

== Demo and Q/A ==
Since this is a refactoring there is not much of QA and Demo.
Basically if you observe any changes, this branch is of not good :)


-- 
https://code.launchpad.net/~adiroiban/launchpad/bug-532239/+merge/30108
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~adiroiban/launchpad/bug-532239 into lp:launchpad/devel.
=== modified file 'lib/canonical/launchpad/doc/vocabularies.txt'
--- lib/canonical/launchpad/doc/vocabularies.txt	2010-02-17 11:13:06 +0000
+++ lib/canonical/launchpad/doc/vocabularies.txt	2010-07-16 14:14:50 +0000
@@ -236,12 +236,12 @@
     True
 
 Trying to get a non-existent release will result in a
-NoSuchDistroSeries error.
+NoSuchSeries error.
 
     >>> series_vocabulary.getTermByToken('non-such-release')
     Traceback (most recent call last):
     ...
-    NoSuchDistroSeries...
+    NoSuchSeries...
 
 
 === ProjectProductsVocabularyUsingMalone ===

=== modified file 'lib/canonical/launchpad/xmlrpc/configure.zcml'
--- lib/canonical/launchpad/xmlrpc/configure.zcml	2010-07-01 19:39:54 +0000
+++ lib/canonical/launchpad/xmlrpc/configure.zcml	2010-07-16 14:14:50 +0000
@@ -140,10 +140,6 @@
     <require like_class="xmlrpclib.Fault" />
   </class>
 
-  <class class="canonical.launchpad.xmlrpc.faults.NoSuchProductSeries">
-    <require like_class="xmlrpclib.Fault" />
-  </class>
-
   <class class="canonical.launchpad.xmlrpc.faults.InvalidBranchIdentifier">
     <require like_class="xmlrpclib.Fault" />
   </class>
@@ -196,7 +192,7 @@
     <require like_class="xmlrpclib.Fault" />
   </class>
 
-  <class class="canonical.launchpad.xmlrpc.faults.NoSuchDistroSeries">
+  <class class="canonical.launchpad.xmlrpc.faults.NoSuchSeries">
     <require like_class="xmlrpclib.Fault" />
   </class>
 

=== modified file 'lib/canonical/launchpad/xmlrpc/faults.py'
--- lib/canonical/launchpad/xmlrpc/faults.py	2010-07-02 12:10:21 +0000
+++ lib/canonical/launchpad/xmlrpc/faults.py	2010-07-16 14:14:50 +0000
@@ -32,7 +32,7 @@
     'NoSuchPerson',
     'NoSuchPersonWithName',
     'NoSuchProduct',
-    'NoSuchProductSeries',
+    'NoSuchSeries',
     'NoSuchTeamMailingList',
     'NotInTeam',
     'NoUrlForBranch',
@@ -226,18 +226,6 @@
         LaunchpadFault.__init__(self, object_name=component.displayname)
 
 
-class NoSuchProductSeries(LaunchpadFault):
-    """There is no such series on a particular project."""
-
-    error_code = 180
-    msg_template = (
-        'Project %(product_name)s has no series called "%(series_name)s"')
-
-    def __init__(self, series_name, product):
-        LaunchpadFault.__init__(
-            self, series_name=series_name, product_name=product.name)
-
-
 class InvalidBranchIdentifier(LaunchpadFault):
     """The branch identifier didn't begin with a tilde."""
 
@@ -419,15 +407,15 @@
         LaunchpadFault.__init__(self, path=path)
 
 
-class NoSuchDistroSeries(LaunchpadFault):
-    """Raised when the user tries to get a distroseries that doesn't exist."""
+class NoSuchSeries(LaunchpadFault):
+    """Raised when the user tries to get a series that doesn't exist."""
 
     error_code = 340
-    msg_template = "No such distribution series %(distroseries_name)s."
+    msg_template = "No such series %(series_name)s."
 
-    def __init__(self, distroseries_name):
-        self.distroseries_name = distroseries_name
-        LaunchpadFault.__init__(self, distroseries_name=distroseries_name)
+    def __init__(self, series_name):
+        self.series_name = series_name
+        LaunchpadFault.__init__(self, series_name=series_name)
 
 
 class NoSuchSourcePackageName(LaunchpadFault):

=== modified file 'lib/lp/code/interfaces/branchlookup.py'
--- lib/lp/code/interfaces/branchlookup.py	2009-11-19 15:46:10 +0000
+++ lib/lp/code/interfaces/branchlookup.py	2010-07-16 14:14:50 +0000
@@ -40,7 +40,7 @@
             component of the path.
         :raises NoSuchProduct: If we can't find a product that matches the
             product component of the path.
-        :raises NoSuchProductSeries: If the series component doesn't match an
+        :raises NoSuchSeries: If the series component doesn't match an
             existing series.
         :raises NoSuchSourcePackageName: If the source packagae referred to
             does not exist.
@@ -131,10 +131,8 @@
             component of the path.
         :raises NoSuchProduct: If we can't find a product that matches the
             product component of the path.
-        :raises NoSuchProductSeries: If the product series component doesn't
-            match an existing series.
-        :raises NoSuchDistroSeries: If the distro series component doesn't
-            match an existing series.
+        :raises NoSuchSeries: If the series component doesn't match an 
+            existing series.
         :raises NoSuchSourcePackageName: If the source packagae referred to
             does not exist.
 

=== modified file 'lib/lp/code/interfaces/branchnamespace.py'
--- lib/lp/code/interfaces/branchnamespace.py	2009-08-13 15:12:16 +0000
+++ lib/lp/code/interfaces/branchnamespace.py	2010-07-16 14:14:50 +0000
@@ -188,8 +188,7 @@
         :raise NoSuchProduct: if the product referred to cannot be found.
         :raise NoSuchDistribution: if the distribution referred to cannot be
             found.
-        :raise NoSuchDistroSeries: if the distroseries referred to cannot be-
-            found.
+        :raise NoSuchSeries: if the series referred to cannot be found.
         :raise NoSuchSourcePackageName: if the sourcepackagename referred to
             cannot be found.
         :return: An `IBranchNamespace`.
@@ -203,8 +202,7 @@
         :raise NoSuchProduct: if the product referred to cannot be found.
         :raise NoSuchDistribution: if the distribution referred to cannot be
             found.
-        :raise NoSuchDistroSeries: if the distroseries referred to cannot be-
-            found.
+        :raise NoSuchSeries: if the series referred to cannot be found.
         :raise NoSuchSourcePackageName: if the sourcepackagename referred to
             cannot be found.
         :return: An `IBranchNamespace`.
@@ -273,7 +271,7 @@
             found.
         :raise NoSuchDistribution: if the distribution referred to cannot be
             found.
-        :raise NoSuchDistroSeries: if the distroseries referred to cannot be-
+        :raise NoSuchSeries: if the series referred to cannot be-
             found.
         :raise NoSuchSourcePackageName: if the sourcepackagename referred to
             cannot be found.

=== modified file 'lib/lp/code/model/branchlookup.py'
--- lib/lp/code/model/branchlookup.py	2009-11-20 15:58:09 +0000
+++ lib/lp/code/model/branchlookup.py	2010-07-16 14:14:50 +0000
@@ -30,12 +30,12 @@
     CannotHaveLinkedBranch, get_linked_branch, NoLinkedBranch)
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distroseries import (
-    IDistroSeries, IDistroSeriesSet, NoSuchDistroSeries)
+    IDistroSeries, IDistroSeriesSet)
 from lp.registry.interfaces.person import NoSuchPerson
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.registry.interfaces.product import (
     InvalidProductName, IProduct, NoSuchProduct)
-from lp.registry.interfaces.productseries import NoSuchProductSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.sourcepackagename import (
     NoSuchSourcePackageName)
 from lp.services.utils import iter_split
@@ -109,13 +109,13 @@
     def traverse(self, name, segments):
         """See `ITraversable`.
 
-        :raises NoSuchProductSeries: if 'name' doesn't match an existing
+        :raises NoSuchSeries: if 'name' doesn't match an existing
             series.
         :return: `IProductSeries`.
         """
         series = self.context.getSeries(name)
         if series is None:
-            raise NoSuchProductSeries(name, self.context)
+            raise NoSuchSeries(name)
         return series
 
 
@@ -132,7 +132,7 @@
         """See `ITraversable`."""
         try:
             return getUtility(IDistroSeriesSet).fromSuite(self.context, name)
-        except NoSuchDistroSeries:
+        except NoSuchSeries:
             sourcepackage = self.context.getSourcePackage(name)
             if sourcepackage is None:
                 if segments:
@@ -233,8 +233,7 @@
             except (
                 CannotHaveLinkedBranch, InvalidNamespace, InvalidProductName,
                 NoSuchBranch, NoSuchPerson, NoSuchProduct,
-                NoSuchProductSeries, NoSuchDistroSeries,
-                NoSuchSourcePackageName, NoLinkedBranch):
+                NoSuchSeries, NoSuchSourcePackageName, NoLinkedBranch):
                 return None
 
         return Branch.selectOneBy(url=url)

=== modified file 'lib/lp/code/model/branchnamespace.py'
--- lib/lp/code/model/branchnamespace.py	2010-05-25 04:16:25 +0000
+++ lib/lp/code/model/branchnamespace.py	2010-07-16 14:14:50 +0000
@@ -37,8 +37,8 @@
 from lp.code.model.branch import Branch
 from lp.registry.interfaces.distribution import (
     IDistributionSet, NoSuchDistribution)
-from lp.registry.interfaces.distroseries import (
-    IDistroSeriesSet, NoSuchDistroSeries)
+from lp.registry.interfaces.series import NoSuchSeries  
+from lp.registry.interfaces.distroseries import IDistroSeriesSet
 from lp.registry.interfaces.person import IPersonSet, NoSuchPerson
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.registry.interfaces.projectgroup import IProjectGroup
@@ -585,7 +585,7 @@
 
     def _findDistroSeries(self, distribution, distroseries_name):
         return self._findOrRaise(
-            NoSuchDistroSeries, distroseries_name,
+            NoSuchSeries, distroseries_name,
             getUtility(IDistroSeriesSet).queryByName, distribution)
 
     def _findSourcePackageName(self, sourcepackagename_name):

=== modified file 'lib/lp/code/model/linkedbranch.py'
--- lib/lp/code/model/linkedbranch.py	2010-04-09 01:46:52 +0000
+++ lib/lp/code/model/linkedbranch.py	2010-07-16 14:14:50 +0000
@@ -17,7 +17,7 @@
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage)
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.product import IProduct
 from lp.registry.interfaces.productseries import IProductSeries
@@ -230,7 +230,7 @@
         development_package = (
             self.distribution_sourcepackage.development_version)
         if development_package is None:
-            raise NoSuchDistroSeries('no current series')
+            raise NoSuchSeries('no current series')
         suite_sourcepackage = development_package.getSuiteSourcePackage(
             PackagePublishingPocket.RELEASE)
         ICanHasLinkedBranch(suite_sourcepackage).setBranch(branch, registrant)

=== modified file 'lib/lp/code/model/tests/test_branchlookup.py'
--- lib/lp/code/model/tests/test_branchlookup.py	2010-04-16 15:06:55 +0000
+++ lib/lp/code/model/tests/test_branchlookup.py	2010-07-16 14:14:50 +0000
@@ -20,12 +20,11 @@
     get_branch_namespace, InvalidNamespace)
 from lp.code.interfaces.linkedbranch import (
     CannotHaveLinkedBranch, ICanHasLinkedBranch, NoLinkedBranch)
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
 from lp.registry.interfaces.person import NoSuchPerson
 from lp.registry.interfaces.product import (
     InvalidProductName, NoSuchProduct)
-from lp.registry.interfaces.productseries import NoSuchProductSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.sourcepackagename import (
     NoSuchSourcePackageName)
@@ -364,13 +363,13 @@
     def test_error_fallthrough_product_series(self):
         # For the short name of a series branch, `traverse` raises
         # `NoSuchProduct` if the first component refers to a non-existent
-        # product, and `NoSuchProductSeries` if the second component refers to
+        # product, and `NoSuchSeries` if the second component refers to
         # a non-existent series.
         self.assertRaises(
             NoSuchProduct, self.traverser.traverse, 'bb/dd')
         self.factory.makeProduct(name='bb')
         self.assertRaises(
-            NoSuchProductSeries, self.traverser.traverse, 'bb/dd')
+            NoSuchSeries, self.traverser.traverse, 'bb/dd')
 
     def test_product_series(self):
         # `traverse` resolves the path to a product series to the product
@@ -428,11 +427,11 @@
             'distro/series/package')
 
     def test_no_such_distro_series(self):
-        # `traverse` raises `NoSuchDistroSeries` if the distro series doesn't
+        # `traverse` raises `NoSuchSeries` if the distro series doesn't
         # exist.
         self.factory.makeDistribution(name='distro')
         self.assertRaises(
-            NoSuchDistroSeries, self.traverser.traverse,
+            NoSuchSeries, self.traverser.traverse,
             'distro/series/package')
 
     def test_no_such_sourcepackagename(self):
@@ -617,14 +616,14 @@
     def test_too_long_product(self):
         # If the provided path points to an existing product with a linked
         # branch but there are also extra path segments, then raise a
-        # NoSuchProductSeries error, since we can't tell the difference
+        # NoSuchSeries error, since we can't tell the difference
         # between a trailing path and an attempt to load a non-existent series
         # branch.
         branch = self.factory.makeProductBranch()
         product = removeSecurityProxy(branch.product)
         product.development_focus.branch = branch
         self.assertRaises(
-            NoSuchProductSeries,
+            NoSuchSeries,
             self.branch_lookup.getByLPPath, '%s/other/bits' % product.name)
 
     def test_too_long_product_series(self):

=== modified file 'lib/lp/code/model/tests/test_branchnamespace.py'
--- lib/lp/code/model/tests/test_branchnamespace.py	2010-05-21 17:04:28 +0000
+++ lib/lp/code/model/tests/test_branchnamespace.py	2010-07-16 14:14:50 +0000
@@ -27,7 +27,7 @@
     IBranchNamespaceSet, lookup_branch_namespace, InvalidNamespace)
 from lp.code.interfaces.branchtarget import IBranchTarget
 from lp.registry.interfaces.distribution import NoSuchDistribution
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.person import NoSuchPerson
 from lp.registry.interfaces.product import NoSuchProduct
 from lp.registry.interfaces.sourcepackagename import (
@@ -527,7 +527,7 @@
         person = self.factory.makePerson()
         distribution = self.factory.makeDistribution()
         self.assertRaises(
-            NoSuchDistroSeries, lookup_branch_namespace,
+            NoSuchSeries, lookup_branch_namespace,
             '~%s/%s/no-such-series/whocares'
             % (person.name, distribution.name))
 
@@ -769,7 +769,7 @@
         segments = iter(
             [person.name, distro.name, 'no-such-series', 'package', 'branch'])
         self.assertRaises(
-            NoSuchDistroSeries, self.namespace_set.traverse, segments)
+            NoSuchSeries, self.namespace_set.traverse, segments)
         self.assertEqual(['package', 'branch'], list(segments))
 
     def test_traverse_sourcepackagename_not_found(self):

=== modified file 'lib/lp/code/model/tests/test_linkedbranch.py'
--- lib/lp/code/model/tests/test_linkedbranch.py	2010-04-16 15:06:55 +0000
+++ lib/lp/code/model/tests/test_linkedbranch.py	2010-07-16 14:14:50 +0000
@@ -15,7 +15,7 @@
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.code.interfaces.linkedbranch import (
     CannotHaveLinkedBranch, get_linked_branch, ICanHasLinkedBranch)
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.testing import run_with_login, TestCaseWithFactory
 
@@ -175,7 +175,7 @@
         ubuntu_branches = getUtility(ILaunchpadCelebrities).ubuntu_branches
         registrant = ubuntu_branches.teamowner
         self.assertRaises(
-            NoSuchDistroSeries,
+            NoSuchSeries,
             linked_branch.setBranch, self.factory.makeAnyBranch(), registrant)
 
     def test_bzr_path(self):

=== modified file 'lib/lp/code/xmlrpc/branch.py'
--- lib/lp/code/xmlrpc/branch.py	2010-02-21 21:13:34 +0000
+++ lib/lp/code/xmlrpc/branch.py	2010-07-16 14:14:50 +0000
@@ -31,11 +31,10 @@
     get_branch_namespace, InvalidNamespace)
 from lp.code.interfaces.linkedbranch import (
     CannotHaveLinkedBranch, NoLinkedBranch)
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
 from lp.registry.interfaces.person import NoSuchPerson
 from lp.registry.interfaces.product import (
     InvalidProductName, NoSuchProduct)
-from lp.registry.interfaces.productseries import NoSuchProductSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from lp.registry.interfaces.sourcepackagename import (
     NoSuchSourcePackageName)
 from canonical.launchpad.validators import LaunchpadValidationError
@@ -241,15 +240,12 @@
         # or using a narrower range of faults (e.g. only one "NoSuch" fault).
         except InvalidProductName, e:
             raise faults.InvalidProductIdentifier(urlutils.escape(e.name))
-        except NoSuchProductSeries, e:
-            raise faults.NoSuchProductSeries(
-                urlutils.escape(e.name), e.product)
         except NoSuchPerson, e:
             raise faults.NoSuchPersonWithName(urlutils.escape(e.name))
         except NoSuchProduct, e:
             raise faults.NoSuchProduct(urlutils.escape(e.name))
-        except NoSuchDistroSeries, e:
-            raise faults.NoSuchDistroSeries(urlutils.escape(e.name))
+        except NoSuchSeries, e:
+            raise faults.NoSuchSeries(urlutils.escape(e.name))
         except NoSuchSourcePackageName, e:
             raise faults.NoSuchSourcePackageName(urlutils.escape(e.name))
         except NoLinkedBranch, e:

=== modified file 'lib/lp/code/xmlrpc/tests/test_branch.py'
--- lib/lp/code/xmlrpc/tests/test_branch.py	2010-04-09 12:58:01 +0000
+++ lib/lp/code/xmlrpc/tests/test_branch.py	2010-07-16 14:14:50 +0000
@@ -192,35 +192,35 @@
             faults.NoLinkedBranch(series))
 
     def test_no_such_product_series(self):
-        # Return a NoSuchProductSeries fault if there is no series of the
+        # Return a NoSuchSeries fault if there is no series of the
         # given name associated with the product.
         self.assertFault(
             '%s/%s' % (self.product.name, "doesntexist"),
-            faults.NoSuchProductSeries("doesntexist", self.product))
+            faults.NoSuchSeries("doesntexist"))
 
     def test_no_such_product_series_non_ascii(self):
-        # lp:product/<non-ascii-string> returns NoSuchProductSeries with the
+        # lp:product/<non-ascii-string> returns NoSuchSeries with the
         # name escaped.
         self.assertFault(
             '%s/%s' % (self.product.name, NON_ASCII_NAME),
-            faults.NoSuchProductSeries(
-                urlutils.escape(NON_ASCII_NAME), self.product))
+            faults.NoSuchSeries(
+                urlutils.escape(NON_ASCII_NAME)))
 
     def test_no_such_distro_series(self):
-        # Return a NoSuchDistroSeries fault if there is no series of the given
+        # Return a NoSuchSeries fault if there is no series of the given
         # name on that distribution.
         distro = self.factory.makeDistribution()
         self.assertFault(
             '%s/doesntexist/whocares' % distro.name,
-            faults.NoSuchDistroSeries("doesntexist"))
+            faults.NoSuchSeries("doesntexist"))
 
     def test_no_such_distro_series_non_ascii(self):
-        # lp:distro/<non-ascii-string>/whatever returns NoSuchDistroSeries
+        # lp:distro/<non-ascii-string>/whatever returns NoSuchSeries
         # with the name escaped.
         distro = self.factory.makeDistribution()
         self.assertFault(
             '%s/%s/whocares' % (distro.name, NON_ASCII_NAME),
-            faults.NoSuchDistroSeries(urlutils.escape(NON_ASCII_NAME)))
+            faults.NoSuchSeries(urlutils.escape(NON_ASCII_NAME)))
 
     def test_no_such_source_package(self):
         # Return a NoSuchSourcePackageName fault if there is no source package

=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py	2010-06-08 15:58:04 +0000
+++ lib/lp/registry/interfaces/distroseries.py	2010-07-16 14:14:50 +0000
@@ -12,7 +12,6 @@
     'IDistroSeriesEditRestricted',
     'IDistroSeriesPublic',
     'IDistroSeriesSet',
-    'NoSuchDistroSeries',
     ]
 
 from zope.component import getUtility
@@ -53,28 +52,6 @@
 from lp.translations.interfaces.languagepack import ILanguagePack
 from lp.translations.interfaces.potemplate import IHasTranslationTemplates
 
-
-class DistroSeriesNameField(ContentNameField):
-    """A class to ensure `IDistroSeries` has unique names."""
-    errormessage = _("%s is already in use by another series.")
-
-    @property
-    def _content_iface(self):
-        """See `IField`."""
-        return IDistroSeries
-
-    def _getByName(self, name):
-        """See `IField`."""
-        try:
-            if self._content_iface.providedBy(self.context):
-                return self.context.distribution.getSeries(name)
-            else:
-                return self.context.getSeries(name)
-        except NoSuchDistroSeries:
-            # The name is available for the new series.
-            return None
-
-
 class DistroSeriesVersionField(UniqueField):
     """A class to ensure `IDistroSeries` has unique versions."""
     errormessage = _(
@@ -147,11 +124,7 @@
     """Public IDistroSeries properties."""
 
     id = Attribute("The distroseries's unique number.")
-    name = exported(
-        DistroSeriesNameField(
-            title=_("Name"), required=True,
-            description=_("The name of this series."),
-            constraint=name_validator))
+
     displayname = exported(
         TextLine(
             title=_("Display name"), required=True,
@@ -855,12 +828,5 @@
         released == None will do no filtering on status.
         """
 
-
-class NoSuchDistroSeries(NameLookupFailed):
-    """Raised when we try to find a DistroSeries that doesn't exist."""
-    webservice_error(400) #Bad request.
-    _message_prefix = "No such distribution series"
-
-
 # Monkey patch for circular import avoidance done in
 # _schema_circular_imports.py

=== modified file 'lib/lp/registry/interfaces/productseries.py'
--- lib/lp/registry/interfaces/productseries.py	2010-06-11 18:05:59 +0000
+++ lib/lp/registry/interfaces/productseries.py	2010-07-16 14:14:50 +0000
@@ -12,7 +12,6 @@
     'IProductSeriesEditRestricted',
     'IProductSeriesPublic',
     'IProductSeriesSet',
-    'NoSuchProductSeries',
     ]
 
 from zope.schema import Bool, Choice, Datetime, Int, TextLine
@@ -51,23 +50,6 @@
     rename_parameters_as)
 
 
-class ProductSeriesNameField(ContentNameField):
-    """A class to ensure `IProductSeries` has unique names."""
-    errormessage = _("%s is already in use by another series.")
-
-    @property
-    def _content_iface(self):
-        """See `IField`."""
-        return IProductSeries
-
-    def _getByName(self, name):
-        """See `IField`."""
-        if self._content_iface.providedBy(self.context):
-            return self.context.product.getSeries(name)
-        else:
-            return self.context.getSeries(name)
-
-
 def validate_release_glob(value):
     """Validate that the URL is supported."""
     parts = urlparse(value)
@@ -112,16 +94,6 @@
 
     parent = Attribute('The structural parent of this series - the product')
 
-    name = exported(
-        ProductSeriesNameField(
-            title=_('Name'),
-            description=_(
-                "The name of the series is a short, unique name "
-                "that identifies it, being used in URLs. It must be all "
-                "lowercase, with no special characters. For example, '2.0' "
-                "or 'trunk'."),
-            constraint=name_validator))
-
     datecreated = exported(
         Datetime(title=_('Date Registered'),
                  required=True,
@@ -318,13 +290,3 @@
         :param force_translations_upload: Actually ignore if translations are
         enabled for this series.
         """
-
-
-class NoSuchProductSeries(NameLookupFailed):
-    """Raised when we try to find a product that doesn't exist."""
-
-    _message_prefix = "No such product series"
-
-    def __init__(self, name, product, message=None):
-        NameLookupFailed.__init__(self, name, message)
-        self.product = product

=== modified file 'lib/lp/registry/interfaces/series.py'
--- lib/lp/registry/interfaces/series.py	2010-05-17 09:35:18 +0000
+++ lib/lp/registry/interfaces/series.py	2010-07-16 14:14:50 +0000
@@ -8,20 +8,23 @@
 __metaclass__ = type
 
 __all__ = [
+    'ISeriesMixin',
+    'NoSuchSeries',
     'SeriesStatus',
-    'ISeriesMixin',
     ]
 
 from zope.schema import Bool
 
 from lazr.enum import DBEnumeratedType, DBItem
 from lazr.restful.fields import CollectionField, Reference
-from lazr.restful.declarations import exported
+from lazr.restful.declarations import exported, webservice_error
 
 from canonical.launchpad import _
 from canonical.launchpad.fields import (
-    PublicPersonChoice, Summary)
+    ContentNameField, PublicPersonChoice, Summary)
 from canonical.launchpad.interfaces.launchpad import IHasDrivers
+from canonical.launchpad.validators.name import name_validator
+from canonical.launchpad.webapp.interfaces import NameLookupFailed
 from lp.registry.interfaces.person import IPerson
 
 
@@ -89,6 +92,33 @@
         """)
 
 
+class NoSuchSeries(NameLookupFailed):
+    """Raised when we try to find a series that doesn't exist."""
+    webservice_error(400) #Bad request.
+    _message_prefix = "No such series"
+
+
+class SeriesNameField(ContentNameField):
+    """A class to ensure an series has an unique name."""
+    errormessage = _("%s is already in use by another series.")
+
+    @property
+    def _content_iface(self):
+        """See `IField`."""
+        return ISeriesMixin
+
+    def _getByName(self, name):
+        """See `IField`."""
+        try:
+            if self._content_iface.providedBy(self.context):
+                return self.context.parent.getSeries(name)
+            else:
+                return self.context.getSeries(name)
+        except NoSuchSeries:
+            # The name is available for the new series.
+            return None
+
+
 class ISeriesMixin(IHasDrivers):
     """Methods & properties shared between distro & product series."""
 
@@ -99,6 +129,16 @@
             "under current development. This excludes series which "
             "are experimental or obsolete.")))
 
+    name = exported(
+        SeriesNameField(
+            title=_('Name'),
+            description=_(
+                "The name of the series is a short, unique name "
+                "that identifies it, being used in URLs. It must be all "
+                "lowercase, with no special characters. For example, '2.0', "
+                "'hardy' or 'trunk'."),
+            constraint=name_validator))
+
     summary = exported(
         Summary(title=_("Summary"),
              description=_('A single paragraph that explains the goals of '

=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py	2010-06-29 10:17:06 +0000
+++ lib/lp/registry/model/distribution.py	2010-07-16 14:14:50 +0000
@@ -90,7 +90,7 @@
 from lp.registry.interfaces.distributionmirror import (
     IDistributionMirror, MirrorContent, MirrorStatus)
 from lp.registry.interfaces.series import SeriesStatus
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
+from lp.registry.interfaces.series import NoSuchSeries
 from canonical.launchpad.interfaces.launchpad import (
     IHasIcon, IHasLogo, IHasMugshot, ILaunchpadCelebrities, ILaunchpadUsage)
 from lp.soyuz.interfaces.queue import PackageUploadStatus
@@ -529,7 +529,7 @@
                DistroSeries.version == name_or_version),
             DistroSeries.distribution == self).one()
         if not distroseries:
-            raise NoSuchDistroSeries(name_or_version)
+            raise NoSuchSeries(name_or_version)
         return distroseries
 
     def getDevelopmentSeries(self):

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2010-06-22 19:42:51 +0000
+++ lib/lp/registry/model/distroseries.py	2010-07-16 14:14:50 +0000
@@ -136,7 +136,6 @@
 
     distribution = ForeignKey(
         dbName='distribution', foreignKey='Distribution', notNull=True)
-    name = StringCol(notNull=True)
     displayname = StringCol(notNull=True)
     title = StringCol(notNull=True)
     description = StringCol(notNull=True)

=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py	2010-06-09 08:26:26 +0000
+++ lib/lp/registry/model/productseries.py	2010-07-16 14:14:50 +0000
@@ -91,7 +91,6 @@
     status = EnumCol(
         notNull=True, schema=SeriesStatus,
         default=SeriesStatus.DEVELOPMENT)
-    name = StringCol(notNull=True)
     datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
     owner = ForeignKey(
         dbName="owner", foreignKey="Person",

=== modified file 'lib/lp/registry/model/series.py'
--- lib/lp/registry/model/series.py	2010-05-17 09:35:18 +0000
+++ lib/lp/registry/model/series.py	2010-07-16 14:14:50 +0000
@@ -22,6 +22,7 @@
 
     implements(ISeriesMixin)
 
+    name = StringCol(notNull=True)
     summary = StringCol(notNull=True)
 
     @property

=== modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt'
--- lib/lp/registry/stories/webservice/xx-distribution.txt	2010-06-09 08:26:26 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution.txt	2010-07-16 14:14:50 +0000
@@ -69,7 +69,7 @@
     ...     name_or_version='fnord')
     HTTP/1.1 400 Bad Request
     ...
-    NoSuchDistroSeries: No such distribution series: 'fnord'.
+    NoSuchSeries: No such series: 'fnord'.
 
 "getDevelopmentSeries" returns all the distribution series for the
 distribution that are marked as in development.

=== modified file 'lib/lp/registry/tests/test_distribution.py'
--- lib/lp/registry/tests/test_distribution.py	2010-06-21 13:33:36 +0000
+++ lib/lp/registry/tests/test_distribution.py	2010-07-16 14:14:50 +0000
@@ -11,8 +11,7 @@
 
 from lp.registry.tests.test_distroseries import (
     TestDistroSeriesCurrentSourceReleases)
-from lp.registry.interfaces.distroseries import NoSuchDistroSeries
-from lp.registry.interfaces.series import SeriesStatus
+from lp.registry.interfaces.series import NoSuchSeries, SeriesStatus
 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
     IDistributionSourcePackageRelease)
 from lp.testing import TestCaseWithFactory
@@ -129,7 +128,7 @@
 
     def test_get_none(self):
         distro = self.factory.makeDistribution()
-        self.assertRaises(NoSuchDistroSeries, distro.getSeries, "astronomy")
+        self.assertRaises(NoSuchSeries, distro.getSeries, "astronomy")
 
     def test_get_by_name(self):
         distro = self.factory.makeDistribution()

=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py	2010-06-23 23:23:28 +0000
+++ lib/lp/registry/tests/test_distroseries.py	2010-07-16 14:14:50 +0000
@@ -14,8 +14,8 @@
 
 from canonical.launchpad.ftests import ANONYMOUS, login
 from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet
-from lp.registry.interfaces.distroseries import (
-    IDistroSeriesSet, NoSuchDistroSeries)
+from lp.registry.interfaces.series import NoSuchSeries
+from lp.registry.interfaces.distroseries import IDistroSeriesSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.soyuz.interfaces.component import IComponentSet
 from lp.soyuz.interfaces.distroseriessourcepackagerelease import (
@@ -390,7 +390,7 @@
     def test_fromSuite_no_such_series(self):
         distribution = self.factory.makeDistribution()
         self.assertRaises(
-            NoSuchDistroSeries,
+            NoSuchSeries,
             getUtility(IDistroSeriesSet).fromSuite,
             distribution, 'doesntexist')