1 |
commit: d8fd31b8c0576c4e32decfc907293175d131a2f3 |
2 |
Author: Michał Górny <mgorny <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Apr 24 19:23:35 2020 +0000 |
4 |
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org> |
5 |
CommitDate: Fri Apr 24 19:24:24 2020 +0000 |
6 |
URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=d8fd31b8 |
7 |
|
8 |
dev-python/twisted: Backport py3.8 fixes to 20.3.0 |
9 |
|
10 |
Signed-off-by: Michał Górny <mgorny <AT> gentoo.org> |
11 |
|
12 |
.../twisted/files/twisted-20.3.0-py38-cgi.patch | 259 +++++++++++++++++++++ |
13 |
.../twisted/files/twisted-20.3.0-py38-hmac.patch | 94 ++++++++ |
14 |
dev-python/twisted/twisted-20.3.0.ebuild | 5 + |
15 |
3 files changed, 358 insertions(+) |
16 |
|
17 |
diff --git a/dev-python/twisted/files/twisted-20.3.0-py38-cgi.patch b/dev-python/twisted/files/twisted-20.3.0-py38-cgi.patch |
18 |
new file mode 100644 |
19 |
index 00000000000..5151f297f7f |
20 |
--- /dev/null |
21 |
+++ b/dev-python/twisted/files/twisted-20.3.0-py38-cgi.patch |
22 |
@@ -0,0 +1,259 @@ |
23 |
+From 62ab0203c59c1f9788c53dfad4a212774094d05c Mon Sep 17 00:00:00 2001 |
24 |
+From: Craig Rodrigues <rodrigc@×××××××.org> |
25 |
+Date: Mon, 13 Apr 2020 01:22:23 -0700 |
26 |
+Subject: [PATCH 2/2] Merge 9801-rodrigc-cgi: Change import of cgi.parse_qs to |
27 |
+ urllib.parse.parse_qs |
28 |
+ |
29 |
+Author: rodrigc |
30 |
+Reviewer: hawkowl |
31 |
+Fixes: ticket:9801 |
32 |
+--- |
33 |
+ src/twisted/web/client.py | 17 ++++----- |
34 |
+ src/twisted/web/http.py | 49 ++++++++++++------------- |
35 |
+ src/twisted/web/newsfragments/9801.misc | 0 |
36 |
+ src/twisted/web/test/test_http.py | 41 +++------------------ |
37 |
+ src/twisted/web/test/test_webclient.py | 5 +-- |
38 |
+ 5 files changed, 38 insertions(+), 74 deletions(-) |
39 |
+ create mode 100644 src/twisted/web/newsfragments/9801.misc |
40 |
+ |
41 |
+diff --git a/src/twisted/web/client.py b/src/twisted/web/client.py |
42 |
+index 7e4642ef3..8209f5a5e 100644 |
43 |
+--- a/src/twisted/web/client.py |
44 |
++++ b/src/twisted/web/client.py |
45 |
+@@ -12,15 +12,8 @@ import os |
46 |
+ import collections |
47 |
+ import warnings |
48 |
+ |
49 |
+-try: |
50 |
+- from urlparse import urlunparse, urljoin, urldefrag |
51 |
+-except ImportError: |
52 |
+- from urllib.parse import urljoin, urldefrag |
53 |
+- from urllib.parse import urlunparse as _urlunparse |
54 |
+- |
55 |
+- def urlunparse(parts): |
56 |
+- result = _urlunparse(tuple([p.decode("charmap") for p in parts])) |
57 |
+- return result.encode("charmap") |
58 |
++from urllib.parse import urljoin, urldefrag |
59 |
++from urllib.parse import urlunparse as _urlunparse |
60 |
+ |
61 |
+ import zlib |
62 |
+ from functools import wraps |
63 |
+@@ -51,6 +44,12 @@ from twisted.web._newclient import _ensureValidURI, _ensureValidMethod |
64 |
+ |
65 |
+ |
66 |
+ |
67 |
++def urlunparse(parts): |
68 |
++ result = _urlunparse(tuple([p.decode("charmap") for p in parts])) |
69 |
++ return result.encode("charmap") |
70 |
++ |
71 |
++ |
72 |
++ |
73 |
+ class PartialDownloadError(error.Error): |
74 |
+ """ |
75 |
+ Page was only partially downloaded, we got disconnected in middle. |
76 |
+diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py |
77 |
+index b7afa8b0d..94d0ae81f 100644 |
78 |
+--- a/src/twisted/web/http.py |
79 |
++++ b/src/twisted/web/http.py |
80 |
+@@ -66,27 +66,10 @@ import time |
81 |
+ import calendar |
82 |
+ import warnings |
83 |
+ import os |
84 |
+-from io import BytesIO as StringIO |
85 |
+- |
86 |
+-try: |
87 |
+- from urlparse import ( |
88 |
+- ParseResult as ParseResultBytes, urlparse as _urlparse) |
89 |
+- from urllib import unquote |
90 |
+- from cgi import parse_header as _parseHeader |
91 |
+-except ImportError: |
92 |
+- from urllib.parse import ( |
93 |
+- ParseResultBytes, urlparse as _urlparse, unquote_to_bytes as unquote) |
94 |
+- |
95 |
+- def _parseHeader(line): |
96 |
+- # cgi.parse_header requires a str |
97 |
+- key, pdict = cgi.parse_header(line.decode('charmap')) |
98 |
+- |
99 |
+- # We want the key as bytes, and cgi.parse_multipart (which consumes |
100 |
+- # pdict) expects a dict of str keys but bytes values |
101 |
+- key = key.encode('charmap') |
102 |
+- pdict = {x:y.encode('charmap') for x, y in pdict.items()} |
103 |
+- return (key, pdict) |
104 |
++from io import BytesIO |
105 |
+ |
106 |
++from urllib.parse import ( |
107 |
++ ParseResultBytes, urlparse as _urlparse, unquote_to_bytes as unquote) |
108 |
+ |
109 |
+ from zope.interface import Attribute, Interface, implementer, provider |
110 |
+ |
111 |
+@@ -163,6 +146,20 @@ monthname = [None, |
112 |
+ weekdayname_lower = [name.lower() for name in weekdayname] |
113 |
+ monthname_lower = [name and name.lower() for name in monthname] |
114 |
+ |
115 |
++ |
116 |
++ |
117 |
++def _parseHeader(line): |
118 |
++ # cgi.parse_header requires a str |
119 |
++ key, pdict = cgi.parse_header(line.decode('charmap')) |
120 |
++ |
121 |
++ # We want the key as bytes, and cgi.parse_multipart (which consumes |
122 |
++ # pdict) expects a dict of str keys but bytes values |
123 |
++ key = key.encode('charmap') |
124 |
++ pdict = {x: y.encode('charmap') for x, y in pdict.items()} |
125 |
++ return (key, pdict) |
126 |
++ |
127 |
++ |
128 |
++ |
129 |
+ def urlparse(url): |
130 |
+ """ |
131 |
+ Parse an URL into six components. |
132 |
+@@ -486,13 +483,15 @@ class _IDeprecatedHTTPChannelToRequestInterface(Interface): |
133 |
+ |
134 |
+ class StringTransport: |
135 |
+ """ |
136 |
+- I am a StringIO wrapper that conforms for the transport API. I support |
137 |
++ I am a BytesIO wrapper that conforms for the transport API. I support |
138 |
+ the `writeSequence' method. |
139 |
+ """ |
140 |
+ def __init__(self): |
141 |
+- self.s = StringIO() |
142 |
++ self.s = BytesIO() |
143 |
++ |
144 |
+ def writeSequence(self, seq): |
145 |
+ self.s.write(b''.join(seq)) |
146 |
++ |
147 |
+ def __getattr__(self, attr): |
148 |
+ return getattr(self.__dict__['s'], attr) |
149 |
+ |
150 |
+@@ -513,7 +512,7 @@ class HTTPClient(basic.LineReceiver): |
151 |
+ @type firstLine: C{bool} |
152 |
+ |
153 |
+ @ivar __buffer: The buffer that stores the response to the HTTP request. |
154 |
+- @type __buffer: A C{StringIO} object. |
155 |
++ @type __buffer: A C{BytesIO} object. |
156 |
+ |
157 |
+ @ivar _header: Part or all of an HTTP request header. |
158 |
+ @type _header: C{bytes} |
159 |
+@@ -579,7 +578,7 @@ class HTTPClient(basic.LineReceiver): |
160 |
+ if self._header != b"": |
161 |
+ # Only extract headers if there are any |
162 |
+ self.extractHeader(self._header) |
163 |
+- self.__buffer = StringIO() |
164 |
++ self.__buffer = BytesIO() |
165 |
+ self.handleEndHeaders() |
166 |
+ self.setRawMode() |
167 |
+ return |
168 |
+@@ -665,7 +664,7 @@ def _getContentFile(length): |
169 |
+ Get a writeable file-like object to which request content can be written. |
170 |
+ """ |
171 |
+ if length is not None and length < 100000: |
172 |
+- return StringIO() |
173 |
++ return BytesIO() |
174 |
+ return tempfile.TemporaryFile() |
175 |
+ |
176 |
+ |
177 |
+diff --git a/src/twisted/web/newsfragments/9801.misc b/src/twisted/web/newsfragments/9801.misc |
178 |
+new file mode 100644 |
179 |
+index 000000000..e69de29bb |
180 |
+diff --git a/src/twisted/web/test/test_http.py b/src/twisted/web/test/test_http.py |
181 |
+index a3067f732..4189b307c 100644 |
182 |
+--- a/src/twisted/web/test/test_http.py |
183 |
++++ b/src/twisted/web/test/test_http.py |
184 |
+@@ -9,15 +9,11 @@ from __future__ import absolute_import, division |
185 |
+ |
186 |
+ import base64 |
187 |
+ import calendar |
188 |
+-import cgi |
189 |
+ import random |
190 |
+ |
191 |
+ import hamcrest |
192 |
+ |
193 |
+-try: |
194 |
+- from urlparse import urlparse, urlunsplit, clear_cache |
195 |
+-except ImportError: |
196 |
+- from urllib.parse import urlparse, urlunsplit, clear_cache |
197 |
++from urllib.parse import urlparse, urlunsplit, clear_cache, parse_qs |
198 |
+ |
199 |
+ from io import BytesIO |
200 |
+ from itertools import cycle |
201 |
+@@ -28,7 +24,7 @@ from zope.interface import ( |
202 |
+ ) |
203 |
+ from zope.interface.verify import verifyObject |
204 |
+ |
205 |
+-from twisted.python.compat import (_PY3, iterbytes, long, networkString, |
206 |
++from twisted.python.compat import (iterbytes, long, networkString, |
207 |
+ unicode, intToBytes) |
208 |
+ from twisted.python.components import proxyForInterface |
209 |
+ from twisted.python.failure import Failure |
210 |
+@@ -2019,33 +2015,6 @@ Content-Type: application/x-www-form-urlencoded |
211 |
+ self.assertEqual(content, [networkString(query)]) |
212 |
+ |
213 |
+ |
214 |
+- def test_missingContentDisposition(self): |
215 |
+- """ |
216 |
+- If the C{Content-Disposition} header is missing, the request is denied |
217 |
+- as a bad request. |
218 |
+- """ |
219 |
+- req = b'''\ |
220 |
+-POST / HTTP/1.0 |
221 |
+-Content-Type: multipart/form-data; boundary=AaB03x |
222 |
+-Content-Length: 103 |
223 |
+- |
224 |
+---AaB03x |
225 |
+-Content-Type: text/plain |
226 |
+-Content-Transfer-Encoding: quoted-printable |
227 |
+- |
228 |
+-abasdfg |
229 |
+---AaB03x-- |
230 |
+-''' |
231 |
+- channel = self.runRequest(req, http.Request, success=False) |
232 |
+- self.assertEqual( |
233 |
+- channel.transport.value(), |
234 |
+- b"HTTP/1.1 400 Bad Request\r\n\r\n") |
235 |
+- |
236 |
+- if _PY3: |
237 |
+- test_missingContentDisposition.skip = ( |
238 |
+- "cgi.parse_multipart is much more error-tolerant on Python 3.") |
239 |
+- |
240 |
+- |
241 |
+ def test_multipartProcessingFailure(self): |
242 |
+ """ |
243 |
+ When the multipart processing fails the client gets a 400 Bad Request. |
244 |
+@@ -2373,15 +2342,15 @@ ok |
245 |
+ class QueryArgumentsTests(unittest.TestCase): |
246 |
+ def testParseqs(self): |
247 |
+ self.assertEqual( |
248 |
+- cgi.parse_qs(b"a=b&d=c;+=f"), |
249 |
++ parse_qs(b"a=b&d=c;+=f"), |
250 |
+ http.parse_qs(b"a=b&d=c;+=f")) |
251 |
+ self.assertRaises( |
252 |
+ ValueError, http.parse_qs, b"blah", strict_parsing=True) |
253 |
+ self.assertEqual( |
254 |
+- cgi.parse_qs(b"a=&b=c", keep_blank_values=1), |
255 |
++ parse_qs(b"a=&b=c", keep_blank_values=1), |
256 |
+ http.parse_qs(b"a=&b=c", keep_blank_values=1)) |
257 |
+ self.assertEqual( |
258 |
+- cgi.parse_qs(b"a=&b=c"), |
259 |
++ parse_qs(b"a=&b=c"), |
260 |
+ http.parse_qs(b"a=&b=c")) |
261 |
+ |
262 |
+ |
263 |
+diff --git a/src/twisted/web/test/test_webclient.py b/src/twisted/web/test/test_webclient.py |
264 |
+index 680e02780..672594993 100644 |
265 |
+--- a/src/twisted/web/test/test_webclient.py |
266 |
++++ b/src/twisted/web/test/test_webclient.py |
267 |
+@@ -11,10 +11,7 @@ import io |
268 |
+ import os |
269 |
+ from errno import ENOSPC |
270 |
+ |
271 |
+-try: |
272 |
+- from urlparse import urlparse, urljoin |
273 |
+-except ImportError: |
274 |
+- from urllib.parse import urlparse, urljoin |
275 |
++from urllib.parse import urlparse, urljoin |
276 |
+ |
277 |
+ from twisted.python.compat import networkString, nativeString, intToBytes |
278 |
+ from twisted.trial import unittest, util |
279 |
+-- |
280 |
+2.26.2 |
281 |
+ |
282 |
|
283 |
diff --git a/dev-python/twisted/files/twisted-20.3.0-py38-hmac.patch b/dev-python/twisted/files/twisted-20.3.0-py38-hmac.patch |
284 |
new file mode 100644 |
285 |
index 00000000000..1c1ee01b218 |
286 |
--- /dev/null |
287 |
+++ b/dev-python/twisted/files/twisted-20.3.0-py38-hmac.patch |
288 |
@@ -0,0 +1,94 @@ |
289 |
+From 653fb2aea0ca1f60558917d52f4ff0c33cd7b067 Mon Sep 17 00:00:00 2001 |
290 |
+From: Craig Rodrigues <rodrigc@××××××××××.org> |
291 |
+Date: Sun, 12 Apr 2020 14:28:23 -0700 |
292 |
+Subject: [PATCH 1/2] Add digestmod parameter to HMAC.__init__() invocations |
293 |
+ |
294 |
+This parameter is now required on Python 3.8+ |
295 |
+--- |
296 |
+ src/twisted/cred/credentials.py | 3 ++- |
297 |
+ src/twisted/cred/test/test_cramauth.py | 11 ++++++++--- |
298 |
+ src/twisted/mail/test/test_pop3.py | 4 +++- |
299 |
+ 3 files changed, 13 insertions(+), 5 deletions(-) |
300 |
+ |
301 |
+diff --git a/src/twisted/cred/credentials.py b/src/twisted/cred/credentials.py |
302 |
+index 5469e5158..67c24cb01 100644 |
303 |
+--- a/src/twisted/cred/credentials.py |
304 |
++++ b/src/twisted/cred/credentials.py |
305 |
+@@ -441,7 +441,8 @@ class CramMD5Credentials(object): |
306 |
+ |
307 |
+ |
308 |
+ def checkPassword(self, password): |
309 |
+- verify = hexlify(hmac.HMAC(password, self.challenge).digest()) |
310 |
++ verify = hexlify(hmac.HMAC(password, self.challenge, |
311 |
++ digestmod=md5).digest()) |
312 |
+ return verify == self.response |
313 |
+ |
314 |
+ |
315 |
+diff --git a/src/twisted/cred/test/test_cramauth.py b/src/twisted/cred/test/test_cramauth.py |
316 |
+index 1ee08712b..d21f2f68c 100644 |
317 |
+--- a/src/twisted/cred/test/test_cramauth.py |
318 |
++++ b/src/twisted/cred/test/test_cramauth.py |
319 |
+@@ -7,6 +7,8 @@ Tests for L{twisted.cred}'s implementation of CRAM-MD5. |
320 |
+ |
321 |
+ from __future__ import division, absolute_import |
322 |
+ |
323 |
++import hashlib |
324 |
++ |
325 |
+ from hmac import HMAC |
326 |
+ from binascii import hexlify |
327 |
+ |
328 |
+@@ -39,7 +41,8 @@ class CramMD5CredentialsTests(TestCase): |
329 |
+ """ |
330 |
+ c = CramMD5Credentials() |
331 |
+ chal = c.getChallenge() |
332 |
+- c.response = hexlify(HMAC(b'secret', chal).digest()) |
333 |
++ c.response = hexlify(HMAC(b'secret', chal, |
334 |
++ digestmod=hashlib.md5).digest()) |
335 |
+ self.assertTrue(c.checkPassword(b'secret')) |
336 |
+ |
337 |
+ |
338 |
+@@ -61,7 +64,8 @@ class CramMD5CredentialsTests(TestCase): |
339 |
+ """ |
340 |
+ c = CramMD5Credentials() |
341 |
+ chal = c.getChallenge() |
342 |
+- c.response = hexlify(HMAC(b'thewrongsecret', chal).digest()) |
343 |
++ c.response = hexlify(HMAC(b'thewrongsecret', chal, |
344 |
++ digestmod=hashlib.md5).digest()) |
345 |
+ self.assertFalse(c.checkPassword(b'secret')) |
346 |
+ |
347 |
+ |
348 |
+@@ -75,7 +79,8 @@ class CramMD5CredentialsTests(TestCase): |
349 |
+ chal = c.getChallenge() |
350 |
+ c.setResponse(b" ".join( |
351 |
+ (b"squirrel", |
352 |
+- hexlify(HMAC(b'supersecret', chal).digest())))) |
353 |
++ hexlify(HMAC(b'supersecret', chal, |
354 |
++ digestmod=hashlib.md5).digest())))) |
355 |
+ self.assertTrue(c.checkPassword(b'supersecret')) |
356 |
+ self.assertEqual(c.username, b"squirrel") |
357 |
+ |
358 |
+diff --git a/src/twisted/mail/test/test_pop3.py b/src/twisted/mail/test/test_pop3.py |
359 |
+index 4a59c3b49..ea513487c 100644 |
360 |
+--- a/src/twisted/mail/test/test_pop3.py |
361 |
++++ b/src/twisted/mail/test/test_pop3.py |
362 |
+@@ -11,6 +11,7 @@ import hmac |
363 |
+ import base64 |
364 |
+ import itertools |
365 |
+ |
366 |
++from hashlib import md5 |
367 |
+ from collections import OrderedDict |
368 |
+ from io import BytesIO |
369 |
+ |
370 |
+@@ -1097,7 +1098,8 @@ class SASLTests(unittest.TestCase): |
371 |
+ p.lineReceived(b"AUTH CRAM-MD5") |
372 |
+ chal = s.getvalue().splitlines()[-1][2:] |
373 |
+ chal = base64.decodestring(chal) |
374 |
+- response = hmac.HMAC(b'testpassword', chal).hexdigest().encode("ascii") |
375 |
++ response = hmac.HMAC(b'testpassword', chal, |
376 |
++ digestmod=md5).hexdigest().encode("ascii") |
377 |
+ |
378 |
+ p.lineReceived( |
379 |
+ base64.encodestring(b'testuser ' + response).rstrip(b'\n')) |
380 |
+-- |
381 |
+2.26.2 |
382 |
+ |
383 |
|
384 |
diff --git a/dev-python/twisted/twisted-20.3.0.ebuild b/dev-python/twisted/twisted-20.3.0.ebuild |
385 |
index 5ec79085b94..1f547fbdba4 100644 |
386 |
--- a/dev-python/twisted/twisted-20.3.0.ebuild |
387 |
+++ b/dev-python/twisted/twisted-20.3.0.ebuild |
388 |
@@ -81,6 +81,11 @@ DEPEND=" |
389 |
S=${WORKDIR}/${TWISTED_P} |
390 |
|
391 |
python_prepare_all() { |
392 |
+ local PATCHES=( |
393 |
+ "${FILESDIR}"/twisted-20.3.0-py38-cgi.patch |
394 |
+ "${FILESDIR}"/twisted-20.3.0-py38-hmac.patch |
395 |
+ ) |
396 |
+ |
397 |
# upstream test for making releases; not very useful and requires |
398 |
# sphinx (including on py2) |
399 |
rm src/twisted/python/test/test_release.py || die |