Gentoo Archives: gentoo-commits

From: "Michał Górny" <mgorny@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: dev-python/twisted/, dev-python/twisted/files/
Date: Wed, 27 May 2020 15:22:21
Message-Id: 1590592933.7766611623513856da2ed5dc3552186453922282.mgorny@gentoo
1 commit: 7766611623513856da2ed5dc3552186453922282
2 Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
3 AuthorDate: Wed May 27 15:10:26 2020 +0000
4 Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
5 CommitDate: Wed May 27 15:22:13 2020 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=77666116
7
8 dev-python/twisted: Port to py39
9
10 Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
11
12 .../twisted/files/twisted-19.10.0-py39-b64.patch | 165 +++++++++++++++++++++
13 .../twisted/files/twisted-20.3.0-py39-b64.patch | 158 ++++++++++++++++++++
14 .../files/twisted-20.3.0-py39-combined.patch | 115 ++++++++++++++
15 dev-python/twisted/twisted-19.10.0.ebuild | 4 +-
16 dev-python/twisted/twisted-20.3.0.ebuild | 4 +-
17 5 files changed, 444 insertions(+), 2 deletions(-)
18
19 diff --git a/dev-python/twisted/files/twisted-19.10.0-py39-b64.patch b/dev-python/twisted/files/twisted-19.10.0-py39-b64.patch
20 new file mode 100644
21 index 00000000000..f67d6240558
22 --- /dev/null
23 +++ b/dev-python/twisted/files/twisted-19.10.0-py39-b64.patch
24 @@ -0,0 +1,165 @@
25 +From f56133a2e0d7ddf9ee6e43bf9e1d62e970cb0b3a Mon Sep 17 00:00:00 2001
26 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
27 +Date: Wed, 27 May 2020 13:23:37 +0200
28 +Subject: [PATCH] Replace base64.*string() functions to fix py3.9 support
29 +
30 +Replace base64.decodestring() and .encodestring() functions as they
31 +were deprecated since Python 3.1 in favor of (equivalent) .decodebytes()
32 +and .encodebytes(), and were eventually removed in Python 3.9.
33 +
34 +While at it, replace most of their uses with base64.b64encode()
35 +and .b64decode() that are preferable to the former wrt ticket #6446,
36 +and they do not introduce line breaks that the twisted code usually
37 +discarded.
38 +
39 +Use .decodebytes() and .encodebytes() in DirDBM as it seems to rely
40 +on the exact presence of newlines, and changing that would break
41 +backwards compatibility.
42 +
43 +Fixes: ticket:6446
44 +Fixes: ticket:9831
45 +---
46 + src/twisted/conch/scripts/tkconch.py | 2 +-
47 + src/twisted/conch/test/test_keys.py | 2 +-
48 + src/twisted/mail/pop3.py | 4 ++--
49 + src/twisted/mail/test/test_pop3.py | 4 ++--
50 + src/twisted/persisted/dirdbm.py | 10 ++++++++--
51 + src/twisted/web/http.py | 2 +-
52 + src/twisted/web/test/test_http.py | 6 +++---
53 + 14 files changed, 18 insertions(+), 12 deletions(-)
54 +
55 +diff --git a/src/twisted/conch/scripts/tkconch.py b/src/twisted/conch/scripts/tkconch.py
56 +index 9c48e8a7f..5e007ebdc 100644
57 +--- a/src/twisted/conch/scripts/tkconch.py
58 ++++ b/src/twisted/conch/scripts/tkconch.py
59 +@@ -409,7 +409,7 @@ class SSHClientTransport(transport.SSHClientTransport):
60 + "known hosts.\r\n" %
61 + (khHost, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType]))
62 + with open(os.path.expanduser('~/.ssh/known_hosts'), 'a') as known_hosts:
63 +- encodedKey = base64.encodestring(pubKey).replace(b'\n', b'')
64 ++ encodedKey = base64.b64encode(pubKey)
65 + known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey))
66 + except:
67 + log.deferr()
68 +diff --git a/src/twisted/conch/test/test_keys.py b/src/twisted/conch/test/test_keys.py
69 +index 41e49f415..795e7b8d7 100644
70 +--- a/src/twisted/conch/test/test_keys.py
71 ++++ b/src/twisted/conch/test/test_keys.py
72 +@@ -352,7 +352,7 @@ SUrCyZXsNh6VXwjs3gKQ
73 +
74 + self.assertRaises(
75 + keys.BadKeyError,
76 +- keys.Key.fromString, data=b'{' + base64.encodestring(sexp) + b'}',
77 ++ keys.Key.fromString, data=b'{' + base64.b64encode(sexp) + b'}',
78 + )
79 +
80 +
81 +diff --git a/src/twisted/mail/pop3.py b/src/twisted/mail/pop3.py
82 +index ffe9714c9..057389e3a 100644
83 +--- a/src/twisted/mail/pop3.py
84 ++++ b/src/twisted/mail/pop3.py
85 +@@ -728,7 +728,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
86 + self._auth = auth()
87 + chal = self._auth.getChallenge()
88 +
89 +- self.sendLine(b'+ ' + base64.encodestring(chal).rstrip(b'\n'))
90 ++ self.sendLine(b'+ ' + base64.b64encode(chal))
91 + self.state = 'AUTH'
92 +
93 +
94 +@@ -747,7 +747,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
95 + """
96 + self.state = "COMMAND"
97 + try:
98 +- parts = base64.decodestring(line).split(None, 1)
99 ++ parts = base64.b64decode(line).split(None, 1)
100 + except binascii.Error:
101 + self.failResponse(b"Invalid BASE64 encoding")
102 + else:
103 +diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py
104 +index ea513487c..36780d9c9 100644
105 +--- a/src/twisted/mail/test/test_pop3.py
106 ++++ b/src/twisted/mail/test/test_pop3.py
107 +@@ -1097,12 +1097,12 @@ class SASLTests(unittest.TestCase):
108 +
109 + p.lineReceived(b"AUTH CRAM-MD5")
110 + chal = s.getvalue().splitlines()[-1][2:]
111 +- chal = base64.decodestring(chal)
112 ++ chal = base64.b64decode(chal)
113 + response = hmac.HMAC(b'testpassword', chal,
114 + digestmod=md5).hexdigest().encode("ascii")
115 +
116 + p.lineReceived(
117 +- base64.encodestring(b'testuser ' + response).rstrip(b'\n'))
118 ++ base64.b64encode(b'testuser ' + response))
119 + self.assertTrue(p.mbox)
120 + self.assertTrue(s.getvalue().splitlines()[-1].find(b"+OK") >= 0)
121 + p.connectionLost(failure.Failure(Exception("Test harness disconnect")))
122 +diff --git a/src/twisted/persisted/dirdbm.py b/src/twisted/persisted/dirdbm.py
123 +index f97c526d0..d9f29cce2 100644
124 +--- a/src/twisted/persisted/dirdbm.py
125 ++++ b/src/twisted/persisted/dirdbm.py
126 +@@ -81,14 +81,20 @@ class DirDBM:
127 + Encode a key so it can be used as a filename.
128 + """
129 + # NOTE: '_' is NOT in the base64 alphabet!
130 +- return base64.encodestring(k).replace(b'\n', b'_').replace(b"/", b"-")
131 ++ try:
132 ++ return base64.encodebytes(k).replace(b'\n', b'_').replace(b"/", b"-")
133 ++ except AttributeError:
134 ++ return base64.encodestring(k).replace(b'\n', b'_').replace(b"/", b"-")
135 +
136 +
137 + def _decode(self, k):
138 + """
139 + Decode a filename to get the key.
140 + """
141 +- return base64.decodestring(k.replace(b'_', b'\n').replace(b"-", b"/"))
142 ++ try:
143 ++ return base64.decodebytes(k.replace(b'_', b'\n').replace(b"-", b"/"))
144 ++ except AttributeError:
145 ++ return base64.decodestring(k.replace(b'_', b'\n').replace(b"-", b"/"))
146 +
147 +
148 + def _readFile(self, path):
149 +diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py
150 +index fe88d3373..602a58f31 100644
151 +--- a/src/twisted/web/http.py
152 ++++ b/src/twisted/web/http.py
153 +@@ -1540,7 +1540,7 @@ class Request:
154 + bas, upw = authh.split()
155 + if bas.lower() != b"basic":
156 + raise ValueError()
157 +- upw = base64.decodestring(upw)
158 ++ upw = base64.b64decode(upw)
159 + self.user, self.password = upw.split(b':', 1)
160 + except (binascii.Error, ValueError):
161 + self.user = self.password = ""
162 +diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py
163 +index 6001d1e40..70065e232 100644
164 +--- a/src/twisted/web/test/test_http.py
165 ++++ b/src/twisted/web/test/test_http.py
166 +@@ -1513,7 +1513,7 @@ class ParsingTests(unittest.TestCase):
167 + requests.append(self)
168 +
169 + for u, p in [(b"foo", b"bar"), (b"hello", b"there:z")]:
170 +- s = base64.encodestring(b":".join((u, p))).strip()
171 ++ s = base64.b64encode(b":".join((u, p)))
172 + f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
173 + self.runRequest(f, Request, 0)
174 + req = requests.pop()
175 +@@ -2139,9 +2139,9 @@ Hello,
176 +
177 + u = b"foo"
178 + p = b"bar"
179 +- s = base64.encodestring(b":".join((u, p))).strip()
180 ++ s = base64.b64encode(b":".join((u, p)))
181 + f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
182 +- self.patch(base64, 'decodestring', lambda x: [])
183 ++ self.patch(base64, 'b64decode', lambda x: [])
184 + self.runRequest(f, Request, 0)
185 + req = requests.pop()
186 + self.assertEqual(('', ''), req.credentials)
187 +--
188 +2.26.2
189 +
190
191 diff --git a/dev-python/twisted/files/twisted-20.3.0-py39-b64.patch b/dev-python/twisted/files/twisted-20.3.0-py39-b64.patch
192 new file mode 100644
193 index 00000000000..f475614df40
194 --- /dev/null
195 +++ b/dev-python/twisted/files/twisted-20.3.0-py39-b64.patch
196 @@ -0,0 +1,158 @@
197 +From f44c2ff111a8961d295409186cc07aaf414c76bc Mon Sep 17 00:00:00 2001
198 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
199 +Date: Wed, 27 May 2020 13:23:37 +0200
200 +Subject: [PATCH 1/4] Replace base64.*string() functions to fix py3.9 support
201 +
202 +Replace base64.decodestring() and .encodestring() functions as they
203 +were deprecated since Python 3.1 in favor of (equivalent) .decodebytes()
204 +and .encodebytes(), and were eventually removed in Python 3.9.
205 +
206 +While at it, replace most of their uses with base64.b64encode()
207 +and .b64decode() that are preferable to the former wrt ticket #6446,
208 +and they do not introduce line breaks that the twisted code usually
209 +discarded.
210 +
211 +Use .decodebytes() and .encodebytes() in DirDBM as it seems to rely
212 +on the exact presence of newlines, and changing that would break
213 +backwards compatibility.
214 +
215 +Fixes: ticket:6446
216 +Fixes: ticket:9831
217 +---
218 + src/twisted/conch/scripts/tkconch.py | 2 +-
219 + src/twisted/conch/test/test_keys.py | 2 +-
220 + src/twisted/mail/pop3.py | 4 ++--
221 + src/twisted/mail/test/test_pop3.py | 4 ++--
222 + src/twisted/persisted/dirdbm.py | 4 ++--
223 + src/twisted/web/http.py | 2 +-
224 + src/twisted/web/test/test_http.py | 6 +++---
225 + 14 files changed, 12 insertions(+), 12 deletions(-)
226 +
227 +diff --git a/src/twisted/conch/scripts/tkconch.py b/src/twisted/conch/scripts/tkconch.py
228 +index a662cabc8..744734343 100644
229 +--- a/src/twisted/conch/scripts/tkconch.py
230 ++++ b/src/twisted/conch/scripts/tkconch.py
231 +@@ -412,7 +412,7 @@ class SSHClientTransport(transport.SSHClientTransport):
232 + "known hosts.\r\n" %
233 + (khHost, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType]))
234 + with open(os.path.expanduser('~/.ssh/known_hosts'), 'a') as known_hosts:
235 +- encodedKey = base64.encodestring(pubKey).replace(b'\n', b'')
236 ++ encodedKey = base64.b64encode(pubKey)
237 + known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey))
238 + except:
239 + log.deferr()
240 +diff --git a/src/twisted/conch/test/test_keys.py b/src/twisted/conch/test/test_keys.py
241 +index 650a19bfb..f76cbd1b4 100644
242 +--- a/src/twisted/conch/test/test_keys.py
243 ++++ b/src/twisted/conch/test/test_keys.py
244 +@@ -404,7 +404,7 @@ SUrCyZXsNh6VXwjs3gKQ
245 +
246 + self.assertRaises(
247 + keys.BadKeyError,
248 +- keys.Key.fromString, data=b'{' + base64.encodestring(sexp) + b'}',
249 ++ keys.Key.fromString, data=b'{' + base64.b64encode(sexp) + b'}',
250 + )
251 +
252 +
253 +diff --git a/src/twisted/mail/pop3.py b/src/twisted/mail/pop3.py
254 +index ffe9714c9..057389e3a 100644
255 +--- a/src/twisted/mail/pop3.py
256 ++++ b/src/twisted/mail/pop3.py
257 +@@ -728,7 +728,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
258 + self._auth = auth()
259 + chal = self._auth.getChallenge()
260 +
261 +- self.sendLine(b'+ ' + base64.encodestring(chal).rstrip(b'\n'))
262 ++ self.sendLine(b'+ ' + base64.b64encode(chal))
263 + self.state = 'AUTH'
264 +
265 +
266 +@@ -747,7 +747,7 @@ class POP3(basic.LineOnlyReceiver, policies.TimeoutMixin):
267 + """
268 + self.state = "COMMAND"
269 + try:
270 +- parts = base64.decodestring(line).split(None, 1)
271 ++ parts = base64.b64decode(line).split(None, 1)
272 + except binascii.Error:
273 + self.failResponse(b"Invalid BASE64 encoding")
274 + else:
275 +diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py
276 +index f7fbfaf1e..af335ab2d 100644
277 +--- a/src/twisted/mail/test/test_pop3.py
278 ++++ b/src/twisted/mail/test/test_pop3.py
279 +@@ -1096,12 +1096,12 @@ class SASLTests(unittest.TestCase):
280 +
281 + p.lineReceived(b"AUTH CRAM-MD5")
282 + chal = s.getvalue().splitlines()[-1][2:]
283 +- chal = base64.decodestring(chal)
284 ++ chal = base64.b64decode(chal)
285 + response = hmac.HMAC(b'testpassword', chal,
286 + digestmod=md5).hexdigest().encode("ascii")
287 +
288 + p.lineReceived(
289 +- base64.encodestring(b'testuser ' + response).rstrip(b'\n'))
290 ++ base64.b64encode(b'testuser ' + response))
291 + self.assertTrue(p.mbox)
292 + self.assertTrue(s.getvalue().splitlines()[-1].find(b"+OK") >= 0)
293 + p.connectionLost(failure.Failure(Exception("Test harness disconnect")))
294 +diff --git a/src/twisted/persisted/dirdbm.py b/src/twisted/persisted/dirdbm.py
295 +index 3ba7a59d4..7659ff765 100644
296 +--- a/src/twisted/persisted/dirdbm.py
297 ++++ b/src/twisted/persisted/dirdbm.py
298 +@@ -77,14 +77,14 @@ class DirDBM:
299 + Encode a key so it can be used as a filename.
300 + """
301 + # NOTE: '_' is NOT in the base64 alphabet!
302 +- return base64.encodestring(k).replace(b'\n', b'_').replace(b"/", b"-")
303 ++ return base64.encodebytes(k).replace(b'\n', b'_').replace(b"/", b"-")
304 +
305 +
306 + def _decode(self, k):
307 + """
308 + Decode a filename to get the key.
309 + """
310 +- return base64.decodestring(k.replace(b'_', b'\n').replace(b"-", b"/"))
311 ++ return base64.decodebytes(k.replace(b'_', b'\n').replace(b"-", b"/"))
312 +
313 +
314 + def _readFile(self, path):
315 +diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py
316 +index 0e115741e..e9a080d21 100644
317 +--- a/src/twisted/web/http.py
318 ++++ b/src/twisted/web/http.py
319 +@@ -1544,7 +1544,7 @@ class Request:
320 + bas, upw = authh.split()
321 + if bas.lower() != b"basic":
322 + raise ValueError()
323 +- upw = base64.decodestring(upw)
324 ++ upw = base64.b64decode(upw)
325 + self.user, self.password = upw.split(b':', 1)
326 + except (binascii.Error, ValueError):
327 + self.user = self.password = b''
328 +diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py
329 +index 112e56f46..02a4674a7 100644
330 +--- a/src/twisted/web/test/test_http.py
331 ++++ b/src/twisted/web/test/test_http.py
332 +@@ -1604,7 +1604,7 @@ class ParsingTests(unittest.TestCase):
333 + requests.append(self)
334 +
335 + for u, p in [(b"foo", b"bar"), (b"hello", b"there:z")]:
336 +- s = base64.encodestring(b":".join((u, p))).strip()
337 ++ s = base64.b64encode(b":".join((u, p)))
338 + f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
339 + self.runRequest(f, Request, 0)
340 + req = requests.pop()
341 +@@ -2209,9 +2209,9 @@ Hello,
342 +
343 + u = b"foo"
344 + p = b"bar"
345 +- s = base64.encodestring(b":".join((u, p))).strip()
346 ++ s = base64.b64encode(b":".join((u, p)))
347 + f = b"GET / HTTP/1.0\nAuthorization: Basic " + s + b"\n\n"
348 +- self.patch(base64, 'decodestring', lambda x: [])
349 ++ self.patch(base64, 'b64decode', lambda x: [])
350 + self.runRequest(f, Request, 0)
351 + req = requests.pop()
352 + self.assertEqual((b'', b''), req.credentials)
353 +--
354 +2.26.2
355
356 diff --git a/dev-python/twisted/files/twisted-20.3.0-py39-combined.patch b/dev-python/twisted/files/twisted-20.3.0-py39-combined.patch
357 new file mode 100644
358 index 00000000000..0ed1f7b8d9c
359 --- /dev/null
360 +++ b/dev-python/twisted/files/twisted-20.3.0-py39-combined.patch
361 @@ -0,0 +1,115 @@
362 +From 2d30860a8b71e90513ead9958f5dd312802b0d36 Mon Sep 17 00:00:00 2001
363 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
364 +Date: Wed, 27 May 2020 14:40:53 +0200
365 +Subject: [PATCH 2/4] Fix imap4-utf-7 codec lookup function for Python 3.9
366 +
367 +Python 3.9 normalizes the codec name into 'imap4_utf_7' rather than
368 +'imap4-utf-7', and therefore the lookup function needs to account
369 +for the former name. Transform the latter locally to preserve support
370 +for all Python versions.
371 +
372 +Fixes: ticket: 9832
373 +---
374 + src/twisted/mail/imap4.py | 2 +-
375 + 2 files changed, 1 insertion(+), 1 deletion(-)
376 +
377 +diff --git a/src/twisted/mail/imap4.py b/src/twisted/mail/imap4.py
378 +index 736ef111d..3f32982ca 100644
379 +--- a/src/twisted/mail/imap4.py
380 ++++ b/src/twisted/mail/imap4.py
381 +@@ -6369,7 +6369,7 @@ _codecInfo = codecs.CodecInfo(encoder, decoder, StreamReader, StreamWriter)
382 +
383 +
384 + def imap4_utf_7(name):
385 +- if name == 'imap4-utf-7':
386 ++ if name.replace('-', '_') == 'imap4_utf_7':
387 + return _codecInfo
388 +
389 + codecs.register(imap4_utf_7)
390 +--
391 +2.26.2
392 +
393 +From daf928bf0f0371816dddbd4929948c4213d0cdcb Mon Sep 17 00:00:00 2001
394 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
395 +Date: Wed, 27 May 2020 15:12:54 +0200
396 +Subject: [PATCH 3/4] Fix verifyCryptedPassword() for crypt.crypt() throwing in
397 + py3.9
398 +
399 +In Python 3.9, the crypt.crypt() function may throw an exception
400 +if the underlying crypt() function fails. Update
401 +verifyCryptedPassword() to account for that, and preserve the existing
402 +behavior of returning False in that case.
403 +
404 +Fixes: ticket:9833
405 +---
406 + src/twisted/conch/checkers.py | 5 ++++-
407 + src/twisted/plugins/cred_unix.py | 5 ++++-
408 + 4 files changed, 8 insertions(+), 2 deletions(-)
409 +
410 +diff --git a/src/twisted/conch/checkers.py b/src/twisted/conch/checkers.py
411 +index 917567a39..e4e327b16 100644
412 +--- a/src/twisted/conch/checkers.py
413 ++++ b/src/twisted/conch/checkers.py
414 +@@ -53,7 +53,10 @@ def verifyCryptedPassword(crypted, pw):
415 +
416 + @rtype: L{bool}
417 + """
418 +- return crypt.crypt(pw, crypted) == crypted
419 ++ try:
420 ++ return crypt.crypt(pw, crypted) == crypted
421 ++ except OSError:
422 ++ return False
423 +
424 +
425 +
426 +diff --git a/src/twisted/plugins/cred_unix.py b/src/twisted/plugins/cred_unix.py
427 +index 211b4ccbc..a662719b6 100644
428 +--- a/src/twisted/plugins/cred_unix.py
429 ++++ b/src/twisted/plugins/cred_unix.py
430 +@@ -43,7 +43,10 @@ def verifyCryptedPassword(crypted, pw):
431 + pw = pw.decode('utf-8')
432 + if not isinstance(crypted, StringType):
433 + crypted = crypted.decode('utf-8')
434 +- return crypt.crypt(pw, crypted) == crypted
435 ++ try:
436 ++ return crypt.crypt(pw, crypted) == crypted
437 ++ except OSError:
438 ++ return False
439 +
440 +
441 +
442 +--
443 +2.26.2
444 +
445 +From 4fc435df0d1eba3e5d6416a2b86d39d3404f82fe Mon Sep 17 00:00:00 2001
446 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@g.o>
447 +Date: Wed, 27 May 2020 15:37:10 +0200
448 +Subject: [PATCH 4/4] Use xml.etree.ElementTree instead of deprecated
449 + cElementTree
450 +
451 +The xml.etree.cElementTree is deprecated, and has been removed in Python
452 +3.9. At the same time, xml.etree.ElementTree has already been using
453 +cElementTree implicitly since Python 3.3. Update test_flatten to use
454 +the latter to provide compatibility with newer Python versions.
455 +
456 +Fixes: ticket:9834
457 +---
458 + src/twisted/web/test/test_flatten.py | 2 +-
459 + 2 files changed, 1 insertion(+), 1 deletion(-)
460 +
461 +diff --git a/src/twisted/web/test/test_flatten.py b/src/twisted/web/test/test_flatten.py
462 +index 677401c55..61d50e20a 100644
463 +--- a/src/twisted/web/test/test_flatten.py
464 ++++ b/src/twisted/web/test/test_flatten.py
465 +@@ -9,7 +9,7 @@ L{twisted.web._flatten}.
466 + import sys
467 + import traceback
468 +
469 +-from xml.etree.cElementTree import XML
470 ++from xml.etree.ElementTree import XML
471 +
472 + from collections import OrderedDict
473 +
474 +--
475 +2.26.2
476 +
477
478 diff --git a/dev-python/twisted/twisted-19.10.0.ebuild b/dev-python/twisted/twisted-19.10.0.ebuild
479 index 6ed6b63c9d3..720e3b98f64 100644
480 --- a/dev-python/twisted/twisted-19.10.0.ebuild
481 +++ b/dev-python/twisted/twisted-19.10.0.ebuild
482 @@ -3,7 +3,7 @@
483
484 EAPI=7
485
486 -PYTHON_COMPAT=( python2_7 python3_{6,7,8} )
487 +PYTHON_COMPAT=( python2_7 python3_{6,7,8,9} )
488 PYTHON_REQ_USE="threads(+)"
489
490 inherit distutils-r1 virtualx
491 @@ -85,6 +85,8 @@ python_prepare_all() {
492 "${FILESDIR}"/${P}-py38.patch
493 "${FILESDIR}"/twisted-19.10.0-py38-cgi.patch
494 "${FILESDIR}"/twisted-20.3.0-py38-hmac.patch
495 + "${FILESDIR}"/twisted-19.10.0-py39-b64.patch
496 + "${FILESDIR}"/twisted-20.3.0-py39-combined.patch
497 )
498
499 # upstream test for making releases; not very useful and requires
500
501 diff --git a/dev-python/twisted/twisted-20.3.0.ebuild b/dev-python/twisted/twisted-20.3.0.ebuild
502 index 47624031dc7..3ee3d08b812 100644
503 --- a/dev-python/twisted/twisted-20.3.0.ebuild
504 +++ b/dev-python/twisted/twisted-20.3.0.ebuild
505 @@ -3,7 +3,7 @@
506
507 EAPI=7
508
509 -PYTHON_COMPAT=( python3_{6,7,8} )
510 +PYTHON_COMPAT=( python3_{6,7,8,9} )
511 PYTHON_REQ_USE="threads(+)"
512
513 inherit distutils-r1 virtualx
514 @@ -84,6 +84,8 @@ python_prepare_all() {
515 local PATCHES=(
516 "${FILESDIR}"/twisted-20.3.0-py38-cgi.patch
517 "${FILESDIR}"/twisted-20.3.0-py38-hmac.patch
518 + "${FILESDIR}"/twisted-20.3.0-py39-b64.patch
519 + "${FILESDIR}"/twisted-20.3.0-py39-combined.patch
520 )
521
522 # upstream test for making releases; not very useful and requires