Skip to content

Commit edf2d29

Browse files
committed
Make responding with binary data work under Py3.
1 parent f604d59 commit edf2d29

File tree

5 files changed

+79
-7
lines changed

5 files changed

+79
-7
lines changed

mig/wsgi-bin/migwsgi.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def _set_os_environ(value):
215215

216216
return _application(environ, start_response, _set_environ=_set_os_environ, _wrap_wsgi_errors=wrap_wsgi_errors)
217217

218-
def _application(environ, start_response, _set_environ, _format_output=format_output, _retrieve_handler=_import_backend, _wrap_wsgi_errors=True, _config_file=None, _skip_log=False):
218+
def _application(environ, start_response, _set_environ, _fieldstorage_to_dict=fieldstorage_to_dict, _format_output=format_output, _retrieve_handler=_import_backend, _wrap_wsgi_errors=True, _config_file=None, _skip_log=False):
219219

220220
# NOTE: pass app environ including apache and query args on to sub handlers
221221
# through the usual 'os.environ' channel expected in functionality
@@ -326,7 +326,7 @@ def _application(environ, start_response, _set_environ, _format_output=format_ou
326326
# (backend, script_name))
327327
fieldstorage = cgi.FieldStorage(fp=environ['wsgi.input'],
328328
environ=environ)
329-
user_arguments_dict = fieldstorage_to_dict(fieldstorage)
329+
user_arguments_dict = _fieldstorage_to_dict(fieldstorage)
330330
if 'output_format' in user_arguments_dict:
331331
output_format = user_arguments_dict['output_format'][0]
332332

@@ -444,7 +444,10 @@ def _application(environ, start_response, _set_environ, _format_output=format_ou
444444
# (backend, i+1, chunk_parts))
445445
# end index may be after end of content - but no problem
446446
part = output[i*download_block_size:(i+1)*download_block_size]
447-
yield _ensure_encoded_string(part)
447+
if output_format == 'file':
448+
yield part
449+
else:
450+
yield _ensure_encoded_string(part)
448451
if chunk_parts > 1:
449452
_logger.info("WSGI %s finished yielding all %d output parts" %
450453
(backend, chunk_parts))

tests/data/loading.gif

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../mig/images/loading.gif

tests/support/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from unittest import TestCase, main as testmain
4242

4343
from tests.support.suppconst import MIG_BASE, TEST_BASE, TEST_FIXTURE_DIR, \
44-
TEST_OUTPUT_DIR
44+
TEST_OUTPUT_DIR, TEST_DATA_DIR
4545

4646
PY2 = (sys.version_info[0] == 2)
4747

tests/support/suppconst.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
# Use abspath for __file__ on Py2
3232
_SUPPORT_DIR = os.path.dirname(os.path.abspath(__file__))
3333
TEST_BASE = os.path.normpath(os.path.join(_SUPPORT_DIR, ".."))
34+
TEST_DATA_DIR = os.path.join(TEST_BASE, "data")
3435
TEST_FIXTURE_DIR = os.path.join(TEST_BASE, "fixture")
3536
TEST_OUTPUT_DIR = os.path.join(TEST_BASE, "output")
3637
MIG_BASE = os.path.realpath(os.path.join(TEST_BASE, ".."))

tests/test_mig_wsgi-bin_migwsgi.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
import os
3333
import stat
3434
import sys
35+
import unittest
3536

36-
from tests.support import MIG_BASE, MigTestCase, testmain
37+
from tests.support import MIG_BASE, TEST_BASE, TEST_DATA_DIR, MigTestCase, testmain
3738
from mig.shared.output import format_output
3839
import mig.shared.returnvalues as returnvalues
3940

@@ -88,14 +89,35 @@ def _is_return_value(return_value):
8889
return return_value in defined_return_values
8990

9091

91-
def _trigger_and_unpack_result(application_result):
92+
def _trigger_and_unpack_result(application_result, result_kind='textual'):
93+
assert result_kind in ('textual', 'binary')
94+
9295
chunks = list(application_result)
9396
assert len(chunks) > 0, "invocation returned no output"
9497
complete_value = b''.join(chunks)
95-
decoded_value = codecs.decode(complete_value, 'utf8')
98+
if result_kind == 'binary':
99+
decoded_value = complete_value
100+
else:
101+
decoded_value = codecs.decode(complete_value, 'utf8')
96102
return decoded_value
97103

98104

105+
def create_instrumented_fieldstorage_to_dict():
106+
def _instrumented_fieldstorage_to_dict(fieldstorage):
107+
return _instrumented_fieldstorage_to_dict._result
108+
109+
_instrumented_fieldstorage_to_dict._result = {
110+
'output_format': ('html',)
111+
}
112+
113+
def set_result(result):
114+
_instrumented_fieldstorage_to_dict._result = result
115+
116+
_instrumented_fieldstorage_to_dict.set_result = set_result
117+
118+
return _instrumented_fieldstorage_to_dict
119+
120+
99121
def create_instrumented_format_output():
100122
def _instrumented_format_output(
101123
configuration,
@@ -110,6 +132,16 @@ def _instrumented_format_output(
110132
call_args = (configuration, backend, ret_val, ret_msg, call_args_out_obj, outputformat,)
111133
_instrumented_format_output.calls.append({ 'args': call_args })
112134

135+
if _instrumented_format_output._file:
136+
return format_output(
137+
configuration,
138+
backend,
139+
ret_val,
140+
ret_msg,
141+
out_obj,
142+
outputformat,
143+
)
144+
113145
# FIXME: the following is a workaround for a bug that exists between the WSGI wrapper
114146
# and the output formatter - specifically, the latter adds default header and
115147
# title if start does not exist, but the former ensures that start always exists
@@ -144,11 +176,18 @@ def _instrumented_format_output(
144176
outputformat,
145177
)
146178
_instrumented_format_output.calls = []
179+
_instrumented_format_output._file = False
147180
_instrumented_format_output.values = dict(
148181
title_text='',
149182
header_text='',
150183
)
151184

185+
186+
def _set_file(is_enabled):
187+
_instrumented_format_output._file = is_enabled
188+
189+
setattr(_instrumented_format_output, 'set_file', _set_file)
190+
152191
def _program_values(**kwargs):
153192
_instrumented_format_output.values.update(kwargs)
154193

@@ -254,12 +293,14 @@ def fake_set_environ(value):
254293
path_info='/',
255294
))
256295

296+
self.instrumented_fieldstorage_to_dict = create_instrumented_fieldstorage_to_dict()
257297
self.instrumented_format_output = create_instrumented_format_output()
258298
self.instrumented_retrieve_handler = create_instrumented_retrieve_handler()
259299

260300
self.application_args = (fake_wsgi_environ, fake_start_response,)
261301
self.application_kwargs = dict(
262302
_format_output=self.instrumented_format_output,
303+
_fieldstorage_to_dict=self.instrumented_fieldstorage_to_dict,
263304
_retrieve_handler=self.instrumented_retrieve_handler,
264305
_set_environ=fake_set_environ,
265306
)
@@ -313,6 +354,32 @@ def test_return_value_ok_returns_expected_title(self):
313354
self.assertInstrumentation()
314355
self.assertHtmlElementTextContent(output, 'title', 'TEST', trim_newlines=True)
315356

357+
def test_xxxx(self):
358+
test_binary_file = os.path.join(TEST_DATA_DIR, 'loading.gif')
359+
with open(test_binary_file, 'rb') as f:
360+
test_binary_data = f.read()
361+
362+
self.instrumented_fieldstorage_to_dict.set_result({
363+
'output_format': ('file',)
364+
})
365+
self.instrumented_format_output.set_file(True)
366+
367+
file_obj = { 'object_type': 'binary', 'data': test_binary_data }
368+
self.instrumented_retrieve_handler.program([file_obj], returnvalues.OK)
369+
370+
application_result = migwsgi._application(
371+
*self.application_args,
372+
_wrap_wsgi_errors=noop,
373+
_config_file=_TEST_CONF_FILE,
374+
_skip_log=True,
375+
**self.application_kwargs
376+
)
377+
378+
output = _trigger_and_unpack_result(application_result, 'binary')
379+
380+
self.assertInstrumentation()
381+
self.assertEqual(output, test_binary_data)
382+
316383

317384
if __name__ == '__main__':
318385
testmain()

0 commit comments

Comments
 (0)