List of commits:
Subject Hash Author Date (UTC)
bpo-29979: Rewrite cgi.parse_multipart to make it consistent with FieldStorage (#991) cc3fa204d357be5fafc10eb8c2a80fe0bca998f1 Pierre Quentel 2017-05-08 12:08:34
Fix a trivial typo in global section (#1497) f34c6850203a2406c4950af7a9c8a134145df4ea Jim Fasarakis-Hilliard 2017-05-08 11:36:29
Closes bpo-30168: indent methods in Logger Class (#1295) 55ace65eba587fe3cf3759a43cccf85214651971 Jim Fasarakis-Hilliard 2017-05-07 18:40:18
Revert bpo-26293 for zipfile breakage. See also bpo-29094. (#1484) 3763ea865cee5bbabcce11cd577811135e0fc747 Serhiy Storchaka 2017-05-06 11:46:01
bpo-30218: support path-like objects in shutil.unpack_archive() (GH-1367) a12df7b7d40dbf47825917c8fa03d2c09b5a382c Jelle Zijlstra 2017-05-05 21:27:12
bpo-29243: Fix Makefile with respect to --enable-optimizations (#1478) a1054c3b0037d4c2a5492e79fc193f36245366c7 torsava 2017-05-05 15:35:50
bpo-29920: Document cgitb.text() and cgitb.html() functions (GH-849) c07b3a15be5e0a68a73b4c532861ed8de6932bd2 masklinn 2017-05-05 08:15:12
bpo-30279: Remove unused Python/thread_foobar.h (#1473) fdaeea620f8c78da89cddba4ab010c64535800e0 Masayuki Yamamoto 2017-05-05 08:04:13
bpo-30264: ExpatParser closes the source on error (#1451) ef9c0e732fc50aefbdd7c5a80e04e14b31684e66 Victor Stinner 2017-05-05 07:46:47
bpo-30277: Replace _sre.getlower() with _sre.ascii_tolower() and _sre.unicode_tolower(). (#1468) 7186cc29be352bed6f1110873283d073fd0643e4 Serhiy Storchaka 2017-05-05 07:42:46
bpo-30243: Fixed the possibility of a crash in _json. (#1420) 76a3e51a403bc84ed536921866c86dd7d07aaa7e Serhiy Storchaka 2017-05-05 07:08:49
bpo-30215: Make re.compile() locale agnostic. (#1361) 898ff03e1e7925ecde3da66327d3cdc7e07625ba Serhiy Storchaka 2017-05-05 05:53:40
Make code coverage less strict (GH-1438) 647c3d381e67490e82cdbbe6c96e46d5e1628ce2 Brett Cannon 2017-05-04 21:58:54
bpo-30273: Update sysconfig (#1464) b109a1d3360fc4bb87b9887264e3634632d392ca Victor Stinner 2017-05-04 21:29:09
bpo-30185: avoid KeyboardInterrupt tracebacks in forkserver (#1319) 6dd4d734ed207ba16b017e38f8909de7ef187e29 Antoine Pitrou 2017-05-04 14:44:53
bpo-30263: regrtest: log system load (#1452) 3d0056842c5e06b4102f990b59ab3b607f932dd8 Victor Stinner 2017-05-04 13:21:12
bpo-29956: Improve the math.exp() related documentation. (#1073) dbaf746b6de0ee431c809d3175ab40ccc18898a8 Serhiy Storchaka 2017-05-04 09:25:09
bpo-30166: Import command-line parsing modules only when needed. (#1293) 7e4db2f253c555568d56177c2fd083bcf8f88d34 Serhiy Storchaka 2017-05-04 05:17:47
bpo-30225: Fix is_valid_fd() on macOS Tiger (#1443) 1c4670ea0cc3d208121af11b9b973e6bb268e570 Victor Stinner 2017-05-03 22:45:56
bpo-30184: Add tests for invalid use of PyArg_ParseTupleAndKeywords. (#1316) 5f161fd86dd5bb936a1a2a13391b13b7e59ec201 Serhiy Storchaka 2017-05-03 21:03:23
Commit cc3fa204d357be5fafc10eb8c2a80fe0bca998f1 - bpo-29979: Rewrite cgi.parse_multipart to make it consistent with FieldStorage (#991)
Author: Pierre Quentel
Author date (UTC): 2017-05-08 12:08
Committer name: Senthil Kumaran
Committer date (UTC): 2017-05-08 12:08
Parent(s): f34c6850203a2406c4950af7a9c8a134145df4ea
Signing key:
Tree: bd84defa311575c96461db01238cd231d94c439f
File Lines added Lines deleted
Doc/library/cgi.rst 10 9
Doc/whatsnew/3.7.rst 8 0
Lib/cgi.py 14 91
Lib/test/test_cgi.py 2 2
Misc/NEWS 4 0
File Doc/library/cgi.rst changed (mode: 100644) (index 41219eeaab..b60e1cc41e)
... ... algorithms implemented in this module in other circumstances.
294 294 This function is deprecated in this module. Use :func:`urllib.parse.parse_qsl` This function is deprecated in this module. Use :func:`urllib.parse.parse_qsl`
295 295 instead. It is maintained here only for backward compatibility. instead. It is maintained here only for backward compatibility.
296 296
297 .. function:: parse_multipart(fp, pdict)
297 .. function:: parse_multipart(fp, pdict, encoding="utf-8")
298 298
299 299 Parse input of type :mimetype:`multipart/form-data` (for file uploads). Parse input of type :mimetype:`multipart/form-data` (for file uploads).
300 Arguments are *fp* for the input file and *pdict* for a dictionary containing
301 other parameters in the :mailheader:`Content-Type` header.
300 Arguments are *fp* for the input file, *pdict* for a dictionary containing
301 other parameters in the :mailheader:`Content-Type` header, and *encoding*,
302 the request encoding.
302 303
303 Returns a dictionary just like :func:`urllib.parse.parse_qs` keys are the field names, each
304 value is a list of values for that field. This is easy to use but not much good
305 if you are expecting megabytes to be uploaded --- in that case, use the
306 :class:`FieldStorage` class instead which is much more flexible.
304 Returns a dictionary just like :func:`urllib.parse.parse_qs`: keys are the
305 field names, each value is a list of values for that field. For non-file
306 fields, the value is a list of strings.
307 307
308 Note that this does not parse nested multipart parts --- use
309 :class:`FieldStorage` for that.
308 This is easy to use but not much good if you are expecting megabytes to be
309 uploaded --- in that case, use the :class:`FieldStorage` class instead
310 which is much more flexible.
310 311
311 312
312 313 .. function:: parse_header(string) .. function:: parse_header(string)
File Doc/whatsnew/3.7.rst changed (mode: 100644) (index 7edf4fc3cf..b2dc995dce)
... ... New Modules
95 95 Improved Modules Improved Modules
96 96 ================ ================
97 97
98 cgi
99 ---
100
101 :func:`~cgi.parse_multipart` returns the same results as
102 :class:`~FieldStorage` : for non-file fields, the value associated to a key
103 is a list of strings, not bytes.
104 (Contributed by Pierre Quentel in :issue:`29979`.)
105
98 106 binascii binascii
99 107 -------- --------
100 108
File Lib/cgi.py changed (mode: 100755) (index 14d15a692b..f5e85aa263)
... ... def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
198 198 DeprecationWarning, 2) DeprecationWarning, 2)
199 199 return urllib.parse.parse_qsl(qs, keep_blank_values, strict_parsing) return urllib.parse.parse_qsl(qs, keep_blank_values, strict_parsing)
200 200
201 def parse_multipart(fp, pdict):
201 def parse_multipart(fp, pdict, encoding="utf-8"):
202 202 """Parse multipart input. """Parse multipart input.
203 203
204 204 Arguments: Arguments:
205 205 fp : input file fp : input file
206 206 pdict: dictionary containing other parameters of content-type header pdict: dictionary containing other parameters of content-type header
207 encoding: request encoding
207 208
208 209 Returns a dictionary just like parse_qs(): keys are the field names, each Returns a dictionary just like parse_qs(): keys are the field names, each
209 value is a list of values for that field. This is easy to use but not
210 much good if you are expecting megabytes to be uploaded -- in that case,
211 use the FieldStorage class instead which is much more flexible. Note
212 that content-type is the raw, unparsed contents of the content-type
213 header.
214
215 XXX This does not parse nested multipart parts -- use FieldStorage for
216 that.
217
218 XXX This should really be subsumed by FieldStorage altogether -- no
219 point in having two implementations of the same parsing algorithm.
220 Also, FieldStorage protects itself better against certain DoS attacks
221 by limiting the size of the data read in one chunk. The API here
222 does not support that kind of protection. This also affects parse()
223 since it can call parse_multipart().
224
210 value is a list of values for that field. For non-file fields, the value
211 is a list of strings.
225 212 """ """
226 import http.client
227
228 boundary = b""
229 if 'boundary' in pdict:
230 boundary = pdict['boundary']
231 if not valid_boundary(boundary):
232 raise ValueError('Invalid boundary in multipart form: %r'
233 % (boundary,))
234
235 nextpart = b"--" + boundary
236 lastpart = b"--" + boundary + b"--"
237 partdict = {}
238 terminator = b""
239
240 while terminator != lastpart:
241 bytes = -1
242 data = None
243 if terminator:
244 # At start of next part. Read headers first.
245 headers = http.client.parse_headers(fp)
246 clength = headers.get('content-length')
247 if clength:
248 try:
249 bytes = int(clength)
250 except ValueError:
251 pass
252 if bytes > 0:
253 if maxlen and bytes > maxlen:
254 raise ValueError('Maximum content length exceeded')
255 data = fp.read(bytes)
256 else:
257 data = b""
258 # Read lines until end of part.
259 lines = []
260 while 1:
261 line = fp.readline()
262 if not line:
263 terminator = lastpart # End outer loop
264 break
265 if line.startswith(b"--"):
266 terminator = line.rstrip()
267 if terminator in (nextpart, lastpart):
268 break
269 lines.append(line)
270 # Done with part.
271 if data is None:
272 continue
273 if bytes < 0:
274 if lines:
275 # Strip final line terminator
276 line = lines[-1]
277 if line[-2:] == b"\r\n":
278 line = line[:-2]
279 elif line[-1:] == b"\n":
280 line = line[:-1]
281 lines[-1] = line
282 data = b"".join(lines)
283 line = headers['content-disposition']
284 if not line:
285 continue
286 key, params = parse_header(line)
287 if key != 'form-data':
288 continue
289 if 'name' in params:
290 name = params['name']
291 else:
292 continue
293 if name in partdict:
294 partdict[name].append(data)
295 else:
296 partdict[name] = [data]
297
298 return partdict
299
213 # RFC 2026, Section 5.1 : The "multipart" boundary delimiters are always
214 # represented as 7bit US-ASCII.
215 boundary = pdict['boundary'].decode('ascii')
216 ctype = "multipart/form-data; boundary={}".format(boundary)
217 headers = Message()
218 headers.set_type(ctype)
219 headers['Content-Length'] = pdict['CONTENT-LENGTH']
220 fs = FieldStorage(fp, headers=headers, encoding=encoding,
221 environ={'REQUEST_METHOD': 'POST'})
222 return {k: fs.getlist(k) for k in fs}
300 223
301 224 def _parseparam(s): def _parseparam(s):
302 225 while s[:1] == ';': while s[:1] == ';':
File Lib/test/test_cgi.py changed (mode: 100644) (index 637322137d..903d0731f9)
... ... class CgiTests(unittest.TestCase):
126 126 env = {'boundary': BOUNDARY.encode('latin1'), env = {'boundary': BOUNDARY.encode('latin1'),
127 127 'CONTENT-LENGTH': '558'} 'CONTENT-LENGTH': '558'}
128 128 result = cgi.parse_multipart(fp, env) result = cgi.parse_multipart(fp, env)
129 expected = {'submit': [b' Add '], 'id': [b'1234'],
130 'file': [b'Testing 123.\n'], 'title': [b'']}
129 expected = {'submit': [' Add '], 'id': ['1234'],
130 'file': [b'Testing 123.\n'], 'title': ['']}
131 131 self.assertEqual(result, expected) self.assertEqual(result, expected)
132 132
133 133 def test_fieldstorage_properties(self): def test_fieldstorage_properties(self):
File Misc/NEWS changed (mode: 100644) (index a72fceff10..5e5ce59e29)
... ... Extension Modules
317 317 Library Library
318 318 ------- -------
319 319
320 - bpo-29979: rewrite cgi.parse_multipart, reusing the FieldStorage class and
321 making its results consistent with those of FieldStorage for
322 multipart/form-data requests. Patch by Pierre Quentel.
323
320 324 - bpo-30243: Removed the __init__ methods of _json's scanner and encoder. - bpo-30243: Removed the __init__ methods of _json's scanner and encoder.
321 325 Misusing them could cause memory leaks or crashes. Now scanner and encoder Misusing them could cause memory leaks or crashes. Now scanner and encoder
322 326 objects are completely initialized in the __new__ methods. objects are completely initialized in the __new__ methods.
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using git:
git clone git://git.rocketgit.com/user/benf_wspdigital/cpython

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main