Merge ~cjwatson/launchpad:py3-tagfiles-bytes into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 8bd3abf11f1b1bc0ca2e89ffc45b8a54d8f94a47
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:py3-tagfiles-bytes
Merge into: launchpad:master
Diff against target: 1708 lines (+370/-326)
22 files modified
lib/lp/archiveuploader/changesfile.py (+18/-14)
lib/lp/archiveuploader/dscfile.py (+11/-10)
lib/lp/archiveuploader/nascentuploadfile.py (+20/-12)
lib/lp/archiveuploader/tagfiles.py (+7/-3)
lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt (+2/-2)
lib/lp/archiveuploader/tests/nascentupload-epoch-handling.txt (+14/-14)
lib/lp/archiveuploader/tests/nascentupload.txt (+7/-4)
lib/lp/archiveuploader/tests/nascentuploadfile.txt (+29/-25)
lib/lp/archiveuploader/tests/test_changesfile.py (+6/-5)
lib/lp/archiveuploader/tests/test_tagfiles.py (+29/-29)
lib/lp/archiveuploader/utils.py (+1/-1)
lib/lp/soyuz/doc/gina.txt (+26/-26)
lib/lp/soyuz/doc/soyuz-upload.txt (+4/-3)
lib/lp/soyuz/mail/packageupload.py (+3/-1)
lib/lp/soyuz/mail/tests/test_packageupload.py (+42/-42)
lib/lp/soyuz/scripts/gina/archive.py (+13/-9)
lib/lp/soyuz/scripts/gina/changelog.py (+16/-14)
lib/lp/soyuz/scripts/gina/dominate.py (+3/-1)
lib/lp/soyuz/scripts/gina/handlers.py (+7/-7)
lib/lp/soyuz/scripts/gina/packages.py (+28/-25)
lib/lp/soyuz/scripts/gina/runner.py (+2/-1)
lib/lp/soyuz/scripts/tests/test_gina.py (+82/-78)
Reviewer Review Type Date Requested Status
Cristian Gonzalez (community) Approve
Review via email: mp+398367@code.launchpad.net

Commit message

Always parse tagfiles as bytes

Description of the change

Most field values are restricted to ASCII anyway, but dealing with everything as bytes makes our data types more consistent, allowing them to work on Python 3.

Apologies for the size of this MP; I couldn't manage to reduce it while making it still reasonably obvious what was going on. It ended up being simplest to convert pretty much all the tagfile parsing logic at once.

To post a comment you must log in.
Revision history for this message
Cristian Gonzalez (cristiangsp) wrote :

