← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~mwhudson/launchpad/move-some-code-vocabularies into lp:launchpad/devel

 

Michael Hudson-Doyle has proposed merging lp:~mwhudson/launchpad/move-some-code-vocabularies into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


Hi,

This lunch hour branch moves the remaining code-related vocabularies to the lp.code tree.

Cheers,
mwh
-- 
https://code.launchpad.net/~mwhudson/launchpad/move-some-code-vocabularies/+merge/35974
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~mwhudson/launchpad/move-some-code-vocabularies into lp:launchpad/devel.
=== modified file 'lib/canonical/launchpad/browser/tests/test_widgets.py'
--- lib/canonical/launchpad/browser/tests/test_widgets.py	2010-08-20 20:31:18 +0000
+++ lib/canonical/launchpad/browser/tests/test_widgets.py	2010-09-20 00:51:46 +0000
@@ -24,7 +24,7 @@
     login,
     logout,
     )
-from canonical.launchpad.vocabularies import (
+from lp.code.vocabularies.branch import (
     BranchRestrictedOnProductVocabulary,
     BranchVocabulary,
     )

=== modified file 'lib/canonical/launchpad/doc/vocabularies.txt'
--- lib/canonical/launchpad/doc/vocabularies.txt	2010-08-27 14:27:22 +0000
+++ lib/canonical/launchpad/doc/vocabularies.txt	2010-09-20 00:51:46 +0000
@@ -364,143 +364,6 @@
     [('pmount', u'pmount')]
 
 
-BranchVocabulary
-................
-
-The list of bzr branches registered in Launchpad.
-
-Searchable by branch name or URL, registrant name, and project name.
-Results are not restricted in any way by the context, but the results
-are restricted based on who is asking (as far as private branches is
-concerned).
-
-    # Just use None as the context.
-    >>> branch_vocabulary = get_naked_vocab(None, "Branch")
-    >>> def print_vocab_branches(vocab, search):
-    ...     for term in vocab.searchForTerms(search):
-    ...         print term.value.unique_name
-
-    >>> print_vocab_branches(branch_vocabulary, 'main')
-    ~name12/firefox/main
-    ~stevea/thunderbird/main
-    ~justdave/+junk/main
-    ~kiko/+junk/main
-    ~vcs-imports/evolution/main
-    ~name12/gnome-terminal/main
-
-    >>> print_vocab_branches(branch_vocabulary, 'vcs-imports')
-    ~vcs-imports/gnome-terminal/import
-    ~vcs-imports/evolution/import
-    ~vcs-imports/evolution/main
-
-    >>> print_vocab_branches(branch_vocabulary, 'evolution')
-    ~carlos/evolution/2.0
-    ~vcs-imports/evolution/import
-    ~vcs-imports/evolution/main
-
-A search with the full branch unique name should also find the branch.
-
-    >>> print_vocab_branches(branch_vocabulary, '~name12/firefox/main')
-    ~name12/firefox/main
-
-The tokens used by terms retrieved from BranchVocabulary use the
-branch unique name as an ID:
-
-    >>> from lp.code.interfaces.branchlookup import IBranchLookup
-    >>> branch = getUtility(IBranchLookup).get(15)
-    >>> print branch.unique_name
-    ~name12/gnome-terminal/main
-    >>> term = branch_vocabulary.toTerm(branch)
-    >>> print term.token
-    ~name12/gnome-terminal/main
-
-The BranchVocabulary recognises both unique names and URLs as tokens:
-
-    >>> term = branch_vocabulary.getTermByToken('~name12/gnome-terminal/main')
-    >>> term.value == branch
-    True
-    >>> term = branch_vocabulary.getTermByToken(
-    ...     'http://bazaar.launchpad.dev/~name12/gnome-terminal/main/')
-    >>> term.value == branch
-    True
-    >>> term = branch_vocabulary.getTermByToken(
-    ...     'http://example.com/gnome-terminal/main')
-    >>> term.value == branch
-    True
-
-The searches that the BranchVocabulary does are private branch aware.
-The results are effectively filtered on what the logged in user is
-able to see.
-
-    >>> print_vocab_branches(branch_vocabulary, 'trunk')
-    ~spiv/+junk/trunk
-    ~limi/+junk/trunk
-    ~landscape-developers/landscape/trunk
-
-    >>> login('no-priv@xxxxxxxxxxxxx')
-    >>> print_vocab_branches(branch_vocabulary, 'trunk')
-    ~spiv/+junk/trunk
-    ~limi/+junk/trunk
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-
-
-BranchRestrictedOnProduct
-.........................
-
-The BranchRestrictedOnProduct vocabulary restricts the result set to
-those of the product of the context.  Currently only two types of
-context are supported: Product; and Branch.  If a branch is the context,
-then the product of the branch is used to restrict the query.
-
-    >>> gnome_terminal = getUtility(IProductSet)["gnome-terminal"]
-    >>> branch_vocabulary = vocabulary_registry.get(
-    ...     gnome_terminal, "BranchRestrictedOnProduct")
-    >>> print_vocab_branches(branch_vocabulary, 'main')
-    ~name12/gnome-terminal/main
-
-    >>> print_vocab_branches(branch_vocabulary, 'vcs-imports')
-    ~vcs-imports/gnome-terminal/import
-
-If a full unique name is entered that has a different product, the
-branch is not part of the vocabulary.
-
-    >>> print_vocab_branches(branch_vocabulary, '~name12/gnome-terminal/main')
-    ~name12/gnome-terminal/main
-
-    >>> print_vocab_branches(branch_vocabulary, '~name12/firefox/main')
-
-
-The BranchRestrictedOnProduct behaves the same way as the more generic
-BranchVocabulary with respect to the tokens and privacy awareness.
-
-
-HostedBranchRestrictedOnOwner
-.............................
-
-Here's a vocabulary for all hosted branches owned by the current user.
-
-    >>> from lp.code.enums import BranchType
-
-    >>> a_user = factory.makePerson(name='a-branching-user')
-    >>> product1 = factory.makeProduct(name='product-one')
-    >>> mirrored_branch = factory.makeBranch(
-    ...     owner=a_user, product=product1, name='mirrored',
-    ...     branch_type=BranchType.MIRRORED)
-    >>> product2 = factory.makeProduct(name='product-two')
-    >>> hosted_branch = factory.makeBranch(
-    ...     owner=a_user, product=product2, name='hosted')
-    >>> foreign_branch = factory.makeBranch()
-
-It returns branches owned by the user, but not ones owned by others, nor
-ones that aren't hosted on Launchpad.
-
-    >>> branch_vocabulary = vocabulary_registry.get(
-    ...     a_user, "HostedBranchRestrictedOnOwner")
-    >>> print_vocab_branches(branch_vocabulary, None)
-    ~a-branching-user/product-two/hosted
-
-
 Processor
 .........
 

=== modified file 'lib/canonical/launchpad/vocabularies/configure.zcml'
--- lib/canonical/launchpad/vocabularies/configure.zcml	2010-08-25 04:12:59 +0000
+++ lib/canonical/launchpad/vocabularies/configure.zcml	2010-09-20 00:51:46 +0000
@@ -13,45 +13,6 @@
   </class>
 
   <securedutility
-    name="Branch"
-    component="canonical.launchpad.vocabularies.BranchVocabulary"
-    provides="zope.schema.interfaces.IVocabularyFactory"
-    >
-    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
-  </securedutility>
-
-  <class class="canonical.launchpad.vocabularies.BranchVocabulary">
-    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
-  </class>
-
-
-  <securedutility
-    name="HostedBranchRestrictedOnOwner"
-    component="canonical.launchpad.vocabularies.HostedBranchRestrictedOnOwnerVocabulary"
-    provides="zope.schema.interfaces.IVocabularyFactory"
-    >
-    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
-  </securedutility>
-
-  <class class="canonical.launchpad.vocabularies.HostedBranchRestrictedOnOwnerVocabulary">
-    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
-  </class>
-
-
-  <securedutility
-    name="BranchRestrictedOnProduct"
-    component="canonical.launchpad.vocabularies.BranchRestrictedOnProductVocabulary"
-    provides="zope.schema.interfaces.IVocabularyFactory"
-    >
-    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
-  </securedutility>
-
-  <class class="canonical.launchpad.vocabularies.BranchRestrictedOnProductVocabulary">
-    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
-  </class>
-
-
-  <securedutility
     name="Bug"
     component="canonical.launchpad.vocabularies.BugVocabulary"
     provides="zope.schema.interfaces.IVocabularyFactory"

=== modified file 'lib/canonical/launchpad/vocabularies/dbobjects.py'
--- lib/canonical/launchpad/vocabularies/dbobjects.py	2010-08-27 14:27:22 +0000
+++ lib/canonical/launchpad/vocabularies/dbobjects.py	2010-09-20 00:51:46 +0000
@@ -1,5 +1,5 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
+# Copyright 2009, 2010 Canonical Ltd.  This software is licensed under the GNU
+# Affero General Public License version 3 (see the file LICENSE).
 
 """Vocabularies pulling stuff from the database.
 
@@ -10,8 +10,6 @@
 __metaclass__ = type
 
 __all__ = [
-    'BranchRestrictedOnProductVocabulary',
-    'BranchVocabulary',
     'BugNominatableDistroSeriesVocabulary',
     'BugNominatableProductSeriesVocabulary',
     'BugNominatableSeriesVocabulary',
@@ -26,7 +24,6 @@
     'FilteredFullLanguagePackVocabulary',
     'FilteredLanguagePackVocabulary',
     'FutureSprintVocabulary',
-    'HostedBranchRestrictedOnOwnerVocabulary',
     'LanguageVocabulary',
     'PackageReleaseVocabulary',
     'PPAVocabulary',
@@ -92,15 +89,8 @@
 from lp.bugs.interfaces.bugtracker import BugTrackerType
 from lp.bugs.model.bug import Bug
 from lp.bugs.model.bugtracker import BugTracker
-from lp.code.enums import BranchType
-from lp.code.interfaces.branch import IBranch
-from lp.code.interfaces.branchcollection import IAllBranches
-from lp.code.model.branch import Branch
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.person import IPerson
-from lp.registry.interfaces.product import IProduct
-from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.projectgroup import IProjectGroup
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.model.distribution import Distribution
@@ -146,106 +136,6 @@
         return SimpleTerm(obj, obj.id, obj.name)
 
 
-class BranchVocabularyBase(SQLObjectVocabularyBase):
-    """A base class for Branch vocabularies.
-
-    Override `BranchVocabularyBase._getCollection` to provide the collection
-    of branches which make up the vocabulary.
-    """
-
-    implements(IHugeVocabulary)
-
-    _table = Branch
-    _orderBy = ['name', 'id']
-    displayname = 'Select a branch'
-
-    def toTerm(self, branch):
-        """The display should include the URL if there is one."""
-        return SimpleTerm(branch, branch.unique_name, branch.unique_name)
-
-    def getTermByToken(self, token):
-        """See `IVocabularyTokenized`."""
-        search_results = self.searchForTerms(token)
-        if search_results.count() == 1:
-            return iter(search_results).next()
-        raise LookupError(token)
-
-    def _getCollection(self):
-        """Override this to return the collection to which the search is
-        restricted.
-        """
-        raise NotImplementedError(self._getCollection)
-
-    def searchForTerms(self, query=None):
-        """See `IHugeVocabulary`."""
-        logged_in_user = getUtility(ILaunchBag).user
-        collection = self._getCollection().visibleByUser(logged_in_user)
-        if query is None:
-            branches = collection.getBranches()
-        else:
-            branches = collection.search(query)
-        return CountableIterator(branches.count(), branches, self.toTerm)
-
-    def __len__(self):
-        """See `IVocabulary`."""
-        return self.search().count()
-
-
-class BranchVocabulary(BranchVocabularyBase):
-    """A vocabulary for searching branches.
-
-    The name and URL of the branch, the name of the product, and the
-    name of the registrant of the branches is checked for the entered
-    value.
-    """
-
-    def _getCollection(self):
-        return getUtility(IAllBranches)
-
-
-class BranchRestrictedOnProductVocabulary(BranchVocabularyBase):
-    """A vocabulary for searching branches restricted on product.
-
-    The query entered checks the name or URL of the branch, or the
-    name of the registrant of the branch.
-    """
-
-    def __init__(self, context=None):
-        BranchVocabularyBase.__init__(self, context)
-        if IProduct.providedBy(self.context):
-            self.product = self.context
-        elif IProductSeries.providedBy(self.context):
-            self.product = self.context.product
-        elif IBranch.providedBy(self.context):
-            self.product = self.context.product
-        else:
-            # An unexpected type.
-            raise AssertionError('Unexpected context type')
-
-    def _getCollection(self):
-        return getUtility(IAllBranches).inProduct(self.product)
-
-
-class HostedBranchRestrictedOnOwnerVocabulary(BranchVocabularyBase):
-    """A vocabulary for hosted branches owned by the current user.
-
-    These are branches that the user is guaranteed to be able to push
-    to.
-    """
-
-    def __init__(self, context=None):
-        """Pass a Person as context, or anything else for the current user."""
-        super(HostedBranchRestrictedOnOwnerVocabulary, self).__init__(context)
-        if IPerson.providedBy(self.context):
-            self.user = context
-        else:
-            self.user = getUtility(ILaunchBag).user
-
-    def _getCollection(self):
-        return getUtility(IAllBranches).ownedBy(self.user).withBranchType(
-            BranchType.HOSTED)
-
-
 class BugVocabulary(SQLObjectVocabularyBase):
 
     _table = Bug

=== modified file 'lib/canonical/launchpad/webapp/configure.zcml'
--- lib/canonical/launchpad/webapp/configure.zcml	2010-09-10 06:38:15 +0000
+++ lib/canonical/launchpad/webapp/configure.zcml	2010-09-20 00:51:46 +0000
@@ -828,7 +828,7 @@
     <view
       type="zope.publisher.interfaces.browser.IBrowserRequest"
       for="zope.schema.interfaces.IChoice
-        canonical.launchpad.vocabularies.dbobjects.BranchVocabularyBase"
+        lp.code.vocabularies.branch.BranchVocabularyBase"
       provides="zope.app.form.interfaces.IInputWidget"
       factory="canonical.launchpad.browser.widgets.BranchPopupWidget"
       permission="zope.Public"

=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml	2010-08-24 02:17:19 +0000
+++ lib/lp/code/browser/configure.zcml	2010-09-20 00:51:46 +0000
@@ -1306,19 +1306,5 @@
             factory="canonical.launchpad.webapp.breadcrumb.NameBreadcrumb"
             permission="zope.Public"/>
     </facet>
-  <securedutility
-    name="BuildableDistroSeries"
-    component="lp.code.vocabularies.sourcepackagerecipe.buildable_distroseries_vocabulary"
-    provides="zope.schema.interfaces.IVocabularyFactory"
-    >
-    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
-  </securedutility>
-  <securedutility
-    name="TargetPPAs"
-    component="lp.code.vocabularies.sourcepackagerecipe.target_ppas_vocabulary"
-    provides="zope.schema.interfaces.IVocabularyFactory"
-    >
-    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
-  </securedutility>
 
 </configure>

=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml	2010-09-13 04:56:29 +0000
+++ lib/lp/code/configure.zcml	2010-09-20 00:51:46 +0000
@@ -11,6 +11,7 @@
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
   <include package=".browser"/>
+  <include package=".vocabularies"/>
   <authorizations module="lp.code.security" />
 
   <publisher

=== added file 'lib/lp/code/vocabularies/branch.py'
--- lib/lp/code/vocabularies/branch.py	1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/branch.py	2010-09-20 00:51:46 +0000
@@ -0,0 +1,132 @@
+# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Vocabularies that contain branches."""
+
+
+__metaclass__ = type
+
+__all__ = [
+    'BranchRestrictedOnProductVocabulary',
+    'BranchVocabulary',
+    'HostedBranchRestrictedOnOwnerVocabulary',
+    ]
+
+from zope.component import getUtility
+from zope.interface import implements
+from zope.schema.vocabulary import SimpleTerm
+
+from canonical.launchpad.webapp.interfaces import ILaunchBag
+from canonical.launchpad.webapp.vocabulary import (
+    CountableIterator,
+    IHugeVocabulary,
+    SQLObjectVocabularyBase,
+    )
+
+from lp.code.enums import BranchType
+from lp.code.interfaces.branch import IBranch
+from lp.code.interfaces.branchcollection import IAllBranches
+from lp.code.model.branch import Branch
+from lp.registry.interfaces.person import IPerson
+from lp.registry.interfaces.product import IProduct
+from lp.registry.interfaces.productseries import IProductSeries
+
+
+class BranchVocabularyBase(SQLObjectVocabularyBase):
+    """A base class for Branch vocabularies.
+
+    Override `BranchVocabularyBase._getCollection` to provide the collection
+    of branches which make up the vocabulary.
+    """
+
+    implements(IHugeVocabulary)
+
+    _table = Branch
+    _orderBy = ['name', 'id']
+    displayname = 'Select a branch'
+
+    def toTerm(self, branch):
+        """The display should include the URL if there is one."""
+        return SimpleTerm(branch, branch.unique_name, branch.unique_name)
+
+    def getTermByToken(self, token):
+        """See `IVocabularyTokenized`."""
+        search_results = self.searchForTerms(token)
+        if search_results.count() == 1:
+            return iter(search_results).next()
+        raise LookupError(token)
+
+    def _getCollection(self):
+        """Override this to return the collection to which the search is
+        restricted.
+        """
+        raise NotImplementedError(self._getCollection)
+
+    def searchForTerms(self, query=None):
+        """See `IHugeVocabulary`."""
+        logged_in_user = getUtility(ILaunchBag).user
+        collection = self._getCollection().visibleByUser(logged_in_user)
+        if query is None:
+            branches = collection.getBranches()
+        else:
+            branches = collection.search(query)
+        return CountableIterator(branches.count(), branches, self.toTerm)
+
+    def __len__(self):
+        """See `IVocabulary`."""
+        return self.search().count()
+
+
+class BranchVocabulary(BranchVocabularyBase):
+    """A vocabulary for searching branches.
+
+    The name and URL of the branch, the name of the product, and the
+    name of the registrant of the branches is checked for the entered
+    value.
+    """
+
+    def _getCollection(self):
+        return getUtility(IAllBranches)
+
+
+class BranchRestrictedOnProductVocabulary(BranchVocabularyBase):
+    """A vocabulary for searching branches restricted on product.
+
+    The query entered checks the name or URL of the branch, or the
+    name of the registrant of the branch.
+    """
+
+    def __init__(self, context=None):
+        BranchVocabularyBase.__init__(self, context)
+        if IProduct.providedBy(self.context):
+            self.product = self.context
+        elif IProductSeries.providedBy(self.context):
+            self.product = self.context.product
+        elif IBranch.providedBy(self.context):
+            self.product = self.context.product
+        else:
+            # An unexpected type.
+            raise AssertionError('Unexpected context type')
+
+    def _getCollection(self):
+        return getUtility(IAllBranches).inProduct(self.product)
+
+
+class HostedBranchRestrictedOnOwnerVocabulary(BranchVocabularyBase):
+    """A vocabulary for hosted branches owned by the current user.
+
+    These are branches that the user is guaranteed to be able to push
+    to.
+    """
+
+    def __init__(self, context=None):
+        """Pass a Person as context, or anything else for the current user."""
+        super(HostedBranchRestrictedOnOwnerVocabulary, self).__init__(context)
+        if IPerson.providedBy(self.context):
+            self.user = context
+        else:
+            self.user = getUtility(ILaunchBag).user
+
+    def _getCollection(self):
+        return getUtility(IAllBranches).ownedBy(self.user).withBranchType(
+            BranchType.HOSTED)

=== added file 'lib/lp/code/vocabularies/configure.zcml'
--- lib/lp/code/vocabularies/configure.zcml	1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/configure.zcml	2010-09-20 00:51:46 +0000
@@ -0,0 +1,54 @@
+<!-- Copyright 2010 Canonical Ltd.  This software is licensed under the
+     GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<configure xmlns="http://namespaces.zope.org/zope";>
+
+  <securedutility
+     name="BuildableDistroSeries"
+     component=".sourcepackagerecipe.buildable_distroseries_vocabulary"
+     provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <securedutility
+     name="TargetPPAs"
+     component=".sourcepackagerecipe.target_ppas_vocabulary"
+     provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <securedutility
+    name="Branch"
+    component=".branch.BranchVocabulary"
+    provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <class class=".branch.BranchVocabulary">
+    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
+  </class>
+
+  <securedutility
+    name="HostedBranchRestrictedOnOwner"
+    component=".branch.HostedBranchRestrictedOnOwnerVocabulary"
+    provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <class class=".branch.HostedBranchRestrictedOnOwnerVocabulary">
+    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
+  </class>
+
+  <securedutility
+    name="BranchRestrictedOnProduct"
+    component=".branch.BranchRestrictedOnProductVocabulary"
+    provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <class class=".branch.BranchRestrictedOnProductVocabulary">
+    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
+  </class>
+
+</configure>

=== added file 'lib/lp/code/vocabularies/tests/branch.txt'
--- lib/lp/code/vocabularies/tests/branch.txt	1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/tests/branch.txt	2010-09-20 00:51:46 +0000
@@ -0,0 +1,148 @@
+Branch Vocabularies
+===================
+
+Launchpad has a few vocabularies that contain branches filtered in
+various ways.
+
+    >>> from zope.schema.vocabulary import getVocabularyRegistry
+    >>> vocabulary_registry = getVocabularyRegistry()
+
+BranchVocabulary
+----------------
+
+The list of bzr branches registered in Launchpad.
+
+Searchable by branch name or URL, registrant name, and project name.
+Results are not restricted in any way by the context, but the results
+are restricted based on who is asking (as far as private branches is
+concerned).
+
+    # Just use None as the context.
+    >>> branch_vocabulary = vocabulary_registry.get(None, "Branch")
+    >>> def print_vocab_branches(vocab, search):
+    ...     for term in vocab.searchForTerms(search):
+    ...         print term.value.unique_name
+
+    >>> print_vocab_branches(branch_vocabulary, 'main')
+    ~name12/firefox/main
+    ~stevea/thunderbird/main
+    ~justdave/+junk/main
+    ~kiko/+junk/main
+    ~vcs-imports/evolution/main
+    ~name12/gnome-terminal/main
+
+    >>> print_vocab_branches(branch_vocabulary, 'vcs-imports')
+    ~vcs-imports/gnome-terminal/import
+    ~vcs-imports/evolution/import
+    ~vcs-imports/evolution/main
+
+    >>> print_vocab_branches(branch_vocabulary, 'evolution')
+    ~carlos/evolution/2.0
+    ~vcs-imports/evolution/import
+    ~vcs-imports/evolution/main
+
+A search with the full branch unique name should also find the branch.
+
+    >>> print_vocab_branches(branch_vocabulary, '~name12/firefox/main')
+    ~name12/firefox/main
+
+The tokens used by terms retrieved from BranchVocabulary use the
+branch unique name as an ID:
+
+    >>> from lp.code.interfaces.branchlookup import IBranchLookup
+    >>> branch = getUtility(IBranchLookup).get(15)
+    >>> print branch.unique_name
+    ~name12/gnome-terminal/main
+    >>> from zope.security.proxy import removeSecurityProxy
+    >>> term = removeSecurityProxy(branch_vocabulary).toTerm(branch)
+    >>> print term.token
+    ~name12/gnome-terminal/main
+
+The BranchVocabulary recognises both unique names and URLs as tokens:
+
+    >>> term = branch_vocabulary.getTermByToken('~name12/gnome-terminal/main')
+    >>> term.value == branch
+    True
+    >>> term = branch_vocabulary.getTermByToken(
+    ...     'http://bazaar.launchpad.dev/~name12/gnome-terminal/main/')
+    >>> term.value == branch
+    True
+    >>> term = branch_vocabulary.getTermByToken(
+    ...     'http://example.com/gnome-terminal/main')
+    >>> term.value == branch
+    True
+
+The searches that the BranchVocabulary does are private branch aware.
+The results are effectively filtered on what the logged in user is
+able to see.
+
+    >>> from lp.testing import login, ANONYMOUS
+    >>> from lp.testing.sampledata import ADMIN_EMAIL
+
+    >>> login(ADMIN_EMAIL)
+    >>> print_vocab_branches(branch_vocabulary, 'trunk')
+    ~spiv/+junk/trunk
+    ~limi/+junk/trunk
+    ~landscape-developers/landscape/trunk
+
+    >>> login(ANONYMOUS)
+    >>> print_vocab_branches(branch_vocabulary, 'trunk')
+    ~spiv/+junk/trunk
+    ~limi/+junk/trunk
+
+
+BranchRestrictedOnProduct
+-------------------------
+
+The BranchRestrictedOnProduct vocabulary restricts the result set to
+those of the product of the context.  Currently only two types of
+context are supported: Product; and Branch.  If a branch is the context,
+then the product of the branch is used to restrict the query.
+
+    >>> from lp.registry.interfaces.product import IProductSet
+    >>> gnome_terminal = getUtility(IProductSet)["gnome-terminal"]
+    >>> branch_vocabulary = vocabulary_registry.get(
+    ...     gnome_terminal, "BranchRestrictedOnProduct")
+    >>> print_vocab_branches(branch_vocabulary, 'main')
+    ~name12/gnome-terminal/main
+
+    >>> print_vocab_branches(branch_vocabulary, 'vcs-imports')
+    ~vcs-imports/gnome-terminal/import
+
+If a full unique name is entered that has a different product, the
+branch is not part of the vocabulary.
+
+    >>> print_vocab_branches(branch_vocabulary, '~name12/gnome-terminal/main')
+    ~name12/gnome-terminal/main
+
+    >>> print_vocab_branches(branch_vocabulary, '~name12/firefox/main')
+
+
+The BranchRestrictedOnProduct behaves the same way as the more generic
+BranchVocabulary with respect to the tokens and privacy awareness.
+
+
+HostedBranchRestrictedOnOwner
+-----------------------------
+
+Here's a vocabulary for all hosted branches owned by the current user.
+
+    >>> from lp.code.enums import BranchType
+
+    >>> a_user = factory.makePerson(name='a-branching-user')
+    >>> product1 = factory.makeProduct(name='product-one')
+    >>> mirrored_branch = factory.makeBranch(
+    ...     owner=a_user, product=product1, name='mirrored',
+    ...     branch_type=BranchType.MIRRORED)
+    >>> product2 = factory.makeProduct(name='product-two')
+    >>> hosted_branch = factory.makeBranch(
+    ...     owner=a_user, product=product2, name='hosted')
+    >>> foreign_branch = factory.makeBranch()
+
+It returns branches owned by the user, but not ones owned by others, nor
+ones that aren't hosted on Launchpad.
+
+    >>> branch_vocabulary = vocabulary_registry.get(
+    ...     a_user, "HostedBranchRestrictedOnOwner")
+    >>> print_vocab_branches(branch_vocabulary, None)
+    ~a-branching-user/product-two/hosted

=== modified file 'lib/lp/code/vocabularies/tests/test_branch_vocabularies.py'
--- lib/lp/code/vocabularies/tests/test_branch_vocabularies.py	2010-08-20 20:31:18 +0000
+++ lib/lp/code/vocabularies/tests/test_branch_vocabularies.py	2010-09-20 00:51:46 +0000
@@ -17,7 +17,7 @@
     login,
     logout,
     )
-from canonical.launchpad.vocabularies.dbobjects import (
+from lp.code.vocabularies.branch import (
     BranchRestrictedOnProductVocabulary,
     BranchVocabulary,
     )

=== added file 'lib/lp/code/vocabularies/tests/test_doc.py'
--- lib/lp/code/vocabularies/tests/test_doc.py	1970-01-01 00:00:00 +0000
+++ lib/lp/code/vocabularies/tests/test_doc.py	2010-09-20 00:51:46 +0000
@@ -0,0 +1,17 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""
+Run the doctests.
+"""
+
+import os
+
+from lp.services.testing import build_doctest_suite
+
+
+here = os.path.dirname(os.path.realpath(__file__))
+
+
+def test_suite():
+    return build_doctest_suite(here, '')