7
Vocabularies are lists of terms. In Launchpad's Component Architecture
8
(CA), a vocabulary is a list of terms that a widget (normally a selection
9
style widget) "speaks", i.e., its allowed values.
11
>>> from zope.component import getUtility
12
>>> from canonical.launchpad.ftests import login
13
>>> from canonical.database.sqlbase import flush_database_updates
14
>>> from canonical.launchpad.webapp.interfaces import IOpenLaunchBag
15
>>> from lp.registry.interfaces.person import IPersonSet
16
>>> from lp.registry.interfaces.product import IProductSet
17
>>> person_set = getUtility(IPersonSet)
18
>>> product_set = getUtility(IProductSet)
19
>>> login('foo.bar@canonical.com')
20
>>> launchbag = getUtility(IOpenLaunchBag)
24
Values, Tokens, and Titles
25
..........................
27
In Launchpad, we generally use "tokenized vocabularies." Each term in
28
a vocabulary has a value, token and title. A term is rendered in a
29
select widget like this:
31
<option value="$token">$title</option>
33
The $token is probably the data you would store in your DB. The Token is
34
used to uniquely identify a Term, and the Title is the thing you display
38
Launchpad Vocabularies
39
----------------------
41
There are two kinds of vocabularies in Launchpad: enumerable and
42
non-enumerable. Enumerable vocabularies are short enough to render in a
43
select widget. Non-enumerable vocabularies require a query interface to make
44
it easy to choose just one or a couple of options from several hundred,
45
several thousand, or more.
47
Vocabularies should not be imported - they can be retrieved from the
50
>>> from zope.schema.vocabulary import getVocabularyRegistry
51
>>> from zope.security.proxy import removeSecurityProxy
52
>>> vocabulary_registry = getVocabularyRegistry()
53
>>> def get_naked_vocab(context, name):
54
... return removeSecurityProxy(
55
... vocabulary_registry.get(context, name))
56
>>> product_vocabulary = vocabulary_registry.get(None, "Product")
57
>>> product_vocabulary.displayname
61
Iterating over non-enumerable vocabularies, while possible, will
62
probably kill the database. Instead, these vocabularies are
66
BinaryAndSourcePackageNameVocabulary
67
....................................
69
The list of binary and source package names, ordered by name.
71
>>> package_name_vocabulary = vocabulary_registry.get(
72
... None, "BinaryAndSourcePackageName")
73
>>> package_name_vocabulary.displayname
76
When a package name matches both a binary package name and a source
77
package of the exact same name, the binary package name is
78
returned. This allows us, in bug reporting for example, to collect the
79
most specific information possible.
81
Let's demonstrate by searching for "mozilla-firefox", for which there is
82
both a source and binary package of that name.
84
>>> package_name_terms = package_name_vocabulary.searchForTerms(
85
... "mozilla-firefox")
86
>>> package_name_terms.count()
88
>>> [(term.token, term.title) for term in package_name_terms]
89
[('mozilla-firefox', u'mozilla-firefox'),
90
('mozilla-firefox-data', u'mozilla-firefox-data')]
92
Searching for "mozilla" should return the binary package name above, and
93
the source package named "mozilla".
95
>>> package_name_terms = package_name_vocabulary.searchForTerms("mozilla")
96
>>> package_name_terms.count()
98
>>> [(term.token, term.title) for term in package_name_terms]
99
[('mozilla', u'mozilla'),
100
('mozilla-firefox', u'mozilla-firefox'),
101
('mozilla-firefox-data', u'mozilla-firefox-data')]
103
The search does a case-insensitive, substring match.
105
>>> package_name_terms = package_name_vocabulary.searchForTerms("lInuX")
106
>>> package_name_terms.count()
108
>>> [(term.token, term.title) for term in package_name_terms]
109
[('linux-2.6.12', u'linux-2.6.12'),
110
('linux-source-2.6.15', u'linux-source-2.6.15')]
113
BinaryPackageNameVocabulary
114
...........................
116
All the binary packages in Launchpad.
118
>>> bpn_vocabulary = vocabulary_registry.get(None, 'BinaryPackageName')
119
>>> len(bpn_vocabulary)
122
>>> bpn_terms = bpn_vocabulary.searchForTerms("mozilla")
125
>>> [(term.token, term.title) for term in bpn_terms]
126
[('mozilla-firefox', u'iceweasel huh ?'),
127
('mozilla-firefox-data', u'Mozilla Firefox Data is .....')]
130
SourcePackageNameVocabulary
131
...........................
133
All the source packages in Launchpad.
135
>>> spn_vocabulary = vocabulary_registry.get(None, 'SourcePackageName')
136
>>> len(spn_vocabulary)
139
>>> spn_terms = spn_vocabulary.searchForTerms("mozilla")
142
>>> [(term.token, term.title) for term in spn_terms]
143
[('mozilla', u'mozilla'), ('mozilla-firefox', u'mozilla-firefox')]
145
>>> spn_terms = spn_vocabulary.searchForTerms("pmount")
148
>>> [(term.token, term.title) for term in spn_terms]
149
[('pmount', u'pmount')]
155
All processors type available in Launchpad.
157
>>> vocab = vocabulary_registry.get(None, "Processor")
158
>>> vocab.displayname
161
>>> [term.token for term in vocab.searchForTerms('386')]
168
The PPA vocabulary contains all the PPAs available in a particular
169
collection. It provides the IHugeVocabulary interface.
171
>>> from canonical.launchpad.webapp.testing import verifyObject
172
>>> from canonical.launchpad.webapp.vocabulary import IHugeVocabulary
174
>>> vocabulary = get_naked_vocab(None, 'PPA')
175
>>> verifyObject(IHugeVocabulary, vocabulary)
178
>>> print vocabulary.displayname
181
Iterations over the PPA vocabulary will return on PPA archives.
183
>>> sorted([term.value.owner.name for term in vocabulary])
184
[u'cprov', u'mark', u'no-priv']
186
PPA vocabulary terms contain:
188
* token: the PPA owner name combined with the archive name (using '/');
189
* value: the IArchive object;
190
* title: the first line of the PPA description text.
192
>>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
194
>>> print cprov_term.token
197
>>> print cprov_term.value
200
>>> print cprov_term.title
201
packages to help my friends.
203
Not found terms result in LookupError.
205
>>> vocabulary.getTermByToken('foobar')
206
Traceback (most recent call last):
210
PPA vocabulary searches consider the owner FTI and the PPA FTI.
212
>>> def print_search_results(results):
213
... for archive in results:
214
... term = vocabulary.toTerm(archive)
215
... print '%s: %s' % (term.token, term.title)
217
>>> cprov_search = vocabulary.search('cprov')
218
>>> print_search_results(cprov_search)
219
cprov/ppa: packages to help my friends.
221
>>> celso_search = vocabulary.search('celso')
222
>>> print_search_results(celso_search)
223
cprov/ppa: packages to help my friends.
225
>>> friends_search = vocabulary.search('friends')
226
>>> print_search_results(friends_search)
227
cprov/ppa: packages to help my friends.
229
We will create an additional PPA for Celso named 'testing'
231
>>> from lp.soyuz.enums import ArchivePurpose
232
>>> from lp.soyuz.interfaces.archive import IArchiveSet
234
>>> login('foo.bar@canonical.com')
235
>>> cprov = getUtility(IPersonSet).getByName('cprov')
236
>>> cprov_testing = getUtility(IArchiveSet).new(
237
... owner=cprov, name='testing', purpose=ArchivePurpose.PPA,
238
... description='testing packages.')
240
Now, a search for 'cprov' will return 2 ppas and the result is ordered
243
>>> cprov_search = vocabulary.search('cprov')
244
>>> print_search_results(cprov_search)
245
cprov/ppa: packages to help my friends.
246
cprov/testing: testing packages.
248
The vocabulary search also supports specific named PPA lookups
249
follwing the same combined syntax used to build unique tokens.
251
>>> named_search = vocabulary.search('cprov/testing')
252
>>> print_search_results(named_search)
253
cprov/testing: testing packages.
255
As mentioned the PPA vocabulary term title only contains the first
256
line of the PPA description.
258
>>> cprov.archive.description = "Single line."
259
>>> flush_database_updates()
261
>>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
262
>>> print cprov_term.title
265
>>> cprov.archive.description = "First line\nSecond line."
266
>>> flush_database_updates()
268
>>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
269
>>> print cprov_term.title
272
PPAs with empty description are identified and have a title saying so.
274
>>> cprov.archive.description = None
275
>>> flush_database_updates()
277
>>> cprov_term = vocabulary.getTermByToken('cprov/ppa')
278
>>> print cprov_term.title
279
No description available
281
Queries on empty strings also results in a valid SelectResults.
283
>>> empty_search = vocabulary.search('')
284
>>> empty_search.count() == 0