Bytes everywhere!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/archiveuploader/changesfile.py b/lib/lp/archiveuploader/changesfile.py
2index e3d5c5c..09cb65e 100644
3--- a/lib/lp/archiveuploader/changesfile.py
4+++ b/lib/lp/archiveuploader/changesfile.py
5@@ -17,6 +17,8 @@ __all__ = [
6
7 import os
8
9+import six
10+
11 from lp.archiveuploader.buildinfofile import BuildInfoFile
12 from lp.archiveuploader.dscfile import (
13 DSCFile,
14@@ -47,6 +49,7 @@ from lp.registry.interfaces.sourcepackage import (
15 SourcePackageFileType,
16 SourcePackageUrgency,
17 )
18+from lp.services.encoding import guess as guess_encoding
19 from lp.soyuz.enums import BinaryPackageFileType
20
21
22@@ -241,14 +244,14 @@ class ChangesFile(SignableTagFile):
23
24 if 'Urgency' not in self._dict:
25 # Urgency is recommended but not mandatory. Default to 'low'
26- self._dict['Urgency'] = "low"
27+ self._dict['Urgency'] = b"low"
28
29- raw_urgency = self._dict['Urgency'].lower()
30+ raw_urgency = six.ensure_text(self._dict['Urgency']).lower()
31 if raw_urgency not in self.urgency_map:
32 yield UploadWarning(
33 "Unable to grok urgency %s, overriding with 'low'"
34 % (raw_urgency))
35- self._dict['Urgency'] = "low"
36+ self._dict['Urgency'] = b"low"
37
38 if not self.policy.unsigned_changes_ok:
39 assert self.signer is not None, (
40@@ -297,7 +300,7 @@ class ChangesFile(SignableTagFile):
41
42 For example, 'hoary' or 'hoary-security'.
43 """
44- return self._dict['Distribution']
45+ return six.ensure_text(self._dict['Distribution'])
46
47 @property
48 def architectures(self):
49@@ -306,22 +309,23 @@ class ChangesFile(SignableTagFile):
50 For instance ['source', 'all'] or ['source', 'i386', 'amd64']
51 or ['source'].
52 """
53- return set(self._dict['Architecture'].split())
54+ return set(six.ensure_text(self._dict['Architecture']).split())
55
56 @property
57 def binaries(self):
58 """Return set of binary package names listed."""
59- return set(self._dict.get('Binary', '').strip().split())
60+ return set(
61+ six.ensure_text(self._dict.get('Binary', '')).strip().split())
62
63 @property
64 def converted_urgency(self):
65 """Return the appropriate SourcePackageUrgency item."""
66- return self.urgency_map[self._dict['Urgency'].lower()]
67+ return self.urgency_map[six.ensure_text(self._dict['Urgency']).lower()]
68
69 @property
70 def version(self):
71 """Return changesfile claimed version."""
72- return self._dict['Version']
73+ return six.ensure_text(self._dict['Version'])
74
75 @classmethod
76 def formatChangesComment(cls, comment):
77@@ -338,24 +342,24 @@ class ChangesFile(SignableTagFile):
78 @property
79 def changes_comment(self):
80 """Return changesfile 'change' comment."""
81- comment = self._dict['Changes']
82+ comment = guess_encoding(self._dict['Changes'])
83
84 return self.formatChangesComment(comment)
85
86 @property
87 def date(self):
88 """Return changesfile date."""
89- return self._dict['Date']
90+ return six.ensure_text(self._dict['Date'])
91
92 @property
93 def source(self):
94 """Return changesfile claimed source name."""
95- return self._dict['Source']
96+ return six.ensure_text(self._dict['Source'])
97
98 @property
99 def architecture_line(self):
100 """Return changesfile archicteture line."""
101- return self._dict['Architecture']
102+ return six.ensure_text(self._dict['Architecture'])
103
104 @property
105 def simulated_changelog(self):
106@@ -369,8 +373,8 @@ class ChangesFile(SignableTagFile):
107 """
108 changes_author = rfc822_encode_address(
109 self.changed_by['name'], self.changed_by['email'])
110- return '%s\n\n -- %s %s' % (
111- self.changes_comment, changes_author.encode('utf-8'), self.date)
112+ return (u'%s\n\n -- %s %s' % (
113+ self.changes_comment, changes_author, self.date)).encode('UTF-8')
114
115
116 def determine_file_class_and_name(filename):
117diff --git a/lib/lp/archiveuploader/dscfile.py b/lib/lp/archiveuploader/dscfile.py
118index 3ae5496..9140ef4 100644
119--- a/lib/lp/archiveuploader/dscfile.py
120+++ b/lib/lp/archiveuploader/dscfile.py
121@@ -127,7 +127,7 @@ class SignableTagFile:
122 if self.signingkey is not None:
123 return self.signingkey.owner
124
125- def parse(self, verify_signature=True, as_bytes=False):
126+ def parse(self, verify_signature=True):
127 """Parse the tag file, optionally verifying the signature.
128
129 If verify_signature is True, signingkey will be set to the signing
130@@ -169,7 +169,7 @@ class SignableTagFile:
131 self.parsed_content = self.raw_content
132 try:
133 self._dict = parse_tagfile_content(
134- self.parsed_content, filename=self.filepath, as_bytes=as_bytes)
135+ self.parsed_content, filename=self.filepath)
136 except TagFileParseError as error:
137 raise UploadError(
138 "Unable to parse %s: %s" % (self.filename, error))
139@@ -223,8 +223,8 @@ class SignableTagFile:
140 raise UploadError("Invalid Maintainer.")
141
142 if person is None and self.policy.create_people:
143- package = self._dict['Source']
144- version = self._dict['Version']
145+ package = six.ensure_text(self._dict['Source'])
146+ version = six.ensure_text(self._dict['Version'])
147 if self.policy.distroseries and self.policy.pocket:
148 policy_suite = ('%s/%s' % (self.policy.distroseries.name,
149 self.policy.pocket.name))
150@@ -295,7 +295,7 @@ class DSCFile(SourceUploadFile, SignableTagFile):
151 SourceUploadFile.__init__(
152 self, filepath, checksums, size, component_and_section, priority,
153 package, version, changes, policy, logger)
154- self.parse(verify_signature=not policy.unsigned_dsc_ok, as_bytes=True)
155+ self.parse(verify_signature=not policy.unsigned_dsc_ok)
156
157 self.logger.debug("Performing DSC verification.")
158 for mandatory_field in self.mandatory_fields:
159@@ -310,7 +310,7 @@ class DSCFile(SourceUploadFile, SignableTagFile):
160 # the wild generates dsc files with format missing, and we need
161 # to accept them.
162 if 'Format' not in self._dict:
163- self._dict['Format'] = "1.0"
164+ self._dict['Format'] = b"1.0"
165
166 if self.format is None:
167 raise EarlyReturnUploadError(
168@@ -323,12 +323,12 @@ class DSCFile(SourceUploadFile, SignableTagFile):
169 @property
170 def source(self):
171 """Return the DSC source name."""
172- return self._dict['Source']
173+ return six.ensure_text(self._dict['Source'])
174
175 @property
176 def dsc_version(self):
177 """Return the DSC source version."""
178- return self._dict['Version']
179+ return six.ensure_text(self._dict['Version'])
180
181 @property
182 def format(self):
183@@ -342,12 +342,12 @@ class DSCFile(SourceUploadFile, SignableTagFile):
184 @property
185 def architecture(self):
186 """Return the DSC source architecture."""
187- return self._dict['Architecture']
188+ return six.ensure_text(self._dict['Architecture'])
189
190 @property
191 def binary(self):
192 """Return the DSC claimed binary line."""
193- return self._dict['Binary']
194+ return six.ensure_text(self._dict['Binary'])
195
196 #
197 # DSC file checks.
198@@ -411,6 +411,7 @@ class DSCFile(SourceUploadFile, SignableTagFile):
199 for field_name in ['Build-Depends', 'Build-Depends-Indep']:
200 field = self._dict.get(field_name, None)
201 if field is not None:
202+ field = six.ensure_text(field)
203 if field.startswith("ARRAY"):
204 yield UploadError(
205 "%s: invalid %s field produced by a broken version "
206diff --git a/lib/lp/archiveuploader/nascentuploadfile.py b/lib/lp/archiveuploader/nascentuploadfile.py
207index dc5a8ed..48f079d 100644
208--- a/lib/lp/archiveuploader/nascentuploadfile.py
209+++ b/lib/lp/archiveuploader/nascentuploadfile.py
210@@ -344,12 +344,12 @@ class PackageUploadFile(NascentUploadFile):
211
212 if self.section_name not in valid_sections:
213 raise UploadError(
214- "%s: Unknown section %r" % (
215+ "%s: Unknown section '%s'" % (
216 self.filename, self.section_name))
217
218 if self.component_name not in valid_components:
219 raise UploadError(
220- "%s: Unknown component %r" % (
221+ "%s: Unknown component '%s'" % (
222 self.filename, self.component_name))
223
224 @property
225@@ -585,6 +585,7 @@ class BaseBinaryUploadFile(PackageUploadFile):
226
227 control_source = self.control.get("Source", None)
228 if control_source is not None:
229+ control_source = six.ensure_text(control_source)
230 if "(" in control_source:
231 src_match = re_extract_src_version.match(control_source)
232 self.source_name = src_match.group(1)
233@@ -595,14 +596,20 @@ class BaseBinaryUploadFile(PackageUploadFile):
234 else:
235 self.source_name = self.control.get("Package")
236 self.source_version = self.control.get("Version")
237+ if self.source_name is not None:
238+ self.source_name = six.ensure_text(self.source_name)
239+ if self.source_version is not None:
240+ self.source_version = six.ensure_text(self.source_version)
241
242 # Store control_version for external use (archive version consistency
243 # checks in nascentupload.py)
244 self.control_version = self.control.get("Version")
245+ if self.control_version is not None:
246+ self.control_version = six.ensure_text(self.control_version)
247
248 def verifyPackage(self):
249 """Check if the binary is in changesfile and its name is valid."""
250- control_package = self.control.get("Package", '')
251+ control_package = six.ensure_text(self.control.get("Package", b''))
252
253 # Since DDEBs are generated after the original DEBs are processed
254 # and considered by `dpkg-genchanges` they are only half-incorporated
255@@ -651,36 +658,37 @@ class BaseBinaryUploadFile(PackageUploadFile):
256
257 Also check if it is a valid architecture in LP context.
258 """
259- control_arch = self.control.get("Architecture", '')
260+ control_arch = six.ensure_text(self.control.get("Architecture", b''))
261 valid_archs = [a.architecturetag
262 for a in self.policy.distroseries.architectures]
263
264 if control_arch not in valid_archs and control_arch != "all":
265 yield UploadError(
266- "%s: Unknown architecture: %r" % (
267+ "%s: Unknown architecture: '%s'" % (
268 self.filename, control_arch))
269
270 if control_arch not in self.changes.architectures:
271 yield UploadError(
272- "%s: control file lists arch as %r which isn't "
273+ "%s: control file lists arch as '%s' which isn't "
274 "in the changes file." % (self.filename, control_arch))
275
276 if control_arch != self.architecture:
277 yield UploadError(
278- "%s: control file lists arch as %r which doesn't "
279- "agree with version %r in the filename."
280+ "%s: control file lists arch as '%s' which doesn't "
281+ "agree with version '%s' in the filename."
282 % (self.filename, control_arch, self.architecture))
283
284 def verifyDepends(self):
285 """Check if control depends field is present and not empty."""
286- control_depends = self.control.get('Depends', "--unset-marker--")
287+ control_depends = self.control.get('Depends', b"--unset-marker--")
288 if not control_depends:
289 yield UploadError(
290 "%s: Depends field present and empty." % self.filename)
291
292 def verifySection(self):
293 """Check the section & priority match those in changesfile."""
294- control_section_and_component = self.control.get('Section', '')
295+ control_section_and_component = six.ensure_text(
296+ self.control.get('Section', b''))
297 control_component, control_section = splitComponentAndSection(
298 control_section_and_component)
299 if ((control_component, control_section) !=
300@@ -693,7 +701,7 @@ class BaseBinaryUploadFile(PackageUploadFile):
301
302 def verifyPriority(self):
303 """Check if priority matches changesfile."""
304- control_priority = self.control.get('Priority', '')
305+ control_priority = six.ensure_text(self.control.get('Priority', b''))
306 if control_priority and self.priority_name != control_priority:
307 yield UploadError(
308 "%s control file lists priority as %s but changes file has "
309@@ -896,7 +904,7 @@ class BaseBinaryUploadFile(PackageUploadFile):
310
311 is_essential = encoded.get('Essential', '').lower() == 'yes'
312 architecturespecific = not self.is_archindep
313- installedsize = int(self.control.get('Installed-Size', '0'))
314+ installedsize = int(self.control.get('Installed-Size', b'0'))
315 binary_name = getUtility(
316 IBinaryPackageNameSet).getOrCreateByName(self.package)
317
318diff --git a/lib/lp/archiveuploader/tagfiles.py b/lib/lp/archiveuploader/tagfiles.py
319index 5d3adc0..666c9c1 100644
320--- a/lib/lp/archiveuploader/tagfiles.py
321+++ b/lib/lp/archiveuploader/tagfiles.py
322@@ -22,7 +22,7 @@ class TagFileParseError(Exception):
323 pass
324
325
326-def parse_tagfile_content(content, filename=None, as_bytes=False):
327+def parse_tagfile_content(content, filename=None):
328 """Parses a tag file and returns a dictionary where each field is a key.
329
330 The mandatory first argument is the contents of the tag file as a
331@@ -30,13 +30,15 @@ def parse_tagfile_content(content, filename=None, as_bytes=False):
332
333 An OpenPGP cleartext signature will be stripped before parsing if
334 one is present.
335+
336+ Header values are always returned as bytes.
337 """
338
339 with tempfile.TemporaryFile() as f:
340 f.write(strip_pgp_signature(content))
341 f.seek(0)
342 try:
343- stanzas = list(apt_pkg.TagFile(f, bytes=as_bytes))
344+ stanzas = list(apt_pkg.TagFile(f, bytes=True))
345 except SystemError as e:
346 raise TagFileParseError("%s: %s" % (filename, e))
347 if len(stanzas) != 1:
348@@ -61,8 +63,10 @@ def parse_tagfile(filename):
349
350 The mandatory first argument is the filename of the tag file, and
351 the contents of that file is passed on to parse_tagfile_content.
352+
353+ Header values are always returned as bytes.
354 """
355- with open(filename, "r") as changes_in:
356+ with open(filename, "rb") as changes_in:
357 content = changes_in.read()
358 if not content:
359 raise TagFileParseError("%s: empty file" % filename)
360diff --git a/lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt b/lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt
361index dc39b37..91db6b5 100644
362--- a/lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt
363+++ b/lib/lp/archiveuploader/tests/nascentupload-closing-bugs.txt
364@@ -78,8 +78,8 @@ This new version fixes bug #6 according its changesfiles:
365 >>> print(bar2_src.changes.changed_by['person'].name)
366 kinnison
367
368- >>> bar2_src.changes._dict['Launchpad-bugs-fixed']
369- '6'
370+ >>> print(six.ensure_str(bar2_src.changes._dict['Launchpad-bugs-fixed']))
371+ 6
372
373 >>> print bar2_src.changes.changes_comment
374 bar (1.0-2) breezy; urgency=low
375diff --git a/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.txt b/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.txt
376index 5acbc91..1ba7b85 100644
377--- a/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.txt
378+++ b/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.txt
379@@ -204,11 +204,11 @@ SourcePackageRelease record store a proper 'version':
380
381 For source uploads, Changes.version == DSC.version == SPR.version:
382
383- >>> bar_src_upload.changes.version
384- '1:1.0-9'
385+ >>> print(bar_src_upload.changes.version)
386+ 1:1.0-9
387
388- >>> bar_src_upload.changes.dsc.dsc_version
389- '1:1.0-9'
390+ >>> print(bar_src_upload.changes.dsc.dsc_version)
391+ 1:1.0-9
392
393 >>> bar_src_queue = bar_src_upload.queue_root
394 >>> bar_spr = bar_src_queue.sources[0].sourcepackagerelease
395@@ -247,21 +247,21 @@ The Changesfile version always refers to the source version and the
396 binary versions included in the upload can diverge between themselves
397 and from the source version.
398
399- >>> bar_bin_upload.changes.version
400- '1:1.0-9'
401+ >>> print(bar_bin_upload.changes.version)
402+ 1:1.0-9
403
404 >>> deb_file = bar_bin_upload.changes.files[0]
405- >>> deb_file.filename
406- 'bar_6.6.6_i386.deb'
407+ >>> print(deb_file.filename)
408+ bar_6.6.6_i386.deb
409
410- >>> deb_file.version
411- '1:1.0-9'
412+ >>> print(deb_file.version)
413+ 1:1.0-9
414
415- >>> deb_file.source_version
416- '1:1.0-9'
417+ >>> print(deb_file.source_version)
418+ 1:1.0-9
419
420- >>> deb_file.control_version
421- '1:6.6.6'
422+ >>> print(deb_file.control_version)
423+ 1:6.6.6
424
425 Anyway, the proper value for BinaryPackageRelease.version is the
426 version stored in the binary control file:
427diff --git a/lib/lp/archiveuploader/tests/nascentupload.txt b/lib/lp/archiveuploader/tests/nascentupload.txt
428index 6bdda3d..37f6836 100644
429--- a/lib/lp/archiveuploader/tests/nascentupload.txt
430+++ b/lib/lp/archiveuploader/tests/nascentupload.txt
431@@ -231,8 +231,9 @@ should match the files target architectures:
432 ... print f.filename, f
433 ed_0.2-20_i386.deb <...DebBinaryUploadFile...>
434
435- >>> [a for a in ed_mismatched_upload.changes.architectures]
436- ['amd64']
437+ >>> for a in ed_mismatched_upload.changes.architectures:
438+ ... print(a)
439+ amd64
440
441 Since the changesfile specify that only 'amd64' will be used and
442 there is a file that depends on 'i386' the upload is rejected:
443@@ -280,8 +281,10 @@ And because of that it's not considered native.
444 But if we check the DSC we will find the reference to the already
445 known ORIG file:
446
447- >>> [f.filename for f in ed_upload.changes.dsc.files]
448- ['ed_0.2.orig.tar.gz', 'ed_0.2-21.diff.gz']
449+ >>> for f in ed_upload.changes.dsc.files:
450+ ... print(f.filename)
451+ ed_0.2.orig.tar.gz
452+ ed_0.2-21.diff.gz
453
454 >>> success = ed_upload.do_accept()
455 >>> success
456diff --git a/lib/lp/archiveuploader/tests/nascentuploadfile.txt b/lib/lp/archiveuploader/tests/nascentuploadfile.txt
457index 0dbd48b..85a2ac7 100644
458--- a/lib/lp/archiveuploader/tests/nascentuploadfile.txt
459+++ b/lib/lp/archiveuploader/tests/nascentuploadfile.txt
460@@ -90,18 +90,18 @@ file name.
461
462 At this point the changesfile content is already parsed:
463
464- >>> ed_binary_changes.source
465- 'ed'
466+ >>> print(ed_binary_changes.source)
467+ ed
468
469- >>> ed_binary_changes.version
470- '0.2-20'
471+ >>> print(ed_binary_changes.version)
472+ 0.2-20
473
474 >>> for item in ed_binary_changes.architectures:
475 ... print(item)
476 i386
477
478- >>> ed_binary_changes.suite_name
479- 'unstable'
480+ >>> print(ed_binary_changes.suite_name)
481+ unstable
482
483 Push upload targeted suite into policy before the checks, nomally done
484 by NascentUpload object:
485@@ -129,15 +129,19 @@ At this point we can inspect the list of files contained in the upload.
486 ... print uploaded_file.filename
487 ed_0.2-20_i386.deb
488
489- >>> [f.filename for f in ed_binary_changes.binary_package_files]
490- ['ed_0.2-20_i386.deb']
491- >>> [f.filename for f in ed_binary_changes.source_package_files]
492- []
493-
494- >>> [f.filename for f in ed_source_changes.binary_package_files]
495- []
496- >>> [f.filename for f in ed_source_changes.source_package_files]
497- ['ed_0.2-20.dsc', 'ed_0.2-20.diff.gz', 'ed_0.2.orig.tar.gz']
498+ >>> for f in ed_binary_changes.binary_package_files:
499+ ... print(f.filename)
500+ ed_0.2-20_i386.deb
501+ >>> for f in ed_binary_changes.source_package_files:
502+ ... print(f.filename)
503+
504+ >>> for f in ed_source_changes.binary_package_files:
505+ ... print(f.filename)
506+ >>> for f in ed_source_changes.source_package_files:
507+ ... print(f.filename)
508+ ed_0.2-20.dsc
509+ ed_0.2-20.diff.gz
510+ ed_0.2.orig.tar.gz
511
512 Similar to what we have in 'processFiles' ChangesFile.verify is also
513 a error generator
514@@ -364,14 +368,14 @@ in the ChangesFile instance.
515 The DSCFile also presents a similar behaviour to access its parsed
516 contents:
517
518- >>> ed_source_dsc.source
519- 'ed'
520- >>> ed_source_dsc.version
521- '0.2-20'
522- >>> ed_source_dsc.architecture
523- 'any'
524- >>> ed_source_dsc.binary
525- 'ed'
526+ >>> print(ed_source_dsc.source)
527+ ed
528+ >>> print(ed_source_dsc.version)
529+ 0.2-20
530+ >>> print(ed_source_dsc.architecture)
531+ any
532+ >>> print(ed_source_dsc.binary)
533+ ed
534
535 The DSC is GPG-signed most of the time, so we can guarantee who was
536 the author. The DSCFile class implements the same address parsing
537@@ -482,7 +486,7 @@ changes file:
538 ... 'main/net', 'important', 'foo', '1.2', ed_binary_changes,
539 ... modified_insecure_policy, DevNullLogger())
540 >>> list(ed_binary_deb.verify())
541- [UploadError('ed_0.2-20_i386.deb
542+ [UploadError(...'ed_0.2-20_i386.deb
543 control file lists section as main/editors but changes file has
544 main/net.',)]
545
546@@ -493,7 +497,7 @@ It also checks the priority against the changes file:
547 ... 'main/editors', 'extra', 'foo', '1.2', ed_binary_changes,
548 ... modified_insecure_policy, DevNullLogger())
549 >>> list(ed_binary_deb.verify())
550- [UploadError('ed_0.2-20_i386.deb
551+ [UploadError(...'ed_0.2-20_i386.deb
552 control file lists priority as important but changes file has extra.',)]
553
554 The timestamp of the files in the .deb are tested against the policy for
555diff --git a/lib/lp/archiveuploader/tests/test_changesfile.py b/lib/lp/archiveuploader/tests/test_changesfile.py
556index 60c8646..8f76d23 100644
557--- a/lib/lp/archiveuploader/tests/test_changesfile.py
558+++ b/lib/lp/archiveuploader/tests/test_changesfile.py
559@@ -10,6 +10,7 @@ __metaclass__ = type
560 import os
561
562 from debian.deb822 import Changes
563+import six
564 from testtools.matchers import (
565 Equals,
566 MatchesDict,
567@@ -281,9 +282,9 @@ class ChangesFileTests(TestCase):
568 "mypkg_0.1_i386.changes", contents)
569 self.assertEqual([], list(changes.processAddresses()))
570 self.assertEqual(
571- "Something changed\n\n"
572- " -- Somebody <somebody@ubuntu.com> "
573- "Fri, 25 Jun 2010 11:20:22 -0600",
574+ b"Something changed\n\n"
575+ b" -- Somebody <somebody@ubuntu.com> "
576+ b"Fri, 25 Jun 2010 11:20:22 -0600",
577 changes.simulated_changelog)
578
579 def test_requires_changed_by(self):
580@@ -377,7 +378,7 @@ class TestSignatureVerification(TestCase):
581 expected = "\\AFormat: 1.7\n.*foo_1.0-1.diff.gz\\Z"
582 self.assertTextMatchesExpressionIgnoreWhitespace(
583 expected,
584- changesfile.parsed_content)
585+ six.ensure_text(changesfile.parsed_content))
586
587 def test_no_signature_rejected(self):
588 # An unsigned changes file is rejected.
589@@ -399,6 +400,6 @@ class TestSignatureVerification(TestCase):
590 expected = "\\AFormat: 1.7\n.*foo_1.0-1.diff.gz\\Z"
591 self.assertTextMatchesExpressionIgnoreWhitespace(
592 expected,
593- changesfile.parsed_content)
594+ six.ensure_text(changesfile.parsed_content))
595 self.assertEqual("breezy", changesfile.suite_name)
596 self.assertNotIn("evil", changesfile.changes_comment)
597diff --git a/lib/lp/archiveuploader/tests/test_tagfiles.py b/lib/lp/archiveuploader/tests/test_tagfiles.py
598index 3deff06..f5bf552 100755
599--- a/lib/lp/archiveuploader/tests/test_tagfiles.py
600+++ b/lib/lp/archiveuploader/tests/test_tagfiles.py
601@@ -31,7 +31,7 @@ class Testtagfiles(unittest.TestCase):
602 reject them if it can't understand.
603 """
604 parsed = parse_tagfile(datadir("bad-multiline-changes"))
605- self.assertEqual('unstable', parsed['Distribution'])
606+ self.assertEqual(b'unstable', parsed['Distribution'])
607
608 def testCheckParseMalformedMultiline(self):
609 """Malformed but somewhat readable files do not raise an exception.
610@@ -40,7 +40,7 @@ class Testtagfiles(unittest.TestCase):
611 reject them if it can't understand.
612 """
613 parsed = parse_tagfile(datadir("bad-multiline-changes"))
614- self.assertEqual('unstable', parsed['Distribution'])
615+ self.assertEqual(b'unstable', parsed['Distribution'])
616 self.assertRaises(KeyError, parsed.__getitem__, 'Fish')
617
618 def testCheckParseEmptyChangesRaises(self):
619@@ -82,7 +82,7 @@ class TestTagFileDebianPolicyCompat(unittest.TestCase):
620
621 tagfile_path = datadir("test436182_0.1_source.changes")
622 tagfile = open(tagfile_path)
623- self.apt_pkg_parsed_version = apt_pkg.TagFile(tagfile)
624+ self.apt_pkg_parsed_version = apt_pkg.TagFile(tagfile, bytes=True)
625 self.apt_pkg_parsed_version.step()
626
627 self.parse_tagfile_version = parse_tagfile(tagfile_path)
628@@ -97,17 +97,17 @@ class TestTagFileDebianPolicyCompat(unittest.TestCase):
629 2. appended a trailing '\n' to the end of the value.
630 """
631
632- expected_text = (
633- 'test75874 anotherbinary\n'
634- ' andanother andonemore\n'
635- '\tlastone')
636+ expected_bytes = (
637+ b'test75874 anotherbinary\n'
638+ b' andanother andonemore\n'
639+ b'\tlastone')
640
641 self.assertEqual(
642- expected_text,
643+ expected_bytes,
644 self.apt_pkg_parsed_version.section['Binary'])
645
646 self.assertEqual(
647- expected_text,
648+ expected_bytes,
649 self.parse_tagfile_version['Binary'])
650
651 def test_parse_tagfile_with_newline_delimited_field(self):
652@@ -123,19 +123,19 @@ class TestTagFileDebianPolicyCompat(unittest.TestCase):
653 see http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Files
654 """
655
656- expected_text = (
657- 'f26bb9b29b1108e53139da3584a4dc92 1511 test75874_0.1.tar.gz\n '
658- '29c955ff520cea32ab3e0316306d0ac1 393742 '
659- 'pmount_0.9.7.orig.tar.gz\n'
660- ' 91a8f46d372c406fadcb57c6ff7016f3 5302 '
661- 'pmount_0.9.7-2ubuntu2.diff.gz')
662+ expected_bytes = (
663+ b'f26bb9b29b1108e53139da3584a4dc92 1511 test75874_0.1.tar.gz\n '
664+ b'29c955ff520cea32ab3e0316306d0ac1 393742 '
665+ b'pmount_0.9.7.orig.tar.gz\n'
666+ b' 91a8f46d372c406fadcb57c6ff7016f3 5302 '
667+ b'pmount_0.9.7-2ubuntu2.diff.gz')
668
669 self.assertEqual(
670- expected_text,
671+ expected_bytes,
672 self.apt_pkg_parsed_version.section['Files'])
673
674 self.assertEqual(
675- expected_text,
676+ expected_bytes,
677 self.parse_tagfile_version['Files'])
678
679 def test_parse_description_field(self):
680@@ -144,19 +144,19 @@ class TestTagFileDebianPolicyCompat(unittest.TestCase):
681
682 See http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
683 """
684- expected_text = (
685- "Here's the single-line synopsis.\n"
686- " Then there is the extended description which can\n"
687- " span multiple lines, and even include blank-lines like this:\n"
688- " .\n"
689- " There you go. If a line starts with two or more spaces,\n"
690- " it will be displayed verbatim. Like this one:\n"
691- " Example verbatim line.\n"
692- " Another verbatim line.\n"
693- " OK, back to normal.")
694+ expected_bytes = (
695+ b"Here's the single-line synopsis.\n"
696+ b" Then there is the extended description which can\n"
697+ b" span multiple lines, and even include blank-lines like this:\n"
698+ b" .\n"
699+ b" There you go. If a line starts with two or more spaces,\n"
700+ b" it will be displayed verbatim. Like this one:\n"
701+ b" Example verbatim line.\n"
702+ b" Another verbatim line.\n"
703+ b" OK, back to normal.")
704
705 self.assertEqual(
706- expected_text,
707+ expected_bytes,
708 self.apt_pkg_parsed_version.section['Description'])
709
710 # In the past our parse_tagfile function replaced blank-line
711@@ -164,5 +164,5 @@ class TestTagFileDebianPolicyCompat(unittest.TestCase):
712 # but it is now compatible with ParseTagFiles (and ready to be
713 # replaced by ParseTagFiles).
714 self.assertEqual(
715- expected_text,
716+ expected_bytes,
717 self.parse_tagfile_version['Description'])
718diff --git a/lib/lp/archiveuploader/utils.py b/lib/lp/archiveuploader/utils.py
719index 334a2aa..bc5cf80 100644
720--- a/lib/lp/archiveuploader/utils.py
721+++ b/lib/lp/archiveuploader/utils.py
722@@ -270,7 +270,7 @@ def parse_file_list(s, field_name, count):
723 if s is None:
724 return None
725 processed = []
726- for line in s.strip().split('\n'):
727+ for line in six.ensure_text(s).strip().split('\n'):
728 split = line.strip().split()
729 if len(split) != count:
730 raise UploadError(
731diff --git a/lib/lp/soyuz/doc/gina.txt b/lib/lp/soyuz/doc/gina.txt
732index 2e2e253..f342cf3 100644
733--- a/lib/lp/soyuz/doc/gina.txt
734+++ b/lib/lp/soyuz/doc/gina.txt
735@@ -134,38 +134,38 @@ Check STDERR for the errors we expected:
736 >>> print(proc.stderr.read())
737 ERROR Error processing package files for clearlooks
738 ...
739- ExecutionError: Error 2 unpacking source
740+ ...ExecutionError: Error 2 unpacking source
741 WARNING Invalid format in db1-compat, assumed '1.0'
742 WARNING Source package ed lacks section, assumed 'misc'
743 ERROR Unable to create SourcePackageData for mkvmlinuz
744 ...
745- InvalidVersionError: mkvmlinuz has an invalid version: None
746+ ...InvalidVersionError: mkvmlinuz has an invalid version: None
747 WARNING Invalid urgency in python-pam, None, assumed 'low'
748 ERROR Error processing package files for util-linux
749 ...
750- PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive
751+ ...PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive
752 ERROR Error processing package files for bsdutils
753 ...
754- PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found
755+ ...PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found
756 WARNING Binary package ed lacks valid priority, assumed 'extra'
757 ERROR Unable to create BinaryPackageData for mount
758 ...
759- InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2
760+ ...InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2
761 WARNING Binary package python-pam lacks a section, assumed 'misc'
762 ERROR Error processing package files for python2.4-pam
763 ...
764- PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found
765+ ...PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found
766 ERROR Error processing package files for python2.4-sqlite
767 ...
768- PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found
769+ ...PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found
770 WARNING No source package rioutil (1.4.4-1.0.1) listed for rioutil (1.4.4-1.0.1), scrubbing archive...
771 WARNING Nope, couldn't find it. Could it be a bin-only-NMU? Checking...
772 ERROR Error processing package files for util-linux
773 ...
774- PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found
775+ ...PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found
776 ERROR Unable to create BinaryPackageData for util-linux-locales
777 ...
778- MissingRequiredArguments: ['installed_size']
779+ ...MissingRequiredArguments: ['installed_size']
780 ERROR Invalid Sources stanza in /tmp/tmp...
781 ...
782 WARNING No changelog file found for mkvmlinuz in mkvmlinuz-14ubuntu1
783@@ -173,13 +173,13 @@ Check STDERR for the errors we expected:
784 WARNING Invalid urgency in mkvmlinuz, None, assumed 'low'
785 ERROR Error processing package files for python-sqlite
786 ...
787- PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive
788+ ...PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive
789 ERROR Error processing package files for util-linux
790 ...
791- PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive
792+ ...PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive
793 ERROR Error processing package files for python-sqlite
794 ...
795- PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found
796+ ...PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found
797 WARNING No source package ubuntu-meta (0.80) listed for ubuntu-base (0.80), scrubbing archive...
798 <BLANKLINE>
799
800@@ -230,7 +230,7 @@ Check if the changelog message was stored correcly:
801
802 Check that the changelog was uploaded to the librarian correctly:
803
804- >>> print(x11p.changelog.read())
805+ >>> print(six.ensure_text(x11p.changelog.read()))
806 x11proto-damage (6.8.99.7-2) breezy; urgency=low
807 <BLANKLINE>
808 * Add dependency on x11proto-fixes-dev.
809@@ -521,45 +521,45 @@ run.
810 >>> print(proc.stderr.read())
811 ERROR Error processing package files for clearlooks
812 ...
813- ExecutionError: Error 2 unpacking source
814+ ...ExecutionError: Error 2 unpacking source
815 WARNING Source package ed lacks section, assumed 'misc'
816 ERROR Unable to create SourcePackageData for mkvmlinuz
817 ...
818- InvalidVersionError: mkvmlinuz has an invalid version: None
819+ ...InvalidVersionError: mkvmlinuz has an invalid version: None
820 ERROR Error processing package files for util-linux
821 ...
822- PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive
823+ ...PoolFileNotFound: File util-linux_2.12p-2ubuntu2.2.dsc not in archive
824 ERROR Error processing package files for bsdutils
825 ...
826- PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found
827+ ...PoolFileNotFound: .../bsdutils_2.12p-2ubuntu2_i386.deb not found
828 WARNING Binary package ed lacks valid priority, assumed 'extra'
829 ERROR Unable to create BinaryPackageData for mount
830 ...
831- InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2
832+ ...InvalidVersionError: mount has an invalid version: -ewePP2.12p-2ubuntu2
833 WARNING Binary package python-pam lacks a section, assumed 'misc'
834 ERROR Error processing package files for python2.4-pam
835 ...
836- PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found
837+ ...PoolFileNotFound: .../python2.4-pam_0.4.2-10.1ubuntu3_i386.deb not found
838 ERROR Error processing package files for python2.4-sqlite
839 ...
840- PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found
841+ ...PoolFileNotFound: .../python2.4-sqlite_1.0.1-1ubuntu1_i386.deb not found
842 ERROR Error processing package files for util-linux
843 ...
844- PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found
845+ ...PoolFileNotFound: .../util-linux_2.12p-2ubuntu2_i386.deb not found
846 ERROR Unable to create BinaryPackageData for util-linux-locales
847 ...
848- MissingRequiredArguments: ['installed_size']
849+ ...MissingRequiredArguments: ['installed_size']
850 ERROR Invalid Sources stanza in /tmp/tmp...
851 ...
852 ERROR Error processing package files for python-sqlite
853 ...
854- PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive
855+ ...PoolFileNotFound: File python-sqlite_1.0.1-2ubuntu1.dsc not in archive
856 ERROR Error processing package files for util-linux
857 ...
858- PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive
859+ ...PoolFileNotFound: File util-linux_2.12p-6ubuntu5.dsc not in archive
860 ERROR Error processing package files for python-sqlite
861 ...
862- PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found
863+ ...PoolFileNotFound: .../python-sqlite_1.0.1-2ubuntu1_all.deb not found
864 <BLANKLINE>
865 >>> proc.wait()
866 0
867@@ -815,7 +815,7 @@ For kicks, finally, run gina on a configured but incomplete archive:
868 >>> print(proc.stderr.read())
869 ERROR Failed to analyze archive for bogoland
870 ...
871- MangledArchiveError: No archive directory for bogoland/main
872+ ...MangledArchiveError: No archive directory for bogoland/main
873 <BLANKLINE>
874 >>> proc.wait()
875 1
876diff --git a/lib/lp/soyuz/doc/soyuz-upload.txt b/lib/lp/soyuz/doc/soyuz-upload.txt
877index 5030c6a..7962cb2 100644
878--- a/lib/lp/soyuz/doc/soyuz-upload.txt
879+++ b/lib/lp/soyuz/doc/soyuz-upload.txt
880@@ -61,13 +61,14 @@ been uploaded over FTP.
881 ... tf = {}
882 ...
883 ... if "Source" in tf:
884- ... package_names.append(tf["Source"])
885+ ... package_names.append(six.ensure_text(tf["Source"]))
886 ...
887 ... send_filepaths = [changes_filepath]
888 ... if "Files" in tf:
889 ... send_filepaths.extend(
890 ... [os.path.join(test_files_dir, line.split()[-1])
891- ... for line in tf["Files"].splitlines() if line])
892+ ... for line in six.ensure_text(tf["Files"]).splitlines()
893+ ... if line])
894 ...
895 ... sent_filenames.extend(
896 ... os.path.basename(filepath) for filepath in send_filepaths)
897@@ -87,7 +88,7 @@ been uploaded over FTP.
898 Check that what we've just uploaded (everything in test_files_dir) is
899 what we were expecting to have uploaded.
900
901- >>> package_names
902+ >>> print(pretty(package_names))
903 ['drdsl', 'etherwake']
904
905 At that point we must have a bunch of directories in the upload base
906diff --git a/lib/lp/soyuz/mail/packageupload.py b/lib/lp/soyuz/mail/packageupload.py
907index f194061..a5686a1 100644
908--- a/lib/lp/soyuz/mail/packageupload.py
909+++ b/lib/lp/soyuz/mail/packageupload.py
910@@ -140,6 +140,8 @@ def fetch_information(spr, bprs, changes, previous_version=None):
911 changelog = ChangesFile.formatChangesComment(
912 sanitize_string(changes.get('Changes')))
913 date = changes.get('Date')
914+ if date is not None:
915+ date = six.ensure_text(date)
916 try:
917 changedby = parse_maintainer_bytes(
918 changes.get('Changed-By'), 'Changed-By')
919@@ -151,7 +153,7 @@ def fetch_information(spr, bprs, changes, previous_version=None):
920 except ParseMaintError:
921 pass
922 notify_changed_by = changes.get(
923- 'Launchpad-Notify-Changed-By', '') == 'yes'
924+ 'Launchpad-Notify-Changed-By', b'') == b'yes'
925 elif spr or bprs:
926 if not spr and bprs:
927 spr = bprs[0].build.source_package_release
928diff --git a/lib/lp/soyuz/mail/tests/test_packageupload.py b/lib/lp/soyuz/mail/tests/test_packageupload.py
929index 7881462..d39d9c3 100644
930--- a/lib/lp/soyuz/mail/tests/test_packageupload.py
931+++ b/lib/lp/soyuz/mail/tests/test_packageupload.py
932@@ -254,10 +254,10 @@ class TestNotification(TestCaseWithFactory):
933
934 def test_fetch_information_changes(self):
935 changes = {
936- 'Date': '2001-01-01',
937- 'Changed-By': 'Foo Bar <foo.bar@example.com>',
938- 'Maintainer': 'Foo Bar <foo.bar@example.com>',
939- 'Changes': ' * Foo!',
940+ 'Date': b'2001-01-01',
941+ 'Changed-By': b'Foo Bar <foo.bar@example.com>',
942+ 'Maintainer': b'Foo Bar <foo.bar@example.com>',
943+ 'Changes': b' * Foo!',
944 }
945 info = fetch_information(None, None, changes)
946 self.assertEqual('2001-01-01', info['date'])
947@@ -272,11 +272,11 @@ class TestNotification(TestCaseWithFactory):
948
949 def test_fetch_information_changes_notify_changed_by(self):
950 changes = {
951- 'Date': '2001-01-01',
952- 'Changed-By': 'Foo Bar <foo.bar@example.com>',
953- 'Maintainer': 'Foo Bar <foo.bar@example.com>',
954- 'Changes': ' * Foo!',
955- 'Launchpad-Notify-Changed-By': 'yes',
956+ 'Date': b'2001-01-01',
957+ 'Changed-By': b'Foo Bar <foo.bar@example.com>',
958+ 'Maintainer': b'Foo Bar <foo.bar@example.com>',
959+ 'Changes': b' * Foo!',
960+ 'Launchpad-Notify-Changed-By': b'yes',
961 }
962 info = fetch_information(None, None, changes)
963 self.assertEqual('2001-01-01', info['date'])
964@@ -410,10 +410,10 @@ class TestNotification(TestCaseWithFactory):
965 # Test getRecipientsForAction with good email addresses..
966 blamer, maintainer, changer = self._setup_recipients()
967 changes = {
968- 'Date': '2001-01-01',
969- 'Changed-By': 'Changer <changer@example.com>',
970- 'Maintainer': 'Maintainer <maintainer@example.com>',
971- 'Changes': ' * Foo!',
972+ 'Date': b'2001-01-01',
973+ 'Changed-By': b'Changer <changer@example.com>',
974+ 'Maintainer': b'Maintainer <maintainer@example.com>',
975+ 'Changes': b' * Foo!',
976 }
977 self.assertRecipientsEqual(
978 [blamer, maintainer, changer],
979@@ -422,10 +422,10 @@ class TestNotification(TestCaseWithFactory):
980 def test_getRecipientsForAction_bad_maintainer_email(self):
981 blamer, maintainer, changer = self._setup_recipients()
982 changes = {
983- 'Date': '2001-01-01',
984- 'Changed-By': 'Changer <changer@example.com>',
985- 'Maintainer': 'Maintainer <maintainer at example.com>',
986- 'Changes': ' * Foo!',
987+ 'Date': b'2001-01-01',
988+ 'Changed-By': b'Changer <changer@example.com>',
989+ 'Maintainer': b'Maintainer <maintainer at example.com>',
990+ 'Changes': b' * Foo!',
991 }
992 self.assertRecipientsEqual(
993 [blamer, changer], changes, blamer, maintainer, changer)
994@@ -434,10 +434,10 @@ class TestNotification(TestCaseWithFactory):
995 # Test getRecipientsForAction with invalid changedby email address.
996 blamer, maintainer, changer = self._setup_recipients()
997 changes = {
998- 'Date': '2001-01-01',
999- 'Changed-By': 'Changer <changer at example.com>',
1000- 'Maintainer': 'Maintainer <maintainer@example.com>',
1001- 'Changes': ' * Foo!',
1002+ 'Date': b'2001-01-01',
1003+ 'Changed-By': b'Changer <changer at example.com>',
1004+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1005+ 'Changes': b' * Foo!',
1006 }
1007 self.assertRecipientsEqual(
1008 [blamer, maintainer], changes, blamer, maintainer, changer)
1009@@ -447,10 +447,10 @@ class TestNotification(TestCaseWithFactory):
1010 # to the archive owner.
1011 _, maintainer, changer = self._setup_recipients()
1012 changes = {
1013- 'Date': '2001-01-01',
1014- 'Changed-By': 'Changer <changer@example.com>',
1015- 'Maintainer': 'Maintainer <maintainer@example.com>',
1016- 'Changes': ' * Foo!',
1017+ 'Date': b'2001-01-01',
1018+ 'Changed-By': b'Changer <changer@example.com>',
1019+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1020+ 'Changes': b' * Foo!',
1021 }
1022 self.assertRecipientsEqual(
1023 [], changes, None, maintainer, changer,
1024@@ -461,10 +461,10 @@ class TestNotification(TestCaseWithFactory):
1025 # signed the upload.
1026 blamer, maintainer, changer = self._setup_recipients()
1027 changes = {
1028- 'Date': '2001-01-01',
1029- 'Changed-By': 'Changer <changer@example.com>',
1030- 'Maintainer': 'Maintainer <maintainer@example.com>',
1031- 'Changes': ' * Foo!',
1032+ 'Date': b'2001-01-01',
1033+ 'Changed-By': b'Changer <changer@example.com>',
1034+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1035+ 'Changes': b' * Foo!',
1036 }
1037 self.assertRecipientsEqual(
1038 [blamer], changes, blamer, maintainer, changer,
1039@@ -475,11 +475,11 @@ class TestNotification(TestCaseWithFactory):
1040 # notifications go to the changer even for PPA uploads.
1041 blamer, maintainer, changer = self._setup_recipients()
1042 changes = {
1043- 'Date': '2001-01-01',
1044- 'Changed-By': 'Changer <changer@example.com>',
1045- 'Maintainer': 'Maintainer <maintainer@example.com>',
1046- 'Changes': ' * Foo!',
1047- 'Launchpad-Notify-Changed-By': 'yes',
1048+ 'Date': b'2001-01-01',
1049+ 'Changed-By': b'Changer <changer@example.com>',
1050+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1051+ 'Changes': b' * Foo!',
1052+ 'Launchpad-Notify-Changed-By': b'yes',
1053 }
1054 self.assertRecipientsEqual(
1055 [blamer, changer], changes, blamer, maintainer, changer,
1056@@ -500,10 +500,10 @@ class TestNotification(TestCaseWithFactory):
1057 spr = self.factory.makeSourcePackageRelease(
1058 component=component, section_name="libs")
1059 changes = {
1060- 'Date': '2001-01-01',
1061- 'Changed-By': 'Changer <changer@example.com>',
1062- 'Maintainer': 'Maintainer <maintainer@example.com>',
1063- 'Changes': ' * Foo!',
1064+ 'Date': b'2001-01-01',
1065+ 'Changed-By': b'Changer <changer@example.com>',
1066+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1067+ 'Changes': b' * Foo!',
1068 }
1069 mailer = PackageUploadMailer.forAction(
1070 "accepted", blamer, spr, [], [], archive, distroseries,
1071@@ -545,10 +545,10 @@ class TestNotification(TestCaseWithFactory):
1072 spr = self.factory.makeSourcePackageRelease(
1073 component=component, section_name="libs")
1074 changes = {
1075- 'Date': '2001-01-01',
1076- 'Changed-By': 'Changer <changer@example.com>',
1077- 'Maintainer': 'Maintainer <maintainer@example.com>',
1078- 'Changes': ' * Foo!',
1079+ 'Date': b'2001-01-01',
1080+ 'Changed-By': b'Changer <changer@example.com>',
1081+ 'Maintainer': b'Maintainer <maintainer@example.com>',
1082+ 'Changes': b' * Foo!',
1083 }
1084 mailer = PackageUploadMailer.forAction(
1085 "accepted", blamer, spr, [], [], archive, distroseries,
1086diff --git a/lib/lp/soyuz/scripts/gina/archive.py b/lib/lp/soyuz/scripts/gina/archive.py
1087index 6ecb30a..e2f524c 100644
1088--- a/lib/lp/soyuz/scripts/gina/archive.py
1089+++ b/lib/lp/soyuz/scripts/gina/archive.py
1090@@ -22,6 +22,7 @@ import shutil
1091 import tempfile
1092
1093 import apt_pkg
1094+import six
1095
1096 from lp.services.scripts import log
1097 from lp.soyuz.scripts.gina import call
1098@@ -202,13 +203,14 @@ class PackagesMap:
1099 # because most of them are the same for all architectures,
1100 # but we go over it to also cover source packages that only
1101 # compile for one architecture.
1102- sources = apt_pkg.TagFile(info_set.srcfile)
1103+ sources = apt_pkg.TagFile(info_set.srcfile, bytes=True)
1104 try:
1105 for section in sources:
1106 try:
1107 src_tmp = dict(section)
1108- src_tmp['Component'] = info_set.component
1109- src_name = src_tmp['Package']
1110+ src_tmp['Component'] = six.ensure_binary(
1111+ info_set.component)
1112+ src_name = six.ensure_text(src_tmp['Package'])
1113 except KeyError:
1114 log.exception(
1115 "Invalid Sources stanza in %s",
1116@@ -229,13 +231,14 @@ class PackagesMap:
1117
1118 tmpbin_map = self.bin_map[info_set.arch]
1119
1120- binaries = apt_pkg.TagFile(info_set.binfile)
1121+ binaries = apt_pkg.TagFile(info_set.binfile, bytes=True)
1122 for section in binaries:
1123 try:
1124 bin_tmp = dict(section)
1125 # The component isn't listed in the tagfile.
1126- bin_tmp['Component'] = info_set.component
1127- bin_name = bin_tmp['Package']
1128+ bin_tmp['Component'] = six.ensure_binary(
1129+ info_set.component)
1130+ bin_name = six.ensure_text(bin_tmp['Package'])
1131 except KeyError:
1132 log.exception(
1133 "Invalid Releases stanza in %s",
1134@@ -244,12 +247,13 @@ class PackagesMap:
1135 tmpbin_map[bin_name] = bin_tmp
1136
1137 # Run over the D-I stanzas and store info in tmp_bin_map.
1138- dibinaries = apt_pkg.TagFile(info_set.difile)
1139+ dibinaries = apt_pkg.TagFile(info_set.difile, bytes=True)
1140 for section in dibinaries:
1141 try:
1142 dibin_tmp = dict(section)
1143- dibin_tmp['Component'] = info_set.component
1144- dibin_name = dibin_tmp['Package']
1145+ dibin_tmp['Component'] = six.ensure_binary(
1146+ info_set.component)
1147+ dibin_name = six.ensure_text(dibin_tmp['Package'])
1148 except KeyError:
1149 log.exception("Invalid D-I Releases stanza in %s" %
1150 info_set.difile)
1151diff --git a/lib/lp/soyuz/scripts/gina/changelog.py b/lib/lp/soyuz/scripts/gina/changelog.py
1152index bd637da..ec20556 100644
1153--- a/lib/lp/soyuz/scripts/gina/changelog.py
1154+++ b/lib/lp/soyuz/scripts/gina/changelog.py
1155@@ -8,13 +8,15 @@ __metaclass__ = type
1156 import re
1157 import sys
1158
1159-
1160-first_re = re.compile(r"^([a-z0-9][a-z0-9\\+\\.\\-]+)\s+\(([^ ]+)\)")
1161-urgency_re = re.compile(r'(?:urgency|priority)=([^ ,;:.]+)')
1162+import six
1163
1164 from lp.archivepublisher.debversion import Version
1165
1166
1167+first_re = re.compile(br"^([a-z0-9][a-z0-9\\+\\.\\-]+)\s+\(([^ ]+)\)")
1168+urgency_re = re.compile(br'(?:urgency|priority)=([^ ,;:.]+)')
1169+
1170+
1171 def parse_first_line(line):
1172 # SRCPKGNAME (VERSION).*((urgency|priority)=\S+)?
1173 match = first_re.match(line)
1174@@ -33,8 +35,8 @@ def parse_first_line(line):
1175
1176
1177 def parse_last_line(line):
1178- maint = line[:line.find(">") + 1].strip()
1179- date = line[line.find(">") + 1:].strip()
1180+ maint = line[:line.find(b">") + 1].strip()
1181+ date = line[line.find(b">") + 1:].strip()
1182 return (maint, date)
1183
1184
1185@@ -48,40 +50,40 @@ def parse_changelog_stanza(firstline, stanza, lastline):
1186 "urgency": urgency,
1187 "maintainer": maint,
1188 "date": date,
1189- "changes": "".join(stanza).strip("\n")
1190+ "changes": b"".join(stanza).strip(b"\n")
1191 }
1192
1193
1194 def parse_changelog(changelines):
1195 state = 0
1196- firstline = ""
1197+ firstline = b""
1198 stanza = []
1199 rets = []
1200
1201 for line in changelines:
1202 #print line[:-1]
1203 if state == 0:
1204- if (line.startswith(" ") or line.startswith("\t") or
1205+ if (line.startswith(b" ") or line.startswith(b"\t") or
1206 not line.rstrip()):
1207 #print "State0 skip"
1208 continue
1209 try:
1210 (source, version, urgency) = parse_first_line(line.strip())
1211- Version(version)
1212+ Version(six.ensure_text(version))
1213 except:
1214 stanza.append(line)
1215 #print "state0 Exception skip"
1216 continue
1217 firstline = line.strip()
1218- stanza = [line, '\n']
1219+ stanza = [line, b'\n']
1220 state = 1
1221 continue
1222
1223 if state == 1:
1224 stanza.append(line)
1225- stanza.append('\n')
1226+ stanza.append(b'\n')
1227
1228- if line.startswith(" --") and "@" in line:
1229+ if line.startswith(b" --") and b"@" in line:
1230 #print "state1 accept"
1231 # Last line of stanza
1232 rets.append(parse_changelog_stanza(firstline,
1233@@ -93,12 +95,12 @@ def parse_changelog(changelines):
1234 if state == 1:
1235 rets[-1]["changes"] += firstline
1236 if len(rets):
1237- rets[-1]["changes"] += "".join(stanza).strip("\n")
1238+ rets[-1]["changes"] += b"".join(stanza).strip(b"\n")
1239
1240 return rets
1241
1242
1243 if __name__ == '__main__':
1244 import pprint
1245- with open(sys.argv[1]) as f:
1246+ with open(sys.argv[1], "rb") as f:
1247 pprint.pprint(parse_changelog(f))
1248diff --git a/lib/lp/soyuz/scripts/gina/dominate.py b/lib/lp/soyuz/scripts/gina/dominate.py
1249index 6b27a18..0745f92 100644
1250--- a/lib/lp/soyuz/scripts/gina/dominate.py
1251+++ b/lib/lp/soyuz/scripts/gina/dominate.py
1252@@ -8,6 +8,7 @@ __all__ = [
1253 'dominate_imported_source_packages',
1254 ]
1255
1256+import six
1257 from zope.component import getUtility
1258
1259 from lp.archivepublisher.domination import Dominator
1260@@ -27,7 +28,8 @@ def dominate_imported_source_packages(txn, logger, distro_name, series_name,
1261 for package_name, pub_count in package_counts:
1262 entries = packages_map.src_map.get(package_name, [])
1263 live_versions = [
1264- entry['Version'] for entry in entries if 'Version' in entry]
1265+ six.ensure_text(entry['Version'])
1266+ for entry in entries if 'Version' in entry]
1267
1268 # Gina import just ensured that any live version in the Sources
1269 # file has a Published publication. So there should be at least
1270diff --git a/lib/lp/soyuz/scripts/gina/handlers.py b/lib/lp/soyuz/scripts/gina/handlers.py
1271index d3d4333..85f7fce 100644
1272--- a/lib/lp/soyuz/scripts/gina/handlers.py
1273+++ b/lib/lp/soyuz/scripts/gina/handlers.py
1274@@ -513,21 +513,21 @@ class SourcePackageHandler:
1275 # Since the dsc doesn't know, we add in the directory, package
1276 # component and section
1277 dsc_contents['directory'] = os.path.join("pool",
1278- poolify(sp_name, sp_component))
1279- dsc_contents['package'] = sp_name
1280- dsc_contents['component'] = sp_component
1281- dsc_contents['section'] = sp_section
1282+ poolify(sp_name, sp_component)).encode("ASCII")
1283+ dsc_contents['package'] = sp_name.encode("ASCII")
1284+ dsc_contents['component'] = sp_component.encode("ASCII")
1285+ dsc_contents['section'] = sp_section.encode("ASCII")
1286
1287 # the dsc doesn't list itself so add it ourselves
1288 if 'files' not in dsc_contents:
1289 log.error('DSC for %s didn\'t contain a files entry: %r' %
1290 (dsc_name, dsc_contents))
1291 return None
1292- if not dsc_contents['files'].endswith("\n"):
1293- dsc_contents['files'] += "\n"
1294+ if not dsc_contents['files'].endswith(b"\n"):
1295+ dsc_contents['files'] += b"\n"
1296 # XXX kiko 2005-10-21: Why do we hack the md5sum and size of the DSC?
1297 # Should probably calculate it properly.
1298- dsc_contents['files'] += "xxx 000 %s" % dsc_name
1299+ dsc_contents['files'] += ("xxx 000 %s" % dsc_name).encode("ASCII")
1300
1301 # SourcePackageData requires capitals
1302 capitalized_dsc = {}
1303diff --git a/lib/lp/soyuz/scripts/gina/packages.py b/lib/lp/soyuz/scripts/gina/packages.py
1304index 6f294d2..504e907 100644
1305--- a/lib/lp/soyuz/scripts/gina/packages.py
1306+++ b/lib/lp/soyuz/scripts/gina/packages.py
1307@@ -29,6 +29,7 @@ import shutil
1308 import tempfile
1309
1310 import scandir
1311+import six
1312
1313 from lp.app.validators.version import valid_debian_version
1314 from lp.archivepublisher.diskpool import poolify
1315@@ -123,7 +124,7 @@ def read_dsc(package, version, component, distro_name, archive_root):
1316 distro_name, archive_root)
1317
1318 try:
1319- with open(dsc_path) as f:
1320+ with open(dsc_path, "rb") as f:
1321 dsc = f.read().strip()
1322
1323 fullpath = os.path.join(source_dir, "debian", "changelog")
1324@@ -331,11 +332,11 @@ class SourcePackageData(AbstractPackageData):
1325 def __init__(self, **args):
1326 for k, v in args.items():
1327 if k == 'Binary':
1328- self.binaries = stripseq(v.split(","))
1329+ self.binaries = stripseq(six.ensure_text(v).split(","))
1330 elif k == 'Section':
1331- self.section = parse_section(v)
1332+ self.section = parse_section(six.ensure_text(v))
1333 elif k == 'Urgency':
1334- urgency = v
1335+ urgency = six.ensure_text(v)
1336 # This is to handle cases like:
1337 # - debget: 'high (actually works)
1338 # - lxtools: 'low, closes=90239'
1339@@ -345,23 +346,20 @@ class SourcePackageData(AbstractPackageData):
1340 urgency = urgency.split(",")[0]
1341 self.urgency = urgency
1342 elif k == 'Maintainer':
1343- displayname, emailaddress = parse_person(v)
1344 try:
1345- self.maintainer = (
1346- encoding.guess(displayname),
1347- emailaddress,
1348- )
1349+ maintainer = encoding.guess(v)
1350 except UnicodeDecodeError:
1351 raise DisplayNameDecodingError(
1352- "Could not decode name %s" % displayname)
1353+ "Could not decode Maintainer field %r" % v)
1354+ self.maintainer = parse_person(maintainer)
1355 elif k == 'Files' or k.startswith('Checksums-'):
1356 if not hasattr(self, 'files'):
1357 self.files = []
1358- files = v.split("\n")
1359+ files = six.ensure_text(v).split("\n")
1360 for f in files:
1361 self.files.append(stripseq(f.split(" "))[-1])
1362 else:
1363- self.set_field(k, v)
1364+ self.set_field(k, encoding.guess(v))
1365
1366 if self.section is None:
1367 self.section = 'misc'
1368@@ -390,7 +388,7 @@ class SourcePackageData(AbstractPackageData):
1369 self.copyright = encoding.guess(copyright)
1370 parsed_changelog = None
1371 if changelog:
1372- parsed_changelog = parse_changelog(changelog.split('\n'))
1373+ parsed_changelog = parse_changelog(changelog.split(b'\n'))
1374
1375 self.urgency = None
1376 self.changelog = None
1377@@ -398,17 +396,21 @@ class SourcePackageData(AbstractPackageData):
1378 if parsed_changelog and parsed_changelog[0]:
1379 cldata = parsed_changelog[0]
1380 if 'changes' in cldata:
1381- if cldata["package"] != self.package:
1382+ cldata_package = six.ensure_text(cldata["package"])
1383+ cldata_version = six.ensure_text(cldata["version"])
1384+ if cldata_package != self.package:
1385 log.warning(
1386 "Changelog package %s differs from %s" %
1387- (cldata["package"], self.package))
1388- if cldata["version"] != self.version:
1389+ (cldata_package, self.package))
1390+ if cldata_version != self.version:
1391 log.warning(
1392 "Changelog version %s differs from %s" %
1393- (cldata["version"], self.version))
1394+ (cldata_version, self.version))
1395 self.changelog_entry = encoding.guess(cldata["changes"])
1396 self.changelog = changelog
1397 self.urgency = cldata["urgency"]
1398+ if self.urgency is not None:
1399+ self.urgency = six.ensure_text(self.urgency)
1400 else:
1401 log.warning(
1402 "Changelog empty for source %s (%s)" %
1403@@ -483,11 +485,11 @@ class BinaryPackageData(AbstractPackageData):
1404 def __init__(self, **args):
1405 for k, v in args.items():
1406 if k == "Maintainer":
1407- self.maintainer = parse_person(v)
1408+ self.maintainer = parse_person(encoding.guess(v))
1409 elif k == "Essential":
1410- self.essential = (v == "yes")
1411+ self.essential = (v == b"yes")
1412 elif k == 'Section':
1413- self.section = parse_section(v)
1414+ self.section = parse_section(six.ensure_text(v))
1415 elif k == "Description":
1416 self.description = encoding.guess(v)
1417 summary = self.description.split("\n")[0].strip()
1418@@ -495,21 +497,22 @@ class BinaryPackageData(AbstractPackageData):
1419 summary = summary + '.'
1420 self.summary = summary
1421 elif k == "Installed-Size":
1422+ installed_size = six.ensure_text(v)
1423 try:
1424- self.installed_size = int(v)
1425+ self.installed_size = int(installed_size)
1426 except ValueError:
1427 raise MissingRequiredArguments("Installed-Size is "
1428- "not a valid integer: %r" % v)
1429+ "not a valid integer: %r" % installed_size)
1430 elif k == "Built-Using":
1431- self.built_using = v
1432+ self.built_using = six.ensure_text(v)
1433 # Preserve the original form of Built-Using to avoid
1434 # possible unfortunate apt behaviour. This is most easily
1435 # done by adding it to _user_defined as well.
1436 if self._user_defined is None:
1437 self._user_defined = []
1438- self._user_defined.append([k, v])
1439+ self._user_defined.append([k, self.built_using])
1440 else:
1441- self.set_field(k, v)
1442+ self.set_field(k, encoding.guess(v))
1443
1444 if self.source:
1445 # We need to handle cases like "Source: myspell
1446diff --git a/lib/lp/soyuz/scripts/gina/runner.py b/lib/lp/soyuz/scripts/gina/runner.py
1447index f5056c6..c956744 100644
1448--- a/lib/lp/soyuz/scripts/gina/runner.py
1449+++ b/lib/lp/soyuz/scripts/gina/runner.py
1450@@ -10,6 +10,7 @@ import sys
1451 import time
1452
1453 import psycopg2
1454+import six
1455 from zope.component import getUtility
1456
1457 from lp.registry.interfaces.pocket import PackagePublishingPocket
1458@@ -120,7 +121,7 @@ def run_gina(options, ztm, target_section):
1459 def attempt_source_package_import(distro, source, package_root,
1460 importer_handler):
1461 """Attempt to import a source package, and handle typical errors."""
1462- package_name = source.get("Package", "unknown")
1463+ package_name = six.ensure_text(source.get("Package", "unknown"))
1464 try:
1465 try:
1466 do_one_sourcepackage(
1467diff --git a/lib/lp/soyuz/scripts/tests/test_gina.py b/lib/lp/soyuz/scripts/tests/test_gina.py
1468index e36bc6a..0fbd765 100644
1469--- a/lib/lp/soyuz/scripts/tests/test_gina.py
1470+++ b/lib/lp/soyuz/scripts/tests/test_gina.py
1471@@ -12,6 +12,7 @@ from unittest import TestLoader
1472
1473 import apt_pkg
1474 from fixtures import EnvironmentVariableFixture
1475+import six
1476 from testtools.matchers import (
1477 MatchesSetwise,
1478 MatchesStructure,
1479@@ -274,10 +275,10 @@ class TestSourcePackageData(TestCaseWithFactory):
1480 len(debian_tar_contents))))
1481
1482 dsc_contents = parse_tagfile(dsc_path)
1483- dsc_contents["Directory"] = pool_dir
1484- dsc_contents["Package"] = "foo"
1485- dsc_contents["Component"] = "main"
1486- dsc_contents["Section"] = "misc"
1487+ dsc_contents["Directory"] = six.ensure_binary(pool_dir)
1488+ dsc_contents["Package"] = b"foo"
1489+ dsc_contents["Component"] = b"main"
1490+ dsc_contents["Section"] = b"misc"
1491
1492 sp_data = SourcePackageData(**dsc_contents)
1493 # Unpacking this in an Ubuntu context fails.
1494@@ -329,10 +330,10 @@ class TestSourcePackageData(TestCaseWithFactory):
1495 len(debian_tar_contents))))
1496
1497 dsc_contents = parse_tagfile(dsc_path)
1498- dsc_contents["Directory"] = pool_dir
1499- dsc_contents["Package"] = "foo"
1500- dsc_contents["Component"] = "main"
1501- dsc_contents["Section"] = "misc"
1502+ dsc_contents["Directory"] = six.ensure_binary(pool_dir)
1503+ dsc_contents["Package"] = b"foo"
1504+ dsc_contents["Component"] = b"main"
1505+ dsc_contents["Section"] = b"misc"
1506
1507 sp_data = SourcePackageData(**dsc_contents)
1508 unpack_tmpdir = self.makeTemporaryDirectory()
1509@@ -351,20 +352,20 @@ class TestSourcePackageData(TestCaseWithFactory):
1510 def test_checksum_fields(self):
1511 # We only need one of Files or Checksums-*.
1512 base_dsc_contents = {
1513- "Package": "foo",
1514- "Binary": "foo",
1515- "Version": "1.0-1",
1516- "Maintainer": "Foo Bar <foo@canonical.com>",
1517- "Section": "misc",
1518- "Architecture": "all",
1519- "Directory": "pool/main/f/foo",
1520- "Component": "main",
1521+ "Package": b"foo",
1522+ "Binary": b"foo",
1523+ "Version": b"1.0-1",
1524+ "Maintainer": b"Foo Bar <foo@canonical.com>",
1525+ "Section": b"misc",
1526+ "Architecture": b"all",
1527+ "Directory": b"pool/main/f/foo",
1528+ "Component": b"main",
1529 }
1530 for field in (
1531 "Files", "Checksums-Sha1", "Checksums-Sha256",
1532 "Checksums-Sha512"):
1533 dsc_contents = dict(base_dsc_contents)
1534- dsc_contents[field] = "xxx 000 foo_1.0-1.dsc"
1535+ dsc_contents[field] = b"xxx 000 foo_1.0-1.dsc"
1536 sp_data = SourcePackageData(**dsc_contents)
1537 self.assertEqual(["foo_1.0-1.dsc"], sp_data.files)
1538 self.assertRaises(
1539@@ -382,18 +383,18 @@ class TestSourcePackageHandler(TestCaseWithFactory):
1540 series.distribution.name, archive_root,
1541 PackagePublishingPocket.RELEASE, None)
1542 dsc_contents = {
1543- "Format": "3.0 (quilt)",
1544- "Source": "foo",
1545- "Binary": "foo",
1546- "Architecture": "all arm64",
1547- "Version": "1.0-1",
1548- "Maintainer": "Foo Bar <foo@canonical.com>",
1549- "Files": "xxx 000 foo_1.0-1.dsc",
1550- "Build-Indep-Architecture": "amd64",
1551- "Directory": "pool/main/f/foo",
1552- "Package": "foo",
1553- "Component": "main",
1554- "Section": "misc",
1555+ "Format": b"3.0 (quilt)",
1556+ "Source": b"foo",
1557+ "Binary": b"foo",
1558+ "Architecture": b"all arm64",
1559+ "Version": b"1.0-1",
1560+ "Maintainer": b"Foo Bar <foo@canonical.com>",
1561+ "Files": b"xxx 000 foo_1.0-1.dsc",
1562+ "Build-Indep-Architecture": b"amd64",
1563+ "Directory": b"pool/main/f/foo",
1564+ "Package": b"foo",
1565+ "Component": b"main",
1566+ "Section": b"misc",
1567 }
1568 sp_data = SourcePackageData(**dsc_contents)
1569 self.assertEqual(
1570@@ -427,9 +428,10 @@ class TestSourcePackagePublisher(TestCaseWithFactory):
1571
1572 publisher = SourcePackagePublisher(series, pocket, None)
1573 publisher.publish(spr, SourcePackageData(
1574- component='main', section=section.name, version='1.0',
1575- maintainer=maintainer.preferredemail, architecture='all',
1576- files='foo.py', binaries='foo.py'))
1577+ component=b'main', section=section.name.encode('ASCII'),
1578+ version=b'1.0',
1579+ maintainer=maintainer.preferredemail.email.encode('ASCII'),
1580+ architecture=b'all', files=b'foo.py', binaries=b'foo.py'))
1581
1582 [spph] = series.main_archive.getPublishedSources()
1583 self.assertEqual(PackagePublishingStatus.PUBLISHED, spph.status)
1584@@ -442,21 +444,21 @@ class TestBinaryPackageData(TestCaseWithFactory):
1585 def test_checksum_fields(self):
1586 # We only need one of MD5sum or SHA*.
1587 base_deb_contents = {
1588- "Package": "foo",
1589- "Installed-Size": "0",
1590- "Maintainer": "Foo Bar <foo@canonical.com>",
1591- "Section": "misc",
1592- "Architecture": "all",
1593- "Version": "1.0-1",
1594- "Filename": "pool/main/f/foo/foo_1.0-1_all.deb",
1595- "Component": "main",
1596- "Size": "0",
1597- "Description": "",
1598- "Priority": "extra",
1599+ "Package": b"foo",
1600+ "Installed-Size": b"0",
1601+ "Maintainer": b"Foo Bar <foo@canonical.com>",
1602+ "Section": b"misc",
1603+ "Architecture": b"all",
1604+ "Version": b"1.0-1",
1605+ "Filename": b"pool/main/f/foo/foo_1.0-1_all.deb",
1606+ "Component": b"main",
1607+ "Size": b"0",
1608+ "Description": b"",
1609+ "Priority": b"extra",
1610 }
1611 for field in ("MD5sum", "SHA1", "SHA256", "SHA512"):
1612 deb_contents = dict(base_deb_contents)
1613- deb_contents[field] = "0"
1614+ deb_contents[field] = b"0"
1615 BinaryPackageData(**deb_contents)
1616 self.assertRaises(
1617 MissingRequiredArguments, BinaryPackageData, **base_deb_contents)
1618@@ -477,21 +479,21 @@ class TestBinaryPackageHandler(TestCaseWithFactory):
1619 spr = self.factory.makeSourcePackageRelease(
1620 distroseries=das.distroseries)
1621 deb_contents = {
1622- "Package": "foo",
1623- "Installed-Size": "0",
1624- "Maintainer": "Foo Bar <foo@canonical.com>",
1625- "Section": "misc",
1626- "Architecture": "amd64",
1627- "Version": "1.0-1",
1628- "Filename": "pool/main/f/foo/foo_1.0-1_amd64.deb",
1629- "Component": "main",
1630- "Size": "0",
1631- "MD5sum": "0" * 32,
1632- "Description": "",
1633- "Summary": "",
1634- "Priority": "extra",
1635- "Python-Version": "2.7",
1636- "Built-Using": "nonexistent (= 0.1)",
1637+ "Package": b"foo",
1638+ "Installed-Size": b"0",
1639+ "Maintainer": b"Foo Bar <foo@canonical.com>",
1640+ "Section": b"misc",
1641+ "Architecture": b"amd64",
1642+ "Version": b"1.0-1",
1643+ "Filename": b"pool/main/f/foo/foo_1.0-1_amd64.deb",
1644+ "Component": b"main",
1645+ "Size": b"0",
1646+ "MD5sum": b"0" * 32,
1647+ "Description": b"",
1648+ "Summary": b"",
1649+ "Priority": b"extra",
1650+ "Python-Version": b"2.7",
1651+ "Built-Using": b"nonexistent (= 0.1)",
1652 }
1653 bp_data = BinaryPackageData(**deb_contents)
1654 self.assertContentEqual(
1655@@ -529,20 +531,20 @@ class TestBinaryPackageHandler(TestCaseWithFactory):
1656 built_using_relationship = "%s (= %s)" % (
1657 built_using_spr.name, built_using_spr.version)
1658 deb_contents = {
1659- "Package": "foo",
1660- "Installed-Size": "0",
1661- "Maintainer": "Foo Bar <foo@canonical.com>",
1662- "Section": "misc",
1663- "Architecture": "amd64",
1664- "Version": "1.0-1",
1665- "Filename": "pool/main/f/foo/foo_1.0-1_amd64.deb",
1666- "Component": "main",
1667- "Size": "0",
1668- "MD5sum": "0" * 32,
1669- "Description": "",
1670- "Summary": "",
1671- "Priority": "extra",
1672- "Built-Using": built_using_relationship,
1673+ "Package": b"foo",
1674+ "Installed-Size": b"0",
1675+ "Maintainer": b"Foo Bar <foo@canonical.com>",
1676+ "Section": b"misc",
1677+ "Architecture": b"amd64",
1678+ "Version": b"1.0-1",
1679+ "Filename": b"pool/main/f/foo/foo_1.0-1_amd64.deb",
1680+ "Component": b"main",
1681+ "Size": b"0",
1682+ "MD5sum": b"0" * 32,
1683+ "Description": b"",
1684+ "Summary": b"",
1685+ "Priority": b"extra",
1686+ "Built-Using": built_using_relationship.encode("ASCII"),
1687 }
1688 bp_data = BinaryPackageData(**deb_contents)
1689 self.assertContentEqual(
1690@@ -579,11 +581,13 @@ class TestBinaryPackagePublisher(TestCaseWithFactory):
1691
1692 publisher = BinaryPackagePublisher(series, pocket, None)
1693 publisher.publish(bpr, BinaryPackageData(
1694- component='main', section=section.name, version='1.0',
1695- maintainer=maintainer.preferredemail, architecture='all',
1696- files='foo.py', binaries='foo.py', size=128, installed_size=1024,
1697- md5sum='e83b5dd68079d727a494a469d40dc8db', description='test',
1698- summary='Test!'))
1699+ component=b'main', section=section.name.encode('ASCII'),
1700+ version=b'1.0',
1701+ maintainer=maintainer.preferredemail.email.encode('ASCII'),
1702+ architecture=b'all', files=b'foo.py', binaries=b'foo.py',
1703+ size=128, installed_size=1024,
1704+ md5sum=b'e83b5dd68079d727a494a469d40dc8db', description=b'test',
1705+ summary=b'Test!'))
1706
1707 [bpph] = series.main_archive.getAllPublishedBinaries()
1708 self.assertEqual(PackagePublishingStatus.PUBLISHED, bpph.status)

Subscribers

People subscribed via source and target branches

to status/vote changes: