Show More
@@ -10,15 +10,17 b' IO related utilities.' | |||||
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #----------------------------------------------------------------------------- |
|
11 | #----------------------------------------------------------------------------- | |
12 | from __future__ import print_function |
|
12 | from __future__ import print_function | |
|
13 | from __future__ import absolute_import | |||
13 |
|
14 | |||
14 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
15 | # Imports |
|
16 | # Imports | |
16 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | import codecs | |||
17 | import os |
|
19 | import os | |
18 | import sys |
|
20 | import sys | |
19 | import tempfile |
|
21 | import tempfile | |
20 | from .capture import CapturedIO, capture_output |
|
22 | from .capture import CapturedIO, capture_output | |
21 | from .py3compat import string_types, input |
|
23 | from .py3compat import string_types, input, PY3 | |
22 |
|
24 | |||
23 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
24 | # Code |
|
26 | # Code | |
@@ -227,3 +229,26 b' def raw_print_err(*args, **kw):' | |||||
227 | # Short aliases for quick debugging, do NOT use these in production code. |
|
229 | # Short aliases for quick debugging, do NOT use these in production code. | |
228 | rprint = raw_print |
|
230 | rprint = raw_print | |
229 | rprinte = raw_print_err |
|
231 | rprinte = raw_print_err | |
|
232 | ||||
|
233 | def unicode_std_stream(stream='stdout'): | |||
|
234 | """Get a wrapper to write unicode to stdout/stderr as UTF-8. | |||
|
235 | ||||
|
236 | This ignores environment variables and default encodings, to reliably write | |||
|
237 | unicode to stdout or stderr. | |||
|
238 | ||||
|
239 | :: | |||
|
240 | ||||
|
241 | unicode_std_stream().write(u'ł@e¶ŧ←') | |||
|
242 | """ | |||
|
243 | assert stream in ('stdout', 'stderr') | |||
|
244 | stream = getattr(sys, stream) | |||
|
245 | if PY3: | |||
|
246 | try: | |||
|
247 | stream_b = stream.buffer | |||
|
248 | except AttributeError: | |||
|
249 | # sys.stdout has been replaced - use it directly | |||
|
250 | return stream | |||
|
251 | else: | |||
|
252 | stream_b = stream | |||
|
253 | ||||
|
254 | return codecs.getwriter('utf-8')(stream_b) |
@@ -12,7 +12,9 b'' | |||||
12 | # Imports |
|
12 | # Imports | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 | from __future__ import print_function |
|
14 | from __future__ import print_function | |
|
15 | from __future__ import absolute_import | |||
15 |
|
16 | |||
|
17 | import io as stdlib_io | |||
16 | import sys |
|
18 | import sys | |
17 |
|
19 | |||
18 | from subprocess import Popen, PIPE |
|
20 | from subprocess import Popen, PIPE | |
@@ -20,7 +22,8 b' import unittest' | |||||
20 |
|
22 | |||
21 | import nose.tools as nt |
|
23 | import nose.tools as nt | |
22 |
|
24 | |||
23 | from IPython.utils.io import Tee, capture_output |
|
25 | from IPython.testing.decorators import skipif | |
|
26 | from IPython.utils.io import Tee, capture_output, unicode_std_stream | |||
24 | from IPython.utils.py3compat import doctest_refactor_print, PY3 |
|
27 | from IPython.utils.py3compat import doctest_refactor_print, PY3 | |
25 |
|
28 | |||
26 | if PY3: |
|
29 | if PY3: | |
@@ -88,3 +91,34 b' def test_capture_output():' | |||||
88 |
|
91 | |||
89 | nt.assert_equal(io.stdout, 'hi, stdout\n') |
|
92 | nt.assert_equal(io.stdout, 'hi, stdout\n') | |
90 | nt.assert_equal(io.stderr, 'hi, stderr\n') |
|
93 | nt.assert_equal(io.stderr, 'hi, stderr\n') | |
|
94 | ||||
|
95 | def test_UnicodeStdStream(): | |||
|
96 | # Test wrapping a bytes-level stdout | |||
|
97 | if PY3: | |||
|
98 | stdoutb = stdlib_io.BytesIO() | |||
|
99 | stdout = stdlib_io.TextIOWrapper(stdoutb, encoding='ascii') | |||
|
100 | else: | |||
|
101 | stdout = stdoutb = stdlib_io.BytesIO() | |||
|
102 | ||||
|
103 | orig_stdout = sys.stdout | |||
|
104 | sys.stdout = stdout | |||
|
105 | try: | |||
|
106 | sample = u"@łe¶ŧ←" | |||
|
107 | unicode_std_stream().write(sample) | |||
|
108 | ||||
|
109 | output = stdoutb.getvalue().decode('utf-8') | |||
|
110 | nt.assert_equal(output, sample) | |||
|
111 | assert not stdout.closed | |||
|
112 | finally: | |||
|
113 | sys.stdout = orig_stdout | |||
|
114 | ||||
|
115 | @skipif(not PY3, "Not applicable on Python 2") | |||
|
116 | def test_UnicodeStdStream_nowrap(): | |||
|
117 | # If we replace stdout with a StringIO, it shouldn't get wrapped. | |||
|
118 | orig_stdout = sys.stdout | |||
|
119 | sys.stdout = StringIO() | |||
|
120 | try: | |||
|
121 | nt.assert_is(unicode_std_stream(), sys.stdout) | |||
|
122 | assert not sys.stdout.closed | |||
|
123 | finally: | |||
|
124 | sys.stdout = orig_stdout No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now