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: Fri, 24 Apr 2020 19:24:31
Message-Id: 1587756264.d8fd31b8c0576c4e32decfc907293175d131a2f3.mgorny@gentoo
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