~launchpad-pqm/launchpad/devel

7675.918.12 by Henning Eggers
Fixed copyright notices.
1
# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4983.1.2 by Curtis Hovey
Added pylint exceptions to database classes.
4
# pylint: disable-msg=E0611,W0212
1670 by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts.
5
6
__metaclass__ = type
7117.4.7 by Jeroen Vermeulen
Unit-test translations upload filter callback in Soyuz.
7
__all__ = [
8
    'SourcePackageRelease',
9
    '_filter_ubuntu_translation_file',
10
    ]
1670 by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts.
11
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
12
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
13
import apt_pkg
3023.2.24 by Celso Providelo
Fix Build.datecreated and build-buildlog page partially.
14
import datetime
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
15
from debian.changelog import (
16
    Changelog,
17
    ChangelogCreateError,
18
    ChangelogParseError,
19
    )
5718.6.12 by Julian Edwards
Fix errant test ordering.
20
import operator
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
21
import re
22
from StringIO import StringIO
23
3023.2.24 by Celso Providelo
Fix Build.datecreated and build-buildlog page partially.
24
import pytz
7675.788.2 by Henning Eggers
Reformated imports using format-imports script r37.
25
import simplejson
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
26
from sqlobject import (
27
    ForeignKey,
28
    SQLMultipleJoin,
29
    StringCol,
30
    )
7675.539.12 by William Grant
Add (I)SourcePackageRelease.source_package_recipe_build (was already in the DB).
31
from storm.expr import Join
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
32
from storm.locals import (
33
    Int,
34
    Reference,
35
    )
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
36
from storm.store import Store
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
37
from zope.component import getUtility
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
38
from zope.interface import implements
39
13130.1.12 by Curtis Hovey
Sorted imports.
40
from canonical.database.constants import UTC_NOW
1650.1.2 by James Henstridge
commit the first part of the timezone awareness code
41
from canonical.database.datetimecol import UtcDateTimeCol
3691.373.5 by Christian Reis
Move DBSchema and Item into webapp.enum, and put EnumCol into canonical.database.enumcol
42
from canonical.database.enumcol import EnumCol
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
43
from canonical.database.sqlbase import (
44
    cursor,
45
    SQLBase,
46
    sqlvalues,
47
    )
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
48
from canonical.launchpad.components.decoratedresultset import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
49
    DecoratedResultSet,
50
    )
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
51
from canonical.launchpad.database.librarian import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
52
    LibraryFileAlias,
53
    LibraryFileContent,
54
    )
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
55
from canonical.launchpad.helpers import shortlist
13130.1.6 by Curtis Hovey
Move ILaunchpadCelebrity to lp.app.
56
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
7675.424.6 by William Grant
Use the new determine_source_file_type when creating an SPRF.
57
from lp.archiveuploader.utils import determine_source_file_type
11458.1.1 by Jelmer Vernooij
Move enums of buildmaster.
58
from lp.buildmaster.enums import BuildStatus
7675.575.1 by William Grant
Move lp.soyuz.interfaces.build.BuildStatus to lp.buildmaster.interfaces.buildbase. Sort various imports along the way.
59
from lp.registry.interfaces.person import validate_public_person
60
from lp.registry.interfaces.sourcepackage import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
61
    SourcePackageType,
62
    SourcePackageUrgency,
63
    )
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
64
from lp.services.propertycache import cachedproperty
11411.6.12 by Julian Edwards
Move PackagePublishingStatus/Priority
65
from lp.soyuz.enums import (
66
    PackageDiffStatus,
67
    PackagePublishingStatus,
68
    )
13444.6.31 by Gavin Panella
Fix lint.
69
from lp.soyuz.interfaces.archive import MAIN_ARCHIVE_PURPOSES
7675.687.48 by Michael Nelson
Initial update of binarypackagebuild to use new table, replaced calculated_buildstart with build_started.
70
from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
11382.6.44 by Gavin Panella
Fix and reformat imports.
71
from lp.soyuz.interfaces.packagediff import PackageDiffAlreadyRequested
7675.424.28 by William Grant
s/SourceFormatSelection/SourcePackageFormatSelection/, and make it use an enum instead.
72
from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
10667.2.3 by Michael Nelson
Updated model.binarypackagebuild.
73
from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
8294.6.9 by Julian Edwards
migrate files modules to lp.soyuz
74
from lp.soyuz.model.files import SourcePackageReleaseFile
8294.6.1 by Julian Edwards
First stab at code-reorg. Still got a discrepancy on stuff I assigned to registry but not migrated yet.
75
from lp.soyuz.model.packagediff import PackageDiff
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
76
from lp.soyuz.model.publishing import SourcePackagePublishingHistory
77
from lp.soyuz.model.queue import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
78
    PackageUpload,
79
    PackageUploadSource,
80
    )
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
81
from lp.soyuz.scripts.queue import QueueActionError
7675.575.1 by William Grant
Move lp.soyuz.interfaces.build.BuildStatus to lp.buildmaster.interfaces.buildbase. Sort various imports along the way.
82
from lp.translations.interfaces.translationimportqueue import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
83
    ITranslationImportQueue,
84
    )
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
85
1670 by Canonical.com Patch Queue Manager
Big lot of database clean-up r=stub except for resolution of conflicts.
86
7117.4.7 by Jeroen Vermeulen
Unit-test translations upload filter callback in Soyuz.
87
def _filter_ubuntu_translation_file(filename):
7117.4.1 by Jeroen Vermeulen
First step: make Soyuz' attachTranslationFiles() use TranslationImportQueue.addOrUpdateEntriesFromTarball() instead of duplicating its work.
88
    """Filter for translation filenames in tarball.
7530.4.2 by Celso Providelo
Remove deprecated SPR._cached_published_archives cachedproperty. It doesn't represent any performance improvement anymore since security-adapter are cached per bug #268612.
89
7117.4.1 by Jeroen Vermeulen
First step: make Soyuz' attachTranslationFiles() use TranslationImportQueue.addOrUpdateEntriesFromTarball() instead of duplicating its work.
90
    Grooms filenames of translation files in tarball, returning None or
91
    empty string for files that should be ignored.
92
93
    Passed to `ITranslationImportQueue.addOrUpdateEntriesFromTarball`.
94
    """
95
    source_prefix = 'source/'
96
    if not filename.startswith(source_prefix):
97
        return None
98
99
    filename = filename[len(source_prefix):]
7117.4.7 by Jeroen Vermeulen
Unit-test translations upload filter callback in Soyuz.
100
11204.1.1 by Jeroen Vermeulen
Filter more translation file names from Soyuz-to-Rosetta uploads.
101
    blocked_prefixes = [
102
        # Translations for use by debconf--not used in Ubuntu.
103
        'debian/po/',
104
        # Debian Installer translations--treated separately.
105
        'd-i/',
106
        # Documentation--not translatable in Launchpad.
107
        'help/',
108
        'man/po/',
109
        'man/po4a/',
110
        ]
111
112
    for prefix in blocked_prefixes:
113
        if filename.startswith(prefix):
114
            return None
7117.4.7 by Jeroen Vermeulen
Unit-test translations upload filter callback in Soyuz.
115
7117.4.1 by Jeroen Vermeulen
First step: make Soyuz' attachTranslationFiles() use TranslationImportQueue.addOrUpdateEntriesFromTarball() instead of duplicating its work.
116
    return filename
117
118
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
119
class SourcePackageRelease(SQLBase):
120
    implements(ISourcePackageRelease)
121
    _table = 'SourcePackageRelease'
122
123
    section = ForeignKey(foreignKey='Section', dbName='section')
5485.1.17 by Edwin Grubbs
Fixed indentation
124
    creator = ForeignKey(
125
        dbName='creator', foreignKey='Person',
5821.2.40 by James Henstridge
* Move all the uses of public_person_validator over to the Storm
126
        storm_validator=validate_public_person, notNull=True)
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
127
    component = ForeignKey(foreignKey='Component', dbName='component')
128
    sourcepackagename = ForeignKey(foreignKey='SourcePackageName',
2705 by Canonical.com Patch Queue Manager
r=spiv, mark's soyuz loving.
129
        dbName='sourcepackagename', notNull=True)
5485.1.17 by Edwin Grubbs
Fixed indentation
130
    maintainer = ForeignKey(
131
        dbName='maintainer', foreignKey='Person',
5821.2.40 by James Henstridge
* Move all the uses of public_person_validator over to the Storm
132
        storm_validator=validate_public_person, notNull=True)
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
133
    dscsigningkey = ForeignKey(foreignKey='GPGKey', dbName='dscsigningkey')
1515 by Canonical.com Patch Queue Manager
Require launchpad.Edit to change a Person. Use removeSecurityProxy() in logintoken to make that work. Change SourcePackageRelease.urgency to an EnumCol and remove .pkgurgency, for great justice. Move property(method) lines to underneath the methods they apply to. Improve __getitem__ from VSourcePackageReleasePublishing. Add some unfinished debug code to web publication component.
134
    urgency = EnumCol(dbName='urgency', schema=SourcePackageUrgency,
2705 by Canonical.com Patch Queue Manager
r=spiv, mark's soyuz loving.
135
        default=SourcePackageUrgency.LOW, notNull=True)
1650.1.2 by James Henstridge
commit the first part of the timezone awareness code
136
    dateuploaded = UtcDateTimeCol(dbName='dateuploaded', notNull=True,
2705 by Canonical.com Patch Queue Manager
r=spiv, mark's soyuz loving.
137
        default=UTC_NOW)
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
138
    dsc = StringCol(dbName='dsc')
7675.1041.5 by Julian Edwards
Modify schema change to turn version into a debversion column
139
    version = StringCol(dbName='version', notNull=True)
10475.3.16 by Michael Casadevall
Make necessary changes to SourcePackageRelease to handle changelog being a librarian id versus a string
140
    changelog = ForeignKey(foreignKey='LibraryFileAlias', dbName='changelog')
5398.7.2 by Julian Edwards
Rename sourcepackagerelease.changelog to changelog_entry
141
    changelog_entry = StringCol(dbName='changelog_entry')
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
142
    builddepends = StringCol(dbName='builddepends')
143
    builddependsindep = StringCol(dbName='builddependsindep')
5433.2.1 by Celso Providelo
Fixing bug 172308 part 1, populating the new fields in SPR/BPR.
144
    build_conflicts = StringCol(dbName='build_conflicts')
145
    build_conflicts_indep = StringCol(dbName='build_conflicts_indep')
1433 by Canonical.com Patch Queue Manager
Got classes related with source and binary packages into diferent files on database, interfaces and zcml directories.
146
    architecturehintlist = StringCol(dbName='architecturehintlist')
7675.795.2 by Jelmer Vernooij
Add model code for homepage fields.
147
    homepage = StringCol(dbName='homepage')
7675.424.27 by William Grant
Rename SourcePackageFormat (DPKG, RPM, etc.) to SourcePackageType, since Format is actually something different.
148
    format = EnumCol(dbName='format', schema=SourcePackageType,
149
        default=SourcePackageType.DPKG, notNull=True)
5121.2.6 by Stuart Bishop
Some required code updates
150
    upload_distroseries = ForeignKey(foreignKey='DistroSeries',
151
        dbName='upload_distroseries')
3691.442.29 by Celso Providelo
optmise DB patch and use proper name for SPR.upload_archive.
152
    upload_archive = ForeignKey(
153
        foreignKey='Archive', dbName='upload_archive', notNull=True)
2705 by Canonical.com Patch Queue Manager
r=spiv, mark's soyuz loving.
154
7675.539.12 by William Grant
Add (I)SourcePackageRelease.source_package_recipe_build (was already in the DB).
155
    source_package_recipe_build_id = Int(name='sourcepackage_recipe_build')
156
    source_package_recipe_build = Reference(
157
        source_package_recipe_build_id, 'SourcePackageRecipeBuild.id')
158
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
159
    # XXX cprov 2006-09-26: Those fields are set as notNull and required in
3686.2.24 by Celso Providelo
Adding new required fields to DB, change nascentupload to populate them and add a dedicated ftest file to test archive index generation.
160
    # ISourcePackageRelease, however they can't be not NULL in DB since old
161
    # records doesn't satisfy this condition. We will sort it before using
5433.2.1 by Celso Providelo
Fixing bug 172308 part 1, populating the new fields in SPR/BPR.
162
    # 'NoMoreAptFtparchive' implementation for PRIMARY archive. For PPA
5433.2.6 by Celso Providelo
applying review comments, r=intellectronica.
163
    # (primary target) we don't need to populate old records.
3691.373.11 by Christian Reis
Don't lie about notNull because storm doesn't like it
164
    dsc_maintainer_rfc822 = StringCol(dbName='dsc_maintainer_rfc822')
165
    dsc_standards_version = StringCol(dbName='dsc_standards_version')
166
    dsc_format = StringCol(dbName='dsc_format')
167
    dsc_binaries = StringCol(dbName='dsc_binaries')
2705 by Canonical.com Patch Queue Manager
r=spiv, mark's soyuz loving.
168
3686.2.24 by Celso Providelo
Adding new required fields to DB, change nascentupload to populate them and add a dedicated ftest file to test archive index generation.
169
    # MultipleJoins
5992.2.6 by Muharem Hrnjadovic
minor fixes
170
    files = SQLMultipleJoin('SourcePackageReleaseFile',
171
        joinColumn='sourcepackagerelease', orderBy="libraryfile")
172
    publishings = SQLMultipleJoin('SourcePackagePublishingHistory',
173
        joinColumn='sourcepackagerelease', orderBy="-datecreated")
174
    package_diffs = SQLMultipleJoin(
6382.5.1 by Celso Providelo
Package-Diff revolution. Inverting the 'natural' diff order, so the diffs will read better as from ANCESTRY to CURRENT. Modifying SPR.package_diffs join to return all diff *to* the context SPR and not *from* it. Finally making the diff title and filename less redundant.
175
        'PackageDiff', joinColumn='to_source', orderBy="-date_requested")
5992.2.6 by Muharem Hrnjadovic
minor fixes
176
7675.786.4 by Jelmer Vernooij
Add tests for sourcepackagerelease user_defined_fields.
177
    _user_defined_fields = StringCol(dbName='user_defined_fields')
178
179
    def __init__(self, *args, **kwargs):
180
        if 'user_defined_fields' in kwargs:
181
            kwargs['_user_defined_fields'] = simplejson.dumps(
182
                kwargs['user_defined_fields'])
183
            del kwargs['user_defined_fields']
12570.1.1 by William Grant
Turn SourcePackageRelease.copyright into a property, so it isn't lloaded every time we load an SPR. It gets big.
184
        # copyright isn't on the Storm class, since we don't want it
185
        # loaded every time. Set it separately.
186
        if 'copyright' in kwargs:
187
            copyright = kwargs.pop('copyright')
7675.786.8 by Jelmer Vernooij
Review feedback from Michael.
188
        super(SourcePackageRelease, self).__init__(*args, **kwargs)
12570.1.1 by William Grant
Turn SourcePackageRelease.copyright into a property, so it isn't lloaded every time we load an SPR. It gets big.
189
        self.copyright = copyright
190
191
    @property
192
    def copyright(self):
193
        """See `ISourcePackageRelease`."""
194
        store = Store.of(self)
195
        store.flush()
196
        return store.execute(
197
            "SELECT copyright FROM sourcepackagerelease WHERE id=%s",
198
            (self.id,)).get_one()[0]
199
200
    @copyright.setter
201
    def copyright(self, content):
202
        """See `ISourcePackageRelease`."""
203
        store = Store.of(self)
204
        store.flush()
205
        store.execute(
206
            "UPDATE sourcepackagerelease SET copyright=%s WHERE id=%s",
207
            (content, self.id))
7675.786.4 by Jelmer Vernooij
Add tests for sourcepackagerelease user_defined_fields.
208
209
    @property
210
    def user_defined_fields(self):
211
        """See `IBinaryPackageRelease`."""
212
        if self._user_defined_fields is None:
213
            return []
214
        return simplejson.loads(self._user_defined_fields)
215
5992.2.6 by Muharem Hrnjadovic
minor fixes
216
    @property
217
    def builds(self):
218
        """See `ISourcePackageRelease`."""
219
        # Excluding PPA builds may seem like a strange thing to do but
220
        # when copy-package works for copying packages across archives,
221
        # a build may well have a different archive to the corresponding
222
        # sourcepackagerelease.
10667.2.3 by Michael Nelson
Updated model.binarypackagebuild.
223
        return BinaryPackageBuild.select("""
7675.687.82 by Michael Nelson
Fixed for doc/binarypackagerelease.txt.
224
            source_package_release = %s AND
225
            package_build = packagebuild.id AND
226
            archive.id = packagebuild.archive AND
227
            packagebuild.build_farm_job = buildfarmjob.id AND
7344.2.1 by Muharem Hrnjadovic
branch import
228
            archive.purpose IN %s
229
            """ % sqlvalues(self.id, MAIN_ARCHIVE_PURPOSES),
7675.687.82 by Michael Nelson
Fixed for doc/binarypackagerelease.txt.
230
            orderBy=['-buildfarmjob.date_created', 'id'],
231
            clauseTables=['Archive', 'PackageBuild', 'BuildFarmJob'])
5992.2.6 by Muharem Hrnjadovic
minor fixes
232
233
    @property
234
    def age(self):
235
        """See ISourcePackageRelease."""
236
        now = datetime.datetime.now(pytz.timezone('UTC'))
237
        return now - self.dateuploaded
238
239
    @property
240
    def latest_build(self):
241
        builds = self._cached_builds
