Merge ~cjwatson/launchpad:py3-base64-types into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: ac80d2d37760f34a71254726bab51c220ecc79cc
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:py3-base64-types
Merge into: launchpad:master
Diff against target: 260 lines (+34/-28)
17 files modified
lib/lp/app/browser/stringformatter.py (+2/-1)
lib/lp/archivepublisher/htaccess.py (+2/-1)
lib/lp/code/scripts/tests/test_request_daily_builds.py (+4/-1)
lib/lp/registry/model/person.py (+2/-2)
lib/lp/registry/stories/team-polls/xx-poll-confirm-vote.txt (+2/-1)
lib/lp/services/gpg/tests/test_gpghandler.py (+0/-6)
lib/lp/services/signing/tests/test_proxy.py (+2/-3)
lib/lp/services/webapp/doc/webapp-publication.txt (+1/-1)
lib/lp/services/webapp/tests/test_authutility.py (+3/-3)
lib/lp/services/webservice/stories/xx-service.txt (+3/-1)
lib/lp/snappy/model/snapbuildbehaviour.py (+1/-1)
lib/lp/snappy/model/snapstoreclient.py (+2/-2)
lib/lp/soyuz/adapters/archivedependencies.py (+2/-1)
lib/lp/soyuz/tests/soyuz.py (+1/-1)
lib/lp/testing/factory.py (+2/-1)
utilities/massage-bug-import-xml (+2/-1)
utilities/roundup-sniffer.py (+3/-1)
Reviewer Review Type Date Requested Status
Cristian Gonzalez (community) Approve
Review via email: mp+398930@code.launchpad.net

Commit message

Use correct types for base64 encoding/decoding

Description of the change

