##// END OF EJS Templates
Added new Tee class, that works much like Unix's 'tee' command....
Fernando Perez -
Show More
@@ -60,9 +60,9 b' import nosepatch # monkeypatch nose'
60
60
61 # We already have python3-compliant code for parametric tests
61 # We already have python3-compliant code for parametric tests
62 if sys.version[0]=='2':
62 if sys.version[0]=='2':
63 from _paramtestpy2 import parametric
63 from _paramtestpy2 import parametric, ParametricTestCase
64 else:
64 else:
65 from _paramtestpy3 import parametric
65 from _paramtestpy3 import parametric, ParametricTestCase
66
66
67 # Expose the unittest-driven decorators
67 # Expose the unittest-driven decorators
68 from ipunittest import ipdoctest, ipdocstring
68 from ipunittest import ipdoctest, ipdocstring
@@ -105,6 +105,65 b" if sys.platform == 'win32' and readline.have_readline:"
105 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
105 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
106
106
107
107
108 class Tee(object):
109 """A class to duplicate an output stream to stdout/err.
110
111 This works in a manner very similar to the Unix 'tee' command.
112
113 When the object is closed or deleted, it closes the original file given to
114 it for duplication.
115 """
116 # Inspired by:
117 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
118
119 def __init__(self, file, mode=None, channel='stdout'):
120 """Construct a new Tee object.
121
122 Parameters
123 ----------
124 file : filename or open filehandle (writable)
125 File that will be duplicated
126
127 mode : optional, valid mode for open().
128 If a filename was give, open with this mode.
129
130 channel : str, one of ['stdout', 'stderr']
131 """
132 if channel not in ['stdout', 'stderr']:
133 raise ValueError('Invalid channel spec %s' % channel)
134
135 if hasattr(file, 'write') and hasattr(file, 'seek'):
136 self.file = file
137 else:
138 self.file = open(name, mode)
139 self.channel = channel
140 self.ostream = getattr(sys, channel)
141 setattr(sys, channel, self)
142 self._closed = False
143
144 def close(self):
145 """Close the file and restore the channel."""
146 self.flush()
147 setattr(sys, self.channel, self.ostream)
148 self.file.close()
149 self._closed = True
150
151 def write(self, data):
152 """Write data to both channels."""
153 self.file.write(data)
154 self.ostream.write(data)
155 self.ostream.flush()
156
157 def flush(self):
158 """Flush both channels."""
159 self.file.flush()
160 self.ostream.flush()
161
162 def __del__(self):
163 if not self._closed:
164 self.close()
165
166
108 #****************************************************************************
167 #****************************************************************************
109 # Generic warning/error printer, used by everything else
168 # Generic warning/error printer, used by everything else
110 def warn(msg,level=2,exit_val=1):
169 def warn(msg,level=2,exit_val=1):
@@ -20,7 +20,9 b' import os'
20 import shutil
20 import shutil
21 import sys
21 import sys
22 import tempfile
22 import tempfile
23 import unittest
23
24
25 from cStringIO import StringIO
24 from os.path import join, abspath, split
26 from os.path import join, abspath, split
25
27
26 # third-party
28 # third-party
@@ -32,6 +34,7 b' from nose.tools import raises'
32 # Our own
34 # Our own
33 import IPython
35 import IPython
34 from IPython.utils import genutils
36 from IPython.utils import genutils
37 from IPython.testing import decorators as dec
35 from IPython.testing.decorators import skipif, skip_if_not_win32
38 from IPython.testing.decorators import skipif, skip_if_not_win32
36
39
37 # Platform-dependent imports
40 # Platform-dependent imports
@@ -107,7 +110,7 b' def teardown_environment():'
107 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
110 (wreg.OpenKey, wreg.QueryValueEx,) = platformstuff
108
111
109 # Build decorator that uses the setup_environment/setup_environment
112 # Build decorator that uses the setup_environment/setup_environment
110 with_enivronment = with_setup(setup_environment, teardown_environment)
113 with_environment = with_setup(setup_environment, teardown_environment)
111
114
112
115
113 #
116 #
@@ -115,7 +118,7 b' with_enivronment = with_setup(setup_environment, teardown_environment)'
115 #
118 #
116
119
117 @skip_if_not_win32
120 @skip_if_not_win32
118 @with_enivronment
121 @with_environment
119 def test_get_home_dir_1():
122 def test_get_home_dir_1():
120 """Testcase for py2exe logic, un-compressed lib
123 """Testcase for py2exe logic, un-compressed lib
121 """
124 """
@@ -128,7 +131,7 b' def test_get_home_dir_1():'
128 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
131 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
129
132
130 @skip_if_not_win32
133 @skip_if_not_win32
131 @with_enivronment
134 @with_environment
132 def test_get_home_dir_2():
135 def test_get_home_dir_2():
133 """Testcase for py2exe logic, compressed lib
136 """Testcase for py2exe logic, compressed lib
134 """
137 """
@@ -139,14 +142,14 b' def test_get_home_dir_2():'
139 home_dir = genutils.get_home_dir()
142 home_dir = genutils.get_home_dir()
140 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
143 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR).lower())
141
144
142 @with_enivronment
145 @with_environment
143 def test_get_home_dir_3():
146 def test_get_home_dir_3():
144 """Testcase $HOME is set, then use its value as home directory."""
147 """Testcase $HOME is set, then use its value as home directory."""
145 env["HOME"] = HOME_TEST_DIR
148 env["HOME"] = HOME_TEST_DIR
146 home_dir = genutils.get_home_dir()
149 home_dir = genutils.get_home_dir()
147 nt.assert_equal(home_dir, env["HOME"])
150 nt.assert_equal(home_dir, env["HOME"])
148
151
149 @with_enivronment
152 @with_environment
150 def test_get_home_dir_4():
153 def test_get_home_dir_4():
151 """Testcase $HOME is not set, os=='poix'.
154 """Testcase $HOME is not set, os=='poix'.
152 This should fail with HomeDirError"""
155 This should fail with HomeDirError"""
@@ -156,7 +159,7 b' def test_get_home_dir_4():'
156 nt.assert_raises(genutils.HomeDirError, genutils.get_home_dir)
159 nt.assert_raises(genutils.HomeDirError, genutils.get_home_dir)
157
160
158 @skip_if_not_win32
161 @skip_if_not_win32
159 @with_enivronment
162 @with_environment
160 def test_get_home_dir_5():
163 def test_get_home_dir_5():
161 """Testcase $HOME is not set, os=='nt'
164 """Testcase $HOME is not set, os=='nt'
162 env['HOMEDRIVE'],env['HOMEPATH'] points to path."""
165 env['HOMEDRIVE'],env['HOMEPATH'] points to path."""
@@ -169,7 +172,7 b' def test_get_home_dir_5():'
169 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
172 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
170
173
171 @skip_if_not_win32
174 @skip_if_not_win32
172 @with_enivronment
175 @with_environment
173 def test_get_home_dir_6():
176 def test_get_home_dir_6():
174 """Testcase $HOME is not set, os=='nt'
177 """Testcase $HOME is not set, os=='nt'
175 env['HOMEDRIVE'],env['HOMEPATH'] do not point to path.
178 env['HOMEDRIVE'],env['HOMEPATH'] do not point to path.
@@ -186,7 +189,7 b' def test_get_home_dir_6():'
186
189
187 # Should we stub wreg fully so we can run the test on all platforms?
190 # Should we stub wreg fully so we can run the test on all platforms?
188 @skip_if_not_win32
191 @skip_if_not_win32
189 @with_enivronment
192 @with_environment
190 def test_get_home_dir_7():
193 def test_get_home_dir_7():
191 """Testcase $HOME is not set, os=='nt'
194 """Testcase $HOME is not set, os=='nt'
192 env['HOMEDRIVE'],env['HOMEPATH'], env['USERPROFILE'] missing
195 env['HOMEDRIVE'],env['HOMEPATH'], env['USERPROFILE'] missing
@@ -214,7 +217,7 b' def test_get_home_dir_7():'
214 # Tests for get_ipython_dir
217 # Tests for get_ipython_dir
215 #
218 #
216
219
217 @with_enivronment
220 @with_environment
218 def test_get_ipython_dir_1():
221 def test_get_ipython_dir_1():
219 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
222 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
220 env['IPYTHONDIR'] = "someplace/.ipython"
223 env['IPYTHONDIR'] = "someplace/.ipython"
@@ -222,7 +225,7 b' def test_get_ipython_dir_1():'
222 nt.assert_equal(ipdir, "someplace/.ipython")
225 nt.assert_equal(ipdir, "someplace/.ipython")
223
226
224
227
225 @with_enivronment
228 @with_environment
226 def test_get_ipython_dir_2():
229 def test_get_ipython_dir_2():
227 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
230 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
228 genutils.get_home_dir = lambda : "someplace"
231 genutils.get_home_dir = lambda : "someplace"
@@ -283,3 +286,38 b' def test_filefind():'
283 def test_get_ipython_package_dir():
286 def test_get_ipython_package_dir():
284 ipdir = genutils.get_ipython_package_dir()
287 ipdir = genutils.get_ipython_package_dir()
285 nt.assert_true(os.path.isdir(ipdir))
288 nt.assert_true(os.path.isdir(ipdir))
289
290
291 def test_tee_simple():
292 "Very simple check with stdout only"
293 chan = StringIO()
294 text = 'Hello'
295 tee = genutils.Tee(chan, channel='stdout')
296 print >> chan, text,
297 nt.assert_equal(chan.getvalue(), text)
298
299
300 class TeeTestCase(dec.ParametricTestCase):
301
302 def tchan(self, channel, check='close'):
303 trap = StringIO()
304 chan = StringIO()
305 text = 'Hello'
306
307 std_ori = getattr(sys, channel)
308 setattr(sys, channel, trap)
309
310 tee = genutils.Tee(chan, channel=channel)
311 print >> chan, text,
312 setattr(sys, channel, std_ori)
313 trap_val = trap.getvalue()
314 nt.assert_equals(chan.getvalue(), text)
315 if check=='close':
316 tee.close()
317 else:
318 del tee
319
320 def test(self):
321 for chan in ['stdout', 'stderr']:
322 for check in ['close', 'del']:
323 yield self.tchan(chan, check)
General Comments 0
You need to be logged in to leave comments. Login now