##// END OF EJS Templates
Ensure that we don't damage the __builtins__ object after %run....
Fernando Perez -
Show More
@@ -0,0 +1,88 b''
1 """Generic testing tools that do NOT depend on Twisted.
2
3 In particular, this module exposes a set of top-level assert* functions that
4 can be used in place of nose.tools.assert* in method generators (the ones in
5 nose can not, at least as of nose 0.10.4).
6
7 Note: our testing package contains testing.util, which does depend on Twisted
8 and provides utilities for tests that manage Deferreds. All testing support
9 tools that only depend on nose, IPython or the standard library should go here
10 instead.
11
12
13 Authors
14 -------
15 - Fernando Perez <Fernando.Perez@berkeley.edu>
16 """
17
18 #*****************************************************************************
19 # Copyright (C) 2009 The IPython Development Team
20 #
21 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
23 #*****************************************************************************
24
25 #-----------------------------------------------------------------------------
26 # Required modules and packages
27 #-----------------------------------------------------------------------------
28
29 # Standard Python lib
30 import os
31 import sys
32
33 # Third-party
34 import nose.tools as nt
35
36 # From this project
37 from IPython.tools import utils
38
39 #-----------------------------------------------------------------------------
40 # Globals
41 #-----------------------------------------------------------------------------
42
43 # Make a bunch of nose.tools assert wrappers that can be used in test
44 # generators. This will expose an assert* function for each one in nose.tools.
45
46 _tpl = """
47 def %(name)s(*a,**kw):
48 return nt.%(name)s(*a,**kw)
49 """
50
51 for _x in [a for a in dir(nt) if a.startswith('assert')]:
52 exec _tpl % dict(name=_x)
53
54 #-----------------------------------------------------------------------------
55 # Functions and classes
56 #-----------------------------------------------------------------------------
57
58 def full_path(startPath,files):
59 """Make full paths for all the listed files, based on startPath.
60
61 Only the base part of startPath is kept, since this routine is typically
62 used with a script's __file__ variable as startPath. The base of startPath
63 is then prepended to all the listed files, forming the output list.
64
65 :Parameters:
66 startPath : string
67 Initial path to use as the base for the results. This path is split
68 using os.path.split() and only its first component is kept.
69
70 files : string or list
71 One or more files.
72
73 :Examples:
74
75 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
76 ['/foo/a.txt', '/foo/b.txt']
77
78 >>> full_path('/foo',['a.txt','b.txt'])
79 ['/a.txt', '/b.txt']
80
81 If a single file is given, the output is still a list:
82 >>> full_path('/foo','a.txt')
83 ['/a.txt']
84 """
85
86 files = utils.list_strings(files)
87 base = os.path.split(startPath)[0]
88 return [ os.path.join(base,f) for f in files ]
@@ -1709,6 +1709,16 b' Currently the magic system has the following functions:\\n"""'
1709 del prog_ns['__name__']
1709 del prog_ns['__name__']
1710 self.shell.user_ns.update(prog_ns)
1710 self.shell.user_ns.update(prog_ns)
1711 finally:
1711 finally:
1712 # It's a bit of a mystery why, but __builtins__ can change from
1713 # being a module to becoming a dict missing some key data after
1714 # %run. As best I can see, this is NOT something IPython is doing
1715 # at all, and similar problems have been reported before:
1716 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
1717 # Since this seems to be done by the interpreter itself, the best
1718 # we can do is to at least restore __builtins__ for the user on
1719 # exit.
1720 self.shell.user_ns['__builtins__'] = __builtin__
1721
1712 # Ensure key global structures are restored
1722 # Ensure key global structures are restored
1713 sys.argv = save_argv
1723 sys.argv = save_argv
1714 if restore_main:
1724 if restore_main:
@@ -123,10 +123,19 b' class ipnsdict(dict):'
123 def update(self,other):
123 def update(self,other):
124 self._checkpoint()
124 self._checkpoint()
125 dict.update(self,other)
125 dict.update(self,other)
126
126 # If '_' is in the namespace, python won't set it when executing code,
127 # If '_' is in the namespace, python won't set it when executing code,
127 # and we have examples that test it. So we ensure that the namespace
128 # and we have examples that test it. So we ensure that the namespace
128 # is always 'clean' of it before it's used for test code execution.
129 # is always 'clean' of it before it's used for test code execution.
129 self.pop('_',None)
130 self.pop('_',None)
131
132 # The builtins namespace must *always* be the real __builtin__ module,
133 # else weird stuff happens. The main ipython code does have provisions
134 # to ensure this after %run, but since in this class we do some
135 # aggressive low-level cleaning of the execution namespace, we need to
136 # correct for that ourselves, to ensure consitency with the 'real'
137 # ipython.
138 self['__builtins__'] = __builtin__
130
139
131
140
132 def start_ipython():
141 def start_ipython():
@@ -18,6 +18,25 b' def doctest_simple():'
18 """
18 """
19
19
20
20
21 def doctest_run_builtins():
22 """Check that %run doesn't damage __builtins__ via a doctest.
23
24 This is similar to the test_run_builtins, but I want *both* forms of the
25 test to catch any possible glitches in our testing machinery, since that
26 modifies %run somewhat. So for this, we have both a normal test (below)
27 and a doctest (this one).
28
29 In [1]: import tempfile
30
31 In [3]: f = tempfile.NamedTemporaryFile()
32
33 In [4]: f.write('pass\\n')
34
35 In [5]: f.flush()
36
37 In [7]: %run $f.name
38 """
39
21 def doctest_multiline1():
40 def doctest_multiline1():
22 """The ipdoctest machinery must handle multiline examples gracefully.
41 """The ipdoctest machinery must handle multiline examples gracefully.
23
42
@@ -6,12 +6,16 b' Needs to be run by nose (to make ipython session available).'
6 # Standard library imports
6 # Standard library imports
7 import os
7 import os
8 import sys
8 import sys
9 import tempfile
10 import types
9
11
10 # Third-party imports
12 # Third-party imports
11 import nose.tools as nt
13 import nose.tools as nt
12
14
13 # From our own code
15 # From our own code
14 from IPython.testing import decorators as dec
16 from IPython.testing import decorators as dec
17 from IPython.testing import tools as tt
18
15 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
16 # Test functions begin
20 # Test functions begin
17
21
@@ -33,26 +37,6 b' def test_rehashx():'
33 assert len(scoms) > 10
37 assert len(scoms) > 10
34
38
35
39
36 def doctest_run_ns():
37 """Classes declared %run scripts must be instantiable afterwards.
38
39 In [11]: run tclass foo
40
41 In [12]: isinstance(f(),foo)
42 Out[12]: True
43 """
44
45
46 def doctest_run_ns2():
47 """Classes declared %run scripts must be instantiable afterwards.
48
49 In [4]: run tclass C-first_pass
50
51 In [5]: run tclass C-second_pass
52 tclass.py: deleting object: C-first_pass
53 """
54
55
56 def doctest_hist_f():
40 def doctest_hist_f():
57 """Test %hist -f with temporary filename.
41 """Test %hist -f with temporary filename.
58
42
@@ -63,59 +47,6 b' def doctest_hist_f():'
63 In [11]: %history -n -f $tfile 3
47 In [11]: %history -n -f $tfile 3
64 """
48 """
65
49
66 def doctest_run_builtins():
67 """Check that %run doesn't damage __builtins__ via a doctest.
68
69 This is similar to the test_run_builtins, but I want *both* forms of the
70 test to catch any possible glitches in our testing machinery, since that
71 modifies %run somewhat. So for this, we have both a normal test (below)
72 and a doctest (this one).
73
74 In [1]: import tempfile
75
76 In [2]: bid1 = id(__builtins__)
77
78 In [3]: f = tempfile.NamedTemporaryFile()
79
80 In [4]: f.write('pass\\n')
81
82 In [5]: f.flush()
83
84 In [6]: print 'B1:',type(__builtins__)
85 B1: <type 'module'>
86
87 In [7]: %run $f.name
88
89 In [8]: bid2 = id(__builtins__)
90
91 In [9]: print 'B2:',type(__builtins__)
92 B2: <type 'module'>
93
94 In [10]: bid1 == bid2
95 Out[10]: True
96 """
97
98 def test_run_builtins():
99 """Check that %run doesn't damage __builtins__ """
100 import sys
101 import tempfile
102 import types
103
104 # Make an empty file and put 'pass' in it
105 f = tempfile.NamedTemporaryFile()
106 f.write('pass\n')
107 f.flush()
108
109 # Our first test is that the id of __builtins__ is not modified by %run
110 bid1 = id(__builtins__)
111 _ip.magic('run %s' % f.name)
112 bid2 = id(__builtins__)
113 yield nt.assert_equals,bid1,bid2
114 # However, the above could pass if __builtins__ was already modified to be
115 # a dict (it should be a module) by a previous use of %run. So we also
116 # check explicitly that it really is a module:
117 yield nt.assert_equals,type(__builtins__),type(sys)
118
119
50
120 def doctest_hist_r():
51 def doctest_hist_r():
121 """Test %hist -r
52 """Test %hist -r
@@ -201,3 +132,104 b' def doctest_refbug():'
201 lowercased: hello
132 lowercased: hello
202 lowercased: hello
133 lowercased: hello
203 """
134 """
135
136 #-----------------------------------------------------------------------------
137 # Tests for %run
138 #-----------------------------------------------------------------------------
139
140 # %run is critical enough that it's a good idea to have a solid collection of
141 # tests for it, some as doctests and some as normal tests.
142
143 def doctest_run_ns():
144 """Classes declared %run scripts must be instantiable afterwards.
145
146 In [11]: run tclass foo
147
148 In [12]: isinstance(f(),foo)
149 Out[12]: True
150 """
151
152
153 def doctest_run_ns2():
154 """Classes declared %run scripts must be instantiable afterwards.
155
156 In [4]: run tclass C-first_pass
157
158 In [5]: run tclass C-second_pass
159 tclass.py: deleting object: C-first_pass
160 """
161
162 def doctest_run_builtins():
163 """Check that %run doesn't damage __builtins__ via a doctest.
164
165 This is similar to the test_run_builtins, but I want *both* forms of the
166 test to catch any possible glitches in our testing machinery, since that
167 modifies %run somewhat. So for this, we have both a normal test (below)
168 and a doctest (this one).
169
170 In [1]: import tempfile
171
172 In [2]: bid1 = id(__builtins__)
173
174 In [3]: f = tempfile.NamedTemporaryFile()
175
176 In [4]: f.write('pass\\n')
177
178 In [5]: f.flush()
179
180 In [6]: print 'B1:',type(__builtins__)
181 B1: <type 'module'>
182
183 In [7]: %run $f.name
184
185 In [8]: bid2 = id(__builtins__)
186
187 In [9]: print 'B2:',type(__builtins__)
188 B2: <type 'module'>
189
190 In [10]: bid1 == bid2
191 Out[10]: True
192 """
193
194 # For some tests, it will be handy to organize them in a class with a common
195 # setup that makes a temp file
196
197 class TestMagicRun(object):
198
199 def setup(self):
200 """Make a valid python temp file."""
201 f = tempfile.NamedTemporaryFile()
202 f.write('pass\n')
203 f.flush()
204 self.tmpfile = f
205
206 def run_tmpfile(self):
207 _ip.magic('run %s' % self.tmpfile.name)
208
209 def test_builtins_id(self):
210 """Check that %run doesn't damage __builtins__ """
211
212 # Test that the id of __builtins__ is not modified by %run
213 bid1 = id(_ip.user_ns['__builtins__'])
214 self.run_tmpfile()
215 bid2 = id(_ip.user_ns['__builtins__'])
216 tt.assert_equals(bid1, bid2)
217
218 def test_builtins_type(self):
219 """Check that the type of __builtins__ doesn't change with %run.
220
221 However, the above could pass if __builtins__ was already modified to
222 be a dict (it should be a module) by a previous use of %run. So we
223 also check explicitly that it really is a module:
224 """
225 self.run_tmpfile()
226 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
227
228 def test_prompts(self):
229 """Test that prompts correctly generate after %run"""
230 self.run_tmpfile()
231 p2 = str(_ip.IP.outputcache.prompt2).strip()
232 nt.assert_equals(p2[:3], '...')
233
234 def teardown(self):
235 self.tmpfile.close()
General Comments 0
You need to be logged in to leave comments. Login now