242
        if len(builds) > 0:
243
            return builds[0]
244
        return None
245
246
    def failed_builds(self):
247
        return [build for build in self._cached_builds
248
                if build.buildstate == BuildStatus.FAILEDTOBUILD]
249
250
    @property
251
    def needs_building(self):
252
        for build in self._cached_builds:
7675.687.82 by Michael Nelson
Fixed for doc/binarypackagerelease.txt.
253
            if build.status in [BuildStatus.NEEDSBUILD,
5992.2.6 by Muharem Hrnjadovic
minor fixes
254
                                    BuildStatus.MANUALDEPWAIT,
255
                                    BuildStatus.CHROOTWAIT]:
256
                return True
257
        return False
258
259
    @cachedproperty
260
    def _cached_builds(self):
261
        # The reason we have this as a cachedproperty is that all the
262
        # *build* methods here need access to it; better not to
263
        # recalculate it multiple times.
264
        return list(self.builds)
265
266
    @property
267
    def name(self):
268
        return self.sourcepackagename.name
269
270
    @property
271
    def sourcepackage(self):
272
        """See ISourcePackageRelease."""
273
        # By supplying the sourcepackagename instead of its string name,
6476.3.13 by Matthew Paul Thomas
Adds IDistroSeriesSourcePackageRelease.sourcepackage @property, so that links from distribution series pages to distribution series source package pages go to the right place.
274
        # we avoid doing an extra query doing getSourcepackage.
6912.5.20 by Curtis Hovey
Updated XXX comments that were missing their bug numbers.
275
        # XXX 2008-06-16 mpt bug=241298: cprov says this property "won't be as
276
        # useful as it looks once we start supporting derivation ... [It] is
277
        # dangerous and should be renamed (or removed)".
5992.2.6 by Muharem Hrnjadovic
minor fixes
278
        series = self.upload_distroseries
279
        return series.getSourcePackage(self.sourcepackagename)
280
281
    @property
282
    def distrosourcepackage(self):
283
        """See ISourcePackageRelease."""
284
        # By supplying the sourcepackagename instead of its string name,
285
        # we avoid doing an extra query doing getSourcepackage
286
        distribution = self.upload_distroseries.distribution
287
        return distribution.getSourcePackage(self.sourcepackagename)
288
289
    @property
290
    def title(self):
291
        return '%s - %s' % (self.sourcepackagename.name, self.version)
292
293
    @property
294
    def productrelease(self):
295
        """See ISourcePackageRelease."""
296
        series = None
297
298
        # Use any published source package to find the product series.
299
        # We can do this because if we ever find out that a source package
300
        # release in two product series, we've almost certainly got a data
301
        # problem there.
302
        publishings = SourcePackagePublishingHistory.select(
303
            """
304
            sourcepackagerelease = %s AND
305
            status = %s
306
            """ % sqlvalues(self, PackagePublishingStatus.PUBLISHED))
307
308
        for publishing in publishings:
309
            # imports us, so avoid circular import
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
310
            from lp.registry.model.sourcepackage import \
5992.2.6 by Muharem Hrnjadovic
minor fixes
311
                 SourcePackage
7344.2.1 by Muharem Hrnjadovic
branch import
312
            # Only process main archives and skip PPA/copy archives.
313
            if publishing.archive.purpose not in MAIN_ARCHIVE_PURPOSES:
5992.2.6 by Muharem Hrnjadovic
minor fixes
314
                continue
315
            sp = SourcePackage(self.sourcepackagename,
316
                               publishing.distroseries)
317
            sp_series = sp.productseries
318
            if sp_series is not None:
319
                if series is None:
320
                    series = sp_series
321
                elif series != sp_series:
322
                    # XXX: keybuk 2005-06-22: We could warn about this.
323
                    pass
324
325
        # No series -- no release
326
        if series is None:
327
            return None
328
329
        # XXX: keybuk 2005-06-22:
330
        # Find any release with the exact same version, or which
331
        # we begin with and after a dash.  We could be more intelligent
332
        # about this, but for now this will work for most.
333
        for release in series.releases:
334
            if release.version == self.version:
335
                return release
336
            elif self.version.startswith("%s-" % release.version):
337
                return release
338
        else:
339
            return None
340
341
    @property
342
    def current_publishings(self):
343
        """See ISourcePackageRelease."""
8294.6.1 by Julian Edwards
First stab at code-reorg. Still got a discrepancy on stuff I assigned to registry but not migrated yet.
344
        from lp.soyuz.model.distroseriessourcepackagerelease \
5992.2.6 by Muharem Hrnjadovic
minor fixes
345
            import DistroSeriesSourcePackageRelease
346
        return [DistroSeriesSourcePackageRelease(pub.distroseries, self)
347
                for pub in self.publishings]
348
349
    @property
350
    def published_archives(self):
10245.5.2 by Jelmer Vernooij
Fix typo in docstring.
351
        """See `ISourcePackageRelease`."""
7840.2.1 by Julian Edwards
ISourcePackageRelease.published_archives was ignoring superseded and deleted publications, causing some builds pages to return a 403 error.
352
        archives = set(
353
            pub.archive for pub in self.publishings.prejoin(['archive']))
5992.2.6 by Muharem Hrnjadovic
minor fixes
354
        return sorted(archives, key=operator.attrgetter('id'))
355
356
    def addFile(self, file):
357
        """See ISourcePackageRelease."""
7675.424.6 by William Grant
Use the new determine_source_file_type when creating an SPRF.
358
        return SourcePackageReleaseFile(
359
            sourcepackagerelease=self,
360
            filetype=determine_source_file_type(file.filename),
361
            libraryfile=file)
5992.2.6 by Muharem Hrnjadovic
minor fixes
362
7675.403.5 by Muharem Hrnjadovic
Made the method that calculates source package sizes public.
363
    def getPackageSize(self):
364
        """See ISourcePackageRelease."""
365
        size_query = """
366
            SELECT
367
                SUM(LibraryFileContent.filesize)/1024.0
368
            FROM
369
                SourcePackagereLease
370
                JOIN SourcePackageReleaseFile ON
371
                    SourcePackageReleaseFile.sourcepackagerelease =
372
                    SourcePackageRelease.id
373
                JOIN LibraryFileAlias ON
374
                    SourcePackageReleaseFile.libraryfile =
375
                    LibraryFileAlias.id
376
                JOIN LibraryFileContent ON
377
                    LibraryFileAlias.content = LibraryFileContent.id
378
            WHERE
379
                SourcePackageRelease.id = %s
380
            """ % sqlvalues(self)
381
382
        cur = cursor()
383
        cur.execute(size_query)
384
        results = cur.fetchone()
385
386
        if len(results) == 1 and results[0] is not None:
387
            return float(results[0])
388
        else:
389
            return 0.0
390
7675.687.48 by Michael Nelson
Initial update of binarypackagebuild to use new table, replaced calculated_buildstart with build_started.
391
    def createBuild(self, distro_arch_series, pocket, archive, processor=None,
6642.1.6 by Celso Providelo
applying review comment, r=barry. Lots of code cleanup and removing upload_log for successfully re-processed build in upload-time.
392
                    status=None):
2713 by Canonical.com Patch Queue Manager
Various fixes to queue, build etc. add zcml for queue, DB patch included. r=stevea,stub. Also fix dbschema security proxy bug (bug 1971) r=stevea
393
        """See ISourcePackageRelease."""
394
        # Guess a processor if one is not provided
395
        if processor is None:
7675.687.48 by Michael Nelson
Initial update of binarypackagebuild to use new table, replaced calculated_buildstart with build_started.
396
            pf = distro_arch_series.processorfamily
2713 by Canonical.com Patch Queue Manager
Various fixes to queue, build etc. add zcml for queue, DB patch included. r=stevea,stub. Also fix dbschema security proxy bug (bug 1971) r=stevea
397
            # We guess at the first processor in the family
398
            processor = shortlist(pf.processors)[0]
2733 by Canonical.com Patch Queue Manager
Upload processor. r=stevea
399
6642.1.6 by Celso Providelo
applying review comment, r=barry. Lots of code cleanup and removing upload_log for successfully re-processed build in upload-time.
400
        if status is None:
401
            status = BuildStatus.NEEDSBUILD
402
5992.2.1 by Muharem Hrnjadovic
setting of estimated build duration for new builds implemented and tested
403
        # Force the current timestamp instead of the default
3023.2.24 by Celso Providelo
Fix Build.datecreated and build-buildlog page partially.
404
        # UTC_NOW for the transaction, avoid several row with
405
        # same datecreated.
7675.687.48 by Michael Nelson
Initial update of binarypackagebuild to use new table, replaced calculated_buildstart with build_started.
406
        date_created = datetime.datetime.now(pytz.timezone('UTC'))
3023.2.24 by Celso Providelo
Fix Build.datecreated and build-buildlog page partially.
407
7675.687.48 by Michael Nelson
Initial update of binarypackagebuild to use new table, replaced calculated_buildstart with build_started.
408
        return getUtility(IBinaryPackageBuildSet).new(
409
            distro_arch_series=distro_arch_series,
410
            source_package_release=self,
411
            processor=processor,
412
            status=status,
413
            date_created=date_created,
414
            pocket=pocket,
415
            archive=archive)
2713 by Canonical.com Patch Queue Manager
Various fixes to queue, build etc. add zcml for queue, DB patch included. r=stevea,stub. Also fix dbschema security proxy bug (bug 1971) r=stevea
416
4285.2.1 by Mark Shuttleworth
Massive renaming of distrorelease to distroseries
417
    def getBuildByArch(self, distroarchseries, archive):
2755 by Canonical.com Patch Queue Manager
[r=stevea] Fixing bug # 2812, loading zcml info in builddmaster and consequently removing the database imports, fixing bug # 1305 by using standards in both buildd cronscripts.
418
        """See ISourcePackageRelease."""
6125.2.3 by Celso Providelo
applying review comments, r=bigjools.
419
        # First we try to follow any binaries built from the given source
6125.2.1 by Celso Providelo
Fixing bug #219828. Using a better approach to identify builds built and published in the same architecture (BPR.architecturespecific it's not exaclty the same).
420
        # in a distroarchseries with the given architecturetag and published
421
        # in the given (distroarchseries, archive) location.
5152.6.4 by Celso Providelo
Fixing bug #181736 (Erroneously creating build for source+binary copied across PPAs). Now, first we follow the build_path related to the published binaries related to the given source.
422
        clauseTables = [
6125.2.1 by Celso Providelo
Fixing bug #219828. Using a better approach to identify builds built and published in the same architecture (BPR.architecturespecific it's not exaclty the same).
423
            'BinaryPackagePublishingHistory', 'BinaryPackageRelease',
424
            'DistroArchSeries']
5152.6.4 by Celso Providelo
Fixing bug #181736 (Erroneously creating build for source+binary copied across PPAs). Now, first we follow the build_path related to the published binaries related to the given source.
425
426
        query = """
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
427
            BinaryPackageBuild.source_package_release = %s AND
428
            BinaryPackageRelease.build = BinaryPackageBuild.id AND
429
            DistroArchSeries.id = BinaryPackageBuild.distro_arch_series AND
6125.2.1 by Celso Providelo
Fixing bug #219828. Using a better approach to identify builds built and published in the same architecture (BPR.architecturespecific it's not exaclty the same).
430
            DistroArchSeries.architecturetag = %s AND
5152.6.4 by Celso Providelo
Fixing bug #181736 (Erroneously creating build for source+binary copied across PPAs). Now, first we follow the build_path related to the published binaries related to the given source.
431
            BinaryPackagePublishingHistory.binarypackagerelease =
432
                BinaryPackageRelease.id AND
433
            BinaryPackagePublishingHistory.distroarchseries = %s AND
434
            BinaryPackagePublishingHistory.archive = %s
6125.2.1 by Celso Providelo
Fixing bug #219828. Using a better approach to identify builds built and published in the same architecture (BPR.architecturespecific it's not exaclty the same).
435
        """ % sqlvalues(self, distroarchseries.architecturetag,
436
                        distroarchseries, archive)
5152.6.4 by Celso Providelo
Fixing bug #181736 (Erroneously creating build for source+binary copied across PPAs). Now, first we follow the build_path related to the published binaries related to the given source.
437
10667.2.3 by Michael Nelson
Updated model.binarypackagebuild.
438
        select_results = BinaryPackageBuild.select(
5152.6.9 by Celso Providelo
Workaround the fact that SelectOne does not support 'distinct' in SPR.getBuildByArch.
439
            query, clauseTables=clauseTables, distinct=True,
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
440
            orderBy='-BinaryPackageBuild.id')
5152.6.9 by Celso Providelo
Workaround the fact that SelectOne does not support 'distinct' in SPR.getBuildByArch.
441
442
        # XXX cprov 20080216: this if/elif/else block could be avoided or,
443
        # at least, simplified if SelectOne accepts 'distinct' argument.
444
        # The query above results in multiple identical builds for ..
445
        results = list(select_results)
446
        if len(results) == 1:
447
            # If there was any published binary we can use its original build.
448
            # This case covers the situations when both, source and binaries
449
            # got copied from another location.
450
            return results[0]
451
        elif len(results) > 1:
452
            # If more than one distinct build was found we have a problem.
453
            # A build was created when it shouldn't, possible due to bug
454
            # #181736. The broken build should be manually removed.
455
            raise AssertionError(
456
                    "Found more than one build candidate: %s. It possibly "
457
                    "means we have a serious problem in out DB model, "
458
                    "further investigation is required." %
459
                    [build.id for build in results])
460
        else:
461
            # If there was no published binary we have to try to find a
462
            # suitable build in all possible location across the distroseries
463
            # inheritance tree. See bellow.
464
            pass
465
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
466
        queries = [
467
            "BinaryPackageBuild.package_build = PackageBuild.id AND "
468
            "PackageBuild.build_farm_job = BuildFarmJob.id AND "
12965.2.1 by Julian Edwards
getBuildsByArch() no longer relies on parent_series
469
            "DistroArchSeries.id = BinaryPackageBuild.distro_arch_series AND "
470
            "PackageBuild.archive = %s AND "
471
            "DistroArchSeries.architecturetag = %s AND "
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
472
            "BinaryPackageBuild.source_package_release = %s" % (
12965.2.1 by Julian Edwards
getBuildsByArch() no longer relies on parent_series
473
            sqlvalues(archive.id, distroarchseries.architecturetag, self))]
474
5152.6.4 by Celso Providelo
Fixing bug #181736 (Erroneously creating build for source+binary copied across PPAs). Now, first we follow the build_path related to the published binaries related to the given source.
475
        # Query only the last build record for this sourcerelease
4918.1.1 by Celso Providelo
Fix #65712 (SPR.getBuildByArch was not accurate for architecture dependend builds and P-a-s). Also land a prelimiar fix that allow correct results without requiring us to fix our sampledata (removing duplicated/impossible build records).
476
        # across all possible locations.
477
        query = " AND ".join(queries)
478
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
479
        return BinaryPackageBuild.selectFirst(
12965.2.1 by Julian Edwards
getBuildsByArch() no longer relies on parent_series
480
            query, clauseTables=[
481
                'BuildFarmJob', 'PackageBuild', 'DistroArchSeries'],
7675.687.60 by Michael Nelson
Updated getBuildByArch() to match new tables.
482
            orderBy=['-BuildFarmJob.date_created'])
2755 by Canonical.com Patch Queue Manager
[r=stevea] Fixing bug # 2812, loading zcml info in builddmaster and consequently removing the database imports, fixing bug # 1305 by using standards in both buildd cronscripts.
483
2865.2.32 by Celso Providelo
Landing redesigned ftp-master/queue.py, sample deb packages for soyuz-uploads and missed test file for binarypackagerelease.
484
    def override(self, component=None, section=None, urgency=None):
485
        """See ISourcePackageRelease."""
3048.5.25 by Celso Providelo
Fix bug # 30621 (missed author's line in changelog) and statement style in SPR/BPR override method, testing not passing.
486
        if component is not None:
2865.2.32 by Celso Providelo
Landing redesigned ftp-master/queue.py, sample deb packages for soyuz-uploads and missed test file for binarypackagerelease.
487
            self.component = component
4376.2.22 by Julian Edwards
Fix overrides in the queue tool so that component changes that change the
488
            # See if the new component requires a new archive:
5121.2.6 by Stuart Bishop
Some required code updates
489
            distribution = self.upload_distroseries.distribution
4376.2.54 by Julian Edwards
IArchive.getByDistroComponent is now IDistribution.getArchiveByComponent
490
            new_archive = distribution.getArchiveByComponent(component.name)
4376.2.22 by Julian Edwards
Fix overrides in the queue tool so that component changes that change the
491
            if new_archive is not None:
492
                self.upload_archive = new_archive
4376.2.52 by Julian Edwards
Pre-publication overrides of source packages should abort if the override
493
            else:
494
                raise QueueActionError(
495
                    "New component '%s' requires a non-existent archive.")
3048.5.25 by Celso Providelo
Fix bug # 30621 (missed author's line in changelog) and statement style in SPR/BPR override method, testing not passing.
496
        if section is not None:
2865.2.32 by Celso Providelo
Landing redesigned ftp-master/queue.py, sample deb packages for soyuz-uploads and missed test file for binarypackagerelease.
497
            self.section = section
3048.5.25 by Celso Providelo
Fix bug # 30621 (missed author's line in changelog) and statement style in SPR/BPR override method, testing not passing.
498
        if urgency is not None:
2865.2.32 by Celso Providelo
Landing redesigned ftp-master/queue.py, sample deb packages for soyuz-uploads and missed test file for binarypackagerelease.
499
            self.urgency = urgency
2755 by Canonical.com Patch Queue Manager
[r=stevea] Fixing bug # 2812, loading zcml info in builddmaster and consequently removing the database imports, fixing bug # 1305 by using standards in both buildd cronscripts.
500
5099.2.2 by Christian Robottom Reis
PPA pages fix part 2. Fix for bug #157102, apt lines in PPA page are wrong. He was right and I had to add a JS control to actually allow dynamic updating of them. Also include a link to the changes file in the package listing.
501
    @property
502
    def upload_changesfile(self):
6300.2.1 by Julian Edwards
Ensure changesfiles are unembargoed by the unembargoing script.
503
        """See `ISourcePackageRelease`."""
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
504
        package_upload = self.package_upload
505
        # Cope with `SourcePackageRelease`s imported by gina, they do not
506
        # have a corresponding `PackageUpload` record.
507
        if package_upload is None:
6300.2.1 by Julian Edwards
Ensure changesfiles are unembargoed by the unembargoing script.
508
            return None
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
509
        return package_upload.changesfile
510
511
    @property
512
    def package_upload(self):
6300.2.1 by Julian Edwards
Ensure changesfiles are unembargoed by the unembargoing script.
513
        """See `ISourcepackageRelease`."""
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
514
        store = Store.of(self)
8688.1.4 by Celso Providelo
new comment versions on PU.changesfile JOIN, it's complex.
515
        # The join on 'changesfile' is not only used only for
516
        # pre-fetching the corresponding library file, so callsites
517
        # don't have to issue an extra query. It is also important
518
        # for excluding delayed-copies, because they might match
519
        # the publication context but will not contain as changesfile.
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
520
        origin = [
521
            PackageUploadSource,
522
            Join(PackageUpload,
523
                 PackageUploadSource.packageuploadID == PackageUpload.id),
524
            Join(LibraryFileAlias,
525
                 LibraryFileAlias.id == PackageUpload.changesfileID),
526
            Join(LibraryFileContent,
527
                 LibraryFileContent.id == LibraryFileAlias.contentID),
5099.2.2 by Christian Robottom Reis
PPA pages fix part 2. Fix for bug #157102, apt lines in PPA page are wrong. He was right and I had to add a JS control to actually allow dynamic updating of them. Also include a link to the changes file in the package listing.
528
            ]
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
529
        results = store.using(*origin).find(
530
            (PackageUpload, LibraryFileAlias, LibraryFileContent),
531
            PackageUploadSource.sourcepackagerelease == self,
532
            PackageUpload.archive == self.upload_archive,
533
            PackageUpload.distroseries == self.upload_distroseries)
534
8537.6.5 by Celso Providelo
applying review comments, r=allenap.
535
        # Return the unique `PackageUpload` record that corresponds to the
536
        # upload of this `SourcePackageRelease`, load the `LibraryFileAlias`
8537.6.1 by Celso Providelo
Re-implementing the PackageUpload lookups on SourcePackageRelese and Build in preparation to soyuz-delayed-copies.
537
        # and the `LibraryFileContent` in cache because it's most likely
538
        # they will be needed.
8537.6.3 by Celso Providelo
Locking PackageUpload lookups to expect a single result and add a doctests checking it against the current sampledata.
539
        return DecoratedResultSet(results, operator.itemgetter(0)).one()
5099.2.2 by Christian Robottom Reis
PPA pages fix part 2. Fix for bug #157102, apt lines in PPA page are wrong. He was right and I had to add a JS control to actually allow dynamic updating of them. Also include a link to the changes file in the package listing.
540
4363.1.1 by Mark Shuttleworth
Start improving the distrosourcepackage overview
541
    @property
11239.2.3 by Paul Hummer
Fixed the failing test
542
    def uploader(self):
11239.2.10 by Paul Hummer
Refactored ISourcePackageRelease.uploader
543
        """See `ISourcePackageRelease`"""
11239.2.9 by Paul Hummer
Fixed a test
544
        if self.source_package_recipe_build is not None:
545
            return self.source_package_recipe_build.requester
11239.2.10 by Paul Hummer
Refactored ISourcePackageRelease.uploader
546
        if self.dscsigningkey is not None:
547
            return self.dscsigningkey.owner
548
        return None
11239.2.3 by Paul Hummer
Fixed the failing test
549
550
    @property
4363.1.1 by Mark Shuttleworth
Start improving the distrosourcepackage overview
551
    def change_summary(self):
552
        """See ISourcePackageRelease"""
4363.1.10 by Mark Shuttleworth
Show sequence of versions with changes by default for source package
553
        # this regex is copied from apt-listchanges.py courtesy of MDZ
4363.1.17 by Mark Shuttleworth
Address review comments for r=kiko
554
        new_stanza_line = re.compile(
555
            '^\S+ \((?P<version>.*)\) .*;.*urgency=(?P<urgency>\w+).*')
5398.6.1 by Julian Edwards
Amend sourcepackagerelease.changelog to be changelog_entry to more accurately
556
        logfile = StringIO(self.changelog_entry)
4363.1.10 by Mark Shuttleworth
Show sequence of versions with changes by default for source package
557
        change = ''
558
        top_stanza = False
559
        for line in logfile.readlines():
560
            match = new_stanza_line.match(line)
561
            if match:
562
                if top_stanza:
563
                    break
564
                top_stanza = True
565
            change += line
566
567
        return change
4363.1.1 by Mark Shuttleworth
Start improving the distrosourcepackage overview
568
7675.916.98 by Henning Eggers
Merged db-stable at r10026 (recife roll-back) but without accepting the changes.
569
    def attachTranslationFiles(self, tarball_alias, by_maintainer,
8963.3.1 by Celso Providelo
Fixing bug #400944 (extending the translation-source binding code to cope with restricted librarian files).
570
                               importer=None):
2570.1.23 by Carlos Perelló Marín
Integrated distribution translation imports into the buildd
571
        """See ISourcePackageRelease."""
