Skip to content

Commit 2f582cd

Browse files
committed
Implement different workaround for https://bugs.python.org/issue27777
suggested by jsm/Joseph Myers. Subclass cgi.FieldStorage into BinaryFieldStorage and use that class rather then monkey patching cgi.FieldStorage.make_file. This should eliminate race conditions issues inherent with the prior way I tried to do it by flipping the class method back and forth at runtime. Also import new class into rest_common.py and use it in place of old mechanism.
1 parent 5897cbb commit 2f582cd

File tree

2 files changed

+20
-31
lines changed

2 files changed

+20
-31
lines changed

roundup/cgi/client.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,18 @@ def update(self, set_cookie=False, expire=None):
230230
if set_cookie:
231231
self.client.add_cookie(self.cookie_name, self._sid, expire=expire)
232232

233+
class BinaryFieldStorage(cgi.FieldStorage):
234+
'''This class works around the bug https://bugs.python.org/issue27777.
233235
236+
cgi.FieldStorage must save all data as binary/bytes. This is
237+
needed for handling json and xml data blobs under python
238+
3. Under python 2, str and binary are interchangable, not so
239+
under 3.
240+
'''
241+
def make_file(self, mode=None):
242+
''' work around https://bugs.python.org/issue27777 '''
243+
import tempfile
244+
return tempfile.TemporaryFile("wb+")
234245

235246
class Client:
236247
"""Instantiate to handle one CGI request.
@@ -376,18 +387,10 @@ def __init__(self, instance, request, env, form=None, translator=None):
376387
self.env['REQUEST_METHOD'])
377388

378389
# cgi.FieldStorage must save all data as
379-
# binary/bytes. This is needed for handling json and xml
380-
# data blobs under python 3. Under python 2, str and binary
381-
# are interchangable, not so under 3.
382-
def make_file(self):
383-
''' work around https://bugs.python.org/issue27777 '''
384-
import tempfile
385-
return tempfile.TemporaryFile("wb+")
386-
387-
saved_make_file = cgi.FieldStorage.make_file
388-
cgi.FieldStorage.make_file = make_file
389-
self.form = cgi.FieldStorage(fp=request.rfile, environ=env)
390-
cgi.FieldStorage.make_file = saved_make_file
390+
# binary/bytes. Subclass BinaryFieldStorage does this.
391+
# It's a workaround for a bug in cgi.FieldStorage. See class
392+
# def for details.
393+
self.form = BinaryFieldStorage(fp=request.rfile, environ=env)
391394
# In some case (e.g. content-type application/xml), cgi
392395
# will not parse anything. Fake a list property in this case
393396
if self.form.list is None:

test/rest_common.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,6 @@ def testEtagProcessing(self):
399399
else:
400400
self.assertEqual(self.dummy_client.response_code, 412)
401401

402-
def make_file(self, arg=None):
403-
''' work around https://bugs.python.org/issue27777 '''
404-
import tempfile
405-
return tempfile.TemporaryFile("wb+")
406-
407402
def testDispatch(self):
408403
"""
409404
run changes through rest dispatch(). This also tests
@@ -412,12 +407,6 @@ def testDispatch(self):
412407
process.
413408
"""
414409

415-
# Override the make_file so it is always set to binary
416-
# read mode. This is needed so we can send a json
417-
# body.
418-
saved_make_file = cgi.FieldStorage.make_file
419-
cgi.FieldStorage.make_file = self.make_file
420-
421410
# TEST #1
422411
# PUT: joe's 'realname' using json data.
423412
# simulate: /rest/data/user/<id>/realname
@@ -438,7 +427,7 @@ def testDispatch(self):
438427
# FieldStorage(None, None, 'string') rather than
439428
# FieldStorage(None, None, [])
440429
body_file=BytesIO(body) # FieldStorage needs a file
441-
form = cgi.FieldStorage(body_file,
430+
form = client.BinaryFieldStorage(body_file,
442431
headers=headers,
443432
environ=env)
444433
self.server.client.request.headers.get=self.get_header
@@ -465,7 +454,7 @@ def testDispatch(self):
465454
}
466455
self.headers=None # have FieldStorage get len from env.
467456
body_file=BytesIO(body) # FieldStorage needs a file
468-
form = cgi.FieldStorage(body_file,
457+
form = client.BinaryFieldStorage(body_file,
469458
headers=None,
470459
environ=env)
471460
self.server.client.request.headers.get=self.get_header
@@ -544,7 +533,7 @@ def testDispatch(self):
544533
}
545534
self.headers=headers
546535
body_file=BytesIO(body) # FieldStorage needs a file
547-
form = cgi.FieldStorage(body_file,
536+
form = client.BinaryFieldStorage(body_file,
548537
headers=headers,
549538
environ=env)
550539
self.server.client.request.headers.get=self.get_header
@@ -565,7 +554,7 @@ def testDispatch(self):
565554
etag))
566555
# reuse env and headers from prior test.
567556
body_file=BytesIO(body) # FieldStorage needs a file
568-
form = cgi.FieldStorage(body_file,
557+
form = client.BinaryFieldStorage(body_file,
569558
headers=headers,
570559
environ=env)
571560
self.server.client.request.headers.get=self.get_header
@@ -598,7 +587,7 @@ def testDispatch(self):
598587
}
599588
self.headers=headers
600589
body_file=BytesIO(body) # FieldStorage needs a file
601-
form = cgi.FieldStorage(body_file,
590+
form = client.BinaryFieldStorage(body_file,
602591
headers=headers,
603592
environ=env)
604593
self.server.client.request.headers.get=self.get_header
@@ -617,9 +606,6 @@ def testDispatch(self):
617606
'foo bar')
618607
del(self.headers)
619608

620-
# reset the make_file method in the class
621-
cgi.FieldStorage.make_file = saved_make_file
622-
623609
def testPut(self):
624610
"""
625611
Change joe's 'realname'

0 commit comments

Comments
 (0)