launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #17464
[Merge] lp:~wgrant/launchpad/indices-to-archivepublisher into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/indices-to-archivepublisher into lp:launchpad.
Commit message:
Move the index stanza build methods from the publishing classes to lp.archivepublisher.indices.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/indices-to-archivepublisher/+merge/240260
Despite living in the 2000-line lp.soyuz.model.publishing module, the apt index stanza generation methods are used by a single callsite each, all the way over in lp.archivepublisher.publishing. Let's move them nearby to try to make things a bit less monolithic.
--
https://code.launchpad.net/~wgrant/launchpad/indices-to-archivepublisher/+merge/240260
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/indices-to-archivepublisher into lp:launchpad.
=== added file 'lib/lp/archivepublisher/indices.py'
--- lib/lp/archivepublisher/indices.py 1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/indices.py 2014-10-31 13:23:01 +0000
@@ -0,0 +1,236 @@
+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__all__ = [
+ 'IndexStanzaFields',
+ 'build_binary_stanza_fields',
+ 'build_source_stanza_fields',
+ 'build_translations_stanza_fields',
+ ]
+
+__metaclass__ = type
+
+import hashlib
+import os.path
+import re
+
+from lp.soyuz.model.publishing import makePoolPath
+
+
+class IndexStanzaFields:
+ """Store and format ordered Index Stanza fields."""
+
+ def __init__(self):
+ self._names_lower = set()
+ self.fields = []
+
+ def append(self, name, value):
+ """Append an (field, value) tuple to the internal list.
+
+ Then we can use the FIFO-like behaviour in makeOutput().
+ """
+ if name.lower() in self._names_lower:
+ return
+ self._names_lower.add(name.lower())
+ self.fields.append((name, value))
+
+ def extend(self, entries):
+ """Extend the internal list with the key-value pairs in entries.
+ """
+ for name, value in entries:
+ self.append(name, value)
+
+ def makeOutput(self):
+ """Return a line-by-line aggregation of appended fields.
+
+ Empty fields values will cause the exclusion of the field.
+ The output order will preserve the insertion order, FIFO.
+ """
+ output_lines = []
+ for name, value in self.fields:
+ if not value:
+ continue
+
+ # do not add separation space for the special file list fields.
+ if name not in ('Files', 'Checksums-Sha1', 'Checksums-Sha256'):
+ value = ' %s' % value
+
+ # XXX Michael Nelson 20090930 bug=436182. We have an issue
+ # in the upload parser that has
+ # 1. introduced '\n' at the end of multiple-line-spanning
+ # fields, such as dsc_binaries, but potentially others,
+ # 2. stripped the leading space from each subsequent line
+ # of dsc_binaries values that span multiple lines.
+ # This is causing *incorrect* Source indexes to be created.
+ # This work-around can be removed once the fix for bug 436182
+ # is in place and the tainted data has been cleaned.
+ # First, remove any trailing \n or spaces.
+ value = value.rstrip()
+
+ # Second, as we have corrupt data where subsequent lines
+ # of values spanning multiple lines are not preceded by a
+ # space, we ensure that any \n in the value that is *not*
+ # followed by a white-space character has a space inserted.
+ value = re.sub(r"\n(\S)", r"\n \1", value)
+
+ output_lines.append('%s:%s' % (name, value))
+
+ return '\n'.join(output_lines)
+
+
+def format_file_list(l):
+ return ''.join('\n %s %s %s' % ((h,) + f) for (h, f) in l)
+
+
+def format_description(summary, description):
+ # description field in index is an association of summary and
+ # description or the summary only if include_long_descriptions
+ # is false, as:
+ #
+ # Descrition: <SUMMARY>\n
+ # <DESCRIPTION L1>
+ # ...
+ # <DESCRIPTION LN>
+ descr_lines = [line.lstrip() for line in description.splitlines()]
+ bin_description = '%s\n %s' % (summary, '\n '.join(descr_lines))
+ return bin_description
+
+
+def build_source_stanza_fields(spr, component, section):
+ """See `IPublishing`."""
+ # Special fields preparation.
+ pool_path = makePoolPath(spr.name, component.name)
+ files_list = []
+ sha1_list = []
+ sha256_list = []
+ for spf in spr.files:
+ common = (
+ spf.libraryfile.content.filesize, spf.libraryfile.filename)
+ files_list.append((spf.libraryfile.content.md5, common))
+ sha1_list.append((spf.libraryfile.content.sha1, common))
+ sha256_list.append((spf.libraryfile.content.sha256, common))
+ # Filling stanza options.
+ fields = IndexStanzaFields()
+ fields.append('Package', spr.name)
+ fields.append('Binary', spr.dsc_binaries)
+ fields.append('Version', spr.version)
+ fields.append('Section', section.name)
+ fields.append('Maintainer', spr.dsc_maintainer_rfc822)
+ fields.append('Build-Depends', spr.builddepends)
+ fields.append('Build-Depends-Indep', spr.builddependsindep)
+ fields.append('Build-Conflicts', spr.build_conflicts)
+ fields.append('Build-Conflicts-Indep', spr.build_conflicts_indep)
+ fields.append('Architecture', spr.architecturehintlist)
+ fields.append('Standards-Version', spr.dsc_standards_version)
+ fields.append('Format', spr.dsc_format)
+ fields.append('Directory', pool_path)
+ fields.append('Files', format_file_list(files_list))
+ fields.append('Checksums-Sha1', format_file_list(sha1_list))
+ fields.append('Checksums-Sha256', format_file_list(sha256_list))
+ fields.append('Homepage', spr.homepage)
+ if spr.user_defined_fields:
+ fields.extend(spr.user_defined_fields)
+
+ return fields
+
+
+def build_binary_stanza_fields(bpr, component, section, priority,
+ phased_update_percentage,
+ separate_long_descriptions=False):
+ """See `IPublishing`."""
+ spr = bpr.build.source_package_release
+
+ # binaries have only one file, the DEB
+ bin_file = bpr.files[0]
+ bin_filename = bin_file.libraryfile.filename
+ bin_size = bin_file.libraryfile.content.filesize
+ bin_md5 = bin_file.libraryfile.content.md5
+ bin_sha1 = bin_file.libraryfile.content.sha1
+ bin_sha256 = bin_file.libraryfile.content.sha256
+ bin_filepath = os.path.join(
+ makePoolPath(spr.name, component.name), bin_filename)
+ description = format_description(bpr.summary, bpr.description)
+ # Our formatted description isn't \n-terminated, but apt
+ # considers the trailing \n to be part of the data to hash.
+ bin_description_md5 = hashlib.md5(
+ description.encode('utf-8') + '\n').hexdigest()
+ if separate_long_descriptions:
+ # If distroseries.include_long_descriptions is False, the
+ # description should be the summary
+ bin_description = bpr.summary
+ else:
+ bin_description = description
+
+ # Dealing with architecturespecific field.
+ # Present 'all' in every archive index for architecture
+ # independent binaries.
+ if bpr.architecturespecific:
+ architecture = bpr.build.distro_arch_series.architecturetag
+ else:
+ architecture = 'all'
+
+ essential = None
+ if bpr.essential:
+ essential = 'yes'
+
+ source = None
+ if bpr.version != spr.version:
+ source = '%s (%s)' % (spr.name, spr.version)
+ elif bpr.name != spr.name:
+ source = spr.name
+
+ fields = IndexStanzaFields()
+ fields.append('Package', bpr.name)
+ fields.append('Source', source)
+ fields.append('Priority', priority.title.lower())
+ fields.append('Section', section.name)
+ fields.append('Installed-Size', bpr.installedsize)
+ fields.append('Maintainer', spr.dsc_maintainer_rfc822)
+ fields.append('Architecture', architecture)
+ fields.append('Version', bpr.version)
+ fields.append('Recommends', bpr.recommends)
+ fields.append('Replaces', bpr.replaces)
+ fields.append('Suggests', bpr.suggests)
+ fields.append('Provides', bpr.provides)
+ fields.append('Depends', bpr.depends)
+ fields.append('Conflicts', bpr.conflicts)
+ fields.append('Pre-Depends', bpr.pre_depends)
+ fields.append('Enhances', bpr.enhances)
+ fields.append('Breaks', bpr.breaks)
+ fields.append('Essential', essential)
+ fields.append('Filename', bin_filepath)
+ fields.append('Size', bin_size)
+ fields.append('MD5sum', bin_md5)
+ fields.append('SHA1', bin_sha1)
+ fields.append('SHA256', bin_sha256)
+ fields.append('Phased-Update-Percentage', phased_update_percentage)
+ fields.append('Description', bin_description)
+ if separate_long_descriptions:
+ fields.append('Description-md5', bin_description_md5)
+ if bpr.user_defined_fields:
+ fields.extend(bpr.user_defined_fields)
+
+ # XXX cprov 2006-11-03: the extra override fields (Bugs, Origin and
+ # Task) included in the template be were not populated.
+ # When we have the information this will be the place to fill them.
+
+ return fields
+
+
+def build_translations_stanza_fields(bpr, packages):
+ """See `IPublishing`."""
+ bin_description = format_description(bpr.summary, bpr.description)
+ # Our formatted description isn't \n-terminated, but apt
+ # considers the trailing \n to be part of the data to hash.
+ bin_description_md5 = hashlib.md5(
+ bin_description.encode('utf-8') + '\n').hexdigest()
+ if (bpr.name, bin_description_md5) not in packages:
+ fields = IndexStanzaFields()
+ fields.append('Package', bpr.name)
+ fields.append('Description-md5', bin_description_md5)
+ fields.append('Description-en', bin_description)
+ packages.add((bpr.name, bin_description_md5))
+
+ return fields
+ else:
+ return None
=== modified file 'lib/lp/archivepublisher/publishing.py'
--- lib/lp/archivepublisher/publishing.py 2014-10-29 14:08:19 +0000
+++ lib/lp/archivepublisher/publishing.py 2014-10-31 13:23:01 +0000
@@ -39,6 +39,11 @@
write_htaccess,
write_htpasswd,
)
+from lp.archivepublisher.indices import (
+ build_binary_stanza_fields,
+ build_source_stanza_fields,
+ build_translations_stanza_fields,
+ )
from lp.archivepublisher.interfaces.archivesigningkey import (
IArchiveSigningKey,
)
@@ -645,8 +650,9 @@
if (not distroseries.include_long_descriptions and
getFeatureFlag("soyuz.ppa.separate_long_descriptions")):
# If include_long_descriptions is False and the feature flag is
- # enabled, create a Translation-en file. getIndexStanza() will
- # also omit long descriptions from the Packages.
+ # enabled, create a Translation-en file.
+ # build_binary_stanza_fields will also omit long descriptions
+ # from the Packages.
separate_long_descriptions = True
packages = set()
translation_en = RepositoryIndexFile(
@@ -660,8 +666,9 @@
for spp in distroseries.getSourcePackagePublishing(
pocket, component, self.archive):
- stanza = spp.getIndexStanza().encode('utf8') + '\n\n'
- source_index.write(stanza)
+ stanza = build_source_stanza_fields(
+ spp.sourcepackagerelease, spp.component, spp.section)
+ source_index.write(stanza.makeOutput().encode('utf-8') + '\n\n')
source_index.close()
@@ -693,19 +700,24 @@
# for, eg. ddebs where publish_debug_symbols is
# disabled.
continue
- stanza = bpp.getIndexStanza(separate_long_descriptions).encode(
- 'utf-8') + '\n\n'
- indices[subcomp].write(stanza)
+ stanza = build_binary_stanza_fields(
+ bpp.binarypackagerelease, bpp.component, bpp.section,
+ bpp.priority, bpp.phased_update_percentage,
+ separate_long_descriptions)
+ indices[subcomp].write(
+ stanza.makeOutput().encode('utf-8') + '\n\n')
if separate_long_descriptions:
- # If the (Package, Description-md5) pair already exists in
- # the set, getTranslationsStanza will return None.
- # Otherwise it will add the pair to the set and return a
- # stanza to be written to Translation-en.
- translation_stanza = bpp.getTranslationsStanza(packages)
- if translation_stanza:
- translation_stanza = translation_stanza.encode(
- 'utf-8') + '\n\n'
- translation_en.write(translation_stanza)
+ # If the (Package, Description-md5) pair already exists
+ # in the set, build_translations_stanza_fields will
+ # return None. Otherwise it will add the pair to
+ # the set and return a stanza to be written to
+ # Translation-en.
+ translation_stanza = build_translations_stanza_fields(
+ bpp.binarypackagerelease, packages)
+ if translation_stanza is not None:
+ translation_en.write(
+ translation_stanza.makeOutput().encode('utf-8')
+ + '\n\n')
for index in indices.itervalues():
index.close()
=== renamed file 'lib/lp/soyuz/tests/test_publish_archive_indexes.py' => 'lib/lp/archivepublisher/tests/test_indices.py'
--- lib/lp/soyuz/tests/test_publish_archive_indexes.py 2013-06-18 05:40:26 +0000
+++ lib/lp/archivepublisher/tests/test_indices.py 2014-10-31 13:23:01 +0000
@@ -9,10 +9,27 @@
import apt_pkg
-from lp.soyuz.model.publishing import IndexStanzaFields
+from lp.archivepublisher.indices import (
+ build_binary_stanza_fields,
+ build_source_stanza_fields,
+ IndexStanzaFields,
+ )
from lp.soyuz.tests.test_publishing import TestNativePublishingBase
+def build_bpph_stanza(bpph):
+ return build_binary_stanza_fields(
+ bpph.binarypackagerelease, bpph.component, bpph.section,
+ bpph.priority, bpph.phased_update_percentage,
+ False)
+
+
+def build_spph_stanza(spph):
+ return build_source_stanza_fields(
+ spph.sourcepackagerelease, spph.component,
+ spph.section)
+
+
def get_field(stanza_fields, name):
return dict(stanza_fields.fields).get(name)
@@ -70,7 +87,7 @@
u'Checksums-Sha256:',
u' %s 28 foo_666.dsc' % self.dsc_sha256,
],
- pub_source.getIndexStanza().splitlines())
+ build_spph_stanza(pub_source).makeOutput().splitlines())
def testSourceStanzaCustomFields(self):
"""Check just-created source publication Index stanza
@@ -108,7 +125,7 @@
u'Checksums-Sha256:',
u' %s 28 foo_666.dsc' % self.dsc_sha256,
u'Python-Version: < 1.5'],
- pub_source.getIndexStanza().splitlines())
+ build_spph_stanza(pub_source).makeOutput().splitlines())
def testBinaryStanza(self):
"""Check just-created binary publication Index stanza.
@@ -149,7 +166,7 @@
u'Description: Foo app is great',
u' Well ...',
u' it does nothing, though'],
- pub_binary.getIndexStanza().splitlines())
+ build_bpph_stanza(pub_binary).makeOutput().splitlines())
def testBinaryStanzaWithCustomFields(self):
"""Check just-created binary publication Index stanza with
@@ -189,7 +206,7 @@
u' Well ...',
u' it does nothing, though',
u'Python-Version: >= 2.4'],
- pub_binary.getIndexStanza().splitlines())
+ build_bpph_stanza(pub_binary).makeOutput().splitlines())
def testBinaryStanzaDescription(self):
""" Check the description field.
@@ -240,7 +257,7 @@
u' .',
u' %s' % ('x' * 100),
],
- pub_binary.getIndexStanza().splitlines())
+ build_bpph_stanza(pub_binary).makeOutput().splitlines())
def testBinaryStanzaWithNonAscii(self):
"""Check how will be a stanza with non-ascii content
@@ -272,7 +289,7 @@
u'Description: Foo app is great',
u' Using non-ascii as: \xe7\xe3\xe9\xf3',
],
- pub_binary.getIndexStanza().splitlines())
+ build_bpph_stanza(pub_binary).makeOutput().splitlines())
def testBinaryOmitsIdenticalSourceName(self):
# Binaries omit the Source field if it identical to Package.
@@ -281,7 +298,7 @@
binaryname='foo', pub_source=pub_source)[0]
self.assertIs(
None,
- get_field(pub_binary.buildIndexStanzaFields(), 'Source'))
+ get_field(build_bpph_stanza(pub_binary), 'Source'))
def testBinaryIncludesDifferingSourceName(self):
# Binaries include a Source field if their name differs.
@@ -290,7 +307,7 @@
binaryname='foo-bin', pub_source=pub_source)[0]
self.assertEqual(
u'foo',
- get_field(pub_binary.buildIndexStanzaFields(), 'Source'))
+ get_field(build_bpph_stanza(pub_binary), 'Source'))
def testBinaryIncludesDifferingSourceVersion(self):
# Binaries also include a Source field if their versions differ.
@@ -299,7 +316,7 @@
binaryname='foo', version='999', pub_source=pub_source)[0]
self.assertEqual(
u'foo (666)',
- get_field(pub_binary.buildIndexStanzaFields(), 'Source'))
+ get_field(build_bpph_stanza(pub_binary), 'Source'))
class TestNativeArchiveIndexesReparsing(TestNativePublishingBase):
@@ -316,7 +333,7 @@
"""Helper method to return the apt_pkg parser for the stanza."""
index_filename = tempfile.mktemp()
index_file = open(index_filename, 'w')
- index_file.write(stanza.encode('utf-8'))
+ index_file.write(stanza.makeOutput().encode('utf-8'))
index_file.close()
parser = apt_pkg.TagFile(open(index_filename))
@@ -328,27 +345,27 @@
return section
- def test_getIndexStanza_binary_stanza(self):
+ def test_binary_stanza(self):
"""Check a binary stanza with APT parser."""
pub_binary = self.getPubBinaries()[0]
- section = self.write_stanza_and_reparse(pub_binary.getIndexStanza())
+ section = self.write_stanza_and_reparse(build_bpph_stanza(pub_binary))
self.assertEqual(section.get('Package'), 'foo-bin')
self.assertEqual(
section.get('Description').splitlines(),
['Foo app is great', ' Well ...', ' it does nothing, though'])
- def test_getIndexStanza_source_stanza(self):
+ def test_source_stanza(self):
"""Check a source stanza with APT parser."""
pub_source = self.getPubSource()
- section = self.write_stanza_and_reparse(pub_source.getIndexStanza())
+ section = self.write_stanza_and_reparse(build_spph_stanza(pub_source))
self.assertEqual(section.get('Package'), 'foo')
self.assertEqual(section.get('Maintainer'), 'Foo Bar <foo@xxxxxxx>')
- def test_getIndexStanza_with_corrupt_dsc_binaries(self):
+ def test_source_with_corrupt_dsc_binaries(self):
"""Ensure corrupt binary fields are written correctly to indexes.
This is a regression test for bug 436182.
@@ -378,7 +395,7 @@
pub_source.sourcepackagerelease.dsc_binaries = (
'foo_bin,\nbar_bin,\nzed_bin')
- section = self.write_stanza_and_reparse(pub_source.getIndexStanza())
+ section = self.write_stanza_and_reparse(build_spph_stanza(pub_source))
self.assertEqual('foo', section['Package'])
@@ -391,7 +408,7 @@
# Without the fix, the second binary would not be parsed at all.
self.assertEqual('foo_bin,\n bar_bin,\n zed_bin', section['Binary'])
- def test_getIndexStanza_with_correct_dsc_binaries(self):
+ def test_source_with_correct_dsc_binaries(self):
"""Ensure correct binary fields are written correctly to indexes.
During upload, our custom parser at:
@@ -412,7 +429,7 @@
pub_source.sourcepackagerelease.dsc_binaries = (
'foo_bin,\n bar_bin,\n zed_bin')
- section = self.write_stanza_and_reparse(pub_source.getIndexStanza())
+ section = self.write_stanza_and_reparse(build_spph_stanza(pub_source))
self.assertEqual('foo', section['Package'])
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2014-08-19 03:46:03 +0000
+++ lib/lp/registry/model/distroseries.py 2014-10-31 13:23:01 +0000
@@ -1028,8 +1028,8 @@
PackagePublishingStatus.PUBLISHED)
def eager_load(spphs):
- # Preload everything which will be used by
- # SourcePackagePublishingHistory.buildIndexStanzaFields.
+ # Preload everything which will be used by archivepublisher's
+ # build_source_stanza_fields.
load_related(Section, spphs, ["sectionID"])
sprs = load_related(
SourcePackageRelease, spphs, ["sourcepackagereleaseID"])
@@ -1064,8 +1064,8 @@
PackagePublishingStatus.PUBLISHED)
def eager_load(bpphs):
- # Preload everything which will be used by
- # BinaryPackagePublishingHistory.buildIndexStanzaFields.
+ # Preload everything which will be used by archivepublisher's
+ # build_binary_stanza_fields.
load_related(Section, bpphs, ["sectionID"])
bprs = load_related(
BinaryPackageRelease, bpphs, ["binarypackagereleaseID"])
=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py 2014-07-31 00:23:58 +0000
+++ lib/lp/registry/tests/test_distroseries.py 2014-10-31 13:23:01 +0000
@@ -10,17 +10,19 @@
]
from functools import partial
-from logging import getLogger
from testtools.matchers import Equals
import transaction
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
+from lp.archivepublisher.indices import (
+ build_binary_stanza_fields,
+ build_source_stanza_fields,
+ )
from lp.registry.errors import NoSuchDistroSeries
from lp.registry.interfaces.distroseries import IDistroSeriesSet
from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.series import SeriesStatus
from lp.services.database.interfaces import IStore
from lp.soyuz.enums import (
ArchivePurpose,
@@ -510,7 +512,8 @@
for spp in self.series.getSourcePackagePublishing(
PackagePublishingPocket.RELEASE, self.universe_component,
self.series.main_archive):
- spp.getIndexStanza()
+ build_source_stanza_fields(
+ spp.sourcepackagerelease, spp.component, spp.section)
recorder1, recorder2 = record_two_runs(
get_index_stanzas,
@@ -528,7 +531,9 @@
for bpp in self.series.getBinaryPackagePublishing(
das.architecturetag, PackagePublishingPocket.RELEASE,
self.universe_component, self.series.main_archive):
- bpp.getIndexStanza()
+ build_binary_stanza_fields(
+ bpp.binarypackagerelease, bpp.component, bpp.section,
+ bpp.priority, bpp.phased_update_percentage, False)
das = self.factory.makeDistroArchSeries(distroseries=self.series)
recorder1, recorder2 = record_two_runs(
=== modified file 'lib/lp/soyuz/doc/publishing.txt'
--- lib/lp/soyuz/doc/publishing.txt 2014-07-08 06:34:37 +0000
+++ lib/lp/soyuz/doc/publishing.txt 2014-10-31 13:23:01 +0000
@@ -30,8 +30,6 @@
>>> from lp.testing import verifyObject
>>> from lp.registry.interfaces.distroseries import IDistroSeries
>>> from lp.registry.interfaces.sourcepackage import ISourcePackage
- >>> from lp.soyuz.interfaces.distributionsourcepackagerelease import (
- ... IDistributionSourcePackageRelease)
>>> from lp.soyuz.interfaces.publishing import (
... IBinaryPackagePublishingHistory,
... ISourcePackagePublishingHistory,
@@ -302,59 +300,6 @@
>>> print bpph.section_name
base
-
-Stanza generation
-=================
-
- >>> from lp.soyuz.interfaces.publishing import (
- ... IBinaryPackagePublishingHistory,
- ... ISourcePackageFilePublishing,
- ... )
-
-Retrieve any SourcePackagePublishingHistory entry.
-
- >>> spph = SourcePackagePublishingHistory.get(10)
-
- >>> print spph.displayname
- alsa-utils 1.0.8-1ubuntu1 in warty
-
-A 'stanza' is the name given to a group of attributes related to one
-source or binary package in the archive index file, it provides
-information to APT, things like, package name, pool path, DSC format,
-files checksum, etc.
-
-The archive index should contain one entry for each source or binary
-currently published in the archive, it is usually partionated by
-component:
-
-<mirror_url_base>/ubuntu/edgy/main/binary-i386/Packages
-<mirror_url_base>/ubuntu/edgy/main/source/Sources
-
-The archive index is also available in in compressed formats.
-
- >>> print spph.getIndexStanza() #doctest: -NORMALIZE_WHITESPACE
- Package: alsa-utils
- Binary: alsa-mixer
- Version: 1.0.8-1ubuntu1
- Section: base
- Maintainer: Mark Shuttleworth <mark@xxxxxxxxxxx>
- Architecture: all
- Standards-Version: 3.6.2
- Format: 1.0
- Directory: pool/main/a/alsa-utils
- Files:
- 01234567890123456789012345678925 3 alsa-utils_1.0.8-1ubuntu1.dsc
- Checksums-Sha1:
- a10856bfea3f0bdb09550dd41f3c5bc275da8a33 3 alsa-utils_1.0.8-1ubuntu1.dsc
- Checksums-Sha256:
- 0123456789012345678901234567890123456789012345678901234567890123 3 alsa-utils_1.0.8-1ubuntu1.dsc
-
-
-Empty fields like are suppressed, like 'Build-Depends' or
-'Build-Depends-Indep' to avoid extra charge on download.
-See sourcepackagerelease.txt for further information about the fields
-in question.
-
Files published are accessible via the files property:
>>> any_pub_file = spph.files[-1]
@@ -924,32 +869,6 @@
>>> IBinaryPackagePublishingHistory.providedBy(bpph)
True
-Generating respective "Packages" stanzas:
-
- >>> print bpph.getIndexStanza() #doctest: -NORMALIZE_WHITESPACE
- Package: mozilla-firefox
- Priority: important
- Section: editors
- Maintainer: Mark Shuttleworth <mark@xxxxxxxxxxxxx>
- Architecture: i386
- Version: 0.9
- Recommends: gcc-3.4-base, libc6 (>= 2.3.2.ds1-4), gcc-3.4 (>= 3.4.1-4sarge1), gcc-3.4 (<< 3.4.2), libstdc++6-dev (>= 3.4.1-4sarge1)
- Replaces: gnome-mozilla-browser
- Suggests: firefox-gnome-support (= 1.0.7-0ubuntu20), latex-xft-fonts, xprint
- Provides: mozilla-firefox
- Depends: gcc-3.4-base, libc6 (>= 2.3.2.ds1-4), gcc-3.4 (>= 3.4.1-4sarge1), gcc-3.4 (<< 3.4.2), libstdc++6-dev (>= 3.4.1-4sarge1)
- Conflicts: firefox, mozilla-web-browser
- Pre-Depends: pmount, foo
- Enhances: pmount, bar
- Breaks: pmount, baz
- Filename: pool/universe/m/mozilla-firefox/mozilla-firefox_0.9_i386.deb
- Size: 3
- MD5sum: 01234567890123456789012345678926
- SHA1: 5a04c7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
- SHA256: 0123456789012345678901234567890123456789012345678901234567890123
- Description: Mozilla Firefox Web Browser
- Mozilla Firefox Web Browser is .....
-
>>> any_file = bpph.files[-1]
>>> IBinaryPackageFilePublishing.providedBy(any_file)
True
=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
--- lib/lp/soyuz/interfaces/publishing.py 2014-10-31 10:34:51 +0000
+++ lib/lp/soyuz/interfaces/publishing.py 2014-10-31 13:23:01 +0000
@@ -168,26 +168,6 @@
If all the files get published correctly update its status properly.
"""
- def getIndexStanza():
- """Return archive index stanza contents
-
- It's based on the locally provided buildIndexStanzaTemplate method,
- which differs for binary and source instances.
-
- :param separate_long_descriptions: if True, the long description will
- be omitted from the stanza and Description-md5 will be included.
- """
-
- def buildIndexStanzaFields():
- """Build a map of fields and values to be in the Index file.
-
- The fields and values ae mapped into a dictionary, where the key is
- the field name and value is the value string.
-
- :param separate_long_descriptions: if True, the long description will
- be omitted from the stanza and Description-md5 will be included.
- """
-
def requestObsolescence():
"""Make this publication obsolete.
@@ -848,50 +828,6 @@
representing the binaries copied to the destination location.
"""
- def getIndexStanza(separate_long_descriptions=False):
- """Return archive index stanza contents
-
- It's based on the locally provided buildIndexStanzaTemplate method,
- which differs for binary and source instances.
-
- :param separate_long_descriptions: if True, the long description will
- be omitted from the stanza and Description-md5 will be included.
- """
-
- def buildIndexStanzaFields(separate_long_descriptions=False):
- """Build a map of fields and values to be in the Index file.
-
- The fields and values ae mapped into a dictionary, where the key is
- the field name and value is the value string.
-
- :param separate_long_descriptions: if True, the long description will
- be omitted from the stanza and Description-md5 will be included.
- """
-
- def getTranslationsStanza(packages):
- """Return archive Translation-en stanza contents
-
- It's based on the locally provided buildTranslationsStanzaTemplate
- method, which differs for binary and source instances.
-
- :param packages: a set of (Package, Description-md5) tuples used to
- determine if a package has already been added to the translation
- file. The (Package, Description-md5) tuple will be added if it
- doesn't already exist.
- """
-
- def buildTranslationsStanzaFields(packages):
- """Build a map of fields and values to be in the Translation-en file.
-
- The fields and values ae mapped into a dictionary, where the key is
- the field name and value is the value string.
-
- :param packages: a set of (Package, Description-md5) tuples used to
- determine if a package has already been added to the translation
- file. The (Package, Description-md5) tuple will be added if it
- doesn't already exist.
- """
-
@export_read_operation()
def getDownloadCount():
"""Get the download count of this binary package in this archive.
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2014-10-31 10:34:51 +0000
+++ lib/lp/soyuz/model/publishing.py 2014-10-31 13:23:01 +0000
@@ -7,7 +7,6 @@
'BinaryPackageFilePublishing',
'BinaryPackagePublishingHistory',
'get_current_source_releases',
- 'IndexStanzaFields',
'makePoolPath',
'PublishingSet',
'SourcePackageFilePublishing',
@@ -17,10 +16,8 @@
from collections import defaultdict
from datetime import datetime
-import hashlib
from operator import attrgetter
import os
-import re
import sys
import pytz
@@ -310,11 +307,6 @@
else:
self.setPublished()
- def getIndexStanza(self, separate_long_descriptions=False):
- """See `IPublishing`."""
- fields = self.buildIndexStanzaFields(separate_long_descriptions)
- return fields.makeOutput()
-
def setSuperseded(self):
"""Set to SUPERSEDED status."""
self.status = PackagePublishingStatus.SUPERSEDED
@@ -353,67 +345,6 @@
return self.section.name
-class IndexStanzaFields:
- """Store and format ordered Index Stanza fields."""
-
- def __init__(self):
- self._names_lower = set()
- self.fields = []
-
- def append(self, name, value):
- """Append an (field, value) tuple to the internal list.
-
- Then we can use the FIFO-like behaviour in makeOutput().
- """
- if name.lower() in self._names_lower:
- return
- self._names_lower.add(name.lower())
- self.fields.append((name, value))
-
- def extend(self, entries):
- """Extend the internal list with the key-value pairs in entries.
- """
- for name, value in entries:
- self.append(name, value)
-
- def makeOutput(self):
- """Return a line-by-line aggregation of appended fields.
-
- Empty fields values will cause the exclusion of the field.
- The output order will preserve the insertion order, FIFO.
- """
- output_lines = []
- for name, value in self.fields:
- if not value:
- continue
-
- # do not add separation space for the special file list fields.
- if name not in ('Files', 'Checksums-Sha1', 'Checksums-Sha256'):
- value = ' %s' % value
-
- # XXX Michael Nelson 20090930 bug=436182. We have an issue
- # in the upload parser that has
- # 1. introduced '\n' at the end of multiple-line-spanning
- # fields, such as dsc_binaries, but potentially others,
- # 2. stripped the leading space from each subsequent line
- # of dsc_binaries values that span multiple lines.
- # This is causing *incorrect* Source indexes to be created.
- # This work-around can be removed once the fix for bug 436182
- # is in place and the tainted data has been cleaned.
- # First, remove any trailing \n or spaces.
- value = value.rstrip()
-
- # Second, as we have corrupt data where subsequent lines
- # of values spanning multiple lines are not preceded by a
- # space, we ensure that any \n in the value that is *not*
- # followed by a white-space character has a space inserted.
- value = re.sub(r"\n(\S)", r"\n \1", value)
-
- output_lines.append('%s:%s' % (name, value))
-
- return '\n'.join(output_lines)
-
-
class SourcePackagePublishingHistory(SQLBase, ArchivePublisherBase):
"""A source package release publishing record."""
implements(ISourcePackagePublishingHistory)
@@ -636,52 +567,6 @@
name = release.sourcepackagename.name
return "%s %s in %s" % (name, release.version, self.distroseries.name)
- def _formatFileList(self, l):
- return ''.join('\n %s %s %s' % ((h,) + f) for (h, f) in l)
-
- def buildIndexStanzaFields(self):
- """See `IPublishing`."""
- # Special fields preparation.
- spr = self.sourcepackagerelease
- pool_path = makePoolPath(spr.name, self.component.name)
- files_list = []
- sha1_list = []
- sha256_list = []
- for spf in spr.files:
- common = (
- spf.libraryfile.content.filesize, spf.libraryfile.filename)
- files_list.append((spf.libraryfile.content.md5, common))
- sha1_list.append((spf.libraryfile.content.sha1, common))
- sha256_list.append((spf.libraryfile.content.sha256, common))
- # Filling stanza options.
- fields = IndexStanzaFields()
- fields.append('Package', spr.name)
- fields.append('Binary', spr.dsc_binaries)
- fields.append('Version', spr.version)
- fields.append('Section', self.section.name)
- fields.append('Maintainer', spr.dsc_maintainer_rfc822)
- fields.append('Build-Depends', spr.builddepends)
- fields.append('Build-Depends-Indep', spr.builddependsindep)
- fields.append('Build-Conflicts', spr.build_conflicts)
- fields.append('Build-Conflicts-Indep', spr.build_conflicts_indep)
- fields.append('Architecture', spr.architecturehintlist)
- fields.append('Standards-Version', spr.dsc_standards_version)
- fields.append('Format', spr.dsc_format)
- fields.append('Directory', pool_path)
- fields.append('Files', self._formatFileList(files_list))
- fields.append('Checksums-Sha1', self._formatFileList(sha1_list))
- fields.append('Checksums-Sha256', self._formatFileList(sha256_list))
- fields.append('Homepage', spr.homepage)
- if spr.user_defined_fields:
- fields.extend(spr.user_defined_fields)
-
- return fields
-
- def getIndexStanza(self):
- """See `IPublishing`."""
- fields = self.buildIndexStanzaFields()
- return fields.makeOutput()
-
def supersede(self, dominant=None, logger=None):
"""See `ISourcePackagePublishingHistory`."""
assert self.status in active_publishing_status, (
@@ -936,136 +821,6 @@
else:
super(BinaryPackagePublishingHistory, self).publish(diskpool, log)
- def _getFormattedDescription(self, summary, description):
- # description field in index is an association of summary and
- # description or the summary only if include_long_descriptions
- # is false, as:
- #
- # Descrition: <SUMMARY>\n
- # <DESCRIPTION L1>
- # ...
- # <DESCRIPTION LN>
- descr_lines = [line.lstrip() for line in description.splitlines()]
- bin_description = '%s\n %s' % (summary, '\n '.join(descr_lines))
- return bin_description
-
- def buildIndexStanzaFields(self, separate_long_descriptions=False):
- """See `IPublishing`."""
- bpr = self.binarypackagerelease
- spr = bpr.build.source_package_release
-
- # binaries have only one file, the DEB
- bin_file = bpr.files[0]
- bin_filename = bin_file.libraryfile.filename
- bin_size = bin_file.libraryfile.content.filesize
- bin_md5 = bin_file.libraryfile.content.md5
- bin_sha1 = bin_file.libraryfile.content.sha1
- bin_sha256 = bin_file.libraryfile.content.sha256
- bin_filepath = os.path.join(
- makePoolPath(spr.name, self.component.name), bin_filename)
- description = self._getFormattedDescription(
- bpr.summary, bpr.description)
- # Our formatted description isn't \n-terminated, but apt
- # considers the trailing \n to be part of the data to hash.
- bin_description_md5 = hashlib.md5(
- description.encode('utf-8') + '\n').hexdigest()
- if separate_long_descriptions:
- # If distroseries.include_long_descriptions is False, the
- # description should be the summary
- bin_description = bpr.summary
- else:
- bin_description = description
-
- # Dealing with architecturespecific field.
- # Present 'all' in every archive index for architecture
- # independent binaries.
- if bpr.architecturespecific:
- architecture = bpr.build.distro_arch_series.architecturetag
- else:
- architecture = 'all'
-
- essential = None
- if bpr.essential:
- essential = 'yes'
-
- source = None
- if bpr.version != spr.version:
- source = '%s (%s)' % (spr.name, spr.version)
- elif bpr.name != spr.name:
- source = spr.name
-
- fields = IndexStanzaFields()
- fields.append('Package', bpr.name)
- fields.append('Source', source)
- fields.append('Priority', self.priority.title.lower())
- fields.append('Section', self.section.name)
- fields.append('Installed-Size', bpr.installedsize)
- fields.append('Maintainer', spr.dsc_maintainer_rfc822)
- fields.append('Architecture', architecture)
- fields.append('Version', bpr.version)
- fields.append('Recommends', bpr.recommends)
- fields.append('Replaces', bpr.replaces)
- fields.append('Suggests', bpr.suggests)
- fields.append('Provides', bpr.provides)
- fields.append('Depends', bpr.depends)
- fields.append('Conflicts', bpr.conflicts)
- fields.append('Pre-Depends', bpr.pre_depends)
- fields.append('Enhances', bpr.enhances)
- fields.append('Breaks', bpr.breaks)
- fields.append('Essential', essential)
- fields.append('Filename', bin_filepath)
- fields.append('Size', bin_size)
- fields.append('MD5sum', bin_md5)
- fields.append('SHA1', bin_sha1)
- fields.append('SHA256', bin_sha256)
- fields.append(
- 'Phased-Update-Percentage', self.phased_update_percentage)
- fields.append('Description', bin_description)
- if separate_long_descriptions:
- fields.append('Description-md5', bin_description_md5)
- if bpr.user_defined_fields:
- fields.extend(bpr.user_defined_fields)
-
- # XXX cprov 2006-11-03: the extra override fields (Bugs, Origin and
- # Task) included in the template be were not populated.
- # When we have the information this will be the place to fill them.
-
- return fields
-
- def getIndexStanza(self, separate_long_descriptions=False):
- """See `IPublishing`."""
- fields = self.buildIndexStanzaFields(separate_long_descriptions)
- return fields.makeOutput()
-
- def buildTranslationsStanzaFields(self, packages):
- """See `IPublishing`."""
- bpr = self.binarypackagerelease
-
- bin_description = self._getFormattedDescription(
- bpr.summary, bpr.description)
- # Our formatted description isn't \n-terminated, but apt
- # considers the trailing \n to be part of the data to hash.
- bin_description_md5 = hashlib.md5(
- bin_description.encode('utf-8') + '\n').hexdigest()
- if (bpr.name, bin_description_md5) not in packages:
- fields = IndexStanzaFields()
- fields.append('Package', bpr.name)
- fields.append('Description-md5', bin_description_md5)
- fields.append('Description-en', bin_description)
- packages.add((bpr.name, bin_description_md5))
-
- return fields
- else:
- return None
-
- def getTranslationsStanza(self, packages):
- """See `IPublishing`."""
- fields = self.buildTranslationsStanzaFields(packages)
- if fields is None:
- return None
- else:
- return fields.makeOutput()
-
def _getOtherPublications(self):
"""Return remaining publications with the same overrides.
Follow ups