8963.3.1 by Celso Providelo
Fixing bug #400944 (extending the translation-source binding code to cope with restricted librarian files).
572
        tarball = tarball_alias.read()
2570.1.23 by Carlos Perelló Marín
Integrated distribution translation imports into the buildd
573
574
        if importer is None:
5228.1.1 by Christian Robottom Reis
Rename rosetta_expert to rosetta_experts.
575
            importer = getUtility(ILaunchpadCelebrities).rosetta_experts
2570.1.23 by Carlos Perelló Marín
Integrated distribution translation imports into the buildd
576
7117.4.1 by Jeroen Vermeulen
First step: make Soyuz' attachTranslationFiles() use TranslationImportQueue.addOrUpdateEntriesFromTarball() instead of duplicating its work.
577
        queue = getUtility(ITranslationImportQueue)
2570.1.23 by Carlos Perelló Marín
Integrated distribution translation imports into the buildd
578
13444.6.40 by Gavin Panella
Fix lint.
579
        only_templates = self.sourcepackage.has_sharing_translation_templates
12622.6.41 by Henning Eggers
Fix the bug.
580
        queue.addOrUpdateEntriesFromTarball(
581
            tarball, by_maintainer, importer,
582
            sourcepackagename=self.sourcepackagename,
583
            distroseries=self.upload_distroseries,
584
            filename_filter=_filter_ubuntu_translation_file,
585
            only_templates=only_templates)
2570.1.23 by Carlos Perelló Marín
Integrated distribution translation imports into the buildd
586
5568.2.9 by Celso Providelo
applying review comment, r=sinzui.
587
    def getDiffTo(self, to_sourcepackagerelease):
588
        """See ISourcePackageRelease."""
589
        return PackageDiff.selectOneBy(
590
            from_source=self, to_source=to_sourcepackagerelease)
591
592
    def requestDiffTo(self, requester, to_sourcepackagerelease):
593
        """See ISourcePackageRelease."""
594
        candidate = self.getDiffTo(to_sourcepackagerelease)
595
596
        if candidate is not None:
597
            raise PackageDiffAlreadyRequested(
5568.2.11 by Celso Providelo
applying review comments, r=sinzui.
598
                "%s was already requested by %s"
599
                % (candidate.title, candidate.requester.displayname))
5568.2.9 by Celso Providelo
applying review comment, r=sinzui.
600
7675.324.2 by Julian Edwards
Don't attempt diffs for the udev package, as they will currently fail and fill the disk with output.
601
        if self.sourcepackagename.name == 'udev':
7675.324.3 by Julian Edwards
add dates to XXXes
602
            # XXX 2009-11-23 Julian bug=314436
7675.324.2 by Julian Edwards
Don't attempt diffs for the udev package, as they will currently fail and fill the disk with output.
603
            # Currently diff output for udev will fill disks.  It's
604
            # disabled until diffutils is fixed in that bug.
605
            status = PackageDiffStatus.FAILED
606
        else:
607
            status = PackageDiffStatus.PENDING
608
5568.2.6 by Celso Providelo
Finishing PackageDiff content classes, interfaces, zcml and minimal tests.
609
        return PackageDiff(
5568.2.9 by Celso Providelo
applying review comment, r=sinzui.
610
            from_source=self, to_source=to_sourcepackagerelease,
7675.324.2 by Julian Edwards
Don't attempt diffs for the udev package, as they will currently fail and fill the disk with output.
611
            requester=requester, status=status)
13755.2.15 by Julian Edwards
re-instate backed out changes and move the aggregate_changelog method to SPPH and make it parse changelog instead of iterating over many changelog_entry
612
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
613
    def aggregate_changelog(self, since_version):
614
        """See `ISourcePackagePublishingHistory`."""
615
        if self.changelog is None:
616
            return None
617
618
        apt_pkg.InitSystem()
13755.2.25 by Julian Edwards
Remove changelog trailer lines and fix spurious whitespace.
619
        chunks = []
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
620
        changelog = self.changelog
13755.2.22 by Julian Edwards
Lint and: "<jtv> Maybe this just needs a good curse in the comments."
621
        # The python-debian API for parsing changelogs is pretty awful. The
622
        # only useful way of extracting info is to use the iterator on
623
        # Changelog and then compare versions.
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
624
        try:
625
            for block in Changelog(changelog.read()):
626
                version = block._raw_version
13755.2.19 by Julian Edwards
Final fixes. Hopefully this one won't get wgranted.
627
                if (since_version and
13755.2.22 by Julian Edwards
Lint and: "<jtv> Maybe this just needs a good curse in the comments."
628
                    apt_pkg.VersionCompare(version, since_version) <= 0):
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
629
                    break
13755.2.25 by Julian Edwards
Remove changelog trailer lines and fix spurious whitespace.
630
                # Poking in private attributes is not nice but again the
631
                # API is terrible.  We want to ensure that the name/date
632
                # line is omitted from these composite changelogs.
633
                block._no_trailer = True
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
634
                try:
13755.2.25 by Julian Edwards
Remove changelog trailer lines and fix spurious whitespace.
635
                    # python-debian adds an extra blank line to the chunks
636
                    # so we'll have to sort this out.
637
                    chunks.append(str(block).rstrip())
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
638
                except ChangelogCreateError:
639
                    continue
13755.2.19 by Julian Edwards
Final fixes. Hopefully this one won't get wgranted.
640
                if not since_version:
641
                    # If a particular version was not requested we just
642
                    # return the most recent changelog entry.
643
                    break
13755.2.16 by Julian Edwards
Well that was stupid, move it back to SPR.
644
        except ChangelogParseError:
645
            return None
646
13755.2.25 by Julian Edwards
Remove changelog trailer lines and fix spurious whitespace.
647
        output = "\n\n".join(chunks)
13755.2.24 by Julian Edwards
Make aggregate_changelog cope with utf8 changelogs.
648
        return output.decode("utf-8", "replace")