On Python 3, base64.b64encode takes bytes and returns bytes. base64.b64decode in fact takes either bytes or ASCII-only Unicode strings and returns bytes, but it's more symmetrical and thus clearer to treat it as if it only took bytes.

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

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/app/browser/stringformatter.py b/lib/lp/app/browser/stringformatter.py
2index c791cf7..b816ae7 100644
3--- a/lib/lp/app/browser/stringformatter.py
4+++ b/lib/lp/app/browser/stringformatter.py
5@@ -1084,7 +1084,8 @@ class FormattersAPI:
6 # we also have to strip off any padding characters ("=") because
7 # Python's URL-safe base 64 encoding includes those and they
8 # aren't allowed in IDs either.
9- unique_suffix = urlsafe_b64encode(raw_text)
10+ unique_suffix = (
11+ urlsafe_b64encode(raw_text.encode('ASCII')).decode('ASCII'))
12 # Ensure we put a '-' between the id and base 64 encoding.
13 if id_[-1] != '-':
14 id_ += '-'
15diff --git a/lib/lp/archivepublisher/htaccess.py b/lib/lp/archivepublisher/htaccess.py
16index 1d29fb8..613cfde 100644
17--- a/lib/lp/archivepublisher/htaccess.py
18+++ b/lib/lp/archivepublisher/htaccess.py
19@@ -76,7 +76,8 @@ def make_salt(s):
20 """
21 # As long as the input string is at least one character long, there will
22 # be no padding within the first two characters.
23- return base64.b64encode((s or " ").encode("UTF-8"), altchars=b"./")[:2]
24+ return base64.b64encode(
25+ (s or " ").encode("UTF-8"), altchars=b"./")[:2].decode("ASCII")
26
27
28 def htpasswd_credentials_for_archive(archive):
29diff --git a/lib/lp/code/scripts/tests/test_request_daily_builds.py b/lib/lp/code/scripts/tests/test_request_daily_builds.py
30index 1be9b9a..0f16bc8 100644
31--- a/lib/lp/code/scripts/tests/test_request_daily_builds.py
32+++ b/lib/lp/code/scripts/tests/test_request_daily_builds.py
33@@ -142,7 +142,10 @@ class FakeTurnipApplication:
34 if filename not in self.contents[repository_id]:
35 return self._not_found(start_response)
36 blob = self.contents[repository_id][filename]
37- response = {'size': len(blob), 'data': base64.b64encode(blob)}
38+ response = {
39+ 'size': len(blob),
40+ 'data': base64.b64encode(blob).decode('ASCII'),
41+ }
42 start_response(
43 '200 OK', [('Content-Type', 'application/octet-stream')])
44 return [json.dumps(response).encode('UTF-8')]
45diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
46index ec5226d..624fab2 100644
47--- a/lib/lp/registry/model/person.py
48+++ b/lib/lp/registry/model/person.py
49@@ -4096,8 +4096,8 @@ class SSHKey(SQLBase):
50
51 def getFullKeyText(self):
52 try:
53- ssh_keytype = getNS(base64.b64decode(self.keytext))[0].decode(
54- 'ascii')
55+ key_blob = base64.b64decode(self.keytext.encode('UTF-8'))
56+ ssh_keytype = getNS(key_blob)[0].decode('ascii')
57 except Exception:
58 # We didn't always validate keys, so there might be some that
59 # can't be loaded this way.
60diff --git a/lib/lp/registry/stories/team-polls/xx-poll-confirm-vote.txt b/lib/lp/registry/stories/team-polls/xx-poll-confirm-vote.txt
61index 27b6cb3..0b4628a 100644
62--- a/lib/lp/registry/stories/team-polls/xx-poll-confirm-vote.txt
63+++ b/lib/lp/registry/stories/team-polls/xx-poll-confirm-vote.txt
64@@ -2,7 +2,8 @@
65 results of the director-2004 poll.
66
67 >>> import base64
68- >>> jdub_auth = base64.b64encode('jeff.waugh@ubuntulinux.com:test')
69+ >>> jdub_auth = base64.b64encode(
70+ ... b'jeff.waugh@ubuntulinux.com:test').decode('ASCII')
71 >>> print(http(r"""
72 ... GET /~ubuntu-team/+poll/director-2004 HTTP/1.1
73 ... Authorization: Basic %s
74diff --git a/lib/lp/services/gpg/tests/test_gpghandler.py b/lib/lp/services/gpg/tests/test_gpghandler.py
75index 297db6c..e929187 100644
76--- a/lib/lp/services/gpg/tests/test_gpghandler.py
77+++ b/lib/lp/services/gpg/tests/test_gpghandler.py
78@@ -1,7 +1,6 @@
79 # Copyright 2009-2020 Canonical Ltd. This software is licensed under the
80 # GNU Affero General Public License version 3 (see the file LICENSE).
81
82-import base64
83 from datetime import datetime
84 import os
85 import shutil
86@@ -507,8 +506,3 @@ class TestGPGHandler(TestCase):
87 line for line in status if line.startswith(validsig_prefix)]
88 validsig_tokens = validsig_line[len(validsig_prefix):].split()
89 self.assertEqual(gpgme.MD_SHA512, int(validsig_tokens[7]))
90-
91-
92-def construct_url(template, owner_id='', fingerprint=''):
93- owner_id = base64.b64encode(owner_id, altchars='-_')
94- return template.format(owner_id=owner_id, fingerprint=fingerprint)
95diff --git a/lib/lp/services/signing/tests/test_proxy.py b/lib/lp/services/signing/tests/test_proxy.py
96index f50aa8c..4405fe7 100644
97--- a/lib/lp/services/signing/tests/test_proxy.py
98+++ b/lib/lp/services/signing/tests/test_proxy.py
99@@ -72,7 +72,6 @@ class SigningServiceResponseFactory:
100 bytes(self.generated_public_key))
101 self.generated_fingerprint = (
102 u'338D218488DFD597D8FCB9C328C3E9D9ADA16CEE')
103- self.b64_signed_msg = base64.b64encode(b"the-signed-msg")
104
105 self.signed_msg_template = "%s::signed!"
106
107@@ -169,8 +168,8 @@ class SigningServiceResponseFactory:
108
109 def sign_callback(request):
110 call_counts['/sign'] += 1
111- signed = base64.b64encode(
112- self.signed_msg_template % call_counts['/sign'])
113+ signed_msg = self.signed_msg_template % call_counts['/sign']
114+ signed = base64.b64encode(signed_msg.encode('utf8'))
115 data = {'signed-message': signed.decode('utf8'),
116 'public-key': self.b64_generated_public_key.decode('utf8')}
117 return 201, {}, self._encryptPayload(data, self.response_nonce)
118diff --git a/lib/lp/services/webapp/doc/webapp-publication.txt b/lib/lp/services/webapp/doc/webapp-publication.txt
119index 8af06ab..d1f0ff3 100644
120--- a/lib/lp/services/webapp/doc/webapp-publication.txt
121+++ b/lib/lp/services/webapp/doc/webapp-publication.txt
122@@ -1103,7 +1103,7 @@ utility to setup the request.
123 >>> import base64
124 >>> login(ANONYMOUS) # Get rid of the marker object in the interaction.
125 >>> foo_bar_auth = 'Basic %s' % (
126- ... base64.b64encode('foo.bar@canonical.com:test'))
127+ ... base64.b64encode(b'foo.bar@canonical.com:test').decode('ASCII'))
128 >>> request, publication = get_request_and_publication(
129 ... extra_environment=dict(HTTP_AUTHORIZATION=foo_bar_auth))
130 >>> principal = publication.getPrincipal(request)
131diff --git a/lib/lp/services/webapp/tests/test_authutility.py b/lib/lp/services/webapp/tests/test_authutility.py
132index 279dd3d..6d7000b 100644
133--- a/lib/lp/services/webapp/tests/test_authutility.py
134+++ b/lib/lp/services/webapp/tests/test_authutility.py
135@@ -79,9 +79,9 @@ class TestPlacelessAuth(ContainerPlacelessSetup, testtools.TestCase):
136 testtools.TestCase.tearDown(self)
137
138 def _make(self, login, pwd):
139- dict = {
140- 'HTTP_AUTHORIZATION':
141- 'Basic %s' % base64.b64encode('%s:%s' % (login, pwd))}
142+ auth = base64.b64encode(
143+ ('%s:%s' % (login, pwd)).encode('ASCII')).decode('ASCII')
144+ dict = {'HTTP_AUTHORIZATION': 'Basic %s' % auth}
145 request = TestRequest(**dict)
146 return getUtility(IPlacelessAuthUtility), request
147
148diff --git a/lib/lp/services/webservice/stories/xx-service.txt b/lib/lp/services/webservice/stories/xx-service.txt
149index f3a5d3f..c07704b 100644
150--- a/lib/lp/services/webservice/stories/xx-service.txt
151+++ b/lib/lp/services/webservice/stories/xx-service.txt
152@@ -166,7 +166,9 @@ scheme (and don't require OAuth).
153 ... LaunchpadWebServiceCaller)
154 >>> noauth_webservice = LaunchpadWebServiceCaller(
155 ... domain='bugs.launchpad.test')
156- >>> sample_auth = 'Basic %s' % base64.b64encode('test@canonical.com:test')
157+ >>> sample_auth = (
158+ ... 'Basic %s' %
159+ ... base64.b64encode(b'test@canonical.com:test').decode('ASCII'))
160 >>> print(noauth_webservice.get(
161 ... 'http://bugs.launchpad.test/api/devel/people/+me',
162 ... headers={'Authorization': sample_auth}))
163diff --git a/lib/lp/snappy/model/snapbuildbehaviour.py b/lib/lp/snappy/model/snapbuildbehaviour.py
164index cdb374f..4dd89f0 100644
165--- a/lib/lp/snappy/model/snapbuildbehaviour.py
166+++ b/lib/lp/snappy/model/snapbuildbehaviour.py
167@@ -94,7 +94,7 @@ class SnapProxyMixin:
168 build_id=self.build.build_cookie,
169 timestamp=timestamp)
170 auth_string = '{}:{}'.format(admin_username, secret).strip()
171- auth_header = b'Basic ' + base64.b64encode(auth_string)
172+ auth_header = b'Basic ' + base64.b64encode(auth_string.encode('ASCII'))
173
174 token = yield self._slave.process_pool.doWork(
175 RequestProxyTokenCommand,
176diff --git a/lib/lp/snappy/model/snapstoreclient.py b/lib/lp/snappy/model/snapstoreclient.py
177index 917559d..8055438 100644
178--- a/lib/lp/snappy/model/snapstoreclient.py
179+++ b/lib/lp/snappy/model/snapstoreclient.py
180@@ -98,8 +98,8 @@ class MacaroonAuth(requests.auth.AuthBase):
181 try:
182 _, key, value = caveat.caveat_id.split("|")
183 if key == "account":
184- account = json.loads(
185- base64.b64decode(value).decode("UTF-8"))
186+ account = json.loads(base64.b64decode(
187+ value.encode("UTF-8")).decode("UTF-8"))
188 if "openid" in account:
189 self.logger.debug(
190 "%s macaroon: OpenID identifier: %s" %
191diff --git a/lib/lp/soyuz/adapters/archivedependencies.py b/lib/lp/soyuz/adapters/archivedependencies.py
192index d3cbff3..12e5ff2 100644
193--- a/lib/lp/soyuz/adapters/archivedependencies.py
194+++ b/lib/lp/soyuz/adapters/archivedependencies.py
195@@ -405,7 +405,8 @@ def _get_sources_list_for_dependencies(dependencies, logger=None):
196
197 key = yield deferToThread(get_key)
198 if key is not None:
199- trusted_keys[fingerprint] = base64.b64encode(key.export())
200+ trusted_keys[fingerprint] = (
201+ base64.b64encode(key.export()).decode("ASCII"))
202
203 defer.returnValue(
204 (sources_list_lines, [v for k, v in sorted(trusted_keys.items())]))
205diff --git a/lib/lp/soyuz/tests/soyuz.py b/lib/lp/soyuz/tests/soyuz.py
206index 52c9fb7..5e0b9fe 100644
207--- a/lib/lp/soyuz/tests/soyuz.py
208+++ b/lib/lp/soyuz/tests/soyuz.py
209@@ -29,7 +29,7 @@ class Base64KeyMatches(Matcher):
210 self.fingerprint = fingerprint
211
212 def match(self, encoded_key):
213- key = base64.b64decode(encoded_key)
214+ key = base64.b64decode(encoded_key.encode("ASCII"))
215 return Equals(self.fingerprint).match(
216 getUtility(IGPGHandler).importPublicKey(key).fingerprint)
217
218diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
219index 9661a8f..2717264 100644
220--- a/lib/lp/testing/factory.py
221+++ b/lib/lp/testing/factory.py
222@@ -4392,7 +4392,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
223 raise AssertionError(
224 "key_type must be a member of SSH_TEXT_TO_KEY_TYPE, not %r" %
225 key_type)
226- key_text = base64.b64encode(NS(key_type) + b"".join(parameters))
227+ key_text = base64.b64encode(
228+ NS(key_type) + b"".join(parameters)).decode("ASCII")
229 if comment is None:
230 comment = self.getUniqueString()
231 return "%s %s %s" % (key_type, key_text, comment)
232diff --git a/utilities/massage-bug-import-xml b/utilities/massage-bug-import-xml
233index 7d92f84..5a304be 100755
234--- a/utilities/massage-bug-import-xml
235+++ b/utilities/massage-bug-import-xml
236@@ -178,7 +178,8 @@ def massage(root, project_name, fix_nickname, tag_nickname):
237 u"text/plain")
238 etree.SubElement(attachment, '{%s}contents' % NS).text = (
239 standard_b64encode(
240- first_comment_text.text.encode('utf-8')))
241+ first_comment_text.text.encode('utf-8')
242+ ).decode('ascii'))
243 # Trim the comment text.
244 problem_resolution('Trimming comment text.')
245 first_comment_text.text = truncate(
246diff --git a/utilities/roundup-sniffer.py b/utilities/roundup-sniffer.py
247index 224da01..3035d0e 100755
248--- a/utilities/roundup-sniffer.py
249+++ b/utilities/roundup-sniffer.py
250@@ -66,7 +66,9 @@ class RoundupSniffer:
251
252 def fetch(self, url):
253 """Fetch the URL, consulting the cache first."""
254- filename = join(self.cache_dir, urlsafe_b64encode(url))
255+ filename = join(
256+ self.cache_dir,
257+ urlsafe_b64encode(url.encode('UTF-8')).decode('ASCII'))
258 if not exists(filename):
259 open(filename, 'wb').write(urlopen(url).read())
260 return open(filename, 'rb')

Subscribers

People subscribed via source and target branches

to status/vote changes: