~ubuntu-branches/ubuntu/maverick/python-debian/maverick

« back to all changes in this revision

Viewing changes to debian_bundle/debtags.py

  • Committer: Bazaar Package Importer
  • Author(s): John Wright
  • Date: 2008-04-30 23:58:24 UTC
  • mfrom: (1.1.9 hardy)
  • Revision ID: james.westby@ubuntu.com-20080430235824-iq9mp0fbd0efmruv
Tags: 0.1.10
* debian_bundle/deb822.py, tests/test_deb822.py:
  - Do not cache _CaseInsensitiveString objects, since it causes case
    preservation issues under certain circumstances (Closes: #473254)
  - Add a test case
* debian_bundle/deb822.py:
  - Add support for fixed-length subfields in multivalued fields.  I updated
    the Release and PdiffIndex classes to use this.  The default behavior for
    Release is that of apt-ftparchive, just because it's simpler.  Changing
    the behavior to resemble dak requires simply setting the
    size_field_behavior attribute to 'dak'.  (Ideally, deb822 would detect
    which behavior to use if given an actual Release file as input, but this
    is not implemented yet.)  (Closes: #473259)
  - Add support for Checksums-{Sha1,Sha256} multivalued fields in Dsc and
    Changes classes
* debian/control:
  - "python" --> "Python" in the Description field
  - Change the section to "python"

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#
 
1
 
2
2
# debtags.py -- Access and manipulate Debtags information
3
 
#
4
 
# Copyright (C) 2006  Enrico Zini <enrico@enricozini.org>
5
 
6
 
# This library is free software; you can redistribute it and/or
7
 
# modify it under the terms of the GNU Lesser General Public
8
 
# License as published by the Free Software Foundation; either
9
 
# version 2.1 of the License, or (at your option) any later version.
10
 
11
 
# This library is distributed in the hope that it will be useful,
12
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
3
# Copyright (C) 2006-2007  Enrico Zini <enrico@enricozini.org>
 
4
#
 
5
# This program is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation, either version 3 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful, but
 
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
# Lesser General Public License for more details.
15
 
16
 
# You should have received a copy of the GNU Lesser General Public
17
 
# License along with this library; if not, write to the Free Software
18
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
13
# General Public License for more details.
19
14
#
20
 
 
21
 
# TODO: install python-epydoc and try to autogenerate documntation from that
22
 
 
23
 
import math
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
import math, re, cPickle
 
19
 
 
20
def parseTags(input):
 
21
        lre = re.compile(r"^(.+?)(?::?\s*|:\s+(.+?)\s*)$")
 
22
        for line in input:
 
23
                # Is there a way to remove the last character of a line that does not
 
24
                # make a copy of the entire line?
 
25
                m = lre.match(line)
 
26
                pkgs = set(m.group(1).split(', '))
 
27
                if m.group(2):
 
28
                        tags = set(m.group(2).split(', '))
 
29
                else:
 
30
                        tags = set()
 
31
                yield pkgs, tags
24
32
 
25
33
def readTagDatabase(input):
26
34
        "Read the tag database, returning a pkg->tags dictionary"
27
35
        db = {}
28
 
        for line in input:
29
 
                # Is there a way to remove the last character of a line that does not
30
 
                # make a copy of the entire line?
31
 
                line = line.rstrip("\n")
32
 
                pkgs, tags = line.split(": ")
 
36
        for pkgs, tags in parseTags(input):
33
37
                # Create the tag set using the native set
34
 
                tags = set(tags.split(", "))
35
 
                for p in pkgs.split(", "):
 
38
                for p in pkgs:
36
39
                        db[p] = tags.copy()
37
40
        return db;
38
41
 
39
42
def readTagDatabaseReversed(input):
40
43
        "Read the tag database, returning a tag->pkgs dictionary"
41
44
        db = {}
42
 
        for line in input:
43
 
                # Is there a way to remove the last character of a line that does not
44
 
                # make a copy of the entire line?
45
 
                line = line.rstrip("\n")
46
 
                pkgs, tags = line.split(": ")
 
45
        for pkgs, tags in parseTags(input):
47
46
                # Create the tag set using the native set
48
 
                pkgs = set(pkgs.split(", "))
49
 
                for tag in tags.split(", "):
 
47
                for tag in tags:
50
48
                        if db.has_key(tag):
51
49
                                db[tag] |= pkgs
52
50
                        else:
57
55
        "Read the tag database, returning a pkg->tags and a tag->pkgs dictionary"
58
56
        db = {}
59
57
        dbr = {}
60
 
        for line in input:
61
 
                # Is there a way to remove the last character of a line that does not
62
 
                # make a copy of the entire line?
63
 
                line = line.rstrip("\n")
64
 
                pkgs, tags = line.split(": ")
 
58
        for pkgs, tags in parseTags(input):
65
59
                # Create the tag set using the native set
66
 
                pkgs = set(pkgs.split(", "))
67
60
                if tagFilter == None:
68
 
                        tags = set(tags.split(", "))
 
61
                        tags = set(tags)
69
62
                else:
70
 
                        tags = set(filter(tagFilter, tags.split(', ')))
 
63
                        tags = set(filter(tagFilter, tags))
71
64
                for pkg in pkgs:
72
65
                        db[pkg] = tags.copy()
73
66
                for tag in tags:
149
142
                """
150
143
                self.db, self.rdb = readTagDatabaseBothWays(input, tagFilter)
151
144
 
 
145
        def qwrite(self, file):
 
146
                "Quickly write the data to a pickled file"
 
147
                cPickle.dump(self.db, file)
 
148
                cPickle.dump(self.rdb, file)
 
149
 
 
150
        def qread(self, file):
 
151
                "Quickly read the data from a pickled file"
 
152
                self.db = cPickle.load(file)
 
153
                self.rdb = cPickle.load(file)
 
154
 
152
155
        def insert(self, pkg, tags):
153
156
                self.db[pkg] = tags.copy()
154
157
                for tag in tags:
170
173
                res.rdb = self.db
171
174
                return res
172
175
 
 
176
        def facetCollection(self):
 
177
                """
 
178
                Return a copy of this collection, but replaces the tag names
 
179
                with only their facets.
 
180
                """
 
181
                fcoll = DB()
 
182
                tofacet = re.compile(r"^([^:]+).+")
 
183
                for pkg, tags in self.iterPackagesTags():
 
184
                        ftags = set([tofacet.sub(r"\1", t) for t in tags])
 
185
                        fcoll.insert(pkg, ftags)
 
186
                return fcoll
 
187
 
 
188
        def copy(self):
 
189
                """
 
190
                Return a copy of this collection, with the tagsets copied as
 
191
                well.
 
192
                """
 
193
                res = DB()
 
194
                res.db = self.db.copy()
 
195
                res.rdb = self.rdb.copy()
 
196
                return res
 
197
 
173
198
        def reverseCopy(self):
174
199
                """
175
200
                Return the reverse collection, with a copy of the tagsets of
176
 
                this one
 
201
                this one.
177
202
                """
178
203
                res = DB()
179
204
                res.db = self.rdb.copy()
209
234
        def filterPackages(self, packageFilter):
210
235
                """
211
236
                Return a collection with only those packages that match a
212
 
                filter, sharing tagsets with this one
 
237
                filter, sharing tagsets with this one.  The filter will match
 
238
                on the package.
213
239
                """
214
240
                res = DB()
215
241
                db = {}
222
248
        def filterPackagesCopy(self, filter):
223
249
                """
224
250
                Return a collection with only those packages that match a
225
 
                filter, with a copy of the tagsets of this one
 
251
                filter, with a copy of the tagsets of this one.  The filter
 
252
                will match on the package.
226
253
                """
227
254
                res = DB()
228
255
                db = {}
232
259
                res.rdb = reverse(db)
233
260
                return res
234
261
 
 
262
        def filterPackagesTags(self, packageTagFilter):
 
263
                """
 
264
                Return a collection with only those packages that match a
 
265
                filter, sharing tagsets with this one.  The filter will match
 
266
                on (package, tags).
 
267
                """
 
268
                res = DB()
 
269
                db = {}
 
270
                for pkg, tags in filter(packageTagFilter, self.db.iteritems()):
 
271
                        db[pkg] = self.db[pkg]
 
272
                res.db = db
 
273
                res.rdb = reverse(db)
 
274
                return res
 
275
 
 
276
        def filterPackagesTagsCopy(self, packageTagFilter):
 
277
                """
 
278
                Return a collection with only those packages that match a
 
279
                filter, with a copy of the tagsets of this one.  The filter
 
280
                will match on (package, tags).
 
281
                """
 
282
                res = DB()
 
283
                db = {}
 
284
                for pkg, tags in filter(packageTagFilter, self.db.iteritems()):
 
285
                        db[pkg] = self.db[pkg].copy()
 
286
                res.db = db
 
287
                res.rdb = reverse(db)
 
288
                return res
 
289
 
 
290
        def filterTags(self, tagFilter):
 
291
                """
 
292
                Return a collection with only those tags that match a
 
293
                filter, sharing package sets with this one.  The filter will match
 
294
                on the tag.
 
295
                """
 
296
                res = DB()
 
297
                rdb = {}
 
298
                for tag in filter(tagFilter, self.rdb.iterkeys()):
 
299
                        rdb[tag] = self.rdb[tag]
 
300
                res.rdb = rdb
 
301
                res.db = reverse(rdb)
 
302
                return res
 
303
 
 
304
        def filterTagsCopy(self, tagFilter):
 
305
                """
 
306
                Return a collection with only those tags that match a
 
307
                filter, with a copy of the package sets of this one.  The
 
308
                filter will match on the tag.
 
309
                """
 
310
                res = DB()
 
311
                rdb = {}
 
312
                for tag in filter(tagFilter, self.rdb.iterkeys()):
 
313
                        rdb[tag] = self.rdb[tag].copy()
 
314
                res.rdb = rdb
 
315
                res.db = reverse(rdb)
 
316
                return res
 
317
 
235
318
        def hasPackage(self, pkg):
236
319
                """Check if the collection contains the given package"""
237
320
                return self.db.has_key(pkg)
346
429
                        return set(tags[:1])
347
430
                else:
348
431
                        return tagset
 
432
 
 
433
        def correlations(self):
 
434
                """
 
435
                Generate the list of correlation as a tuple (hastag, hasalsotag, score).
 
436
 
 
437
                Every touple will indicate that the tag 'hastag' tends to also
 
438
                have 'hasalsotag' with a score of 'score'.
 
439
                """
 
440
                for pivot in self.iterTags():
 
441
                        with_ = self.filterPackagesTags(lambda pt: pivot in pt[1])
 
442
                        without = self.filterPackagesTags(lambda pt: pivot not in pt[1])
 
443
                        for tag in with_.iterTags():
 
444
                                if tag == pivot: continue
 
445
                                has = float(with_.card(tag)) / float(with_.packageCount())
 
446
                                hasnt = float(without.card(tag)) / float(without.packageCount())
 
447
                                yield pivot, tag, has - hasnt