Merge lp://staging/~fdo.perez/ipython/trunk-dev into lp://staging/~ellisonbg/ipython/bugfixes0411409

Proposed by Brian Granger
Status: Merged
Merged at revision: not available
Proposed branch: lp://staging/~fdo.perez/ipython/trunk-dev
Merge into: lp://staging/~ellisonbg/ipython/bugfixes0411409
Diff against target: None lines
To merge this branch: bzr merge lp://staging/~fdo.perez/ipython/trunk-dev
Reviewer Review Type Date Requested Status
Brian Granger Approve
Review via email: mp+5878@code.staging.launchpad.net
To post a comment you must log in.
Revision history for this message
Brian Granger (ellisonbg) wrote :

This branch should be merged to bugfixes0411409 rather than trunk.

Revision history for this message
Brian Granger (ellisonbg) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'IPython/Magic.py'
2--- IPython/Magic.py 2009-04-14 23:05:00 +0000
3+++ IPython/Magic.py 2009-04-21 23:57:57 +0000
4@@ -1709,6 +1709,16 @@
5 del prog_ns['__name__']
6 self.shell.user_ns.update(prog_ns)
7 finally:
8+ # It's a bit of a mystery why, but __builtins__ can change from
9+ # being a module to becoming a dict missing some key data after
10+ # %run. As best I can see, this is NOT something IPython is doing
11+ # at all, and similar problems have been reported before:
12+ # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
13+ # Since this seems to be done by the interpreter itself, the best
14+ # we can do is to at least restore __builtins__ for the user on
15+ # exit.
16+ self.shell.user_ns['__builtins__'] = __builtin__
17+
18 # Ensure key global structures are restored
19 sys.argv = save_argv
20 if restore_main:
21
22=== modified file 'IPython/Prompts.py'
23--- IPython/Prompts.py 2009-04-08 01:22:53 +0000
24+++ IPython/Prompts.py 2009-04-21 21:12:11 +0000
25@@ -126,9 +126,13 @@
26 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
27 # can get numbers displayed in whatever color they want.
28 r'\N': '${self.cache.prompt_count}',
29+
30 # Prompt/history count, with the actual digits replaced by dots. Used
31 # mainly in continuation prompts (prompt_in2)
32+ #r'\D': '${"."*len(str(self.cache.prompt_count))}',
33+ # More robust form of the above expression, that uses __builtins__
34 r'\D': '${"."*__builtins__.len(__builtins__.str(self.cache.prompt_count))}',
35+
36 # Current working directory
37 r'\w': '${os.getcwd()}',
38 # Current time
39
40=== modified file 'IPython/testing/plugin/ipdoctest.py'
41--- IPython/testing/plugin/ipdoctest.py 2009-04-03 23:53:28 +0000
42+++ IPython/testing/plugin/ipdoctest.py 2009-04-21 23:57:57 +0000
43@@ -84,9 +84,19 @@
44 This is strictly needed for running doctests that call %run.
45 """
46
47- finder = py_file_finder(_run_ns_sync.test_filename)
48+ # When tests call %run directly (not via doctest) these function attributes
49+ # are not set
50+ try:
51+ fname = _run_ns_sync.test_filename
52+ except AttributeError:
53+ fname = arg_s
54+
55+ finder = py_file_finder(fname)
56 out = _ip.IP.magic_run_ori(arg_s,runner,finder)
57- _run_ns_sync.test_globs.update(_ip.user_ns)
58+
59+ # Simliarly, there is no test_globs when a test is NOT a doctest
60+ if hasattr(_run_ns_sync,'test_globs'):
61+ _run_ns_sync.test_globs.update(_ip.user_ns)
62 return out
63
64
65@@ -113,10 +123,19 @@
66 def update(self,other):
67 self._checkpoint()
68 dict.update(self,other)
69+
70 # If '_' is in the namespace, python won't set it when executing code,
71 # and we have examples that test it. So we ensure that the namespace
72 # is always 'clean' of it before it's used for test code execution.
73 self.pop('_',None)
74+
75+ # The builtins namespace must *always* be the real __builtin__ module,
76+ # else weird stuff happens. The main ipython code does have provisions
77+ # to ensure this after %run, but since in this class we do some
78+ # aggressive low-level cleaning of the execution namespace, we need to
79+ # correct for that ourselves, to ensure consitency with the 'real'
80+ # ipython.
81+ self['__builtins__'] = __builtin__
82
83
84 def start_ipython():
85
86=== added file 'IPython/testing/plugin/test_ipdoctest.py'
87--- IPython/testing/plugin/test_ipdoctest.py 1970-01-01 00:00:00 +0000
88+++ IPython/testing/plugin/test_ipdoctest.py 2009-04-21 23:57:57 +0000
89@@ -0,0 +1,94 @@
90+"""Tests for the ipdoctest machinery itself.
91+
92+Note: in a file named test_X, functions whose only test is their docstring (as
93+a doctest) and which have no test functionality of their own, should be called
94+'doctest_foo' instead of 'test_foo', otherwise they get double-counted (the
95+empty function call is counted as a test, which just inflates tests numbers
96+artificially).
97+"""
98+
99+def doctest_simple():
100+ """ipdoctest must handle simple inputs
101+
102+ In [1]: 1
103+ Out[1]: 1
104+
105+ In [2]: print 1
106+ 1
107+ """
108+
109+
110+def doctest_run_builtins():
111+ """Check that %run doesn't damage __builtins__ via a doctest.
112+
113+ This is similar to the test_run_builtins, but I want *both* forms of the
114+ test to catch any possible glitches in our testing machinery, since that
115+ modifies %run somewhat. So for this, we have both a normal test (below)
116+ and a doctest (this one).
117+
118+ In [1]: import tempfile
119+
120+ In [3]: f = tempfile.NamedTemporaryFile()
121+
122+ In [4]: f.write('pass\\n')
123+
124+ In [5]: f.flush()
125+
126+ In [7]: %run $f.name
127+ """
128+
129+def doctest_multiline1():
130+ """The ipdoctest machinery must handle multiline examples gracefully.
131+
132+ In [2]: for i in range(10):
133+ ...: print i,
134+ ...:
135+ 0 1 2 3 4 5 6 7 8 9
136+ """
137+
138+
139+def doctest_multiline2():
140+ """Multiline examples that define functions and print output.
141+
142+ In [7]: def f(x):
143+ ...: return x+1
144+ ...:
145+
146+ In [8]: f(1)
147+ Out[8]: 2
148+
149+ In [9]: def g(x):
150+ ...: print 'x is:',x
151+ ...:
152+
153+ In [10]: g(1)
154+ x is: 1
155+
156+ In [11]: g('hello')
157+ x is: hello
158+ """
159+
160+
161+def doctest_multiline3():
162+ """Multiline examples with blank lines.
163+
164+ In [12]: def h(x):
165+ ....: if x>1:
166+ ....: return x**2
167+ ....: # To leave a blank line in the input, you must mark it
168+ ....: # with a comment character:
169+ ....: #
170+ ....: # otherwise the doctest parser gets confused.
171+ ....: else:
172+ ....: return -1
173+ ....:
174+
175+ In [13]: h(5)
176+ Out[13]: 25
177+
178+ In [14]: h(1)
179+ Out[14]: -1
180+
181+ In [15]: h(0)
182+ Out[15]: -1
183+ """
184
185=== added file 'IPython/testing/tools.py'
186--- IPython/testing/tools.py 1970-01-01 00:00:00 +0000
187+++ IPython/testing/tools.py 2009-04-21 23:57:57 +0000
188@@ -0,0 +1,88 @@
189+"""Generic testing tools that do NOT depend on Twisted.
190+
191+In particular, this module exposes a set of top-level assert* functions that
192+can be used in place of nose.tools.assert* in method generators (the ones in
193+nose can not, at least as of nose 0.10.4).
194+
195+Note: our testing package contains testing.util, which does depend on Twisted
196+and provides utilities for tests that manage Deferreds. All testing support
197+tools that only depend on nose, IPython or the standard library should go here
198+instead.
199+
200+
201+Authors
202+-------
203+- Fernando Perez <Fernando.Perez@berkeley.edu>
204+"""
205+
206+#*****************************************************************************
207+# Copyright (C) 2009 The IPython Development Team
208+#
209+# Distributed under the terms of the BSD License. The full license is in
210+# the file COPYING, distributed as part of this software.
211+#*****************************************************************************
212+
213+#-----------------------------------------------------------------------------
214+# Required modules and packages
215+#-----------------------------------------------------------------------------
216+
217+# Standard Python lib
218+import os
219+import sys
220+
221+# Third-party
222+import nose.tools as nt
223+
224+# From this project
225+from IPython.tools import utils
226+
227+#-----------------------------------------------------------------------------
228+# Globals
229+#-----------------------------------------------------------------------------
230+
231+# Make a bunch of nose.tools assert wrappers that can be used in test
232+# generators. This will expose an assert* function for each one in nose.tools.
233+
234+_tpl = """
235+def %(name)s(*a,**kw):
236+ return nt.%(name)s(*a,**kw)
237+"""
238+
239+for _x in [a for a in dir(nt) if a.startswith('assert')]:
240+ exec _tpl % dict(name=_x)
241+
242+#-----------------------------------------------------------------------------
243+# Functions and classes
244+#-----------------------------------------------------------------------------
245+
246+def full_path(startPath,files):
247+ """Make full paths for all the listed files, based on startPath.
248+
249+ Only the base part of startPath is kept, since this routine is typically
250+ used with a script's __file__ variable as startPath. The base of startPath
251+ is then prepended to all the listed files, forming the output list.
252+
253+ :Parameters:
254+ startPath : string
255+ Initial path to use as the base for the results. This path is split
256+ using os.path.split() and only its first component is kept.
257+
258+ files : string or list
259+ One or more files.
260+
261+ :Examples:
262+
263+ >>> full_path('/foo/bar.py',['a.txt','b.txt'])
264+ ['/foo/a.txt', '/foo/b.txt']
265+
266+ >>> full_path('/foo',['a.txt','b.txt'])
267+ ['/a.txt', '/b.txt']
268+
269+ If a single file is given, the output is still a list:
270+ >>> full_path('/foo','a.txt')
271+ ['/a.txt']
272+ """
273+
274+ files = utils.list_strings(files)
275+ base = os.path.split(startPath)[0]
276+ return [ os.path.join(base,f) for f in files ]
277
278=== modified file 'IPython/tests/test_magic.py'
279--- IPython/tests/test_magic.py 2009-04-07 07:59:15 +0000
280+++ IPython/tests/test_magic.py 2009-04-21 23:57:57 +0000
281@@ -6,12 +6,15 @@
282 # Standard library imports
283 import os
284 import sys
285+import tempfile
286+import types
287
288 # Third-party imports
289 import nose.tools as nt
290
291 # From our own code
292 from IPython.testing import decorators as dec
293+from IPython.testing import tools as tt
294
295 #-----------------------------------------------------------------------------
296 # Test functions begin
297@@ -34,26 +37,6 @@
298 assert len(scoms) > 10
299
300
301-def doctest_run_ns():
302- """Classes declared %run scripts must be instantiable afterwards.
303-
304- In [11]: run tclass foo
305-
306- In [12]: isinstance(f(),foo)
307- Out[12]: True
308- """
309-
310-
311-def doctest_run_ns2():
312- """Classes declared %run scripts must be instantiable afterwards.
313-
314- In [4]: run tclass C-first_pass
315-
316- In [5]: run tclass C-second_pass
317- tclass.py: deleting object: C-first_pass
318- """
319-
320-
321 def doctest_hist_f():
322 """Test %hist -f with temporary filename.
323
324@@ -149,3 +132,104 @@
325 lowercased: hello
326 lowercased: hello
327 """
328+
329+#-----------------------------------------------------------------------------
330+# Tests for %run
331+#-----------------------------------------------------------------------------
332+
333+# %run is critical enough that it's a good idea to have a solid collection of
334+# tests for it, some as doctests and some as normal tests.
335+
336+def doctest_run_ns():
337+ """Classes declared %run scripts must be instantiable afterwards.
338+
339+ In [11]: run tclass foo
340+
341+ In [12]: isinstance(f(),foo)
342+ Out[12]: True
343+ """
344+
345+
346+def doctest_run_ns2():
347+ """Classes declared %run scripts must be instantiable afterwards.
348+
349+ In [4]: run tclass C-first_pass
350+
351+ In [5]: run tclass C-second_pass
352+ tclass.py: deleting object: C-first_pass
353+ """
354+
355+def doctest_run_builtins():
356+ """Check that %run doesn't damage __builtins__ via a doctest.
357+
358+ This is similar to the test_run_builtins, but I want *both* forms of the
359+ test to catch any possible glitches in our testing machinery, since that
360+ modifies %run somewhat. So for this, we have both a normal test (below)
361+ and a doctest (this one).
362+
363+ In [1]: import tempfile
364+
365+ In [2]: bid1 = id(__builtins__)
366+
367+ In [3]: f = tempfile.NamedTemporaryFile()
368+
369+ In [4]: f.write('pass\\n')
370+
371+ In [5]: f.flush()
372+
373+ In [6]: print 'B1:',type(__builtins__)
374+ B1: <type 'module'>
375+
376+ In [7]: %run $f.name
377+
378+ In [8]: bid2 = id(__builtins__)
379+
380+ In [9]: print 'B2:',type(__builtins__)
381+ B2: <type 'module'>
382+
383+ In [10]: bid1 == bid2
384+ Out[10]: True
385+ """
386+
387+# For some tests, it will be handy to organize them in a class with a common
388+# setup that makes a temp file
389+
390+class TestMagicRun(object):
391+
392+ def setup(self):
393+ """Make a valid python temp file."""
394+ f = tempfile.NamedTemporaryFile()
395+ f.write('pass\n')
396+ f.flush()
397+ self.tmpfile = f
398+
399+ def run_tmpfile(self):
400+ _ip.magic('run %s' % self.tmpfile.name)
401+
402+ def test_builtins_id(self):
403+ """Check that %run doesn't damage __builtins__ """
404+
405+ # Test that the id of __builtins__ is not modified by %run
406+ bid1 = id(_ip.user_ns['__builtins__'])
407+ self.run_tmpfile()
408+ bid2 = id(_ip.user_ns['__builtins__'])
409+ tt.assert_equals(bid1, bid2)
410+
411+ def test_builtins_type(self):
412+ """Check that the type of __builtins__ doesn't change with %run.
413+
414+ However, the above could pass if __builtins__ was already modified to
415+ be a dict (it should be a module) by a previous use of %run. So we
416+ also check explicitly that it really is a module:
417+ """
418+ self.run_tmpfile()
419+ tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
420+
421+ def test_prompts(self):
422+ """Test that prompts correctly generate after %run"""
423+ self.run_tmpfile()
424+ p2 = str(_ip.IP.outputcache.prompt2).strip()
425+ nt.assert_equals(p2[:3], '...')
426+
427+ def teardown(self):
428+ self.tmpfile.close()

Subscribers

People subscribed via source and target branches

to all changes: